@neowhale/storefront 0.1.1 → 0.2.1
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 +485 -0
- package/dist/chunk-CUVDHOQ4.js +90 -0
- package/dist/chunk-CUVDHOQ4.js.map +1 -0
- package/dist/chunk-DAM7NZCI.cjs +92 -0
- package/dist/chunk-DAM7NZCI.cjs.map +1 -0
- package/dist/chunk-OP4LOUCV.cjs +291 -0
- package/dist/chunk-OP4LOUCV.cjs.map +1 -0
- package/dist/chunk-QIIKQ7DN.js +288 -0
- package/dist/chunk-QIIKQ7DN.js.map +1 -0
- package/dist/client-y0V1x0px.d.cts +305 -0
- package/dist/client-y0V1x0px.d.ts +305 -0
- package/dist/index.cjs +11 -2
- package/dist/index.d.cts +14 -267
- package/dist/index.d.ts +14 -267
- package/dist/index.js +2 -1
- package/dist/next/index.cjs +7 -7
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +1 -1
- package/dist/next/index.d.ts +1 -1
- package/dist/next/index.js +4 -4
- package/dist/next/index.js.map +1 -1
- package/dist/pixel-manager-CYtRIGo0.d.ts +10 -0
- package/dist/pixel-manager-Db6Czwr2.d.cts +10 -0
- package/dist/react/index.cjs +74 -17
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +25 -3
- package/dist/react/index.d.ts +25 -3
- package/dist/react/index.js +74 -18
- package/dist/react/index.js.map +1 -1
- package/package.json +8 -1
- package/dist/chunk-PR4PUHVN.js +0 -273
- package/dist/chunk-PR4PUHVN.js.map +0 -1
- package/dist/chunk-XMLH3TLA.cjs +0 -275
- package/dist/chunk-XMLH3TLA.cjs.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
# @neowhale/storefront
|
|
2
|
+
|
|
3
|
+
React and Next.js SDK for WhaleTools storefronts. Provides a typed API client, React hooks for cart/auth/analytics, and Next.js server utilities -- everything needed to build a headless storefront against the Whale Gateway API.
|
|
4
|
+
|
|
5
|
+
**v0.1.2** | [npm](https://www.npmjs.com/package/@neowhale/storefront)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @neowhale/storefront zustand
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Peer dependencies:
|
|
16
|
+
|
|
17
|
+
| Package | Version | Required |
|
|
18
|
+
|-------------|----------|----------|
|
|
19
|
+
| react | >=18 | yes |
|
|
20
|
+
| react-dom | >=18 | yes |
|
|
21
|
+
| zustand | >=5 | yes |
|
|
22
|
+
| next | >=14 | no |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### 1. Environment Variables
|
|
29
|
+
|
|
30
|
+
```env
|
|
31
|
+
NEXT_PUBLIC_WHALE_STORE_ID=your-store-id
|
|
32
|
+
NEXT_PUBLIC_WHALE_API_KEY=your-api-key
|
|
33
|
+
WHALE_MEDIA_SIGNING_SECRET=your-signing-secret # optional, for signed image URLs
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. Root Layout
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
// app/layout.tsx
|
|
40
|
+
import { WhaleProvider } from '@neowhale/storefront/react';
|
|
41
|
+
import { getAllProducts } from '@neowhale/storefront/next';
|
|
42
|
+
|
|
43
|
+
export default async function RootLayout({ children }: { children: React.ReactNode }) {
|
|
44
|
+
const products = await getAllProducts();
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<html lang="en">
|
|
48
|
+
<body>
|
|
49
|
+
<WhaleProvider
|
|
50
|
+
storeId={process.env.NEXT_PUBLIC_WHALE_STORE_ID!}
|
|
51
|
+
apiKey={process.env.NEXT_PUBLIC_WHALE_API_KEY!}
|
|
52
|
+
products={products}
|
|
53
|
+
>
|
|
54
|
+
{children}
|
|
55
|
+
</WhaleProvider>
|
|
56
|
+
</body>
|
|
57
|
+
</html>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 3. Use Hooks in a Page
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
'use client';
|
|
66
|
+
|
|
67
|
+
import { useProducts, useCart } from '@neowhale/storefront/react';
|
|
68
|
+
|
|
69
|
+
export default function Shop() {
|
|
70
|
+
const { products } = useProducts();
|
|
71
|
+
const { addItem, itemCount, openCart } = useCart();
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<div>
|
|
75
|
+
<button onClick={openCart}>Cart ({itemCount})</button>
|
|
76
|
+
{products.map((p) => (
|
|
77
|
+
<div key={p.id}>
|
|
78
|
+
<h2>{p.name}</h2>
|
|
79
|
+
<button onClick={() => addItem(p.id, 1)}>Add to Cart</button>
|
|
80
|
+
</div>
|
|
81
|
+
))}
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 4. Next.js Config (optional proxy + security headers)
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
// next.config.ts
|
|
91
|
+
import { withSecurityHeaders, whaleGatewayRewrite } from '@neowhale/storefront/next';
|
|
92
|
+
|
|
93
|
+
const nextConfig = {
|
|
94
|
+
headers: withSecurityHeaders(),
|
|
95
|
+
rewrites: async () => ({
|
|
96
|
+
beforeFiles: [whaleGatewayRewrite()],
|
|
97
|
+
}),
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export default nextConfig;
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Entry Points
|
|
106
|
+
|
|
107
|
+
The package ships three entry points, each with ESM, CJS, and TypeScript declarations.
|
|
108
|
+
|
|
109
|
+
| Import path | Purpose |
|
|
110
|
+
|----------------------------------|----------------------------------|
|
|
111
|
+
| `@neowhale/storefront` | Core API client and types |
|
|
112
|
+
| `@neowhale/storefront/react` | React provider, hooks, components|
|
|
113
|
+
| `@neowhale/storefront/next` | Next.js server utilities |
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## API Reference -- Core
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
import { WhaleClient } from '@neowhale/storefront';
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### WhaleClient
|
|
124
|
+
|
|
125
|
+
#### Constructor
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
new WhaleClient(config: WhaleStorefrontConfig)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
| Config field | Type | Default | Description |
|
|
132
|
+
|----------------------|----------|----------------------------------|------------------------------------|
|
|
133
|
+
| storeId | string | -- | Required. Your store UUID. |
|
|
134
|
+
| apiKey | string | -- | Required. API key for the gateway. |
|
|
135
|
+
| gatewayUrl | string | `https://whale-gateway.fly.dev` | Base URL of the Whale Gateway. |
|
|
136
|
+
| proxyPath | string | `/api/gw` | Local proxy route prefix. |
|
|
137
|
+
| mediaSigningSecret | string | -- | Secret for signed image URLs. |
|
|
138
|
+
| supabaseHost | string | -- | Supabase project host. |
|
|
139
|
+
| storagePrefix | string | `whale` | Prefix for local storage keys. |
|
|
140
|
+
| sessionTtl | number | `1800000` (30 min) | Session TTL in milliseconds. |
|
|
141
|
+
| debug | boolean | -- | Enable debug logging. |
|
|
142
|
+
|
|
143
|
+
#### Products
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
client.listProducts(params?) // → ListResponse<Product>
|
|
147
|
+
client.getProduct(id) // → Product
|
|
148
|
+
client.getAllProducts(options?) // → Product[] (auto-paginates, default status='published', maxPages=20)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### Cart
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
client.createCart(customerEmail?) // → Cart
|
|
155
|
+
client.getCart(cartId) // → Cart
|
|
156
|
+
client.addToCart(cartId, productId, quantity, opts?) // → CartItem
|
|
157
|
+
client.updateCartItem(cartId, itemId, quantity) // → Cart
|
|
158
|
+
client.removeCartItem(cartId, itemId) // → void
|
|
159
|
+
client.checkout(cartId, customerEmail?, payment?) // → Order
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### Customers
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
client.findCustomer(query) // → Customer[]
|
|
166
|
+
client.getCustomer(id) // → Customer
|
|
167
|
+
client.createCustomer(data) // → Customer
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
#### Orders
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
client.listOrders(params?) // → ListResponse<Order>
|
|
174
|
+
client.getOrder(id) // → Order
|
|
175
|
+
client.getCustomerOrders(customerId) // → Order[] (auto-paginates)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### Auth
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
client.sendCode(email) // → SendCodeResponse
|
|
182
|
+
client.verifyCode(email, code) // → VerifyCodeResponse
|
|
183
|
+
client.setSessionToken(token) // → void
|
|
184
|
+
client.getSessionToken() // → string | null
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### Analytics
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
client.getCustomerAnalytics(customerId, customerName?) // → CustomerAnalytics | null
|
|
191
|
+
client.createSession(params) // → StorefrontSession
|
|
192
|
+
client.updateSession(sessionId, params) // → StorefrontSession
|
|
193
|
+
client.trackEvent(params) // → void
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### Locations
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
client.listLocations() // → ListResponse<Location>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### COA
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
client.getCOAEmbedUrl(productId) // → string
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### Static Methods
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
WhaleClient.signMedia(secret, encodedUrl, w, q, f) // → string (signed token)
|
|
212
|
+
WhaleClient.encodeBase64Url(url) // → string (base64url-encoded)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## API Reference -- React
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
import {
|
|
221
|
+
WhaleProvider,
|
|
222
|
+
useCart,
|
|
223
|
+
useCartItemCount,
|
|
224
|
+
useCartTotal,
|
|
225
|
+
useAuth,
|
|
226
|
+
useProducts,
|
|
227
|
+
useProduct,
|
|
228
|
+
useAnalytics,
|
|
229
|
+
useCustomerOrders,
|
|
230
|
+
useCustomerAnalytics,
|
|
231
|
+
useWhaleClient,
|
|
232
|
+
} from '@neowhale/storefront/react';
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### WhaleProvider
|
|
236
|
+
|
|
237
|
+
Wrap your application tree with this provider. It accepts all `WhaleStorefrontConfig` fields as props, plus:
|
|
238
|
+
|
|
239
|
+
| Prop | Type | Description |
|
|
240
|
+
|-----------|------------|-----------------------------------------|
|
|
241
|
+
| products | Product[] | Pre-fetched product catalog to hydrate. |
|
|
242
|
+
| children | ReactNode | Application tree. |
|
|
243
|
+
|
|
244
|
+
Internally renders `AuthInitializer`, `CartInitializer`, and `AnalyticsTracker`.
|
|
245
|
+
|
|
246
|
+
### useCart()
|
|
247
|
+
|
|
248
|
+
Manage cart state and operations.
|
|
249
|
+
|
|
250
|
+
| Return field | Type |
|
|
251
|
+
|------------------|------------------------------------------------|
|
|
252
|
+
| cartId | string \| null |
|
|
253
|
+
| items | CartItem[] |
|
|
254
|
+
| itemCount | number |
|
|
255
|
+
| subtotal | number |
|
|
256
|
+
| taxAmount | number |
|
|
257
|
+
| total | number |
|
|
258
|
+
| taxBreakdown | object |
|
|
259
|
+
| cartOpen | boolean |
|
|
260
|
+
| cartLoading | boolean |
|
|
261
|
+
| productImages | Record<string, string> |
|
|
262
|
+
| addItem | (productId: string, qty: number) => Promise |
|
|
263
|
+
| removeItem | (itemId: string) => Promise |
|
|
264
|
+
| updateQuantity | (itemId: string, qty: number) => Promise |
|
|
265
|
+
| openCart | () => void |
|
|
266
|
+
| closeCart | () => void |
|
|
267
|
+
| toggleCart | () => void |
|
|
268
|
+
| initCart | () => Promise |
|
|
269
|
+
| syncCart | () => Promise |
|
|
270
|
+
| clearCart | () => void |
|
|
271
|
+
| checkout | (email?: string, payment?: object) => Promise |
|
|
272
|
+
|
|
273
|
+
### useCartItemCount()
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
const count: number = useCartItemCount();
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Returns the current number of items in the cart. Optimized selector -- only re-renders when the count changes.
|
|
280
|
+
|
|
281
|
+
### useCartTotal()
|
|
282
|
+
|
|
283
|
+
```ts
|
|
284
|
+
const total: number = useCartTotal();
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Returns the cart total. Optimized selector -- only re-renders when the total changes.
|
|
288
|
+
|
|
289
|
+
### useAuth()
|
|
290
|
+
|
|
291
|
+
Manage authentication state and OTP flow.
|
|
292
|
+
|
|
293
|
+
| Return field | Type |
|
|
294
|
+
|------------------|---------------------------------------------------|
|
|
295
|
+
| customer | Customer \| null |
|
|
296
|
+
| authLoading | boolean |
|
|
297
|
+
| sessionToken | string \| null |
|
|
298
|
+
| isAuthenticated | boolean |
|
|
299
|
+
| sendCode | (email: string) => Promise |
|
|
300
|
+
| verifyCode | (email: string, code: string) => Promise |
|
|
301
|
+
| restoreSession | () => Promise |
|
|
302
|
+
| logout | () => void |
|
|
303
|
+
| fetchCustomer | (id: string) => Promise |
|
|
304
|
+
|
|
305
|
+
### useProducts(opts?)
|
|
306
|
+
|
|
307
|
+
Client-side filtering over the products hydrated by `WhaleProvider`.
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
const { products, allProducts, loading } = useProducts({ categoryId: '...', search: 'term' });
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
| Option | Type | Description |
|
|
314
|
+
|-------------|---------|--------------------------------|
|
|
315
|
+
| categoryId | string | Filter by category ID. |
|
|
316
|
+
| search | string | Filter by search term. |
|
|
317
|
+
|
|
318
|
+
Returns `{ products: Product[], allProducts: Product[], loading: false }`.
|
|
319
|
+
|
|
320
|
+
### useProduct(slug)
|
|
321
|
+
|
|
322
|
+
Look up a single product by slug from the hydrated catalog.
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
const { product, loading } = useProduct('blue-dream-3-5g');
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Returns `{ product: Product | null, loading: false }`.
|
|
329
|
+
|
|
330
|
+
### useAnalytics()
|
|
331
|
+
|
|
332
|
+
Track storefront events.
|
|
333
|
+
|
|
334
|
+
| Method | Description |
|
|
335
|
+
|---------------------|--------------------------------------|
|
|
336
|
+
| track() | Send a custom event. |
|
|
337
|
+
| trackPageView() | Record a page view. |
|
|
338
|
+
| trackProductView() | Record a product detail view. |
|
|
339
|
+
| trackCategoryView() | Record a category page view. |
|
|
340
|
+
| trackSearch() | Record a search query. |
|
|
341
|
+
| trackBeginCheckout()| Record checkout initiation. |
|
|
342
|
+
| trackPurchase() | Record a completed purchase. |
|
|
343
|
+
| trackAddToCart() | Record an add-to-cart event. |
|
|
344
|
+
| trackRemoveFromCart()| Record a remove-from-cart event. |
|
|
345
|
+
| linkCustomer() | Associate session with a customer. |
|
|
346
|
+
| getOrCreateSession()| Get or create an analytics session. |
|
|
347
|
+
|
|
348
|
+
### useCustomerOrders()
|
|
349
|
+
|
|
350
|
+
Auto-fetches orders when a customer is authenticated.
|
|
351
|
+
|
|
352
|
+
```ts
|
|
353
|
+
const { orders, loading, refresh } = useCustomerOrders();
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### useCustomerAnalytics()
|
|
357
|
+
|
|
358
|
+
Returns analytics for the authenticated customer.
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
const { analytics, loading } = useCustomerAnalytics();
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### useWhaleClient()
|
|
365
|
+
|
|
366
|
+
Access the underlying `WhaleClient` instance from context.
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
const client = useWhaleClient();
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## API Reference -- Next.js
|
|
375
|
+
|
|
376
|
+
```ts
|
|
377
|
+
import {
|
|
378
|
+
withSecurityHeaders,
|
|
379
|
+
whaleGatewayRewrite,
|
|
380
|
+
createServerClient,
|
|
381
|
+
getAllProducts,
|
|
382
|
+
createImageLoader,
|
|
383
|
+
createAuthMiddleware,
|
|
384
|
+
} from '@neowhale/storefront/next';
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### withSecurityHeaders(extra?)
|
|
388
|
+
|
|
389
|
+
Returns a `headers()` config function for `next.config`. Applies security headers (CSP, HSTS, etc.). Pass an optional object to merge additional headers.
|
|
390
|
+
|
|
391
|
+
```ts
|
|
392
|
+
// next.config.ts
|
|
393
|
+
export default {
|
|
394
|
+
headers: withSecurityHeaders({ 'X-Custom': 'value' }),
|
|
395
|
+
};
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### whaleGatewayRewrite(gatewayUrl?, proxyPath?)
|
|
399
|
+
|
|
400
|
+
Returns a single rewrite rule that proxies `proxyPath` (default `/api/gw`) to the Whale Gateway (default `https://whale-gateway.fly.dev`).
|
|
401
|
+
|
|
402
|
+
```ts
|
|
403
|
+
export default {
|
|
404
|
+
rewrites: async () => ({
|
|
405
|
+
beforeFiles: [whaleGatewayRewrite()],
|
|
406
|
+
}),
|
|
407
|
+
};
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### createServerClient(config?)
|
|
411
|
+
|
|
412
|
+
Creates a `WhaleClient` on the server. Reads `NEXT_PUBLIC_WHALE_STORE_ID`, `NEXT_PUBLIC_WHALE_API_KEY`, and `WHALE_MEDIA_SIGNING_SECRET` from environment variables when config is omitted.
|
|
413
|
+
|
|
414
|
+
```ts
|
|
415
|
+
const client = createServerClient();
|
|
416
|
+
const products = await client.getAllProducts();
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### getAllProducts(options?)
|
|
420
|
+
|
|
421
|
+
Server-side product fetch with ISR caching. Default `revalidate` is 60 seconds.
|
|
422
|
+
|
|
423
|
+
```ts
|
|
424
|
+
const products = await getAllProducts(); // cached, revalidates every 60s
|
|
425
|
+
const products = await getAllProducts({ revalidate: 120 });
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### createImageLoader(config)
|
|
429
|
+
|
|
430
|
+
Returns a Next.js image loader function for use with `next/image`. Handles media signing when a secret is provided.
|
|
431
|
+
|
|
432
|
+
```ts
|
|
433
|
+
import Image from 'next/image';
|
|
434
|
+
import { createImageLoader } from '@neowhale/storefront/next';
|
|
435
|
+
|
|
436
|
+
const loader = createImageLoader({
|
|
437
|
+
mediaSigningSecret: process.env.WHALE_MEDIA_SIGNING_SECRET,
|
|
438
|
+
supabaseHost: 'your-project.supabase.co',
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
<Image loader={loader} src={product.imageUrl} width={400} height={400} alt="" />
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### createAuthMiddleware(options)
|
|
445
|
+
|
|
446
|
+
Returns a Next.js middleware function for protecting routes with session-based auth.
|
|
447
|
+
|
|
448
|
+
```ts
|
|
449
|
+
// middleware.ts
|
|
450
|
+
import { createAuthMiddleware } from '@neowhale/storefront/next';
|
|
451
|
+
|
|
452
|
+
export default createAuthMiddleware({
|
|
453
|
+
protectedPaths: ['/account', '/orders'],
|
|
454
|
+
loginPath: '/login',
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
export const config = { matcher: ['/account/:path*', '/orders/:path*'] };
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
## Key Types
|
|
463
|
+
|
|
464
|
+
| Type | Description |
|
|
465
|
+
|----------------------|--------------------------------------------|
|
|
466
|
+
| WhaleStorefrontConfig| Client constructor config object. |
|
|
467
|
+
| Product | Product with name, slug, price, images, etc.|
|
|
468
|
+
| Cart | Cart with items, totals, tax breakdown. |
|
|
469
|
+
| CartItem | Single cart line item. |
|
|
470
|
+
| Customer | Customer profile. |
|
|
471
|
+
| Order | Completed or pending order. |
|
|
472
|
+
| ListResponse\<T\> | Paginated list: `{ data: T[], meta }`. |
|
|
473
|
+
| SendCodeResponse | Response from OTP send. |
|
|
474
|
+
| VerifyCodeResponse | Response from OTP verify (includes token). |
|
|
475
|
+
| CustomerAnalytics | Aggregated analytics for a customer. |
|
|
476
|
+
| StorefrontSession | Analytics session object. |
|
|
477
|
+
| Location | Store/pickup location. |
|
|
478
|
+
|
|
479
|
+
All types are exported from the root entry point (`@neowhale/storefront`).
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
## License
|
|
484
|
+
|
|
485
|
+
MIT
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// src/pixels/meta-pixel.ts
|
|
2
|
+
var EVENT_MAP = {
|
|
3
|
+
page_view: "PageView",
|
|
4
|
+
product_view: "ViewContent",
|
|
5
|
+
add_to_cart: "AddToCart",
|
|
6
|
+
remove_from_cart: "RemoveFromCart",
|
|
7
|
+
begin_checkout: "InitiateCheckout",
|
|
8
|
+
purchase: "Purchase",
|
|
9
|
+
search: "Search",
|
|
10
|
+
category_view: "ViewContent"
|
|
11
|
+
};
|
|
12
|
+
var MetaPixelProvider = class {
|
|
13
|
+
constructor(pixelId) {
|
|
14
|
+
this.name = "meta";
|
|
15
|
+
this.loaded = false;
|
|
16
|
+
this.pixelId = pixelId;
|
|
17
|
+
}
|
|
18
|
+
async load() {
|
|
19
|
+
if (typeof window === "undefined") return;
|
|
20
|
+
if (this.loaded) return;
|
|
21
|
+
const w = window;
|
|
22
|
+
if (!w.fbq) {
|
|
23
|
+
const n = w.fbq = function(...args) {
|
|
24
|
+
n.callMethod ? n.callMethod.apply(n, args) : n.queue.push(args);
|
|
25
|
+
};
|
|
26
|
+
if (!w._fbq) w._fbq = n;
|
|
27
|
+
n.push = n;
|
|
28
|
+
n.loaded = true;
|
|
29
|
+
n.version = "2.0";
|
|
30
|
+
n.queue = [];
|
|
31
|
+
}
|
|
32
|
+
await new Promise((resolve) => {
|
|
33
|
+
const script = document.createElement("script");
|
|
34
|
+
script.async = true;
|
|
35
|
+
script.src = "https://connect.facebook.net/en_US/fbevents.js";
|
|
36
|
+
script.onload = () => resolve();
|
|
37
|
+
script.onerror = () => resolve();
|
|
38
|
+
const first = document.getElementsByTagName("script")[0];
|
|
39
|
+
first?.parentNode?.insertBefore(script, first);
|
|
40
|
+
});
|
|
41
|
+
w.fbq("init", this.pixelId);
|
|
42
|
+
w.fbq("track", "PageView");
|
|
43
|
+
this.loaded = true;
|
|
44
|
+
}
|
|
45
|
+
track(event, params) {
|
|
46
|
+
if (typeof window === "undefined") return;
|
|
47
|
+
const w = window;
|
|
48
|
+
if (typeof w.fbq !== "function") return;
|
|
49
|
+
const metaEvent = EVENT_MAP[event];
|
|
50
|
+
if (!metaEvent) return;
|
|
51
|
+
if (params && Object.keys(params).length > 0) {
|
|
52
|
+
w.fbq("track", metaEvent, params);
|
|
53
|
+
} else {
|
|
54
|
+
w.fbq("track", metaEvent);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
isLoaded() {
|
|
58
|
+
return this.loaded;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// src/pixels/pixel-manager.ts
|
|
63
|
+
var PROVIDER_REGISTRY = {
|
|
64
|
+
meta: MetaPixelProvider
|
|
65
|
+
};
|
|
66
|
+
var PixelManager = class {
|
|
67
|
+
constructor(configs) {
|
|
68
|
+
this.providers = [];
|
|
69
|
+
for (const config of configs) {
|
|
70
|
+
const Ctor = PROVIDER_REGISTRY[config.provider];
|
|
71
|
+
if (Ctor) {
|
|
72
|
+
this.providers.push(new Ctor(config.pixel_id));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async initialize() {
|
|
77
|
+
await Promise.all(this.providers.map((p) => p.load()));
|
|
78
|
+
}
|
|
79
|
+
track(event, params) {
|
|
80
|
+
for (const provider of this.providers) {
|
|
81
|
+
if (provider.isLoaded()) {
|
|
82
|
+
provider.track(event, params);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export { PixelManager };
|
|
89
|
+
//# sourceMappingURL=chunk-CUVDHOQ4.js.map
|
|
90
|
+
//# sourceMappingURL=chunk-CUVDHOQ4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/pixels/meta-pixel.ts","../src/pixels/pixel-manager.ts"],"names":[],"mappings":"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","file":"chunk-CUVDHOQ4.js","sourcesContent":["import type { PixelProvider } from './types.js'\n\n/** Map SDK event names → Meta standard event names */\nconst EVENT_MAP: Record<string, string> = {\n page_view: 'PageView',\n product_view: 'ViewContent',\n add_to_cart: 'AddToCart',\n remove_from_cart: 'RemoveFromCart',\n begin_checkout: 'InitiateCheckout',\n purchase: 'Purchase',\n search: 'Search',\n category_view: 'ViewContent',\n}\n\nexport class MetaPixelProvider implements PixelProvider {\n readonly name = 'meta'\n private pixelId: string\n private loaded = false\n\n constructor(pixelId: string) {\n this.pixelId = pixelId\n }\n\n async load(): Promise<void> {\n if (typeof window === 'undefined') return\n if (this.loaded) return\n\n // Initialize fbq queue\n const w = window as any\n if (!w.fbq) {\n const n: any = (w.fbq = function (...args: any[]) {\n n.callMethod ? n.callMethod.apply(n, args) : n.queue.push(args)\n })\n if (!w._fbq) w._fbq = n\n n.push = n\n n.loaded = true\n n.version = '2.0'\n n.queue = []\n }\n\n // Inject script\n await new Promise<void>((resolve) => {\n const script = document.createElement('script')\n script.async = true\n script.src = 'https://connect.facebook.net/en_US/fbevents.js'\n script.onload = () => resolve()\n script.onerror = () => resolve() // don't block on load failure\n const first = document.getElementsByTagName('script')[0]\n first?.parentNode?.insertBefore(script, first)\n })\n\n w.fbq('init', this.pixelId)\n w.fbq('track', 'PageView')\n this.loaded = true\n }\n\n track(event: string, params?: Record<string, unknown>): void {\n if (typeof window === 'undefined') return\n const w = window as any\n if (typeof w.fbq !== 'function') return\n\n const metaEvent = EVENT_MAP[event]\n if (!metaEvent) return\n\n if (params && Object.keys(params).length > 0) {\n w.fbq('track', metaEvent, params)\n } else {\n w.fbq('track', metaEvent)\n }\n }\n\n isLoaded(): boolean {\n return this.loaded\n }\n}\n","import type { PixelConfig, PixelProvider } from './types.js'\nimport { MetaPixelProvider } from './meta-pixel.js'\n\nconst PROVIDER_REGISTRY: Record<string, new (pixelId: string) => PixelProvider> = {\n meta: MetaPixelProvider,\n}\n\nexport class PixelManager {\n private providers: PixelProvider[] = []\n\n constructor(configs: PixelConfig[]) {\n for (const config of configs) {\n const Ctor = PROVIDER_REGISTRY[config.provider]\n if (Ctor) {\n this.providers.push(new Ctor(config.pixel_id))\n }\n }\n }\n\n async initialize(): Promise<void> {\n await Promise.all(this.providers.map((p) => p.load()))\n }\n\n track(event: string, params?: Record<string, unknown>): void {\n for (const provider of this.providers) {\n if (provider.isLoaded()) {\n provider.track(event, params)\n }\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/pixels/meta-pixel.ts
|
|
4
|
+
var EVENT_MAP = {
|
|
5
|
+
page_view: "PageView",
|
|
6
|
+
product_view: "ViewContent",
|
|
7
|
+
add_to_cart: "AddToCart",
|
|
8
|
+
remove_from_cart: "RemoveFromCart",
|
|
9
|
+
begin_checkout: "InitiateCheckout",
|
|
10
|
+
purchase: "Purchase",
|
|
11
|
+
search: "Search",
|
|
12
|
+
category_view: "ViewContent"
|
|
13
|
+
};
|
|
14
|
+
var MetaPixelProvider = class {
|
|
15
|
+
constructor(pixelId) {
|
|
16
|
+
this.name = "meta";
|
|
17
|
+
this.loaded = false;
|
|
18
|
+
this.pixelId = pixelId;
|
|
19
|
+
}
|
|
20
|
+
async load() {
|
|
21
|
+
if (typeof window === "undefined") return;
|
|
22
|
+
if (this.loaded) return;
|
|
23
|
+
const w = window;
|
|
24
|
+
if (!w.fbq) {
|
|
25
|
+
const n = w.fbq = function(...args) {
|
|
26
|
+
n.callMethod ? n.callMethod.apply(n, args) : n.queue.push(args);
|
|
27
|
+
};
|
|
28
|
+
if (!w._fbq) w._fbq = n;
|
|
29
|
+
n.push = n;
|
|
30
|
+
n.loaded = true;
|
|
31
|
+
n.version = "2.0";
|
|
32
|
+
n.queue = [];
|
|
33
|
+
}
|
|
34
|
+
await new Promise((resolve) => {
|
|
35
|
+
const script = document.createElement("script");
|
|
36
|
+
script.async = true;
|
|
37
|
+
script.src = "https://connect.facebook.net/en_US/fbevents.js";
|
|
38
|
+
script.onload = () => resolve();
|
|
39
|
+
script.onerror = () => resolve();
|
|
40
|
+
const first = document.getElementsByTagName("script")[0];
|
|
41
|
+
first?.parentNode?.insertBefore(script, first);
|
|
42
|
+
});
|
|
43
|
+
w.fbq("init", this.pixelId);
|
|
44
|
+
w.fbq("track", "PageView");
|
|
45
|
+
this.loaded = true;
|
|
46
|
+
}
|
|
47
|
+
track(event, params) {
|
|
48
|
+
if (typeof window === "undefined") return;
|
|
49
|
+
const w = window;
|
|
50
|
+
if (typeof w.fbq !== "function") return;
|
|
51
|
+
const metaEvent = EVENT_MAP[event];
|
|
52
|
+
if (!metaEvent) return;
|
|
53
|
+
if (params && Object.keys(params).length > 0) {
|
|
54
|
+
w.fbq("track", metaEvent, params);
|
|
55
|
+
} else {
|
|
56
|
+
w.fbq("track", metaEvent);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
isLoaded() {
|
|
60
|
+
return this.loaded;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// src/pixels/pixel-manager.ts
|
|
65
|
+
var PROVIDER_REGISTRY = {
|
|
66
|
+
meta: MetaPixelProvider
|
|
67
|
+
};
|
|
68
|
+
var PixelManager = class {
|
|
69
|
+
constructor(configs) {
|
|
70
|
+
this.providers = [];
|
|
71
|
+
for (const config of configs) {
|
|
72
|
+
const Ctor = PROVIDER_REGISTRY[config.provider];
|
|
73
|
+
if (Ctor) {
|
|
74
|
+
this.providers.push(new Ctor(config.pixel_id));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async initialize() {
|
|
79
|
+
await Promise.all(this.providers.map((p) => p.load()));
|
|
80
|
+
}
|
|
81
|
+
track(event, params) {
|
|
82
|
+
for (const provider of this.providers) {
|
|
83
|
+
if (provider.isLoaded()) {
|
|
84
|
+
provider.track(event, params);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
exports.PixelManager = PixelManager;
|
|
91
|
+
//# sourceMappingURL=chunk-DAM7NZCI.cjs.map
|
|
92
|
+
//# sourceMappingURL=chunk-DAM7NZCI.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/pixels/meta-pixel.ts","../src/pixels/pixel-manager.ts"],"names":[],"mappings":"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","file":"chunk-DAM7NZCI.cjs","sourcesContent":["import type { PixelProvider } from './types.js'\n\n/** Map SDK event names → Meta standard event names */\nconst EVENT_MAP: Record<string, string> = {\n page_view: 'PageView',\n product_view: 'ViewContent',\n add_to_cart: 'AddToCart',\n remove_from_cart: 'RemoveFromCart',\n begin_checkout: 'InitiateCheckout',\n purchase: 'Purchase',\n search: 'Search',\n category_view: 'ViewContent',\n}\n\nexport class MetaPixelProvider implements PixelProvider {\n readonly name = 'meta'\n private pixelId: string\n private loaded = false\n\n constructor(pixelId: string) {\n this.pixelId = pixelId\n }\n\n async load(): Promise<void> {\n if (typeof window === 'undefined') return\n if (this.loaded) return\n\n // Initialize fbq queue\n const w = window as any\n if (!w.fbq) {\n const n: any = (w.fbq = function (...args: any[]) {\n n.callMethod ? n.callMethod.apply(n, args) : n.queue.push(args)\n })\n if (!w._fbq) w._fbq = n\n n.push = n\n n.loaded = true\n n.version = '2.0'\n n.queue = []\n }\n\n // Inject script\n await new Promise<void>((resolve) => {\n const script = document.createElement('script')\n script.async = true\n script.src = 'https://connect.facebook.net/en_US/fbevents.js'\n script.onload = () => resolve()\n script.onerror = () => resolve() // don't block on load failure\n const first = document.getElementsByTagName('script')[0]\n first?.parentNode?.insertBefore(script, first)\n })\n\n w.fbq('init', this.pixelId)\n w.fbq('track', 'PageView')\n this.loaded = true\n }\n\n track(event: string, params?: Record<string, unknown>): void {\n if (typeof window === 'undefined') return\n const w = window as any\n if (typeof w.fbq !== 'function') return\n\n const metaEvent = EVENT_MAP[event]\n if (!metaEvent) return\n\n if (params && Object.keys(params).length > 0) {\n w.fbq('track', metaEvent, params)\n } else {\n w.fbq('track', metaEvent)\n }\n }\n\n isLoaded(): boolean {\n return this.loaded\n }\n}\n","import type { PixelConfig, PixelProvider } from './types.js'\nimport { MetaPixelProvider } from './meta-pixel.js'\n\nconst PROVIDER_REGISTRY: Record<string, new (pixelId: string) => PixelProvider> = {\n meta: MetaPixelProvider,\n}\n\nexport class PixelManager {\n private providers: PixelProvider[] = []\n\n constructor(configs: PixelConfig[]) {\n for (const config of configs) {\n const Ctor = PROVIDER_REGISTRY[config.provider]\n if (Ctor) {\n this.providers.push(new Ctor(config.pixel_id))\n }\n }\n }\n\n async initialize(): Promise<void> {\n await Promise.all(this.providers.map((p) => p.load()))\n }\n\n track(event: string, params?: Record<string, unknown>): void {\n for (const provider of this.providers) {\n if (provider.isLoaded()) {\n provider.track(event, params)\n }\n }\n }\n}\n"]}
|