@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/rates.md ADDED
@@ -0,0 +1,145 @@
1
+ # Rates in Shipstack
2
+
3
+ Shipstack provides a unified interface for fetching shipping rates from USPS, FedEx, and UPS.
4
+ All rate responses are normalized into a consistent structure, regardless of carrier-specific formats.
5
+
6
+ ---
7
+
8
+ ## Rate Request Structure
9
+
10
+ ```ts
11
+ type RateRequest = {
12
+ carrier: "usps" | "fedex" | "ups";
13
+ originZip: string;
14
+ destZip: string;
15
+ weightOz: number;
16
+ lengthInches: number;
17
+ widthInches: number;
18
+ heightInches: number;
19
+ destCountryCode?: string;
20
+ };
21
+ ```
22
+
23
+ Carrier-specific optional fields such as `mailClass`, `pickupType`, `packagingType`, and `rateRequestType` are also supported when needed.
24
+
25
+ ---
26
+
27
+ ## Fetching Rates
28
+
29
+ ### Using `ShippingClient`
30
+
31
+ ```ts
32
+ const rates = await client.getRates({
33
+ carrier: "usps",
34
+ originZip: "90210",
35
+ destZip: "10001",
36
+ weightOz: 16,
37
+ lengthInches: 10,
38
+ widthInches: 5,
39
+ heightInches: 5
40
+ });
41
+ ```
42
+
43
+ ### Using the Functional API
44
+
45
+ ```ts
46
+ import { getRates } from "@teralabs/shipstack";
47
+
48
+ const rates = await getRates(request, config);
49
+ ```
50
+
51
+ Both methods return the same normalized structure.
52
+
53
+ ---
54
+
55
+ ## Ranking Across Carriers
56
+
57
+ Use `ShippingManager` when you want Shipstack to fetch from multiple carriers and rank the results for you.
58
+
59
+ ```ts
60
+ const ranked = await manager.getRankedRates(
61
+ {
62
+ originZip: "90210",
63
+ destZip: "10001",
64
+ weightOz: 16,
65
+ lengthInches: 10,
66
+ widthInches: 5,
67
+ heightInches: 5
68
+ },
69
+ ["usps", "fedex", "ups"]
70
+ );
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Normalized Rate Structure
76
+
77
+ ```ts
78
+ type NormalizedRate = {
79
+ carrier: "usps" | "fedex" | "ups";
80
+ serviceCode: string;
81
+ serviceName: string;
82
+ deliveryDays?: number;
83
+ estimatedArrival?: string;
84
+ isFastest?: boolean;
85
+ isCheapest?: boolean;
86
+ guaranteed?: boolean;
87
+ cost: {
88
+ amount: number;
89
+ currency: string;
90
+ };
91
+ raw?: unknown;
92
+ };
93
+ ```
94
+
95
+ ### Field Notes
96
+
97
+ - `deliveryDays` is optional when a carrier does not provide an estimate.
98
+ - `estimatedArrival` is typically an ISO-8601 string when available.
99
+ - `isCheapest` and `isFastest` are added by `ShippingManager#getRankedRates`.
100
+ - `cost.amount` is always numeric.
101
+
102
+ ---
103
+
104
+ ## Best Value and Fastest Helpers
105
+
106
+ These helpers work on a single-carrier `RateRequest`.
107
+
108
+ ```ts
109
+ import { getBestValueRate, getFastestService } from "@teralabs/shipstack";
110
+
111
+ const cheapest = await getBestValueRate(request, config);
112
+ const fastest = await getFastestService(request, config);
113
+ ```
114
+
115
+ Both helpers return `null` when no valid rates are available.
116
+
117
+ ---
118
+
119
+ ## Carrier-Specific Notes
120
+
121
+ ### USPS
122
+ - Delivery estimates may be missing for some services.
123
+ - USPS-specific flags such as `mailClass` and `nonStandard` are supported.
124
+
125
+ ### FedEx
126
+ - Requires `accountNumber` in config.
127
+ - Supports FedEx-specific options such as `pickupType`, `dropoffType`, and `rateRequestType`.
128
+
129
+ ### UPS
130
+ - Requires `accountNumber` in config.
131
+ - Dimensions are especially important for many UPS services.
132
+
133
+ ---
134
+
135
+ ## Summary
136
+
137
+ Shipstack's rate system provides:
138
+
139
+ - A unified request format
140
+ - A normalized response structure
141
+ - Single-carrier helper methods
142
+ - Multi-carrier ranking through `ShippingManager`
143
+ - Full support for USPS, FedEx, and UPS
144
+
145
+ ---
@@ -0,0 +1,157 @@
1
+ # Shipments in Shipstack
2
+
3
+ Shipstack supports two distinct shipment workflows:
4
+
5
+ 1. **Staged Shipments (Safe Mode)**
6
+ 2. **Actual Shipments (Advanced Mode)**
7
+
8
+ Understanding the difference is essential for building secure and flexible shipping systems.
9
+
10
+ ---
11
+
12
+ ## 1. Staged Shipments (Safe Mode)
13
+
14
+ Staged shipments generate a **carrier-specific shipment payload** without calling the carrier API or purchasing a label.
15
+
16
+ This mode is ideal for:
17
+
18
+ - Storefronts
19
+ - Serverless and edge environments
20
+ - Email-based label workflows
21
+ - Custom dashboards
22
+ - Platforms where merchants finalize labels later
23
+ - Any environment where you cannot expose carrier credentials
24
+
25
+ ### Example
26
+
27
+ ```ts
28
+ import { buildShipment } from "@teralabs/shipstack";
29
+
30
+ const staged = await buildShipment(
31
+ {
32
+ carrier: "fedex",
33
+ serviceCode: "FEDEX_GROUND",
34
+ fromAddress: {
35
+ name: "Sender Name",
36
+ streetLines: ["123 Warehouse Rd"],
37
+ city: "Los Angeles",
38
+ stateOrProvinceCode: "CA",
39
+ postalCode: "90001",
40
+ countryCode: "US"
41
+ },
42
+ toAddress: {
43
+ name: "Customer Name",
44
+ streetLines: ["55 W 46th St"],
45
+ city: "New York",
46
+ stateOrProvinceCode: "NY",
47
+ postalCode: "10036",
48
+ countryCode: "US"
49
+ },
50
+ package: {
51
+ weightOz: 32,
52
+ lengthInches: 12,
53
+ widthInches: 8,
54
+ heightInches: 4
55
+ }
56
+ },
57
+ config
58
+ );
59
+
60
+ console.log(staged.payload);
61
+ ```
62
+
63
+ ### What Staged Shipments Provide
64
+
65
+ ```ts
66
+ type StagedShipment = {
67
+ carrier: "usps" | "fedex" | "ups";
68
+ serviceCode: string;
69
+ payload: unknown;
70
+ };
71
+ ```
72
+
73
+ ### What Staged Shipments Do Not Do
74
+
75
+ - Do not call USPS, FedEx, or UPS
76
+ - Do not purchase postage
77
+ - Do not generate a label
78
+ - Do not require backend security
79
+
80
+ This makes staged shipments safe for any environment, including the browser.
81
+
82
+ ---
83
+
84
+ ## 2. Actual Shipments (Advanced Mode)
85
+
86
+ Actual shipments call the carrier API and purchase a real label.
87
+ This workflow must only be used in secure backend environments.
88
+
89
+ ### Example
90
+
91
+ ```ts
92
+ import { createShipment } from "@teralabs/shipstack";
93
+
94
+ const shipment = await createShipment(request, config);
95
+
96
+ console.log(shipment.label.base64);
97
+ console.log(shipment.trackingNumber);
98
+ ```
99
+
100
+ ### What Actual Shipments Provide
101
+
102
+ ```ts
103
+ type NormalizedShipment = {
104
+ carrier: "usps" | "fedex" | "ups";
105
+ trackingNumber: string;
106
+ serviceCode: string;
107
+ serviceName: string;
108
+ label: {
109
+ format: "PDF" | "PNG" | "ZPL" | "GIF";
110
+ base64: string;
111
+ };
112
+ charges?: {
113
+ amount: number;
114
+ currency: string;
115
+ };
116
+ raw?: unknown;
117
+ };
118
+ ```
119
+
120
+ ### What Actual Shipments Require
121
+
122
+ - Secure backend environment
123
+ - Valid carrier credentials
124
+ - Merchant authorization to purchase labels
125
+
126
+ ---
127
+
128
+ ## Safe vs Advanced Comparison
129
+
130
+ | Feature | Staged (`buildShipment`) | Actual (`createShipment`) |
131
+ |--------|---------------------------|---------------------------|
132
+ | Generates carrier payload | Yes | Yes |
133
+ | Purchases a label | No | Yes |
134
+ | Safe for frontend | Yes | No |
135
+ | Requires backend | No | Yes |
136
+ | Calls carrier API | No | Yes |
137
+ | Good for email workflows | Yes | Limited |
138
+ | Good for automation | Limited | Yes |
139
+
140
+ ---
141
+
142
+ ## When to Use Each Mode
143
+
144
+ ### Use **Staged Shipments** when:
145
+ - You want to preview or store the carrier payload
146
+ - You want to send the payload to a merchant for approval
147
+ - You are running in a serverless or edge environment
148
+ - You want to avoid accidental label purchases
149
+ - You are building a storefront or checkout flow
150
+
151
+ ### Use **Actual Shipments** when:
152
+ - You are ready to purchase a label
153
+ - You are running in a secure backend
154
+ - You need a real tracking number and label file
155
+ - You are automating fulfillment
156
+
157
+ ---
@@ -0,0 +1,115 @@
1
+ # Tracking in Shipstack
2
+
3
+ Shipstack provides a unified tracking interface for USPS, FedEx, and UPS.
4
+ All tracking responses are normalized into a consistent structure, regardless of carrier-specific formats.
5
+
6
+ ---
7
+
8
+ ## Tracking Request
9
+
10
+ ```ts
11
+ trackShipment(
12
+ trackingNumbers: string | string[],
13
+ carrier: "usps" | "fedex" | "ups",
14
+ config: ShippingConfig
15
+ )
16
+ ```
17
+
18
+ You may track one or many tracking numbers at once.
19
+
20
+ ---
21
+
22
+ ## Functional Example
23
+
24
+ ```ts
25
+ import { trackShipment } from "@teralabs/shipstack";
26
+
27
+ const tracking = await trackShipment(
28
+ ["9400100000000000000000"],
29
+ "usps",
30
+ config
31
+ );
32
+
33
+ console.log(tracking[0]?.status);
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Using `ShippingClient`
39
+
40
+ ```ts
41
+ const tracking = await client.track(["1Z9999999999999999"], "ups");
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Normalized Tracking Structure
47
+
48
+ ```ts
49
+ type NormalizedTracking = {
50
+ carrier: "usps" | "fedex" | "ups";
51
+ trackingNumber: string;
52
+ status: string;
53
+ estimatedDelivery?: string;
54
+ events: Array<{
55
+ description: string;
56
+ dateTime: string;
57
+ city?: string;
58
+ stateOrProvinceCode?: string;
59
+ postalCode?: string;
60
+ countryCode?: string;
61
+ }>;
62
+ raw?: unknown;
63
+ };
64
+ ```
65
+
66
+ ### Field Notes
67
+
68
+ - `status` is a normalized high-level tracking state such as `IN_TRANSIT` or `DELIVERED`.
69
+ - `estimatedDelivery` is optional when the carrier provides an ETA.
70
+ - `events` contain the normalized event history returned by the carrier.
71
+
72
+ ---
73
+
74
+ ## Carrier Batch Limits
75
+
76
+ Shipstack automatically handles batching and concurrency rules:
77
+
78
+ | Carrier | Limit | Behavior |
79
+ |--------|--------|----------|
80
+ | USPS | 35 per request | Automatically chunks requests |
81
+ | FedEx | 30 per request | Automatically chunks requests |
82
+ | UPS | Single-inquiry only | Concurrency-limited to avoid lockouts |
83
+
84
+ You do not need to manually chunk tracking numbers. Shipstack handles it for you.
85
+
86
+ ---
87
+
88
+ ## Notes on Carrier Behavior
89
+
90
+ ### USPS
91
+ - Bulk tracking supports up to 35 items.
92
+ - Some events may not include a full timestamp.
93
+
94
+ ### FedEx
95
+ - Provides detailed event history.
96
+ - Supports up to 30 items per request.
97
+
98
+ ### UPS
99
+ - Only supports single-inquiry tracking.
100
+ - Too many rapid requests may trigger rate limiting.
101
+ - Shipstack automatically throttles UPS tracking calls.
102
+
103
+ ---
104
+
105
+ ## Summary
106
+
107
+ Shipstack's tracking system provides:
108
+
109
+ - A unified request format
110
+ - A normalized response structure
111
+ - Automatic batching and concurrency control
112
+ - Full support for USPS, FedEx, and UPS
113
+ - Compatibility with both stateful and functional APIs
114
+
115
+ ---
package/package.json ADDED
@@ -0,0 +1,98 @@
1
+ {
2
+ "name": "@teralabs/shipstack",
3
+ "version": "1.2.0",
4
+ "description": "Framework-agnostic, type-safe shipping SDK for USPS, FedEx, and UPS with unified orchestration and edge-ready output.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "module": "dist/index.mjs",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/Thecodergabe/shipstack.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/Thecodergabe/shipstack/issues"
14
+ },
15
+ "homepage": "https://github.com/Thecodergabe/shipstack#readme",
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.mjs",
23
+ "require": "./dist/index.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "LICENSE",
29
+ "README.md",
30
+ "docs"
31
+ ],
32
+ "keywords": [
33
+ "shipping",
34
+ "usps",
35
+ "fedex",
36
+ "ups",
37
+ "logistics",
38
+ "api",
39
+ "openapi",
40
+ "typescript",
41
+ "framework-agnostic",
42
+ "serverless"
43
+ ],
44
+ "author": "Gabriel Rodriguez",
45
+ "license": "ISC",
46
+ "packageManager": "npm@10.0.0",
47
+ "funding": {
48
+ "type": "github",
49
+ "url": "https://github.com/sponsors/Thecodergabe"
50
+ },
51
+ "sideEffects": false,
52
+ "engines": {
53
+ "node": ">=16.0.0"
54
+ },
55
+ "scripts": {
56
+ "/* --- Build and Lifecycle --- */": "",
57
+ "prepare": "npm run build",
58
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean",
59
+ "test": "vitest run",
60
+ "example:server": "npm run build && node examples/shipstack-ui/server.mjs",
61
+ "example:ui": "npm --prefix examples/shipstack-ui run dev",
62
+ "prepublishOnly": "npm run test && npm run build",
63
+ "/* --- Core USPS Generation --- */": "",
64
+ "generate:usps:auth": "openapi --input https://developers.usps.com/sites/default/files/apidoc_specs/oauth2_3_update.yaml --output src/usps/auth/generated --client fetch --name UspsAuthSdk",
65
+ "generate:usps:rates": "openapi --input https://developers.usps.com/sites/default/files/apidoc_specs/domestic-prices_10.yaml --output src/usps/rates/generated --client fetch --name UspsRatesSdk",
66
+ "generate:usps:address": "openapi --input https://developers.usps.com/sites/default/files/apidoc_specs/addresses-v3r2_2.yaml --output src/usps/address/generated --client fetch --name UspsAddressSdk",
67
+ "generate:usps:labels": "openapi --input https://developers.usps.com/sites/default/files/apidoc_specs/labels_update_1.yaml --output src/usps/labels/generated --client fetch --name UspsLabelsSdk",
68
+ "generate:usps:tracking": "openapi --input https://developers.usps.com/sites/default/files/apidoc_specs/tracking-v3r2_4.yaml --output src/usps/tracking/generated --client fetch --name UspsTrackingSdk",
69
+ "generate:usps:intl-rates": "openapi --input https://developers.usps.com/sites/default/files/apidoc_specs/international-prices_4.yaml --output src/usps/international/rates/generated --client fetch --name UspsIntlRatesSdk",
70
+ "generate:usps:intl-labels": "openapi --input https://developers.usps.com/sites/default/files/apidoc_specs/international-labels_8.yaml --output src/usps/international/labels/generated --client fetch --name UspsIntlLabelsSdk",
71
+ "generate:usps": "npm run generate:usps:auth && npm run generate:usps:rates && npm run generate:usps:address && npm run generate:usps:labels && npm run generate:usps:tracking && npm run generate:usps:intl-rates && npm run generate:usps:intl-labels",
72
+ "/* --- Core FedEx Generation --- */": "",
73
+ "generate:fedex:address": "openapi --input specs/fedex/address-validation.json --output src/fedex/address/generated --client fetch --name FedexAddressSdk",
74
+ "generate:fedex:rates": "openapi --input specs/fedex/rate.json --output src/fedex/rates/generated --client fetch --name FedexRatesSdk",
75
+ "generate:fedex:ship": "openapi --input specs/fedex/ship.json --output src/fedex/ship/generated --client fetch --name FedexShipSdk",
76
+ "generate:fedex:tracking": "openapi --input specs/fedex/track.json --output src/fedex/tracking/generated --client fetch --name FedexTrackingSdk",
77
+ "generate:fedex": "npm run generate:fedex:address && npm run generate:fedex:rates && npm run generate:fedex:ship && npm run generate:fedex:tracking",
78
+ "/* --- Core UPS Generation --- */": "",
79
+ "generate:ups:auth": "openapi --input specs/ups/OAuthClientCredentials-Ready.yaml --output src/ups/auth/generated --client fetch --name UpsAuthSdk",
80
+ "generate:ups:rates": "openapi --input specs/ups/Rating.yaml --output src/ups/rates/generated --client fetch --name UpsRatesSdk",
81
+ "generate:ups:ship": "openapi --input specs/ups/Shipping.yaml --output src/ups/ship/generated --client fetch --name UpsShipSdk",
82
+ "generate:ups:tracking": "openapi --input specs/ups/Tracking-Ready.yaml --output src/ups/tracking/generated --client fetch --name UpsTrackingSdk",
83
+ "generate:ups": "npm run generate:ups:auth && npm run generate:ups:rates && npm run generate:ups:ship && npm run generate:ups:tracking",
84
+ "/* --- Master Command --- */": "",
85
+ "generate:all": "npm run generate:usps && npm run generate:fedex && npm run generate:ups"
86
+ },
87
+ "devDependencies": {
88
+ "@types/node": "^20.0.0",
89
+ "openapi-typescript-codegen": "^0.29.0",
90
+ "tsup": "^8.5.0",
91
+ "typescript": "^5.9.3",
92
+ "vite-tsconfig-paths": "^6.1.1",
93
+ "vitest": "^4.0.3"
94
+ },
95
+ "dependencies": {
96
+ "cross-fetch": "^4.1.0"
97
+ }
98
+ }