@usethrottle/discounts 0.1.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/CLAUDE.md ADDED
@@ -0,0 +1,27 @@
1
+ # @usethrottle/discounts
2
+
3
+ Headless public SDK for Throttle discount and promotion code APIs.
4
+
5
+ ## Scope
6
+
7
+ - Server-side TypeScript client only. Do not add browser UI components here.
8
+ - Keep support limited to `percentage` and `fixed_amount`.
9
+ - Preserve `metadata` as an arbitrary JSON object on create/update/get/list.
10
+ - `maxRedemptionsPerCustomer` defaults to `1`; `null` means unlimited.
11
+ - Customer redemption usage is counted on cart-to-order creation, not preview.
12
+ - Preview is non-mutating: `POST /api/v1/discounts/preview`.
13
+ - Cart application is mutating and must remain server-side:
14
+ `POST /api/v1/carts/:cartId/apply-discount` and
15
+ `DELETE /api/v1/carts/:cartId/discount`.
16
+ - Never encourage exposing `THROTTLE_API_KEY` to buyer browsers.
17
+
18
+ ## Testing
19
+
20
+ Focused commands:
21
+
22
+ - `pnpm --filter @usethrottle/discounts test`
23
+ - `pnpm --filter @usethrottle/discounts typecheck`
24
+ - `pnpm --filter @usethrottle/discounts build`
25
+
26
+ When endpoint shapes change, update `README.md`, package types, tests, and
27
+ `apps/docs` in the same change.
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # @usethrottle/discounts
2
+
3
+ Headless TypeScript client for Throttle discount codes.
4
+
5
+ Use it from your server, API routes, server actions, or backend jobs. The SDK
6
+ does not ship checkout UI because promotion code fields need to match each
7
+ storefront's cart and checkout experience.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install @usethrottle/discounts
13
+ ```
14
+
15
+ ## Preview a Code
16
+
17
+ ```ts
18
+ import { DiscountsClient } from '@usethrottle/discounts';
19
+
20
+ const discounts = new DiscountsClient({
21
+ apiKey: process.env.THROTTLE_API_KEY!,
22
+ });
23
+
24
+ const preview = await discounts.preview({
25
+ code: 'SUMMER25',
26
+ currency: 'USD',
27
+ subtotal: 10000,
28
+ customerId: 'cust_123',
29
+ });
30
+
31
+ if (preview.valid) {
32
+ console.log(preview.discountTotal); // 2500
33
+ }
34
+ ```
35
+
36
+ ## Apply a Code to a Cart
37
+
38
+ ```ts
39
+ const cart = await discounts.applyToCart('cart_123', 'SUMMER25');
40
+ ```
41
+
42
+ ## Remove a Code from a Cart
43
+
44
+ ```ts
45
+ const cart = await discounts.removeFromCart('cart_123');
46
+ ```
47
+
48
+ ## Create a Discount
49
+
50
+ ```ts
51
+ await discounts.create({
52
+ code: 'SUMMER25',
53
+ name: 'Summer 25% off',
54
+ type: 'percentage',
55
+ value: 25,
56
+ conditions: { minOrder: 5000 },
57
+ maxRedemptionsPerCustomer: 1,
58
+ metadata: {
59
+ campaign: 'summer-2026',
60
+ channel: 'email',
61
+ },
62
+ });
63
+
64
+ await discounts.create({
65
+ code: 'FIVE-OFF',
66
+ name: '$5 off',
67
+ type: 'fixed_amount',
68
+ value: 500,
69
+ });
70
+ ```
71
+
72
+ Throttle supports only `percentage` and `fixed_amount` discounts. `value` is a
73
+ whole percent for percentage discounts and minor currency units for fixed
74
+ amount discounts.
75
+
76
+ `maxRedemptionsPerCustomer` defaults to `1`. Set it to another integer for
77
+ repeat customer use, or `null` for unlimited customer redemptions. Redemption
78
+ usage is counted when a discounted cart becomes an order.
79
+
80
+ ## API Key Safety
81
+
82
+ This package sends `X-API-Key` to the Throttle API. Use it from a trusted server
83
+ environment only. Browser checkout UIs should call your own API route or server
84
+ action, then your server can use this package.
package/dist/index.cjs ADDED
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ DiscountsClient: () => DiscountsClient,
24
+ ThrottleDiscountsError: () => ThrottleDiscountsError,
25
+ createDiscountsClient: () => createDiscountsClient
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/errors.ts
30
+ var ThrottleDiscountsError = class extends Error {
31
+ code;
32
+ statusCode;
33
+ details;
34
+ constructor(args) {
35
+ super(args.message);
36
+ this.name = "ThrottleDiscountsError";
37
+ this.code = args.code;
38
+ this.statusCode = args.statusCode;
39
+ this.details = args.details;
40
+ }
41
+ };
42
+
43
+ // src/client.ts
44
+ var DiscountsClient = class {
45
+ apiKey;
46
+ baseUrl;
47
+ fetchImpl;
48
+ timeoutMs;
49
+ constructor(opts) {
50
+ if (!opts.apiKey) throw new Error("DiscountsClient: apiKey is required");
51
+ const fetchImpl = opts.fetch ?? globalThis.fetch;
52
+ if (!fetchImpl) throw new Error("DiscountsClient: fetch is required");
53
+ this.apiKey = opts.apiKey;
54
+ this.baseUrl = (opts.baseUrl ?? "https://api.usethrottle.dev").replace(/\/+$/, "");
55
+ this.fetchImpl = fetchImpl;
56
+ this.timeoutMs = opts.timeoutMs ?? 3e4;
57
+ }
58
+ async request(method, path, body) {
59
+ const envelope = await this.requestEnvelope(method, path, body);
60
+ return envelope.data;
61
+ }
62
+ async requestEnvelope(method, path, body) {
63
+ const url = path.startsWith("http") ? path : `${this.baseUrl}${path.startsWith("/") ? path : "/" + path}`;
64
+ const ctrl = new AbortController();
65
+ const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
66
+ try {
67
+ const res = await this.fetchImpl(url, {
68
+ method,
69
+ headers: {
70
+ "x-api-key": this.apiKey,
71
+ "content-type": "application/json"
72
+ },
73
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
74
+ signal: ctrl.signal
75
+ });
76
+ const json = await res.json().catch(() => ({}));
77
+ if (!res.ok) {
78
+ throw new ThrottleDiscountsError({
79
+ code: json?.error?.code ?? "unknown_error",
80
+ message: json?.error?.message ?? `HTTP ${res.status}`,
81
+ statusCode: res.status,
82
+ details: json?.error?.details
83
+ });
84
+ }
85
+ return {
86
+ data: json.data,
87
+ meta: json.meta
88
+ };
89
+ } finally {
90
+ clearTimeout(timer);
91
+ }
92
+ }
93
+ create(input) {
94
+ return this.request("POST", "/api/v1/discounts", serializeDates(input));
95
+ }
96
+ async list(params = {}) {
97
+ const query = new URLSearchParams();
98
+ if (params.storeId) query.set("storeId", params.storeId);
99
+ if (params.isActive !== void 0) query.set("isActive", String(params.isActive));
100
+ if (params.code) query.set("code", params.code);
101
+ if (params.limit !== void 0) query.set("limit", String(params.limit));
102
+ if (params.cursor) query.set("cursor", params.cursor);
103
+ if (params.sort) query.set("sort", params.sort);
104
+ const suffix = query.toString() ? `?${query}` : "";
105
+ const envelope = await this.requestEnvelope("GET", `/api/v1/discounts${suffix}`);
106
+ return {
107
+ data: envelope.data,
108
+ pagination: envelope.meta?.pagination ?? void 0
109
+ };
110
+ }
111
+ get(id) {
112
+ return this.request("GET", `/api/v1/discounts/${encodeURIComponent(id)}`);
113
+ }
114
+ update(id, input) {
115
+ return this.request(
116
+ "PATCH",
117
+ `/api/v1/discounts/${encodeURIComponent(id)}`,
118
+ serializeDates(input)
119
+ );
120
+ }
121
+ delete(id) {
122
+ return this.request("DELETE", `/api/v1/discounts/${encodeURIComponent(id)}`);
123
+ }
124
+ validate(code, orderTotal, customerId) {
125
+ return this.request("POST", "/api/v1/discounts/validate", {
126
+ code,
127
+ orderTotal,
128
+ ...customerId ? { customerId } : {}
129
+ });
130
+ }
131
+ preview(input) {
132
+ return this.request("POST", "/api/v1/discounts/preview", input);
133
+ }
134
+ applyToCart(cartId, code) {
135
+ return this.request(
136
+ "POST",
137
+ `/api/v1/carts/${encodeURIComponent(cartId)}/apply-discount`,
138
+ {
139
+ code
140
+ }
141
+ );
142
+ }
143
+ removeFromCart(cartId) {
144
+ return this.request("DELETE", `/api/v1/carts/${encodeURIComponent(cartId)}/discount`);
145
+ }
146
+ };
147
+ function createDiscountsClient(opts) {
148
+ return new DiscountsClient(opts);
149
+ }
150
+ function serializeDates(input) {
151
+ const out = { ...input };
152
+ for (const key of ["startsAt", "endsAt"]) {
153
+ const value = out[key];
154
+ if (value instanceof Date) out[key] = value.toISOString();
155
+ }
156
+ return out;
157
+ }
158
+ // Annotate the CommonJS export names for ESM import in node:
159
+ 0 && (module.exports = {
160
+ DiscountsClient,
161
+ ThrottleDiscountsError,
162
+ createDiscountsClient
163
+ });
@@ -0,0 +1,145 @@
1
+ type DiscountType = 'percentage' | 'fixed_amount';
2
+ interface Discount {
3
+ id: string;
4
+ code: string | null;
5
+ name: string;
6
+ type: DiscountType;
7
+ value: number;
8
+ conditions: Record<string, unknown>;
9
+ usageLimit?: number | null;
10
+ usageCount: number;
11
+ maxRedemptionsPerCustomer?: number | null;
12
+ isActive: boolean;
13
+ startsAt?: string | null;
14
+ endsAt?: string | null;
15
+ metadata: Record<string, unknown>;
16
+ createdAt: string;
17
+ }
18
+ interface CreateDiscountInput {
19
+ storeId?: string;
20
+ code?: string;
21
+ name: string;
22
+ type: DiscountType;
23
+ value: number;
24
+ conditions?: Record<string, unknown>;
25
+ usageLimit?: number;
26
+ maxRedemptionsPerCustomer?: number | null;
27
+ startsAt?: string | Date;
28
+ endsAt?: string | Date;
29
+ metadata?: Record<string, unknown>;
30
+ }
31
+ interface UpdateDiscountInput {
32
+ name?: string;
33
+ value?: number;
34
+ conditions?: Record<string, unknown>;
35
+ usageLimit?: number;
36
+ maxRedemptionsPerCustomer?: number | null;
37
+ isActive?: boolean;
38
+ startsAt?: string | Date;
39
+ endsAt?: string | Date;
40
+ metadata?: Record<string, unknown>;
41
+ }
42
+ interface DiscountListParams {
43
+ storeId?: string;
44
+ isActive?: boolean;
45
+ code?: string;
46
+ limit?: number;
47
+ cursor?: string;
48
+ sort?: 'asc' | 'desc';
49
+ }
50
+ interface DiscountValidationResult {
51
+ valid: boolean;
52
+ discount?: Discount;
53
+ error?: string;
54
+ }
55
+ interface DiscountPreviewInput {
56
+ code: string;
57
+ currency: string;
58
+ subtotal: number;
59
+ customerId?: string;
60
+ lineItems?: Array<{
61
+ id?: string;
62
+ quantity: number;
63
+ unitPrice: number;
64
+ }>;
65
+ }
66
+ interface DiscountPreviewResult {
67
+ valid: boolean;
68
+ code: string;
69
+ type?: DiscountType;
70
+ value?: number;
71
+ currency: string;
72
+ subtotal: number;
73
+ discountTotal: number;
74
+ total: number;
75
+ discount?: Discount;
76
+ message: string | null;
77
+ }
78
+ interface Cart {
79
+ id: string;
80
+ currency: string;
81
+ subtotal: number;
82
+ discountTotal: number;
83
+ shippingTotal: number;
84
+ taxTotal: number;
85
+ total: number;
86
+ discountCode?: string | null;
87
+ appliedDiscount?: {
88
+ discountId?: string | null;
89
+ code: string;
90
+ type: DiscountType;
91
+ amount: number;
92
+ currency: string;
93
+ freeShipping: boolean;
94
+ validUntil: string | null;
95
+ snapshotHash: string;
96
+ } | null;
97
+ [key: string]: unknown;
98
+ }
99
+ interface DiscountsClientOptions {
100
+ apiKey: string;
101
+ baseUrl?: string;
102
+ fetch?: typeof globalThis.fetch;
103
+ timeoutMs?: number;
104
+ }
105
+ interface PaginatedDiscounts {
106
+ data: Discount[];
107
+ pagination?: {
108
+ cursor: string | null;
109
+ hasMore: boolean;
110
+ };
111
+ }
112
+
113
+ declare class DiscountsClient {
114
+ private readonly apiKey;
115
+ private readonly baseUrl;
116
+ private readonly fetchImpl;
117
+ private readonly timeoutMs;
118
+ constructor(opts: DiscountsClientOptions);
119
+ request<T = unknown>(method: string, path: string, body?: unknown): Promise<T>;
120
+ private requestEnvelope;
121
+ create(input: CreateDiscountInput): Promise<Discount>;
122
+ list(params?: DiscountListParams): Promise<PaginatedDiscounts>;
123
+ get(id: string): Promise<Discount>;
124
+ update(id: string, input: UpdateDiscountInput): Promise<Discount>;
125
+ delete(id: string): Promise<void>;
126
+ validate(code: string, orderTotal: number, customerId?: string): Promise<DiscountValidationResult>;
127
+ preview(input: DiscountPreviewInput): Promise<DiscountPreviewResult>;
128
+ applyToCart(cartId: string, code: string): Promise<Cart>;
129
+ removeFromCart(cartId: string): Promise<Cart>;
130
+ }
131
+ declare function createDiscountsClient(opts: DiscountsClientOptions): DiscountsClient;
132
+
133
+ declare class ThrottleDiscountsError extends Error {
134
+ readonly code: string;
135
+ readonly statusCode: number;
136
+ readonly details?: unknown;
137
+ constructor(args: {
138
+ code: string;
139
+ message: string;
140
+ statusCode: number;
141
+ details?: unknown;
142
+ });
143
+ }
144
+
145
+ export { type Cart, type CreateDiscountInput, type Discount, type DiscountListParams, type DiscountPreviewInput, type DiscountPreviewResult, type DiscountType, type DiscountValidationResult, DiscountsClient, type DiscountsClientOptions, type PaginatedDiscounts, ThrottleDiscountsError, type UpdateDiscountInput, createDiscountsClient };
@@ -0,0 +1,145 @@
1
+ type DiscountType = 'percentage' | 'fixed_amount';
2
+ interface Discount {
3
+ id: string;
4
+ code: string | null;
5
+ name: string;
6
+ type: DiscountType;
7
+ value: number;
8
+ conditions: Record<string, unknown>;
9
+ usageLimit?: number | null;
10
+ usageCount: number;
11
+ maxRedemptionsPerCustomer?: number | null;
12
+ isActive: boolean;
13
+ startsAt?: string | null;
14
+ endsAt?: string | null;
15
+ metadata: Record<string, unknown>;
16
+ createdAt: string;
17
+ }
18
+ interface CreateDiscountInput {
19
+ storeId?: string;
20
+ code?: string;
21
+ name: string;
22
+ type: DiscountType;
23
+ value: number;
24
+ conditions?: Record<string, unknown>;
25
+ usageLimit?: number;
26
+ maxRedemptionsPerCustomer?: number | null;
27
+ startsAt?: string | Date;
28
+ endsAt?: string | Date;
29
+ metadata?: Record<string, unknown>;
30
+ }
31
+ interface UpdateDiscountInput {
32
+ name?: string;
33
+ value?: number;
34
+ conditions?: Record<string, unknown>;
35
+ usageLimit?: number;
36
+ maxRedemptionsPerCustomer?: number | null;
37
+ isActive?: boolean;
38
+ startsAt?: string | Date;
39
+ endsAt?: string | Date;
40
+ metadata?: Record<string, unknown>;
41
+ }
42
+ interface DiscountListParams {
43
+ storeId?: string;
44
+ isActive?: boolean;
45
+ code?: string;
46
+ limit?: number;
47
+ cursor?: string;
48
+ sort?: 'asc' | 'desc';
49
+ }
50
+ interface DiscountValidationResult {
51
+ valid: boolean;
52
+ discount?: Discount;
53
+ error?: string;
54
+ }
55
+ interface DiscountPreviewInput {
56
+ code: string;
57
+ currency: string;
58
+ subtotal: number;
59
+ customerId?: string;
60
+ lineItems?: Array<{
61
+ id?: string;
62
+ quantity: number;
63
+ unitPrice: number;
64
+ }>;
65
+ }
66
+ interface DiscountPreviewResult {
67
+ valid: boolean;
68
+ code: string;
69
+ type?: DiscountType;
70
+ value?: number;
71
+ currency: string;
72
+ subtotal: number;
73
+ discountTotal: number;
74
+ total: number;
75
+ discount?: Discount;
76
+ message: string | null;
77
+ }
78
+ interface Cart {
79
+ id: string;
80
+ currency: string;
81
+ subtotal: number;
82
+ discountTotal: number;
83
+ shippingTotal: number;
84
+ taxTotal: number;
85
+ total: number;
86
+ discountCode?: string | null;
87
+ appliedDiscount?: {
88
+ discountId?: string | null;
89
+ code: string;
90
+ type: DiscountType;
91
+ amount: number;
92
+ currency: string;
93
+ freeShipping: boolean;
94
+ validUntil: string | null;
95
+ snapshotHash: string;
96
+ } | null;
97
+ [key: string]: unknown;
98
+ }
99
+ interface DiscountsClientOptions {
100
+ apiKey: string;
101
+ baseUrl?: string;
102
+ fetch?: typeof globalThis.fetch;
103
+ timeoutMs?: number;
104
+ }
105
+ interface PaginatedDiscounts {
106
+ data: Discount[];
107
+ pagination?: {
108
+ cursor: string | null;
109
+ hasMore: boolean;
110
+ };
111
+ }
112
+
113
+ declare class DiscountsClient {
114
+ private readonly apiKey;
115
+ private readonly baseUrl;
116
+ private readonly fetchImpl;
117
+ private readonly timeoutMs;
118
+ constructor(opts: DiscountsClientOptions);
119
+ request<T = unknown>(method: string, path: string, body?: unknown): Promise<T>;
120
+ private requestEnvelope;
121
+ create(input: CreateDiscountInput): Promise<Discount>;
122
+ list(params?: DiscountListParams): Promise<PaginatedDiscounts>;
123
+ get(id: string): Promise<Discount>;
124
+ update(id: string, input: UpdateDiscountInput): Promise<Discount>;
125
+ delete(id: string): Promise<void>;
126
+ validate(code: string, orderTotal: number, customerId?: string): Promise<DiscountValidationResult>;
127
+ preview(input: DiscountPreviewInput): Promise<DiscountPreviewResult>;
128
+ applyToCart(cartId: string, code: string): Promise<Cart>;
129
+ removeFromCart(cartId: string): Promise<Cart>;
130
+ }
131
+ declare function createDiscountsClient(opts: DiscountsClientOptions): DiscountsClient;
132
+
133
+ declare class ThrottleDiscountsError extends Error {
134
+ readonly code: string;
135
+ readonly statusCode: number;
136
+ readonly details?: unknown;
137
+ constructor(args: {
138
+ code: string;
139
+ message: string;
140
+ statusCode: number;
141
+ details?: unknown;
142
+ });
143
+ }
144
+
145
+ export { type Cart, type CreateDiscountInput, type Discount, type DiscountListParams, type DiscountPreviewInput, type DiscountPreviewResult, type DiscountType, type DiscountValidationResult, DiscountsClient, type DiscountsClientOptions, type PaginatedDiscounts, ThrottleDiscountsError, type UpdateDiscountInput, createDiscountsClient };
package/dist/index.js ADDED
@@ -0,0 +1,134 @@
1
+ // src/errors.ts
2
+ var ThrottleDiscountsError = class extends Error {
3
+ code;
4
+ statusCode;
5
+ details;
6
+ constructor(args) {
7
+ super(args.message);
8
+ this.name = "ThrottleDiscountsError";
9
+ this.code = args.code;
10
+ this.statusCode = args.statusCode;
11
+ this.details = args.details;
12
+ }
13
+ };
14
+
15
+ // src/client.ts
16
+ var DiscountsClient = class {
17
+ apiKey;
18
+ baseUrl;
19
+ fetchImpl;
20
+ timeoutMs;
21
+ constructor(opts) {
22
+ if (!opts.apiKey) throw new Error("DiscountsClient: apiKey is required");
23
+ const fetchImpl = opts.fetch ?? globalThis.fetch;
24
+ if (!fetchImpl) throw new Error("DiscountsClient: fetch is required");
25
+ this.apiKey = opts.apiKey;
26
+ this.baseUrl = (opts.baseUrl ?? "https://api.usethrottle.dev").replace(/\/+$/, "");
27
+ this.fetchImpl = fetchImpl;
28
+ this.timeoutMs = opts.timeoutMs ?? 3e4;
29
+ }
30
+ async request(method, path, body) {
31
+ const envelope = await this.requestEnvelope(method, path, body);
32
+ return envelope.data;
33
+ }
34
+ async requestEnvelope(method, path, body) {
35
+ const url = path.startsWith("http") ? path : `${this.baseUrl}${path.startsWith("/") ? path : "/" + path}`;
36
+ const ctrl = new AbortController();
37
+ const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
38
+ try {
39
+ const res = await this.fetchImpl(url, {
40
+ method,
41
+ headers: {
42
+ "x-api-key": this.apiKey,
43
+ "content-type": "application/json"
44
+ },
45
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
46
+ signal: ctrl.signal
47
+ });
48
+ const json = await res.json().catch(() => ({}));
49
+ if (!res.ok) {
50
+ throw new ThrottleDiscountsError({
51
+ code: json?.error?.code ?? "unknown_error",
52
+ message: json?.error?.message ?? `HTTP ${res.status}`,
53
+ statusCode: res.status,
54
+ details: json?.error?.details
55
+ });
56
+ }
57
+ return {
58
+ data: json.data,
59
+ meta: json.meta
60
+ };
61
+ } finally {
62
+ clearTimeout(timer);
63
+ }
64
+ }
65
+ create(input) {
66
+ return this.request("POST", "/api/v1/discounts", serializeDates(input));
67
+ }
68
+ async list(params = {}) {
69
+ const query = new URLSearchParams();
70
+ if (params.storeId) query.set("storeId", params.storeId);
71
+ if (params.isActive !== void 0) query.set("isActive", String(params.isActive));
72
+ if (params.code) query.set("code", params.code);
73
+ if (params.limit !== void 0) query.set("limit", String(params.limit));
74
+ if (params.cursor) query.set("cursor", params.cursor);
75
+ if (params.sort) query.set("sort", params.sort);
76
+ const suffix = query.toString() ? `?${query}` : "";
77
+ const envelope = await this.requestEnvelope("GET", `/api/v1/discounts${suffix}`);
78
+ return {
79
+ data: envelope.data,
80
+ pagination: envelope.meta?.pagination ?? void 0
81
+ };
82
+ }
83
+ get(id) {
84
+ return this.request("GET", `/api/v1/discounts/${encodeURIComponent(id)}`);
85
+ }
86
+ update(id, input) {
87
+ return this.request(
88
+ "PATCH",
89
+ `/api/v1/discounts/${encodeURIComponent(id)}`,
90
+ serializeDates(input)
91
+ );
92
+ }
93
+ delete(id) {
94
+ return this.request("DELETE", `/api/v1/discounts/${encodeURIComponent(id)}`);
95
+ }
96
+ validate(code, orderTotal, customerId) {
97
+ return this.request("POST", "/api/v1/discounts/validate", {
98
+ code,
99
+ orderTotal,
100
+ ...customerId ? { customerId } : {}
101
+ });
102
+ }
103
+ preview(input) {
104
+ return this.request("POST", "/api/v1/discounts/preview", input);
105
+ }
106
+ applyToCart(cartId, code) {
107
+ return this.request(
108
+ "POST",
109
+ `/api/v1/carts/${encodeURIComponent(cartId)}/apply-discount`,
110
+ {
111
+ code
112
+ }
113
+ );
114
+ }
115
+ removeFromCart(cartId) {
116
+ return this.request("DELETE", `/api/v1/carts/${encodeURIComponent(cartId)}/discount`);
117
+ }
118
+ };
119
+ function createDiscountsClient(opts) {
120
+ return new DiscountsClient(opts);
121
+ }
122
+ function serializeDates(input) {
123
+ const out = { ...input };
124
+ for (const key of ["startsAt", "endsAt"]) {
125
+ const value = out[key];
126
+ if (value instanceof Date) out[key] = value.toISOString();
127
+ }
128
+ return out;
129
+ }
130
+ export {
131
+ DiscountsClient,
132
+ ThrottleDiscountsError,
133
+ createDiscountsClient
134
+ };
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@usethrottle/discounts",
3
+ "version": "0.1.0",
4
+ "description": "Headless TypeScript client for Throttle discount and promotion code APIs.",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": ["dist", "README.md", "CLAUDE.md"],
17
+ "scripts": {
18
+ "build": "tsup",
19
+ "test": "vitest run",
20
+ "typecheck": "tsc --noEmit"
21
+ },
22
+ "devDependencies": {
23
+ "tsup": "^8.0.0",
24
+ "typescript": "^5.7.0",
25
+ "vitest": "^2.1.9"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "engines": {
31
+ "node": ">=20"
32
+ }
33
+ }