@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 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"]}