@neowhale/storefront 0.1.2 → 0.2.2

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.
@@ -1,273 +0,0 @@
1
- // src/client.ts
2
- var WhaleClient = class {
3
- constructor(config) {
4
- this._sessionToken = null;
5
- this.storeId = config.storeId;
6
- this.apiKey = config.apiKey;
7
- this.gatewayUrl = config.gatewayUrl || "https://whale-gateway.fly.dev";
8
- this.proxyPath = config.proxyPath || "/api/gw";
9
- }
10
- // ── Token Management ────────────────────────────────────────────────────
11
- setSessionToken(token) {
12
- this._sessionToken = token;
13
- }
14
- getSessionToken() {
15
- return this._sessionToken;
16
- }
17
- // ── Base URL ────────────────────────────────────────────────────────────
18
- get baseUrl() {
19
- const isServer = typeof window === "undefined";
20
- return isServer ? this.gatewayUrl : this.proxyPath;
21
- }
22
- // ── Base Fetcher ────────────────────────────────────────────────────────
23
- async request(path, options = {}, opts) {
24
- const url = `${this.baseUrl}/v1/stores/${this.storeId}${path}`;
25
- const headers = {
26
- "Content-Type": "application/json",
27
- "x-api-key": this.apiKey
28
- };
29
- if (this._sessionToken) {
30
- headers["Authorization"] = `Bearer ${this._sessionToken}`;
31
- }
32
- const fetchOptions = {
33
- ...options,
34
- headers: {
35
- ...headers,
36
- ...options.headers ?? {}
37
- }
38
- };
39
- if (opts?.revalidate !== void 0) {
40
- fetchOptions.next = { revalidate: opts.revalidate };
41
- }
42
- const res = await fetch(url, fetchOptions);
43
- if (!res.ok) {
44
- let message = `Gateway error ${res.status}: ${res.statusText}`;
45
- try {
46
- const body = await res.json();
47
- if (body?.message) message = body.message;
48
- else if (body?.error) message = body.error;
49
- } catch {
50
- }
51
- const err = new Error(message);
52
- err.status = res.status;
53
- throw err;
54
- }
55
- if (res.status === 204) return void 0;
56
- return res.json();
57
- }
58
- // ── Products ────────────────────────────────────────────────────────────
59
- async listProducts(params) {
60
- const sp = new URLSearchParams();
61
- if (params?.limit) sp.set("limit", String(params.limit));
62
- if (params?.starting_after) sp.set("starting_after", params.starting_after);
63
- if (params?.status) sp.set("status", params.status);
64
- const qs = sp.toString();
65
- return this.request(`/products${qs ? `?${qs}` : ""}`);
66
- }
67
- async getProduct(id) {
68
- return this.request(`/products/${id}`);
69
- }
70
- async getAllProducts(options) {
71
- const all = [];
72
- let cursor;
73
- let hasMore = true;
74
- let pages = 0;
75
- const maxPages = options?.maxPages ?? 20;
76
- while (hasMore && pages < maxPages) {
77
- const params = new URLSearchParams({ limit: "100" });
78
- if (options?.status) params.set("status", options.status);
79
- else params.set("status", "published");
80
- if (cursor) params.set("starting_after", cursor);
81
- const data = await this.request(
82
- `/products?${params}`,
83
- {},
84
- options?.revalidate !== void 0 ? { revalidate: options.revalidate } : void 0
85
- );
86
- if (!data.data || data.data.length === 0) break;
87
- for (const p of data.data) {
88
- if (!options?.filter || options.filter(p)) {
89
- all.push(p);
90
- }
91
- cursor = p.id;
92
- }
93
- hasMore = data.has_more;
94
- pages++;
95
- }
96
- return all;
97
- }
98
- // ── Cart ────────────────────────────────────────────────────────────────
99
- async createCart(customerEmail) {
100
- return this.request("/cart", {
101
- method: "POST",
102
- body: JSON.stringify(customerEmail ? { customer_email: customerEmail } : {})
103
- });
104
- }
105
- async getCart(cartId) {
106
- return this.request(`/cart/${cartId}`);
107
- }
108
- async addToCart(cartId, productId, quantity, options) {
109
- return this.request(`/cart/${cartId}/items`, {
110
- method: "POST",
111
- body: JSON.stringify({
112
- product_id: productId,
113
- quantity,
114
- ...options?.tier !== void 0 && { tier: options.tier },
115
- ...options?.unitPrice !== void 0 && { unit_price: options.unitPrice }
116
- })
117
- });
118
- }
119
- async updateCartItem(cartId, itemId, quantity) {
120
- return this.request(`/cart/${cartId}/items/${itemId}`, {
121
- method: "PATCH",
122
- body: JSON.stringify({ quantity })
123
- });
124
- }
125
- async removeCartItem(cartId, itemId) {
126
- return this.request(`/cart/${cartId}/items/${itemId}`, {
127
- method: "DELETE"
128
- });
129
- }
130
- // ── Checkout ────────────────────────────────────────────────────────────
131
- async checkout(cartId, customerEmail, payment) {
132
- return this.request("/checkout", {
133
- method: "POST",
134
- body: JSON.stringify({
135
- cart_id: cartId,
136
- ...customerEmail && { customer_email: customerEmail },
137
- ...payment && {
138
- payment_method: payment.payment_method,
139
- ...payment.opaque_data && { opaque_data: payment.opaque_data },
140
- ...payment.billTo && { bill_to: payment.billTo },
141
- ...payment.shipTo && { ship_to: payment.shipTo }
142
- }
143
- })
144
- });
145
- }
146
- // ── Customers ───────────────────────────────────────────────────────────
147
- async findCustomer(query) {
148
- const encoded = encodeURIComponent(query);
149
- const res = await this.request(`/customers?query=${encoded}`);
150
- return Array.isArray(res) ? res : res?.data ?? [];
151
- }
152
- async getCustomer(id) {
153
- return this.request(`/customers/${id}`);
154
- }
155
- async createCustomer(data) {
156
- return this.request("/customers", {
157
- method: "POST",
158
- body: JSON.stringify(data)
159
- });
160
- }
161
- // ── Orders ──────────────────────────────────────────────────────────────
162
- async listOrders(params) {
163
- const sp = new URLSearchParams();
164
- if (params?.customer_id) sp.set("customer_id", params.customer_id);
165
- if (params?.limit) sp.set("limit", String(params.limit));
166
- if (params?.starting_after) sp.set("starting_after", params.starting_after);
167
- const qs = sp.toString();
168
- return this.request(`/orders${qs ? `?${qs}` : ""}`);
169
- }
170
- async getOrder(id) {
171
- return this.request(`/orders/${id}`);
172
- }
173
- async getCustomerOrders(customerId) {
174
- const encoded = encodeURIComponent(customerId);
175
- const all = [];
176
- let cursor;
177
- let hasMore = true;
178
- while (hasMore) {
179
- const params = new URLSearchParams({ customer_id: encoded, limit: "100" });
180
- if (cursor) params.set("starting_after", cursor);
181
- const res = await this.request(`/orders?${params}`);
182
- const items = res?.data ?? [];
183
- if (items.length === 0) break;
184
- all.push(...items);
185
- cursor = items[items.length - 1].id;
186
- hasMore = res.has_more ?? false;
187
- }
188
- return all;
189
- }
190
- // ── Auth (OTP) ──────────────────────────────────────────────────────────
191
- async sendCode(email) {
192
- return this.request("/storefront/auth/send-code", {
193
- method: "POST",
194
- body: JSON.stringify({ email })
195
- });
196
- }
197
- async verifyCode(email, code) {
198
- return this.request("/storefront/auth/verify-code", {
199
- method: "POST",
200
- body: JSON.stringify({ email, code })
201
- });
202
- }
203
- // ── Customer Analytics ──────────────────────────────────────────────────
204
- async getCustomerAnalytics(customerId, customerName) {
205
- try {
206
- const res = await this.request(
207
- "/analytics/customers?limit=200"
208
- );
209
- const byId = res.customers?.find((c) => c.customer_id === customerId);
210
- if (byId) return byId;
211
- if (customerName) {
212
- const normalized = customerName.toLowerCase().trim();
213
- return res.customers?.find(
214
- (c) => c.customer_name?.toLowerCase().trim() === normalized
215
- ) ?? null;
216
- }
217
- return null;
218
- } catch {
219
- return null;
220
- }
221
- }
222
- // ── Locations ───────────────────────────────────────────────────────────
223
- async listLocations() {
224
- return this.request("/locations");
225
- }
226
- // ── COA ─────────────────────────────────────────────────────────────────
227
- getCOAEmbedUrl(productId) {
228
- return `${this.baseUrl}/v1/stores/${this.storeId}/coa/${productId}/embed`;
229
- }
230
- // ── Analytics / Storefront Sessions ─────────────────────────────────────
231
- async createSession(params) {
232
- return this.request("/storefront/sessions", {
233
- method: "POST",
234
- body: JSON.stringify(params)
235
- });
236
- }
237
- async updateSession(sessionId, params) {
238
- return this.request(`/storefront/sessions/${sessionId}`, {
239
- method: "PATCH",
240
- body: JSON.stringify(params)
241
- });
242
- }
243
- async trackEvent(params) {
244
- return this.request("/storefront/events", {
245
- method: "POST",
246
- body: JSON.stringify(params)
247
- });
248
- }
249
- // ── Media Signing ──────────────────────────────────────────────────────
250
- static encodeBase64Url(url) {
251
- if (typeof Buffer !== "undefined") {
252
- return Buffer.from(url, "utf-8").toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
253
- }
254
- return btoa(url).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
255
- }
256
- static signMedia(signingSecret, encodedUrl, w, q, f) {
257
- const payload = `${signingSecret}|${encodedUrl}|${w}|${q}|${f}`;
258
- let h1 = 2166136261;
259
- let h2 = 3421674724;
260
- for (let i = 0; i < payload.length; i++) {
261
- const c = payload.charCodeAt(i);
262
- h1 ^= c;
263
- h1 = Math.imul(h1, 16777619);
264
- h2 ^= c;
265
- h2 = Math.imul(h2, 16777629);
266
- }
267
- return ((h1 >>> 0).toString(16).padStart(8, "0") + (h2 >>> 0).toString(16).padStart(8, "0")).slice(0, 16);
268
- }
269
- };
270
-
271
- export { WhaleClient };
272
- //# sourceMappingURL=chunk-PR4PUHVN.js.map
273
- //# sourceMappingURL=chunk-PR4PUHVN.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"chunk-PR4PUHVN.js","sourcesContent":["import type {\n Cart,\n CartItem,\n Customer,\n CustomerAnalytics,\n ListResponse,\n Location,\n Order,\n PaymentData,\n Product,\n SendCodeResponse,\n StorefrontSession,\n VerifyCodeResponse,\n WhaleStorefrontConfig,\n EventType,\n} from './types.js'\n\n// ─── WhaleClient ────────────────────────────────────────────────────────────\n// Stateless HTTP wrapper around whale-gateway. Works server-side and client-side.\n// No React, no browser APIs (except fetch).\n\nexport class WhaleClient {\n readonly storeId: string\n readonly apiKey: string\n readonly gatewayUrl: string\n readonly proxyPath: string\n\n private _sessionToken: string | null = null\n\n constructor(config: WhaleStorefrontConfig) {\n this.storeId = config.storeId\n this.apiKey = config.apiKey\n this.gatewayUrl = config.gatewayUrl || 'https://whale-gateway.fly.dev'\n this.proxyPath = config.proxyPath || '/api/gw'\n }\n\n // ── Token Management ────────────────────────────────────────────────────\n\n setSessionToken(token: string | null): void {\n this._sessionToken = token\n }\n\n getSessionToken(): string | null {\n return this._sessionToken\n }\n\n // ── Base URL ────────────────────────────────────────────────────────────\n\n private get baseUrl(): string {\n const isServer = typeof window === 'undefined'\n return isServer ? this.gatewayUrl : this.proxyPath\n }\n\n // ── Base Fetcher ────────────────────────────────────────────────────────\n\n private async request<T = unknown>(\n path: string,\n options: RequestInit = {},\n opts?: { revalidate?: number }\n ): Promise<T> {\n const url = `${this.baseUrl}/v1/stores/${this.storeId}${path}`\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n }\n if (this._sessionToken) {\n headers['Authorization'] = `Bearer ${this._sessionToken}`\n }\n\n const fetchOptions: RequestInit & { next?: { revalidate?: number } } = {\n ...options,\n headers: {\n ...headers,\n ...((options.headers as Record<string, string>) ?? {}),\n },\n }\n\n if (opts?.revalidate !== undefined) {\n fetchOptions.next = { revalidate: opts.revalidate }\n }\n\n const res = await fetch(url, fetchOptions)\n\n if (!res.ok) {\n let message = `Gateway error ${res.status}: ${res.statusText}`\n try {\n const body = await res.json()\n if (body?.message) message = body.message\n else if (body?.error) message = body.error\n } catch {\n // ignore parse errors\n }\n const err = new Error(message) as Error & { status: number }\n err.status = res.status\n throw err\n }\n\n if (res.status === 204) return undefined as T\n return res.json() as Promise<T>\n }\n\n // ── Products ────────────────────────────────────────────────────────────\n\n async listProducts(params?: {\n limit?: number\n starting_after?: string\n status?: string\n }): Promise<ListResponse<Product>> {\n const sp = new URLSearchParams()\n if (params?.limit) sp.set('limit', String(params.limit))\n if (params?.starting_after) sp.set('starting_after', params.starting_after)\n if (params?.status) sp.set('status', params.status)\n const qs = sp.toString()\n return this.request<ListResponse<Product>>(`/products${qs ? `?${qs}` : ''}`)\n }\n\n async getProduct(id: string): Promise<Product> {\n return this.request<Product>(`/products/${id}`)\n }\n\n async getAllProducts(options?: {\n status?: string\n maxPages?: number\n revalidate?: number\n filter?: (product: Product) => boolean\n }): Promise<Product[]> {\n const all: Product[] = []\n let cursor: string | undefined\n let hasMore = true\n let pages = 0\n const maxPages = options?.maxPages ?? 20\n\n while (hasMore && pages < maxPages) {\n const params = new URLSearchParams({ limit: '100' })\n if (options?.status) params.set('status', options.status)\n else params.set('status', 'published')\n if (cursor) params.set('starting_after', cursor)\n\n const data = await this.request<ListResponse<Product>>(\n `/products?${params}`,\n {},\n options?.revalidate !== undefined ? { revalidate: options.revalidate } : undefined\n )\n\n if (!data.data || data.data.length === 0) break\n\n for (const p of data.data) {\n if (!options?.filter || options.filter(p)) {\n all.push(p)\n }\n cursor = p.id\n }\n\n hasMore = data.has_more\n pages++\n }\n\n return all\n }\n\n // ── Cart ────────────────────────────────────────────────────────────────\n\n async createCart(customerEmail?: string): Promise<Cart> {\n return this.request<Cart>('/cart', {\n method: 'POST',\n body: JSON.stringify(customerEmail ? { customer_email: customerEmail } : {}),\n })\n }\n\n async getCart(cartId: string): Promise<Cart> {\n return this.request<Cart>(`/cart/${cartId}`)\n }\n\n async addToCart(\n cartId: string,\n productId: string,\n quantity: number,\n options?: { tier?: string; unitPrice?: number }\n ): Promise<CartItem> {\n return this.request<CartItem>(`/cart/${cartId}/items`, {\n method: 'POST',\n body: JSON.stringify({\n product_id: productId,\n quantity,\n ...(options?.tier !== undefined && { tier: options.tier }),\n ...(options?.unitPrice !== undefined && { unit_price: options.unitPrice }),\n }),\n })\n }\n\n async updateCartItem(cartId: string, itemId: string, quantity: number): Promise<Cart> {\n return this.request<Cart>(`/cart/${cartId}/items/${itemId}`, {\n method: 'PATCH',\n body: JSON.stringify({ quantity }),\n })\n }\n\n async removeCartItem(cartId: string, itemId: string): Promise<void> {\n return this.request<void>(`/cart/${cartId}/items/${itemId}`, {\n method: 'DELETE',\n })\n }\n\n // ── Checkout ────────────────────────────────────────────────────────────\n\n async checkout(\n cartId: string,\n customerEmail?: string,\n payment?: PaymentData\n ): Promise<Order> {\n return this.request<Order>('/checkout', {\n method: 'POST',\n body: JSON.stringify({\n cart_id: cartId,\n ...(customerEmail && { customer_email: customerEmail }),\n ...(payment && {\n payment_method: payment.payment_method,\n ...(payment.opaque_data && { opaque_data: payment.opaque_data }),\n ...(payment.billTo && { bill_to: payment.billTo }),\n ...(payment.shipTo && { ship_to: payment.shipTo }),\n }),\n }),\n })\n }\n\n // ── Customers ───────────────────────────────────────────────────────────\n\n async findCustomer(query: string): Promise<Customer[]> {\n const encoded = encodeURIComponent(query)\n const res = await this.request<{ data: Customer[] } | Customer[]>(`/customers?query=${encoded}`)\n return Array.isArray(res) ? res : res?.data ?? []\n }\n\n async getCustomer(id: string): Promise<Customer> {\n return this.request<Customer>(`/customers/${id}`)\n }\n\n async createCustomer(data: {\n first_name: string\n last_name: string\n email: string\n phone?: string\n }): Promise<Customer> {\n return this.request<Customer>('/customers', {\n method: 'POST',\n body: JSON.stringify(data),\n })\n }\n\n // ── Orders ──────────────────────────────────────────────────────────────\n\n async listOrders(params?: {\n customer_id?: string\n limit?: number\n starting_after?: string\n }): Promise<ListResponse<Order>> {\n const sp = new URLSearchParams()\n if (params?.customer_id) sp.set('customer_id', params.customer_id)\n if (params?.limit) sp.set('limit', String(params.limit))\n if (params?.starting_after) sp.set('starting_after', params.starting_after)\n const qs = sp.toString()\n return this.request<ListResponse<Order>>(`/orders${qs ? `?${qs}` : ''}`)\n }\n\n async getOrder(id: string): Promise<Order> {\n return this.request<Order>(`/orders/${id}`)\n }\n\n async getCustomerOrders(customerId: string): Promise<Order[]> {\n const encoded = encodeURIComponent(customerId)\n const all: Order[] = []\n let cursor: string | undefined\n let hasMore = true\n\n while (hasMore) {\n const params = new URLSearchParams({ customer_id: encoded, limit: '100' })\n if (cursor) params.set('starting_after', cursor)\n\n const res = await this.request<ListResponse<Order>>(`/orders?${params}`)\n const items = res?.data ?? []\n if (items.length === 0) break\n\n all.push(...items)\n cursor = items[items.length - 1].id\n hasMore = res.has_more ?? false\n }\n\n return all\n }\n\n // ── Auth (OTP) ──────────────────────────────────────────────────────────\n\n async sendCode(email: string): Promise<SendCodeResponse> {\n return this.request<SendCodeResponse>('/storefront/auth/send-code', {\n method: 'POST',\n body: JSON.stringify({ email }),\n })\n }\n\n async verifyCode(email: string, code: string): Promise<VerifyCodeResponse> {\n return this.request<VerifyCodeResponse>('/storefront/auth/verify-code', {\n method: 'POST',\n body: JSON.stringify({ email, code }),\n })\n }\n\n // ── Customer Analytics ──────────────────────────────────────────────────\n\n async getCustomerAnalytics(\n customerId: string,\n customerName?: string\n ): Promise<CustomerAnalytics | null> {\n try {\n const res = await this.request<{ customers: CustomerAnalytics[] }>(\n '/analytics/customers?limit=200'\n )\n const byId = res.customers?.find((c) => c.customer_id === customerId)\n if (byId) return byId\n if (customerName) {\n const normalized = customerName.toLowerCase().trim()\n return (\n res.customers?.find(\n (c) => c.customer_name?.toLowerCase().trim() === normalized\n ) ?? null\n )\n }\n return null\n } catch {\n return null\n }\n }\n\n // ── Locations ───────────────────────────────────────────────────────────\n\n async listLocations(): Promise<ListResponse<Location>> {\n return this.request<ListResponse<Location>>('/locations')\n }\n\n // ── COA ─────────────────────────────────────────────────────────────────\n\n getCOAEmbedUrl(productId: string): string {\n return `${this.baseUrl}/v1/stores/${this.storeId}/coa/${productId}/embed`\n }\n\n // ── Analytics / Storefront Sessions ─────────────────────────────────────\n\n async createSession(params: {\n user_agent?: string\n referrer?: string\n }): Promise<StorefrontSession> {\n return this.request<StorefrontSession>('/storefront/sessions', {\n method: 'POST',\n body: JSON.stringify(params),\n })\n }\n\n async updateSession(\n sessionId: string,\n params: { last_active_at?: string; customer_id?: string }\n ): Promise<StorefrontSession> {\n return this.request<StorefrontSession>(`/storefront/sessions/${sessionId}`, {\n method: 'PATCH',\n body: JSON.stringify(params),\n })\n }\n\n async trackEvent(params: {\n session_id: string\n event_type: EventType\n event_data?: Record<string, unknown>\n }): Promise<void> {\n return this.request<void>('/storefront/events', {\n method: 'POST',\n body: JSON.stringify(params),\n })\n }\n\n // ── Media Signing ──────────────────────────────────────────────────────\n\n static encodeBase64Url(url: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(url, 'utf-8')\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '')\n }\n return btoa(url)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '')\n }\n\n static signMedia(\n signingSecret: string,\n encodedUrl: string,\n w: string,\n q: string,\n f: string\n ): string {\n const payload = `${signingSecret}|${encodedUrl}|${w}|${q}|${f}`\n // FNV dual-hash — matches gateway's media-signature.ts\n let h1 = 0x811c9dc5\n let h2 = 0xcbf29ce4\n for (let i = 0; i < payload.length; i++) {\n const c = payload.charCodeAt(i)\n h1 ^= c\n h1 = Math.imul(h1, 0x01000193)\n h2 ^= c\n h2 = Math.imul(h2, 0x0100019d)\n }\n return (\n (h1 >>> 0).toString(16).padStart(8, '0') +\n (h2 >>> 0).toString(16).padStart(8, '0')\n ).slice(0, 16)\n }\n}\n"]}
@@ -1,275 +0,0 @@
1
- 'use strict';
2
-
3
- // src/client.ts
4
- var WhaleClient = class {
5
- constructor(config) {
6
- this._sessionToken = null;
7
- this.storeId = config.storeId;
8
- this.apiKey = config.apiKey;
9
- this.gatewayUrl = config.gatewayUrl || "https://whale-gateway.fly.dev";
10
- this.proxyPath = config.proxyPath || "/api/gw";
11
- }
12
- // ── Token Management ────────────────────────────────────────────────────
13
- setSessionToken(token) {
14
- this._sessionToken = token;
15
- }
16
- getSessionToken() {
17
- return this._sessionToken;
18
- }
19
- // ── Base URL ────────────────────────────────────────────────────────────
20
- get baseUrl() {
21
- const isServer = typeof window === "undefined";
22
- return isServer ? this.gatewayUrl : this.proxyPath;
23
- }
24
- // ── Base Fetcher ────────────────────────────────────────────────────────
25
- async request(path, options = {}, opts) {
26
- const url = `${this.baseUrl}/v1/stores/${this.storeId}${path}`;
27
- const headers = {
28
- "Content-Type": "application/json",
29
- "x-api-key": this.apiKey
30
- };
31
- if (this._sessionToken) {
32
- headers["Authorization"] = `Bearer ${this._sessionToken}`;
33
- }
34
- const fetchOptions = {
35
- ...options,
36
- headers: {
37
- ...headers,
38
- ...options.headers ?? {}
39
- }
40
- };
41
- if (opts?.revalidate !== void 0) {
42
- fetchOptions.next = { revalidate: opts.revalidate };
43
- }
44
- const res = await fetch(url, fetchOptions);
45
- if (!res.ok) {
46
- let message = `Gateway error ${res.status}: ${res.statusText}`;
47
- try {
48
- const body = await res.json();
49
- if (body?.message) message = body.message;
50
- else if (body?.error) message = body.error;
51
- } catch {
52
- }
53
- const err = new Error(message);
54
- err.status = res.status;
55
- throw err;
56
- }
57
- if (res.status === 204) return void 0;
58
- return res.json();
59
- }
60
- // ── Products ────────────────────────────────────────────────────────────
61
- async listProducts(params) {
62
- const sp = new URLSearchParams();
63
- if (params?.limit) sp.set("limit", String(params.limit));
64
- if (params?.starting_after) sp.set("starting_after", params.starting_after);
65
- if (params?.status) sp.set("status", params.status);
66
- const qs = sp.toString();
67
- return this.request(`/products${qs ? `?${qs}` : ""}`);
68
- }
69
- async getProduct(id) {
70
- return this.request(`/products/${id}`);
71
- }
72
- async getAllProducts(options) {
73
- const all = [];
74
- let cursor;
75
- let hasMore = true;
76
- let pages = 0;
77
- const maxPages = options?.maxPages ?? 20;
78
- while (hasMore && pages < maxPages) {
79
- const params = new URLSearchParams({ limit: "100" });
80
- if (options?.status) params.set("status", options.status);
81
- else params.set("status", "published");
82
- if (cursor) params.set("starting_after", cursor);
83
- const data = await this.request(
84
- `/products?${params}`,
85
- {},
86
- options?.revalidate !== void 0 ? { revalidate: options.revalidate } : void 0
87
- );
88
- if (!data.data || data.data.length === 0) break;
89
- for (const p of data.data) {
90
- if (!options?.filter || options.filter(p)) {
91
- all.push(p);
92
- }
93
- cursor = p.id;
94
- }
95
- hasMore = data.has_more;
96
- pages++;
97
- }
98
- return all;
99
- }
100
- // ── Cart ────────────────────────────────────────────────────────────────
101
- async createCart(customerEmail) {
102
- return this.request("/cart", {
103
- method: "POST",
104
- body: JSON.stringify(customerEmail ? { customer_email: customerEmail } : {})
105
- });
106
- }
107
- async getCart(cartId) {
108
- return this.request(`/cart/${cartId}`);
109
- }
110
- async addToCart(cartId, productId, quantity, options) {
111
- return this.request(`/cart/${cartId}/items`, {
112
- method: "POST",
113
- body: JSON.stringify({
114
- product_id: productId,
115
- quantity,
116
- ...options?.tier !== void 0 && { tier: options.tier },
117
- ...options?.unitPrice !== void 0 && { unit_price: options.unitPrice }
118
- })
119
- });
120
- }
121
- async updateCartItem(cartId, itemId, quantity) {
122
- return this.request(`/cart/${cartId}/items/${itemId}`, {
123
- method: "PATCH",
124
- body: JSON.stringify({ quantity })
125
- });
126
- }
127
- async removeCartItem(cartId, itemId) {
128
- return this.request(`/cart/${cartId}/items/${itemId}`, {
129
- method: "DELETE"
130
- });
131
- }
132
- // ── Checkout ────────────────────────────────────────────────────────────
133
- async checkout(cartId, customerEmail, payment) {
134
- return this.request("/checkout", {
135
- method: "POST",
136
- body: JSON.stringify({
137
- cart_id: cartId,
138
- ...customerEmail && { customer_email: customerEmail },
139
- ...payment && {
140
- payment_method: payment.payment_method,
141
- ...payment.opaque_data && { opaque_data: payment.opaque_data },
142
- ...payment.billTo && { bill_to: payment.billTo },
143
- ...payment.shipTo && { ship_to: payment.shipTo }
144
- }
145
- })
146
- });
147
- }
148
- // ── Customers ───────────────────────────────────────────────────────────
149
- async findCustomer(query) {
150
- const encoded = encodeURIComponent(query);
151
- const res = await this.request(`/customers?query=${encoded}`);
152
- return Array.isArray(res) ? res : res?.data ?? [];
153
- }
154
- async getCustomer(id) {
155
- return this.request(`/customers/${id}`);
156
- }
157
- async createCustomer(data) {
158
- return this.request("/customers", {
159
- method: "POST",
160
- body: JSON.stringify(data)
161
- });
162
- }
163
- // ── Orders ──────────────────────────────────────────────────────────────
164
- async listOrders(params) {
165
- const sp = new URLSearchParams();
166
- if (params?.customer_id) sp.set("customer_id", params.customer_id);
167
- if (params?.limit) sp.set("limit", String(params.limit));
168
- if (params?.starting_after) sp.set("starting_after", params.starting_after);
169
- const qs = sp.toString();
170
- return this.request(`/orders${qs ? `?${qs}` : ""}`);
171
- }
172
- async getOrder(id) {
173
- return this.request(`/orders/${id}`);
174
- }
175
- async getCustomerOrders(customerId) {
176
- const encoded = encodeURIComponent(customerId);
177
- const all = [];
178
- let cursor;
179
- let hasMore = true;
180
- while (hasMore) {
181
- const params = new URLSearchParams({ customer_id: encoded, limit: "100" });
182
- if (cursor) params.set("starting_after", cursor);
183
- const res = await this.request(`/orders?${params}`);
184
- const items = res?.data ?? [];
185
- if (items.length === 0) break;
186
- all.push(...items);
187
- cursor = items[items.length - 1].id;
188
- hasMore = res.has_more ?? false;
189
- }
190
- return all;
191
- }
192
- // ── Auth (OTP) ──────────────────────────────────────────────────────────
193
- async sendCode(email) {
194
- return this.request("/storefront/auth/send-code", {
195
- method: "POST",
196
- body: JSON.stringify({ email })
197
- });
198
- }
199
- async verifyCode(email, code) {
200
- return this.request("/storefront/auth/verify-code", {
201
- method: "POST",
202
- body: JSON.stringify({ email, code })
203
- });
204
- }
205
- // ── Customer Analytics ──────────────────────────────────────────────────
206
- async getCustomerAnalytics(customerId, customerName) {
207
- try {
208
- const res = await this.request(
209
- "/analytics/customers?limit=200"
210
- );
211
- const byId = res.customers?.find((c) => c.customer_id === customerId);
212
- if (byId) return byId;
213
- if (customerName) {
214
- const normalized = customerName.toLowerCase().trim();
215
- return res.customers?.find(
216
- (c) => c.customer_name?.toLowerCase().trim() === normalized
217
- ) ?? null;
218
- }
219
- return null;
220
- } catch {
221
- return null;
222
- }
223
- }
224
- // ── Locations ───────────────────────────────────────────────────────────
225
- async listLocations() {
226
- return this.request("/locations");
227
- }
228
- // ── COA ─────────────────────────────────────────────────────────────────
229
- getCOAEmbedUrl(productId) {
230
- return `${this.baseUrl}/v1/stores/${this.storeId}/coa/${productId}/embed`;
231
- }
232
- // ── Analytics / Storefront Sessions ─────────────────────────────────────
233
- async createSession(params) {
234
- return this.request("/storefront/sessions", {
235
- method: "POST",
236
- body: JSON.stringify(params)
237
- });
238
- }
239
- async updateSession(sessionId, params) {
240
- return this.request(`/storefront/sessions/${sessionId}`, {
241
- method: "PATCH",
242
- body: JSON.stringify(params)
243
- });
244
- }
245
- async trackEvent(params) {
246
- return this.request("/storefront/events", {
247
- method: "POST",
248
- body: JSON.stringify(params)
249
- });
250
- }
251
- // ── Media Signing ──────────────────────────────────────────────────────
252
- static encodeBase64Url(url) {
253
- if (typeof Buffer !== "undefined") {
254
- return Buffer.from(url, "utf-8").toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
255
- }
256
- return btoa(url).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
257
- }
258
- static signMedia(signingSecret, encodedUrl, w, q, f) {
259
- const payload = `${signingSecret}|${encodedUrl}|${w}|${q}|${f}`;
260
- let h1 = 2166136261;
261
- let h2 = 3421674724;
262
- for (let i = 0; i < payload.length; i++) {
263
- const c = payload.charCodeAt(i);
264
- h1 ^= c;
265
- h1 = Math.imul(h1, 16777619);
266
- h2 ^= c;
267
- h2 = Math.imul(h2, 16777629);
268
- }
269
- return ((h1 >>> 0).toString(16).padStart(8, "0") + (h2 >>> 0).toString(16).padStart(8, "0")).slice(0, 16);
270
- }
271
- };
272
-
273
- exports.WhaleClient = WhaleClient;
274
- //# sourceMappingURL=chunk-XMLH3TLA.cjs.map
275
- //# sourceMappingURL=chunk-XMLH3TLA.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/client.ts"],"names":[],"mappings":"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","file":"chunk-XMLH3TLA.cjs","sourcesContent":["import type {\n Cart,\n CartItem,\n Customer,\n CustomerAnalytics,\n ListResponse,\n Location,\n Order,\n PaymentData,\n Product,\n SendCodeResponse,\n StorefrontSession,\n VerifyCodeResponse,\n WhaleStorefrontConfig,\n EventType,\n} from './types.js'\n\n// ─── WhaleClient ────────────────────────────────────────────────────────────\n// Stateless HTTP wrapper around whale-gateway. Works server-side and client-side.\n// No React, no browser APIs (except fetch).\n\nexport class WhaleClient {\n readonly storeId: string\n readonly apiKey: string\n readonly gatewayUrl: string\n readonly proxyPath: string\n\n private _sessionToken: string | null = null\n\n constructor(config: WhaleStorefrontConfig) {\n this.storeId = config.storeId\n this.apiKey = config.apiKey\n this.gatewayUrl = config.gatewayUrl || 'https://whale-gateway.fly.dev'\n this.proxyPath = config.proxyPath || '/api/gw'\n }\n\n // ── Token Management ────────────────────────────────────────────────────\n\n setSessionToken(token: string | null): void {\n this._sessionToken = token\n }\n\n getSessionToken(): string | null {\n return this._sessionToken\n }\n\n // ── Base URL ────────────────────────────────────────────────────────────\n\n private get baseUrl(): string {\n const isServer = typeof window === 'undefined'\n return isServer ? this.gatewayUrl : this.proxyPath\n }\n\n // ── Base Fetcher ────────────────────────────────────────────────────────\n\n private async request<T = unknown>(\n path: string,\n options: RequestInit = {},\n opts?: { revalidate?: number }\n ): Promise<T> {\n const url = `${this.baseUrl}/v1/stores/${this.storeId}${path}`\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n }\n if (this._sessionToken) {\n headers['Authorization'] = `Bearer ${this._sessionToken}`\n }\n\n const fetchOptions: RequestInit & { next?: { revalidate?: number } } = {\n ...options,\n headers: {\n ...headers,\n ...((options.headers as Record<string, string>) ?? {}),\n },\n }\n\n if (opts?.revalidate !== undefined) {\n fetchOptions.next = { revalidate: opts.revalidate }\n }\n\n const res = await fetch(url, fetchOptions)\n\n if (!res.ok) {\n let message = `Gateway error ${res.status}: ${res.statusText}`\n try {\n const body = await res.json()\n if (body?.message) message = body.message\n else if (body?.error) message = body.error\n } catch {\n // ignore parse errors\n }\n const err = new Error(message) as Error & { status: number }\n err.status = res.status\n throw err\n }\n\n if (res.status === 204) return undefined as T\n return res.json() as Promise<T>\n }\n\n // ── Products ────────────────────────────────────────────────────────────\n\n async listProducts(params?: {\n limit?: number\n starting_after?: string\n status?: string\n }): Promise<ListResponse<Product>> {\n const sp = new URLSearchParams()\n if (params?.limit) sp.set('limit', String(params.limit))\n if (params?.starting_after) sp.set('starting_after', params.starting_after)\n if (params?.status) sp.set('status', params.status)\n const qs = sp.toString()\n return this.request<ListResponse<Product>>(`/products${qs ? `?${qs}` : ''}`)\n }\n\n async getProduct(id: string): Promise<Product> {\n return this.request<Product>(`/products/${id}`)\n }\n\n async getAllProducts(options?: {\n status?: string\n maxPages?: number\n revalidate?: number\n filter?: (product: Product) => boolean\n }): Promise<Product[]> {\n const all: Product[] = []\n let cursor: string | undefined\n let hasMore = true\n let pages = 0\n const maxPages = options?.maxPages ?? 20\n\n while (hasMore && pages < maxPages) {\n const params = new URLSearchParams({ limit: '100' })\n if (options?.status) params.set('status', options.status)\n else params.set('status', 'published')\n if (cursor) params.set('starting_after', cursor)\n\n const data = await this.request<ListResponse<Product>>(\n `/products?${params}`,\n {},\n options?.revalidate !== undefined ? { revalidate: options.revalidate } : undefined\n )\n\n if (!data.data || data.data.length === 0) break\n\n for (const p of data.data) {\n if (!options?.filter || options.filter(p)) {\n all.push(p)\n }\n cursor = p.id\n }\n\n hasMore = data.has_more\n pages++\n }\n\n return all\n }\n\n // ── Cart ────────────────────────────────────────────────────────────────\n\n async createCart(customerEmail?: string): Promise<Cart> {\n return this.request<Cart>('/cart', {\n method: 'POST',\n body: JSON.stringify(customerEmail ? { customer_email: customerEmail } : {}),\n })\n }\n\n async getCart(cartId: string): Promise<Cart> {\n return this.request<Cart>(`/cart/${cartId}`)\n }\n\n async addToCart(\n cartId: string,\n productId: string,\n quantity: number,\n options?: { tier?: string; unitPrice?: number }\n ): Promise<CartItem> {\n return this.request<CartItem>(`/cart/${cartId}/items`, {\n method: 'POST',\n body: JSON.stringify({\n product_id: productId,\n quantity,\n ...(options?.tier !== undefined && { tier: options.tier }),\n ...(options?.unitPrice !== undefined && { unit_price: options.unitPrice }),\n }),\n })\n }\n\n async updateCartItem(cartId: string, itemId: string, quantity: number): Promise<Cart> {\n return this.request<Cart>(`/cart/${cartId}/items/${itemId}`, {\n method: 'PATCH',\n body: JSON.stringify({ quantity }),\n })\n }\n\n async removeCartItem(cartId: string, itemId: string): Promise<void> {\n return this.request<void>(`/cart/${cartId}/items/${itemId}`, {\n method: 'DELETE',\n })\n }\n\n // ── Checkout ────────────────────────────────────────────────────────────\n\n async checkout(\n cartId: string,\n customerEmail?: string,\n payment?: PaymentData\n ): Promise<Order> {\n return this.request<Order>('/checkout', {\n method: 'POST',\n body: JSON.stringify({\n cart_id: cartId,\n ...(customerEmail && { customer_email: customerEmail }),\n ...(payment && {\n payment_method: payment.payment_method,\n ...(payment.opaque_data && { opaque_data: payment.opaque_data }),\n ...(payment.billTo && { bill_to: payment.billTo }),\n ...(payment.shipTo && { ship_to: payment.shipTo }),\n }),\n }),\n })\n }\n\n // ── Customers ───────────────────────────────────────────────────────────\n\n async findCustomer(query: string): Promise<Customer[]> {\n const encoded = encodeURIComponent(query)\n const res = await this.request<{ data: Customer[] } | Customer[]>(`/customers?query=${encoded}`)\n return Array.isArray(res) ? res : res?.data ?? []\n }\n\n async getCustomer(id: string): Promise<Customer> {\n return this.request<Customer>(`/customers/${id}`)\n }\n\n async createCustomer(data: {\n first_name: string\n last_name: string\n email: string\n phone?: string\n }): Promise<Customer> {\n return this.request<Customer>('/customers', {\n method: 'POST',\n body: JSON.stringify(data),\n })\n }\n\n // ── Orders ──────────────────────────────────────────────────────────────\n\n async listOrders(params?: {\n customer_id?: string\n limit?: number\n starting_after?: string\n }): Promise<ListResponse<Order>> {\n const sp = new URLSearchParams()\n if (params?.customer_id) sp.set('customer_id', params.customer_id)\n if (params?.limit) sp.set('limit', String(params.limit))\n if (params?.starting_after) sp.set('starting_after', params.starting_after)\n const qs = sp.toString()\n return this.request<ListResponse<Order>>(`/orders${qs ? `?${qs}` : ''}`)\n }\n\n async getOrder(id: string): Promise<Order> {\n return this.request<Order>(`/orders/${id}`)\n }\n\n async getCustomerOrders(customerId: string): Promise<Order[]> {\n const encoded = encodeURIComponent(customerId)\n const all: Order[] = []\n let cursor: string | undefined\n let hasMore = true\n\n while (hasMore) {\n const params = new URLSearchParams({ customer_id: encoded, limit: '100' })\n if (cursor) params.set('starting_after', cursor)\n\n const res = await this.request<ListResponse<Order>>(`/orders?${params}`)\n const items = res?.data ?? []\n if (items.length === 0) break\n\n all.push(...items)\n cursor = items[items.length - 1].id\n hasMore = res.has_more ?? false\n }\n\n return all\n }\n\n // ── Auth (OTP) ──────────────────────────────────────────────────────────\n\n async sendCode(email: string): Promise<SendCodeResponse> {\n return this.request<SendCodeResponse>('/storefront/auth/send-code', {\n method: 'POST',\n body: JSON.stringify({ email }),\n })\n }\n\n async verifyCode(email: string, code: string): Promise<VerifyCodeResponse> {\n return this.request<VerifyCodeResponse>('/storefront/auth/verify-code', {\n method: 'POST',\n body: JSON.stringify({ email, code }),\n })\n }\n\n // ── Customer Analytics ──────────────────────────────────────────────────\n\n async getCustomerAnalytics(\n customerId: string,\n customerName?: string\n ): Promise<CustomerAnalytics | null> {\n try {\n const res = await this.request<{ customers: CustomerAnalytics[] }>(\n '/analytics/customers?limit=200'\n )\n const byId = res.customers?.find((c) => c.customer_id === customerId)\n if (byId) return byId\n if (customerName) {\n const normalized = customerName.toLowerCase().trim()\n return (\n res.customers?.find(\n (c) => c.customer_name?.toLowerCase().trim() === normalized\n ) ?? null\n )\n }\n return null\n } catch {\n return null\n }\n }\n\n // ── Locations ───────────────────────────────────────────────────────────\n\n async listLocations(): Promise<ListResponse<Location>> {\n return this.request<ListResponse<Location>>('/locations')\n }\n\n // ── COA ─────────────────────────────────────────────────────────────────\n\n getCOAEmbedUrl(productId: string): string {\n return `${this.baseUrl}/v1/stores/${this.storeId}/coa/${productId}/embed`\n }\n\n // ── Analytics / Storefront Sessions ─────────────────────────────────────\n\n async createSession(params: {\n user_agent?: string\n referrer?: string\n }): Promise<StorefrontSession> {\n return this.request<StorefrontSession>('/storefront/sessions', {\n method: 'POST',\n body: JSON.stringify(params),\n })\n }\n\n async updateSession(\n sessionId: string,\n params: { last_active_at?: string; customer_id?: string }\n ): Promise<StorefrontSession> {\n return this.request<StorefrontSession>(`/storefront/sessions/${sessionId}`, {\n method: 'PATCH',\n body: JSON.stringify(params),\n })\n }\n\n async trackEvent(params: {\n session_id: string\n event_type: EventType\n event_data?: Record<string, unknown>\n }): Promise<void> {\n return this.request<void>('/storefront/events', {\n method: 'POST',\n body: JSON.stringify(params),\n })\n }\n\n // ── Media Signing ──────────────────────────────────────────────────────\n\n static encodeBase64Url(url: string): string {\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(url, 'utf-8')\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '')\n }\n return btoa(url)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '')\n }\n\n static signMedia(\n signingSecret: string,\n encodedUrl: string,\n w: string,\n q: string,\n f: string\n ): string {\n const payload = `${signingSecret}|${encodedUrl}|${w}|${q}|${f}`\n // FNV dual-hash — matches gateway's media-signature.ts\n let h1 = 0x811c9dc5\n let h2 = 0xcbf29ce4\n for (let i = 0; i < payload.length; i++) {\n const c = payload.charCodeAt(i)\n h1 ^= c\n h1 = Math.imul(h1, 0x01000193)\n h2 ^= c\n h2 = Math.imul(h2, 0x0100019d)\n }\n return (\n (h1 >>> 0).toString(16).padStart(8, '0') +\n (h2 >>> 0).toString(16).padStart(8, '0')\n ).slice(0, 16)\n }\n}\n"]}