@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 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://throttle-api-gff1.onrender.com").replace(/\/+$/, "");
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://throttle-api-gff1.onrender.com").replace(/\/+$/, "");
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": "0.1.0",
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": ["dist", "README.md"],
17
- "scripts": {
18
- "build": "tsup",
19
- "test": "vitest run",
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
+ }