@usethrottle/cart 0.1.0 → 1.0.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/README.md +63 -0
- package/dist/index.cjs +61 -1
- package/dist/index.d.cts +158 -2
- package/dist/index.d.ts +158 -2
- package/dist/index.js +60 -1
- package/package.json +11 -8
package/README.md
CHANGED
|
@@ -50,6 +50,69 @@ console.log(order.id, order.status);
|
|
|
50
50
|
|
|
51
51
|
All monetary values are integers in the smallest currency unit (cents for USD).
|
|
52
52
|
|
|
53
|
+
## Shipping and tax
|
|
54
|
+
|
|
55
|
+
Use the secret-key client on your backend to calculate and persist cart totals
|
|
56
|
+
with Throttle's native engine:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
const estimate = await client.shippingTax.calculateCart(cart.id, {
|
|
60
|
+
kind: 'cart_estimate',
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const locked = await client.shippingTax.calculateCart(cart.id, {
|
|
64
|
+
kind: 'checkout_final',
|
|
65
|
+
selectedShippingMethodId: estimate.shipping.selectedMethod?.id,
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
External stores can also request read-only storefront quotes with a publishable
|
|
70
|
+
`pk_` quote token. Create one from the dashboard Shipping & Tax page or with
|
|
71
|
+
`POST /api/v1/shipping-tax/quote-tokens`.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { StorefrontQuoteClient } from '@usethrottle/cart';
|
|
75
|
+
|
|
76
|
+
const quotes = new StorefrontQuoteClient({
|
|
77
|
+
storeId: 'store_uuid',
|
|
78
|
+
quoteToken: 'pk_publishable_quote_token',
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const estimate = await quotes.quote({
|
|
82
|
+
currency: 'USD',
|
|
83
|
+
items: [
|
|
84
|
+
{
|
|
85
|
+
id: 'sku_123',
|
|
86
|
+
quantity: 1,
|
|
87
|
+
subtotalAmount: 12900,
|
|
88
|
+
requiresShipping: true,
|
|
89
|
+
taxCategory: 'standard',
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
addresses: {
|
|
93
|
+
shipping: { countryCode: 'US', stateProvince: 'CA', postalCode: '90001' },
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
For provider-owned totals, set the store to `provider_totals` mode and push the
|
|
99
|
+
final snapshot from your backend:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
await client.shippingTax.pushExternalSnapshot({
|
|
103
|
+
storeId: 'store_uuid',
|
|
104
|
+
cartId: cart.id,
|
|
105
|
+
currency: 'USD',
|
|
106
|
+
totals: {
|
|
107
|
+
subtotal: 12900,
|
|
108
|
+
discountTotal: 0,
|
|
109
|
+
shippingTotal: 600,
|
|
110
|
+
taxTotal: 1215,
|
|
111
|
+
total: 14715,
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
53
116
|
## Subscriptions
|
|
54
117
|
|
|
55
118
|
Pass `type: 'subscription'` and a `recurring` block on the line item:
|
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
CartClient: () => CartClient,
|
|
24
|
+
StorefrontQuoteClient: () => StorefrontQuoteClient,
|
|
24
25
|
ThrottleApiError: () => ThrottleApiError
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -48,7 +49,7 @@ var CartClient = class {
|
|
|
48
49
|
constructor(opts) {
|
|
49
50
|
if (!opts.apiKey) throw new Error("CartClient: apiKey is required");
|
|
50
51
|
this.apiKey = opts.apiKey;
|
|
51
|
-
this.baseUrl = (opts.baseUrl ?? "https://
|
|
52
|
+
this.baseUrl = (opts.baseUrl ?? "https://api.usethrottle.dev").replace(/\/+$/, "");
|
|
52
53
|
this.fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
53
54
|
this.timeoutMs = opts.timeoutMs ?? 3e4;
|
|
54
55
|
}
|
|
@@ -111,6 +112,12 @@ var CartClient = class {
|
|
|
111
112
|
clear: (cartId) => this.request("DELETE", `/api/v1/carts/${cartId}/tax-lines`)
|
|
112
113
|
};
|
|
113
114
|
}
|
|
115
|
+
get shippingTax() {
|
|
116
|
+
return {
|
|
117
|
+
calculateCart: (cartId, input = {}) => this.request("POST", `/api/v1/shipping-tax/carts/${cartId}/calculate`, input),
|
|
118
|
+
pushExternalSnapshot: (input) => this.request("POST", "/api/v1/shipping-tax/external-snapshots", input)
|
|
119
|
+
};
|
|
120
|
+
}
|
|
114
121
|
get events() {
|
|
115
122
|
return {
|
|
116
123
|
list: (cartId, opts) => {
|
|
@@ -123,8 +130,61 @@ var CartClient = class {
|
|
|
123
130
|
};
|
|
124
131
|
}
|
|
125
132
|
};
|
|
133
|
+
|
|
134
|
+
// src/storefront.ts
|
|
135
|
+
var StorefrontQuoteClient = class {
|
|
136
|
+
storeId;
|
|
137
|
+
quoteToken;
|
|
138
|
+
baseUrl;
|
|
139
|
+
fetchImpl;
|
|
140
|
+
timeoutMs;
|
|
141
|
+
origin;
|
|
142
|
+
constructor(opts) {
|
|
143
|
+
if (!opts.storeId) throw new Error("StorefrontQuoteClient: storeId is required");
|
|
144
|
+
if (!opts.quoteToken?.startsWith("pk_")) {
|
|
145
|
+
throw new Error("StorefrontQuoteClient: quoteToken must be a publishable pk_ token");
|
|
146
|
+
}
|
|
147
|
+
this.storeId = opts.storeId;
|
|
148
|
+
this.quoteToken = opts.quoteToken;
|
|
149
|
+
this.baseUrl = (opts.baseUrl ?? "https://api.usethrottle.dev").replace(/\/+$/, "");
|
|
150
|
+
this.fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
151
|
+
this.timeoutMs = opts.timeoutMs ?? 15e3;
|
|
152
|
+
this.origin = opts.origin;
|
|
153
|
+
}
|
|
154
|
+
async quote(input) {
|
|
155
|
+
const ctrl = new AbortController();
|
|
156
|
+
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
157
|
+
try {
|
|
158
|
+
const headers = { "content-type": "application/json" };
|
|
159
|
+
if (this.origin) headers.origin = this.origin;
|
|
160
|
+
const res = await this.fetchImpl(`${this.baseUrl}/api/v1/shipping-tax/quotes`, {
|
|
161
|
+
method: "POST",
|
|
162
|
+
headers,
|
|
163
|
+
body: JSON.stringify({
|
|
164
|
+
storeId: this.storeId,
|
|
165
|
+
quoteToken: this.quoteToken,
|
|
166
|
+
...input
|
|
167
|
+
}),
|
|
168
|
+
signal: ctrl.signal
|
|
169
|
+
});
|
|
170
|
+
const json = await res.json().catch(() => ({}));
|
|
171
|
+
if (!res.ok) {
|
|
172
|
+
throw new ThrottleApiError({
|
|
173
|
+
code: json?.error?.code ?? "unknown_error",
|
|
174
|
+
message: json?.error?.message ?? `HTTP ${res.status}`,
|
|
175
|
+
statusCode: res.status,
|
|
176
|
+
details: json?.error?.details
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return json.data;
|
|
180
|
+
} finally {
|
|
181
|
+
clearTimeout(timer);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
126
185
|
// Annotate the CommonJS export names for ESM import in node:
|
|
127
186
|
0 && (module.exports = {
|
|
128
187
|
CartClient,
|
|
188
|
+
StorefrontQuoteClient,
|
|
129
189
|
ThrottleApiError
|
|
130
190
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -58,7 +58,7 @@ interface SelectedShipping {
|
|
|
58
58
|
}
|
|
59
59
|
interface AppliedDiscount {
|
|
60
60
|
code: string;
|
|
61
|
-
type: 'percentage' | 'fixed_amount' | 'free_shipping';
|
|
61
|
+
type: 'percentage' | 'fixed_amount' | 'free_shipping' | 'buy_x_get_y';
|
|
62
62
|
amount: number;
|
|
63
63
|
currency: string;
|
|
64
64
|
freeShipping: boolean;
|
|
@@ -153,6 +153,139 @@ interface Order {
|
|
|
153
153
|
total: number;
|
|
154
154
|
currency: string;
|
|
155
155
|
}
|
|
156
|
+
interface ShippingTaxAddress {
|
|
157
|
+
countryCode?: string;
|
|
158
|
+
stateProvince?: string;
|
|
159
|
+
postalCode?: string;
|
|
160
|
+
city?: string;
|
|
161
|
+
addressLine1?: string;
|
|
162
|
+
addressLine2?: string;
|
|
163
|
+
[key: string]: unknown;
|
|
164
|
+
}
|
|
165
|
+
interface ShippingTaxQuoteItem {
|
|
166
|
+
id: string;
|
|
167
|
+
quantity?: number;
|
|
168
|
+
unitPrice?: number;
|
|
169
|
+
subtotalAmount: number;
|
|
170
|
+
discountAmount?: number;
|
|
171
|
+
productType?: string;
|
|
172
|
+
requiresShipping?: boolean;
|
|
173
|
+
shippingProfileIds?: string[];
|
|
174
|
+
taxCategory?: string;
|
|
175
|
+
weight?: number;
|
|
176
|
+
weightUnit?: 'lb' | 'kg';
|
|
177
|
+
metadata?: Record<string, unknown>;
|
|
178
|
+
[key: string]: unknown;
|
|
179
|
+
}
|
|
180
|
+
interface ShippingTaxQuoteInput {
|
|
181
|
+
currency?: string;
|
|
182
|
+
items: ShippingTaxQuoteItem[];
|
|
183
|
+
discountsTotal?: number;
|
|
184
|
+
selectedShippingMethodId?: string | null;
|
|
185
|
+
addresses?: {
|
|
186
|
+
shipping?: ShippingTaxAddress | null;
|
|
187
|
+
billing?: ShippingTaxAddress | null;
|
|
188
|
+
customer?: ShippingTaxAddress | null;
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
interface ShippingTaxCartCalculateInput {
|
|
192
|
+
kind?: 'cart_estimate' | 'checkout_final';
|
|
193
|
+
selectedShippingMethodId?: string | null;
|
|
194
|
+
persist?: boolean;
|
|
195
|
+
}
|
|
196
|
+
interface ShippingTaxMethod {
|
|
197
|
+
id: string;
|
|
198
|
+
type?: 'ship' | 'pickup';
|
|
199
|
+
label: string;
|
|
200
|
+
amount: number;
|
|
201
|
+
currency: string;
|
|
202
|
+
deliveryWindowLabel?: string | null;
|
|
203
|
+
deliveryMinDays?: number | null;
|
|
204
|
+
deliveryMaxDays?: number | null;
|
|
205
|
+
warnings?: Array<{
|
|
206
|
+
code: string;
|
|
207
|
+
message: string;
|
|
208
|
+
field?: string;
|
|
209
|
+
}>;
|
|
210
|
+
}
|
|
211
|
+
interface ShippingTaxCalculationResponse {
|
|
212
|
+
snapshotId?: string;
|
|
213
|
+
kind: 'quote' | 'cart_estimate' | 'checkout_final' | 'external_snapshot';
|
|
214
|
+
status: 'estimated' | 'final' | 'failed';
|
|
215
|
+
source: 'throttle_engine' | 'provider_totals' | 'external_push';
|
|
216
|
+
storeId: string;
|
|
217
|
+
merchantId?: string;
|
|
218
|
+
currency: string;
|
|
219
|
+
configId?: string;
|
|
220
|
+
configVersion?: number;
|
|
221
|
+
shippingRequired: boolean;
|
|
222
|
+
shipping: {
|
|
223
|
+
shippingRequired: boolean;
|
|
224
|
+
methods: ShippingTaxMethod[];
|
|
225
|
+
selectedMethod: ShippingTaxMethod | null;
|
|
226
|
+
warnings: Array<{
|
|
227
|
+
code: string;
|
|
228
|
+
message: string;
|
|
229
|
+
field?: string;
|
|
230
|
+
}>;
|
|
231
|
+
errors: Array<{
|
|
232
|
+
code: string;
|
|
233
|
+
message: string;
|
|
234
|
+
field?: string;
|
|
235
|
+
}>;
|
|
236
|
+
matchedZoneIds: string[];
|
|
237
|
+
};
|
|
238
|
+
tax: {
|
|
239
|
+
taxTotal: number;
|
|
240
|
+
lines: TaxLine[];
|
|
241
|
+
warnings: Array<{
|
|
242
|
+
code: string;
|
|
243
|
+
message: string;
|
|
244
|
+
field?: string;
|
|
245
|
+
}>;
|
|
246
|
+
errors: Array<{
|
|
247
|
+
code: string;
|
|
248
|
+
message: string;
|
|
249
|
+
field?: string;
|
|
250
|
+
}>;
|
|
251
|
+
matchedRegionIds: string[];
|
|
252
|
+
};
|
|
253
|
+
selectedShippingMethodId?: string | null;
|
|
254
|
+
taxLines: TaxLine[];
|
|
255
|
+
totals: {
|
|
256
|
+
subtotal: number;
|
|
257
|
+
discountTotal: number;
|
|
258
|
+
shippingTotal: number;
|
|
259
|
+
taxTotal: number;
|
|
260
|
+
total: number;
|
|
261
|
+
};
|
|
262
|
+
prompts: Array<{
|
|
263
|
+
code: 'missing_info';
|
|
264
|
+
field: string;
|
|
265
|
+
message: string;
|
|
266
|
+
}>;
|
|
267
|
+
warnings: Array<{
|
|
268
|
+
code: string;
|
|
269
|
+
message: string;
|
|
270
|
+
field?: string;
|
|
271
|
+
}>;
|
|
272
|
+
errors: Array<{
|
|
273
|
+
code: string;
|
|
274
|
+
message: string;
|
|
275
|
+
field?: string;
|
|
276
|
+
}>;
|
|
277
|
+
}
|
|
278
|
+
interface ExternalShippingTaxSnapshotInput {
|
|
279
|
+
storeId: string;
|
|
280
|
+
cartId?: string | null;
|
|
281
|
+
checkoutSessionId?: string | null;
|
|
282
|
+
orderId?: string | null;
|
|
283
|
+
currency?: string;
|
|
284
|
+
totals: ShippingTaxCalculationResponse['totals'];
|
|
285
|
+
selectedShipping?: Record<string, unknown> | null;
|
|
286
|
+
taxLines?: TaxLine[];
|
|
287
|
+
source?: string;
|
|
288
|
+
}
|
|
156
289
|
|
|
157
290
|
interface CartClientOptions {
|
|
158
291
|
apiKey: string;
|
|
@@ -191,6 +324,10 @@ declare class CartClient {
|
|
|
191
324
|
set: (cartId: string, lines: TaxLineInput[]) => Promise<Cart>;
|
|
192
325
|
clear: (cartId: string) => Promise<Cart>;
|
|
193
326
|
};
|
|
327
|
+
get shippingTax(): {
|
|
328
|
+
calculateCart: (cartId: string, input?: ShippingTaxCartCalculateInput) => Promise<ShippingTaxCalculationResponse>;
|
|
329
|
+
pushExternalSnapshot: (input: ExternalShippingTaxSnapshotInput) => Promise<ShippingTaxCalculationResponse>;
|
|
330
|
+
};
|
|
194
331
|
get events(): {
|
|
195
332
|
list: (cartId: string, opts?: {
|
|
196
333
|
sinceSequence?: number;
|
|
@@ -199,6 +336,25 @@ declare class CartClient {
|
|
|
199
336
|
};
|
|
200
337
|
}
|
|
201
338
|
|
|
339
|
+
interface StorefrontQuoteClientOptions {
|
|
340
|
+
storeId: string;
|
|
341
|
+
quoteToken: string;
|
|
342
|
+
baseUrl?: string;
|
|
343
|
+
fetch?: typeof globalThis.fetch;
|
|
344
|
+
timeoutMs?: number;
|
|
345
|
+
origin?: string;
|
|
346
|
+
}
|
|
347
|
+
declare class StorefrontQuoteClient {
|
|
348
|
+
private readonly storeId;
|
|
349
|
+
private readonly quoteToken;
|
|
350
|
+
private readonly baseUrl;
|
|
351
|
+
private readonly fetchImpl;
|
|
352
|
+
private readonly timeoutMs;
|
|
353
|
+
private readonly origin?;
|
|
354
|
+
constructor(opts: StorefrontQuoteClientOptions);
|
|
355
|
+
quote(input: ShippingTaxQuoteInput): Promise<ShippingTaxCalculationResponse>;
|
|
356
|
+
}
|
|
357
|
+
|
|
202
358
|
declare class ThrottleApiError extends Error {
|
|
203
359
|
readonly code: string;
|
|
204
360
|
readonly statusCode: number;
|
|
@@ -211,4 +367,4 @@ declare class ThrottleApiError extends Error {
|
|
|
211
367
|
});
|
|
212
368
|
}
|
|
213
369
|
|
|
214
|
-
export { type AddLineItemInput, type AppliedDiscount, type Cart, CartClient, type CartEvent, type CheckoutInput, type CreateCartInput, type LineItem, type Order, type SelectShippingInput, type SelectedShipping, type TaxLine, type TaxLineInput, ThrottleApiError, type UpdateCartInput, type UpdateLineItemInput };
|
|
370
|
+
export { type AddLineItemInput, type AppliedDiscount, type Cart, CartClient, type CartEvent, type CheckoutInput, type CreateCartInput, type ExternalShippingTaxSnapshotInput, type LineItem, type Order, type SelectShippingInput, type SelectedShipping, type ShippingTaxAddress, type ShippingTaxCalculationResponse, type ShippingTaxCartCalculateInput, type ShippingTaxMethod, type ShippingTaxQuoteInput, type ShippingTaxQuoteItem, StorefrontQuoteClient, type TaxLine, type TaxLineInput, ThrottleApiError, type UpdateCartInput, type UpdateLineItemInput };
|
package/dist/index.d.ts
CHANGED
|
@@ -58,7 +58,7 @@ interface SelectedShipping {
|
|
|
58
58
|
}
|
|
59
59
|
interface AppliedDiscount {
|
|
60
60
|
code: string;
|
|
61
|
-
type: 'percentage' | 'fixed_amount' | 'free_shipping';
|
|
61
|
+
type: 'percentage' | 'fixed_amount' | 'free_shipping' | 'buy_x_get_y';
|
|
62
62
|
amount: number;
|
|
63
63
|
currency: string;
|
|
64
64
|
freeShipping: boolean;
|
|
@@ -153,6 +153,139 @@ interface Order {
|
|
|
153
153
|
total: number;
|
|
154
154
|
currency: string;
|
|
155
155
|
}
|
|
156
|
+
interface ShippingTaxAddress {
|
|
157
|
+
countryCode?: string;
|
|
158
|
+
stateProvince?: string;
|
|
159
|
+
postalCode?: string;
|
|
160
|
+
city?: string;
|
|
161
|
+
addressLine1?: string;
|
|
162
|
+
addressLine2?: string;
|
|
163
|
+
[key: string]: unknown;
|
|
164
|
+
}
|
|
165
|
+
interface ShippingTaxQuoteItem {
|
|
166
|
+
id: string;
|
|
167
|
+
quantity?: number;
|
|
168
|
+
unitPrice?: number;
|
|
169
|
+
subtotalAmount: number;
|
|
170
|
+
discountAmount?: number;
|
|
171
|
+
productType?: string;
|
|
172
|
+
requiresShipping?: boolean;
|
|
173
|
+
shippingProfileIds?: string[];
|
|
174
|
+
taxCategory?: string;
|
|
175
|
+
weight?: number;
|
|
176
|
+
weightUnit?: 'lb' | 'kg';
|
|
177
|
+
metadata?: Record<string, unknown>;
|
|
178
|
+
[key: string]: unknown;
|
|
179
|
+
}
|
|
180
|
+
interface ShippingTaxQuoteInput {
|
|
181
|
+
currency?: string;
|
|
182
|
+
items: ShippingTaxQuoteItem[];
|
|
183
|
+
discountsTotal?: number;
|
|
184
|
+
selectedShippingMethodId?: string | null;
|
|
185
|
+
addresses?: {
|
|
186
|
+
shipping?: ShippingTaxAddress | null;
|
|
187
|
+
billing?: ShippingTaxAddress | null;
|
|
188
|
+
customer?: ShippingTaxAddress | null;
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
interface ShippingTaxCartCalculateInput {
|
|
192
|
+
kind?: 'cart_estimate' | 'checkout_final';
|
|
193
|
+
selectedShippingMethodId?: string | null;
|
|
194
|
+
persist?: boolean;
|
|
195
|
+
}
|
|
196
|
+
interface ShippingTaxMethod {
|
|
197
|
+
id: string;
|
|
198
|
+
type?: 'ship' | 'pickup';
|
|
199
|
+
label: string;
|
|
200
|
+
amount: number;
|
|
201
|
+
currency: string;
|
|
202
|
+
deliveryWindowLabel?: string | null;
|
|
203
|
+
deliveryMinDays?: number | null;
|
|
204
|
+
deliveryMaxDays?: number | null;
|
|
205
|
+
warnings?: Array<{
|
|
206
|
+
code: string;
|
|
207
|
+
message: string;
|
|
208
|
+
field?: string;
|
|
209
|
+
}>;
|
|
210
|
+
}
|
|
211
|
+
interface ShippingTaxCalculationResponse {
|
|
212
|
+
snapshotId?: string;
|
|
213
|
+
kind: 'quote' | 'cart_estimate' | 'checkout_final' | 'external_snapshot';
|
|
214
|
+
status: 'estimated' | 'final' | 'failed';
|
|
215
|
+
source: 'throttle_engine' | 'provider_totals' | 'external_push';
|
|
216
|
+
storeId: string;
|
|
217
|
+
merchantId?: string;
|
|
218
|
+
currency: string;
|
|
219
|
+
configId?: string;
|
|
220
|
+
configVersion?: number;
|
|
221
|
+
shippingRequired: boolean;
|
|
222
|
+
shipping: {
|
|
223
|
+
shippingRequired: boolean;
|
|
224
|
+
methods: ShippingTaxMethod[];
|
|
225
|
+
selectedMethod: ShippingTaxMethod | null;
|
|
226
|
+
warnings: Array<{
|
|
227
|
+
code: string;
|
|
228
|
+
message: string;
|
|
229
|
+
field?: string;
|
|
230
|
+
}>;
|
|
231
|
+
errors: Array<{
|
|
232
|
+
code: string;
|
|
233
|
+
message: string;
|
|
234
|
+
field?: string;
|
|
235
|
+
}>;
|
|
236
|
+
matchedZoneIds: string[];
|
|
237
|
+
};
|
|
238
|
+
tax: {
|
|
239
|
+
taxTotal: number;
|
|
240
|
+
lines: TaxLine[];
|
|
241
|
+
warnings: Array<{
|
|
242
|
+
code: string;
|
|
243
|
+
message: string;
|
|
244
|
+
field?: string;
|
|
245
|
+
}>;
|
|
246
|
+
errors: Array<{
|
|
247
|
+
code: string;
|
|
248
|
+
message: string;
|
|
249
|
+
field?: string;
|
|
250
|
+
}>;
|
|
251
|
+
matchedRegionIds: string[];
|
|
252
|
+
};
|
|
253
|
+
selectedShippingMethodId?: string | null;
|
|
254
|
+
taxLines: TaxLine[];
|
|
255
|
+
totals: {
|
|
256
|
+
subtotal: number;
|
|
257
|
+
discountTotal: number;
|
|
258
|
+
shippingTotal: number;
|
|
259
|
+
taxTotal: number;
|
|
260
|
+
total: number;
|
|
261
|
+
};
|
|
262
|
+
prompts: Array<{
|
|
263
|
+
code: 'missing_info';
|
|
264
|
+
field: string;
|
|
265
|
+
message: string;
|
|
266
|
+
}>;
|
|
267
|
+
warnings: Array<{
|
|
268
|
+
code: string;
|
|
269
|
+
message: string;
|
|
270
|
+
field?: string;
|
|
271
|
+
}>;
|
|
272
|
+
errors: Array<{
|
|
273
|
+
code: string;
|
|
274
|
+
message: string;
|
|
275
|
+
field?: string;
|
|
276
|
+
}>;
|
|
277
|
+
}
|
|
278
|
+
interface ExternalShippingTaxSnapshotInput {
|
|
279
|
+
storeId: string;
|
|
280
|
+
cartId?: string | null;
|
|
281
|
+
checkoutSessionId?: string | null;
|
|
282
|
+
orderId?: string | null;
|
|
283
|
+
currency?: string;
|
|
284
|
+
totals: ShippingTaxCalculationResponse['totals'];
|
|
285
|
+
selectedShipping?: Record<string, unknown> | null;
|
|
286
|
+
taxLines?: TaxLine[];
|
|
287
|
+
source?: string;
|
|
288
|
+
}
|
|
156
289
|
|
|
157
290
|
interface CartClientOptions {
|
|
158
291
|
apiKey: string;
|
|
@@ -191,6 +324,10 @@ declare class CartClient {
|
|
|
191
324
|
set: (cartId: string, lines: TaxLineInput[]) => Promise<Cart>;
|
|
192
325
|
clear: (cartId: string) => Promise<Cart>;
|
|
193
326
|
};
|
|
327
|
+
get shippingTax(): {
|
|
328
|
+
calculateCart: (cartId: string, input?: ShippingTaxCartCalculateInput) => Promise<ShippingTaxCalculationResponse>;
|
|
329
|
+
pushExternalSnapshot: (input: ExternalShippingTaxSnapshotInput) => Promise<ShippingTaxCalculationResponse>;
|
|
330
|
+
};
|
|
194
331
|
get events(): {
|
|
195
332
|
list: (cartId: string, opts?: {
|
|
196
333
|
sinceSequence?: number;
|
|
@@ -199,6 +336,25 @@ declare class CartClient {
|
|
|
199
336
|
};
|
|
200
337
|
}
|
|
201
338
|
|
|
339
|
+
interface StorefrontQuoteClientOptions {
|
|
340
|
+
storeId: string;
|
|
341
|
+
quoteToken: string;
|
|
342
|
+
baseUrl?: string;
|
|
343
|
+
fetch?: typeof globalThis.fetch;
|
|
344
|
+
timeoutMs?: number;
|
|
345
|
+
origin?: string;
|
|
346
|
+
}
|
|
347
|
+
declare class StorefrontQuoteClient {
|
|
348
|
+
private readonly storeId;
|
|
349
|
+
private readonly quoteToken;
|
|
350
|
+
private readonly baseUrl;
|
|
351
|
+
private readonly fetchImpl;
|
|
352
|
+
private readonly timeoutMs;
|
|
353
|
+
private readonly origin?;
|
|
354
|
+
constructor(opts: StorefrontQuoteClientOptions);
|
|
355
|
+
quote(input: ShippingTaxQuoteInput): Promise<ShippingTaxCalculationResponse>;
|
|
356
|
+
}
|
|
357
|
+
|
|
202
358
|
declare class ThrottleApiError extends Error {
|
|
203
359
|
readonly code: string;
|
|
204
360
|
readonly statusCode: number;
|
|
@@ -211,4 +367,4 @@ declare class ThrottleApiError extends Error {
|
|
|
211
367
|
});
|
|
212
368
|
}
|
|
213
369
|
|
|
214
|
-
export { type AddLineItemInput, type AppliedDiscount, type Cart, CartClient, type CartEvent, type CheckoutInput, type CreateCartInput, type LineItem, type Order, type SelectShippingInput, type SelectedShipping, type TaxLine, type TaxLineInput, ThrottleApiError, type UpdateCartInput, type UpdateLineItemInput };
|
|
370
|
+
export { type AddLineItemInput, type AppliedDiscount, type Cart, CartClient, type CartEvent, type CheckoutInput, type CreateCartInput, type ExternalShippingTaxSnapshotInput, type LineItem, type Order, type SelectShippingInput, type SelectedShipping, type ShippingTaxAddress, type ShippingTaxCalculationResponse, type ShippingTaxCartCalculateInput, type ShippingTaxMethod, type ShippingTaxQuoteInput, type ShippingTaxQuoteItem, StorefrontQuoteClient, type TaxLine, type TaxLineInput, ThrottleApiError, type UpdateCartInput, type UpdateLineItemInput };
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ var CartClient = class {
|
|
|
21
21
|
constructor(opts) {
|
|
22
22
|
if (!opts.apiKey) throw new Error("CartClient: apiKey is required");
|
|
23
23
|
this.apiKey = opts.apiKey;
|
|
24
|
-
this.baseUrl = (opts.baseUrl ?? "https://
|
|
24
|
+
this.baseUrl = (opts.baseUrl ?? "https://api.usethrottle.dev").replace(/\/+$/, "");
|
|
25
25
|
this.fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
26
26
|
this.timeoutMs = opts.timeoutMs ?? 3e4;
|
|
27
27
|
}
|
|
@@ -84,6 +84,12 @@ var CartClient = class {
|
|
|
84
84
|
clear: (cartId) => this.request("DELETE", `/api/v1/carts/${cartId}/tax-lines`)
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
|
+
get shippingTax() {
|
|
88
|
+
return {
|
|
89
|
+
calculateCart: (cartId, input = {}) => this.request("POST", `/api/v1/shipping-tax/carts/${cartId}/calculate`, input),
|
|
90
|
+
pushExternalSnapshot: (input) => this.request("POST", "/api/v1/shipping-tax/external-snapshots", input)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
87
93
|
get events() {
|
|
88
94
|
return {
|
|
89
95
|
list: (cartId, opts) => {
|
|
@@ -96,7 +102,60 @@ var CartClient = class {
|
|
|
96
102
|
};
|
|
97
103
|
}
|
|
98
104
|
};
|
|
105
|
+
|
|
106
|
+
// src/storefront.ts
|
|
107
|
+
var StorefrontQuoteClient = class {
|
|
108
|
+
storeId;
|
|
109
|
+
quoteToken;
|
|
110
|
+
baseUrl;
|
|
111
|
+
fetchImpl;
|
|
112
|
+
timeoutMs;
|
|
113
|
+
origin;
|
|
114
|
+
constructor(opts) {
|
|
115
|
+
if (!opts.storeId) throw new Error("StorefrontQuoteClient: storeId is required");
|
|
116
|
+
if (!opts.quoteToken?.startsWith("pk_")) {
|
|
117
|
+
throw new Error("StorefrontQuoteClient: quoteToken must be a publishable pk_ token");
|
|
118
|
+
}
|
|
119
|
+
this.storeId = opts.storeId;
|
|
120
|
+
this.quoteToken = opts.quoteToken;
|
|
121
|
+
this.baseUrl = (opts.baseUrl ?? "https://api.usethrottle.dev").replace(/\/+$/, "");
|
|
122
|
+
this.fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
123
|
+
this.timeoutMs = opts.timeoutMs ?? 15e3;
|
|
124
|
+
this.origin = opts.origin;
|
|
125
|
+
}
|
|
126
|
+
async quote(input) {
|
|
127
|
+
const ctrl = new AbortController();
|
|
128
|
+
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
129
|
+
try {
|
|
130
|
+
const headers = { "content-type": "application/json" };
|
|
131
|
+
if (this.origin) headers.origin = this.origin;
|
|
132
|
+
const res = await this.fetchImpl(`${this.baseUrl}/api/v1/shipping-tax/quotes`, {
|
|
133
|
+
method: "POST",
|
|
134
|
+
headers,
|
|
135
|
+
body: JSON.stringify({
|
|
136
|
+
storeId: this.storeId,
|
|
137
|
+
quoteToken: this.quoteToken,
|
|
138
|
+
...input
|
|
139
|
+
}),
|
|
140
|
+
signal: ctrl.signal
|
|
141
|
+
});
|
|
142
|
+
const json = await res.json().catch(() => ({}));
|
|
143
|
+
if (!res.ok) {
|
|
144
|
+
throw new ThrottleApiError({
|
|
145
|
+
code: json?.error?.code ?? "unknown_error",
|
|
146
|
+
message: json?.error?.message ?? `HTTP ${res.status}`,
|
|
147
|
+
statusCode: res.status,
|
|
148
|
+
details: json?.error?.details
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return json.data;
|
|
152
|
+
} finally {
|
|
153
|
+
clearTimeout(timer);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
};
|
|
99
157
|
export {
|
|
100
158
|
CartClient,
|
|
159
|
+
StorefrontQuoteClient,
|
|
101
160
|
ThrottleApiError
|
|
102
161
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usethrottle/cart",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Typed REST client for the Throttle Cart API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -13,12 +13,10 @@
|
|
|
13
13
|
"require": "./dist/index.cjs"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
|
-
"files": [
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
"typecheck": "tsc --noEmit"
|
|
21
|
-
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
22
20
|
"devDependencies": {
|
|
23
21
|
"tsup": "^8.0.0",
|
|
24
22
|
"vitest": "^2.1.9",
|
|
@@ -26,5 +24,10 @@
|
|
|
26
24
|
},
|
|
27
25
|
"publishConfig": {
|
|
28
26
|
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsup",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"typecheck": "tsc --noEmit"
|
|
29
32
|
}
|
|
30
|
-
}
|
|
33
|
+
}
|