@neowhale/storefront 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,657 @@
1
+ import { WhaleClient } from '../chunk-PR4PUHVN.js';
2
+ import { createContext, useContext, useRef, useCallback, useEffect, useMemo, useState } from 'react';
3
+ import { usePathname } from 'next/navigation';
4
+ import { createStore } from 'zustand/vanilla';
5
+ import { persist } from 'zustand/middleware';
6
+ import { useStore } from 'zustand';
7
+ import { jsxs, jsx } from 'react/jsx-runtime';
8
+
9
+ var WhaleContext = createContext(null);
10
+ function createCartStore(client, storagePrefix, onAddToCart, onRemoveFromCart) {
11
+ return createStore()(
12
+ persist(
13
+ (set, get) => ({
14
+ // ── Initial state ────────────────────────────────────────────────
15
+ cartId: null,
16
+ items: [],
17
+ itemCount: 0,
18
+ subtotal: 0,
19
+ taxAmount: 0,
20
+ total: 0,
21
+ taxBreakdown: [],
22
+ cartOpen: false,
23
+ cartLoading: false,
24
+ productImages: {},
25
+ addItemInFlight: false,
26
+ // ── Cart UI ──────────────────────────────────────────────────────
27
+ openCart: () => set({ cartOpen: true }),
28
+ closeCart: () => set({ cartOpen: false }),
29
+ toggleCart: () => set((s) => ({ cartOpen: !s.cartOpen })),
30
+ // ── Cart data ────────────────────────────────────────────────────
31
+ initCart: async () => {
32
+ const { cartId, syncCart } = get();
33
+ if (cartId) {
34
+ try {
35
+ await syncCart();
36
+ } catch {
37
+ const cart = await client.createCart();
38
+ applyCart(set, get, cart);
39
+ }
40
+ return;
41
+ }
42
+ try {
43
+ const cart = await client.createCart();
44
+ applyCart(set, get, cart);
45
+ } catch (err) {
46
+ console.error("[whale-storefront] initCart failed:", err);
47
+ }
48
+ },
49
+ syncCart: async () => {
50
+ const { cartId, productImages } = get();
51
+ if (!cartId) return;
52
+ try {
53
+ const cart = await client.getCart(cartId);
54
+ const items = (cart.items ?? []).map((item) => ({
55
+ ...item,
56
+ image_url: item.image_url || productImages[item.product_id] || null
57
+ }));
58
+ set({
59
+ items,
60
+ itemCount: cart.item_count ?? 0,
61
+ subtotal: cart.subtotal ?? 0,
62
+ taxAmount: cart.tax_amount ?? 0,
63
+ total: cart.total ?? 0,
64
+ taxBreakdown: cart.tax_breakdown ?? []
65
+ });
66
+ } catch (err) {
67
+ console.error("[whale-storefront] syncCart failed:", err);
68
+ throw err;
69
+ }
70
+ },
71
+ addItem: async (productId, quantity, tier, unitPrice, imageUrl, productName) => {
72
+ if (get().addItemInFlight) return;
73
+ set({ cartLoading: true, addItemInFlight: true });
74
+ try {
75
+ let { cartId } = get();
76
+ if (!cartId) {
77
+ await get().initCart();
78
+ cartId = get().cartId;
79
+ }
80
+ if (!cartId) throw new Error("Could not initialise cart");
81
+ if (imageUrl) {
82
+ set((s) => ({ productImages: { ...s.productImages, [productId]: imageUrl } }));
83
+ }
84
+ try {
85
+ await client.addToCart(cartId, productId, quantity, { tier, unitPrice });
86
+ } catch (err) {
87
+ const status = err.status;
88
+ if (status === 404 || status === 410) {
89
+ const newCart = await client.createCart();
90
+ set({ cartId: newCart.id });
91
+ await client.addToCart(newCart.id, productId, quantity, { tier, unitPrice });
92
+ } else {
93
+ throw err;
94
+ }
95
+ }
96
+ await get().syncCart();
97
+ onAddToCart?.(productId, productName || "", quantity, unitPrice || 0, tier);
98
+ } finally {
99
+ set({ cartLoading: false, addItemInFlight: false });
100
+ }
101
+ },
102
+ updateQuantity: async (itemId, quantity) => {
103
+ set({ cartLoading: true });
104
+ try {
105
+ const { cartId } = get();
106
+ if (!cartId) return;
107
+ await client.updateCartItem(cartId, itemId, quantity);
108
+ await get().syncCart();
109
+ } finally {
110
+ set({ cartLoading: false });
111
+ }
112
+ },
113
+ removeItem: async (itemId, productName) => {
114
+ set({ cartLoading: true });
115
+ try {
116
+ const { cartId, items } = get();
117
+ if (!cartId) return;
118
+ const item = items.find((i) => i.id === itemId);
119
+ await client.removeCartItem(cartId, itemId);
120
+ await get().syncCart();
121
+ if (item) {
122
+ onRemoveFromCart?.(item.product_id, productName || item.product_name);
123
+ }
124
+ } finally {
125
+ set({ cartLoading: false });
126
+ }
127
+ },
128
+ clearCart: () => {
129
+ set({
130
+ cartId: null,
131
+ items: [],
132
+ itemCount: 0,
133
+ subtotal: 0,
134
+ taxAmount: 0,
135
+ total: 0,
136
+ taxBreakdown: [],
137
+ productImages: {}
138
+ });
139
+ },
140
+ checkout: async (customerEmail, payment) => {
141
+ const { cartId } = get();
142
+ if (!cartId) throw new Error("No active cart");
143
+ set({ cartLoading: true });
144
+ try {
145
+ const order = await client.checkout(cartId, customerEmail, payment);
146
+ set({
147
+ cartId: null,
148
+ items: [],
149
+ itemCount: 0,
150
+ subtotal: 0,
151
+ taxAmount: 0,
152
+ total: 0,
153
+ taxBreakdown: [],
154
+ productImages: {},
155
+ cartOpen: false
156
+ });
157
+ return order;
158
+ } finally {
159
+ set({ cartLoading: false });
160
+ }
161
+ }
162
+ }),
163
+ // ── Persist config ─────────────────────────────────────────────────
164
+ {
165
+ name: `${storagePrefix}-cart`,
166
+ partialize: (state) => ({
167
+ cartId: state.cartId,
168
+ productImages: state.productImages
169
+ })
170
+ }
171
+ )
172
+ );
173
+ }
174
+ function applyCart(set, get, cart) {
175
+ const productImages = get().productImages;
176
+ const items = (cart.items ?? []).map((item) => ({
177
+ ...item,
178
+ image_url: item.image_url || productImages[item.product_id] || null
179
+ }));
180
+ set({
181
+ cartId: cart.id,
182
+ items,
183
+ itemCount: cart.item_count ?? 0,
184
+ subtotal: cart.subtotal ?? 0,
185
+ taxAmount: cart.tax_amount ?? 0,
186
+ total: cart.total ?? 0,
187
+ taxBreakdown: cart.tax_breakdown ?? []
188
+ });
189
+ }
190
+ function createAuthStore(client, storagePrefix) {
191
+ return createStore()(
192
+ persist(
193
+ (set, get) => ({
194
+ // ── Initial state ────────────────────────────────────────────────
195
+ customer: null,
196
+ sessionToken: null,
197
+ sessionExpiresAt: null,
198
+ authLoading: false,
199
+ // ── Actions ──────────────────────────────────────────────────────
200
+ sendOTP: async (email) => {
201
+ set({ authLoading: true });
202
+ try {
203
+ const res = await client.sendCode(email);
204
+ return res.sent;
205
+ } catch (err) {
206
+ const message = err instanceof Error ? err.message : "Could not send code";
207
+ throw new Error(message);
208
+ } finally {
209
+ set({ authLoading: false });
210
+ }
211
+ },
212
+ verifyOTP: async (email, code) => {
213
+ set({ authLoading: true });
214
+ try {
215
+ const res = await client.verifyCode(email, code);
216
+ client.setSessionToken(res.token_hash);
217
+ set({
218
+ sessionToken: res.token_hash,
219
+ sessionExpiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1e3).toISOString()
220
+ });
221
+ if (res.customer?.id) {
222
+ try {
223
+ const full = await client.getCustomer(res.customer.id);
224
+ set({ customer: full });
225
+ } catch {
226
+ set({ customer: res.customer });
227
+ }
228
+ }
229
+ return res.needs_profile ?? false;
230
+ } catch (err) {
231
+ const message = err instanceof Error ? err.message : "Verification failed";
232
+ throw new Error(message);
233
+ } finally {
234
+ set({ authLoading: false });
235
+ }
236
+ },
237
+ restoreSession: async () => {
238
+ const { sessionToken, sessionExpiresAt, customer } = get();
239
+ if (!sessionToken || !sessionExpiresAt) return;
240
+ if (new Date(sessionExpiresAt) <= /* @__PURE__ */ new Date()) {
241
+ client.setSessionToken(null);
242
+ set({ sessionToken: null, sessionExpiresAt: null, customer: null });
243
+ return;
244
+ }
245
+ client.setSessionToken(sessionToken);
246
+ if (customer?.id) {
247
+ try {
248
+ const fresh = await client.getCustomer(customer.id);
249
+ set({ customer: fresh });
250
+ } catch {
251
+ client.setSessionToken(null);
252
+ set({ sessionToken: null, sessionExpiresAt: null, customer: null });
253
+ }
254
+ }
255
+ },
256
+ isSessionValid: () => {
257
+ const { sessionToken, sessionExpiresAt } = get();
258
+ if (!sessionToken || !sessionExpiresAt) return false;
259
+ return new Date(sessionExpiresAt) > /* @__PURE__ */ new Date();
260
+ },
261
+ logout: () => {
262
+ client.setSessionToken(null);
263
+ set({ customer: null, sessionToken: null, sessionExpiresAt: null });
264
+ },
265
+ fetchCustomer: async (id) => {
266
+ try {
267
+ const customer = await client.getCustomer(id);
268
+ set({ customer });
269
+ } catch (err) {
270
+ console.error("[whale-storefront] fetchCustomer failed:", err);
271
+ }
272
+ }
273
+ }),
274
+ // ── Persist config ─────────────────────────────────────────────────
275
+ {
276
+ name: `${storagePrefix}-auth`,
277
+ partialize: (state) => ({
278
+ sessionToken: state.sessionToken,
279
+ sessionExpiresAt: state.sessionExpiresAt,
280
+ customer: state.customer ? {
281
+ id: state.customer.id,
282
+ email: state.customer.email,
283
+ first_name: state.customer.first_name,
284
+ last_name: state.customer.last_name,
285
+ phone: state.customer.phone,
286
+ loyalty_points: state.customer.loyalty_points,
287
+ loyalty_tier: state.customer.loyalty_tier,
288
+ total_spent: state.customer.total_spent,
289
+ total_orders: state.customer.total_orders
290
+ } : null
291
+ })
292
+ }
293
+ )
294
+ );
295
+ }
296
+ var SESSION_KEY_SUFFIX = "-analytics-session";
297
+ function useAnalytics() {
298
+ const ctx = useContext(WhaleContext);
299
+ if (!ctx) throw new Error("useAnalytics must be used within <WhaleProvider>");
300
+ const { client, config } = ctx;
301
+ const sessionPromiseRef = useRef(null);
302
+ const sessionKey = `${config.storagePrefix}${SESSION_KEY_SUFFIX}`;
303
+ const getOrCreateSession = useCallback(async () => {
304
+ if (sessionPromiseRef.current) return sessionPromiseRef.current;
305
+ sessionPromiseRef.current = (async () => {
306
+ try {
307
+ const raw = localStorage.getItem(sessionKey);
308
+ if (raw) {
309
+ const stored = JSON.parse(raw);
310
+ if (Date.now() - stored.createdAt < config.sessionTtl) {
311
+ client.updateSession(stored.id, { last_active_at: (/* @__PURE__ */ new Date()).toISOString() }).catch(() => {
312
+ });
313
+ return stored.id;
314
+ }
315
+ }
316
+ } catch {
317
+ }
318
+ try {
319
+ const session = await client.createSession({
320
+ user_agent: navigator.userAgent,
321
+ referrer: document.referrer || void 0
322
+ });
323
+ if (session?.id) {
324
+ localStorage.setItem(sessionKey, JSON.stringify({ id: session.id, createdAt: Date.now() }));
325
+ return session.id;
326
+ }
327
+ } catch {
328
+ }
329
+ const fallbackId = `local-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
330
+ localStorage.setItem(sessionKey, JSON.stringify({ id: fallbackId, createdAt: Date.now() }));
331
+ return fallbackId;
332
+ })();
333
+ sessionPromiseRef.current.finally(() => {
334
+ sessionPromiseRef.current = null;
335
+ });
336
+ return sessionPromiseRef.current;
337
+ }, [client, config.sessionTtl, sessionKey]);
338
+ const track = useCallback(
339
+ async (eventType, data = {}) => {
340
+ try {
341
+ const sessionId = await getOrCreateSession();
342
+ await client.trackEvent({ session_id: sessionId, event_type: eventType, event_data: data });
343
+ } catch {
344
+ }
345
+ },
346
+ [client, getOrCreateSession]
347
+ );
348
+ const linkCustomer = useCallback(
349
+ async (customerId) => {
350
+ try {
351
+ const sessionId = await getOrCreateSession();
352
+ if (sessionId.startsWith("local-")) return;
353
+ await client.updateSession(sessionId, { customer_id: customerId });
354
+ } catch {
355
+ }
356
+ },
357
+ [client, getOrCreateSession]
358
+ );
359
+ const trackPageView = useCallback(
360
+ (url, referrer) => {
361
+ track("page_view", { url, referrer });
362
+ },
363
+ [track]
364
+ );
365
+ const trackProductView = useCallback(
366
+ (productId, productName, category, price) => {
367
+ track("product_view", { product_id: productId, product_name: productName, category, price });
368
+ },
369
+ [track]
370
+ );
371
+ const trackCategoryView = useCallback(
372
+ (categoryId, categoryName) => {
373
+ track("category_view", { category_id: categoryId, category_name: categoryName });
374
+ },
375
+ [track]
376
+ );
377
+ const trackSearch = useCallback(
378
+ (query, resultCount) => {
379
+ track("search", { query, result_count: resultCount });
380
+ },
381
+ [track]
382
+ );
383
+ const trackBeginCheckout = useCallback(
384
+ (cartId, total, itemCount) => {
385
+ track("begin_checkout", { cart_id: cartId, total, item_count: itemCount });
386
+ },
387
+ [track]
388
+ );
389
+ const trackPurchase = useCallback(
390
+ (orderId, orderNumber, total) => {
391
+ track("purchase", { order_id: orderId, order_number: orderNumber, total });
392
+ },
393
+ [track]
394
+ );
395
+ const trackAddToCart = useCallback(
396
+ (productId, productName, quantity, price, tier) => {
397
+ track("add_to_cart", { product_id: productId, product_name: productName, quantity, price, tier });
398
+ },
399
+ [track]
400
+ );
401
+ const trackRemoveFromCart = useCallback(
402
+ (productId, productName) => {
403
+ track("remove_from_cart", { product_id: productId, product_name: productName });
404
+ },
405
+ [track]
406
+ );
407
+ return {
408
+ track,
409
+ trackPageView,
410
+ trackProductView,
411
+ trackCategoryView,
412
+ trackSearch,
413
+ trackBeginCheckout,
414
+ trackPurchase,
415
+ trackAddToCart,
416
+ trackRemoveFromCart,
417
+ linkCustomer,
418
+ getOrCreateSession
419
+ };
420
+ }
421
+ function useAuth() {
422
+ const ctx = useContext(WhaleContext);
423
+ if (!ctx) throw new Error("useAuth must be used within <WhaleProvider>");
424
+ return useStore(ctx.authStore, (s) => ({
425
+ customer: s.customer,
426
+ authLoading: s.authLoading,
427
+ sessionToken: s.sessionToken,
428
+ isAuthenticated: s.isSessionValid(),
429
+ sendCode: s.sendOTP,
430
+ verifyCode: s.verifyOTP,
431
+ restoreSession: s.restoreSession,
432
+ logout: s.logout,
433
+ fetchCustomer: s.fetchCustomer
434
+ }));
435
+ }
436
+
437
+ // src/react/components/analytics-tracker.tsx
438
+ function AnalyticsTracker({ pathname }) {
439
+ const { trackPageView, linkCustomer } = useAnalytics();
440
+ const { customer } = useAuth();
441
+ const prevPathname = useRef(null);
442
+ const linkedCustomerId = useRef(null);
443
+ useEffect(() => {
444
+ if (pathname === prevPathname.current) return;
445
+ const referrer = prevPathname.current || (typeof document !== "undefined" ? document.referrer : "");
446
+ prevPathname.current = pathname;
447
+ trackPageView(pathname, referrer || void 0);
448
+ }, [pathname, trackPageView]);
449
+ useEffect(() => {
450
+ if (customer?.id && customer.id !== linkedCustomerId.current) {
451
+ linkedCustomerId.current = customer.id;
452
+ linkCustomer(customer.id);
453
+ }
454
+ }, [customer?.id, linkCustomer]);
455
+ return null;
456
+ }
457
+ function useCart() {
458
+ const ctx = useContext(WhaleContext);
459
+ if (!ctx) throw new Error("useCart must be used within <WhaleProvider>");
460
+ return useStore(ctx.cartStore, (s) => ({
461
+ cartId: s.cartId,
462
+ items: s.items,
463
+ itemCount: s.itemCount,
464
+ subtotal: s.subtotal,
465
+ taxAmount: s.taxAmount,
466
+ total: s.total,
467
+ taxBreakdown: s.taxBreakdown,
468
+ cartOpen: s.cartOpen,
469
+ cartLoading: s.cartLoading,
470
+ productImages: s.productImages,
471
+ addItem: s.addItem,
472
+ removeItem: s.removeItem,
473
+ updateQuantity: s.updateQuantity,
474
+ toggleCart: s.toggleCart,
475
+ openCart: s.openCart,
476
+ closeCart: s.closeCart,
477
+ initCart: s.initCart,
478
+ syncCart: s.syncCart,
479
+ clearCart: s.clearCart,
480
+ checkout: s.checkout
481
+ }));
482
+ }
483
+ function useCartItemCount() {
484
+ const ctx = useContext(WhaleContext);
485
+ if (!ctx) throw new Error("useCartItemCount must be used within <WhaleProvider>");
486
+ return useStore(ctx.cartStore, (s) => s.itemCount);
487
+ }
488
+ function useCartTotal() {
489
+ const ctx = useContext(WhaleContext);
490
+ if (!ctx) throw new Error("useCartTotal must be used within <WhaleProvider>");
491
+ return useStore(ctx.cartStore, (s) => s.total);
492
+ }
493
+
494
+ // src/react/components/cart-initializer.tsx
495
+ function CartInitializer() {
496
+ const { cartId, syncCart } = useCart();
497
+ const initialized = useRef(false);
498
+ useEffect(() => {
499
+ if (initialized.current) return;
500
+ initialized.current = true;
501
+ if (cartId) {
502
+ syncCart().catch(() => {
503
+ });
504
+ }
505
+ }, []);
506
+ return null;
507
+ }
508
+ function AuthInitializer() {
509
+ const { restoreSession } = useAuth();
510
+ const restored = useRef(false);
511
+ useEffect(() => {
512
+ if (restored.current) return;
513
+ restored.current = true;
514
+ restoreSession();
515
+ }, []);
516
+ return null;
517
+ }
518
+ function WhaleProvider({
519
+ children,
520
+ products = [],
521
+ storeId,
522
+ apiKey,
523
+ gatewayUrl,
524
+ proxyPath,
525
+ mediaSigningSecret,
526
+ supabaseHost,
527
+ storagePrefix,
528
+ sessionTtl,
529
+ debug
530
+ }) {
531
+ const pathname = usePathname();
532
+ const ctx = useMemo(() => {
533
+ const resolvedConfig = {
534
+ storeId,
535
+ apiKey,
536
+ gatewayUrl: gatewayUrl || "https://whale-gateway.fly.dev",
537
+ proxyPath: proxyPath || "/api/gw",
538
+ mediaSigningSecret: mediaSigningSecret || "",
539
+ supabaseHost: supabaseHost || "",
540
+ storagePrefix: storagePrefix || "whale",
541
+ sessionTtl: sessionTtl || 30 * 60 * 1e3,
542
+ debug: debug || false
543
+ };
544
+ const client = new WhaleClient({
545
+ storeId,
546
+ apiKey,
547
+ gatewayUrl: resolvedConfig.gatewayUrl,
548
+ proxyPath: resolvedConfig.proxyPath
549
+ });
550
+ const cartStore = createCartStore(client, resolvedConfig.storagePrefix);
551
+ const authStore = createAuthStore(client, resolvedConfig.storagePrefix);
552
+ return {
553
+ client,
554
+ config: resolvedConfig,
555
+ cartStore,
556
+ authStore,
557
+ products
558
+ };
559
+ }, [storeId, apiKey]);
560
+ const value = useMemo(
561
+ () => ({ ...ctx, products }),
562
+ [ctx, products]
563
+ );
564
+ return /* @__PURE__ */ jsxs(WhaleContext.Provider, { value, children: [
565
+ /* @__PURE__ */ jsx(AuthInitializer, {}),
566
+ /* @__PURE__ */ jsx(CartInitializer, {}),
567
+ /* @__PURE__ */ jsx(AnalyticsTracker, { pathname }),
568
+ children
569
+ ] });
570
+ }
571
+ function useProducts(opts) {
572
+ const ctx = useContext(WhaleContext);
573
+ if (!ctx) throw new Error("useProducts must be used within <WhaleProvider>");
574
+ const allProducts = ctx.products;
575
+ const products = useMemo(() => {
576
+ let result = allProducts;
577
+ if (opts?.categoryId) {
578
+ result = result.filter((p) => p.primary_category_id === opts.categoryId);
579
+ }
580
+ if (opts?.search) {
581
+ const q = opts.search.toLowerCase();
582
+ result = result.filter(
583
+ (p) => p.name.toLowerCase().includes(q) || p.description?.toLowerCase().includes(q) || p.slug.toLowerCase().includes(q)
584
+ );
585
+ }
586
+ return result;
587
+ }, [allProducts, opts?.categoryId, opts?.search]);
588
+ return {
589
+ products,
590
+ allProducts,
591
+ loading: false
592
+ };
593
+ }
594
+ function useProduct(slug) {
595
+ const ctx = useContext(WhaleContext);
596
+ if (!ctx) throw new Error("useProduct must be used within <WhaleProvider>");
597
+ const product = useMemo(() => {
598
+ if (!slug) return null;
599
+ return ctx.products.find((p) => p.slug === slug) ?? null;
600
+ }, [ctx.products, slug]);
601
+ return {
602
+ product,
603
+ loading: false
604
+ };
605
+ }
606
+ function useWhaleClient() {
607
+ const ctx = useContext(WhaleContext);
608
+ if (!ctx) throw new Error("useWhaleClient must be used within <WhaleProvider>");
609
+ return ctx.client;
610
+ }
611
+ function useCustomerOrders() {
612
+ const ctx = useContext(WhaleContext);
613
+ if (!ctx) throw new Error("useCustomerOrders must be used within <WhaleProvider>");
614
+ const customer = useStore(ctx.authStore, (s) => s.customer);
615
+ const [orders, setOrders] = useState([]);
616
+ const [loading, setLoading] = useState(false);
617
+ const refresh = useCallback(async () => {
618
+ if (!customer?.id) {
619
+ setOrders([]);
620
+ return;
621
+ }
622
+ setLoading(true);
623
+ try {
624
+ const data = await ctx.client.getCustomerOrders(customer.id);
625
+ setOrders(data);
626
+ } catch {
627
+ setOrders([]);
628
+ } finally {
629
+ setLoading(false);
630
+ }
631
+ }, [customer?.id, ctx.client]);
632
+ useEffect(() => {
633
+ refresh();
634
+ }, [refresh]);
635
+ return { orders, loading, refresh };
636
+ }
637
+ function useCustomerAnalytics() {
638
+ const ctx = useContext(WhaleContext);
639
+ if (!ctx) throw new Error("useCustomerAnalytics must be used within <WhaleProvider>");
640
+ const customer = useStore(ctx.authStore, (s) => s.customer);
641
+ const [analytics, setAnalytics] = useState(null);
642
+ const [loading, setLoading] = useState(false);
643
+ useEffect(() => {
644
+ if (!customer?.id) {
645
+ setAnalytics(null);
646
+ return;
647
+ }
648
+ setLoading(true);
649
+ const name = `${customer.first_name} ${customer.last_name}`.trim();
650
+ ctx.client.getCustomerAnalytics(customer.id, name || void 0).then(setAnalytics).catch(() => setAnalytics(null)).finally(() => setLoading(false));
651
+ }, [customer?.id, customer?.first_name, customer?.last_name, ctx.client]);
652
+ return { analytics, loading };
653
+ }
654
+
655
+ export { AnalyticsTracker, AuthInitializer, CartInitializer, WhaleContext, WhaleProvider, useAnalytics, useAuth, useCart, useCartItemCount, useCartTotal, useCustomerAnalytics, useCustomerOrders, useProduct, useProducts, useWhaleClient };
656
+ //# sourceMappingURL=index.js.map
657
+ //# sourceMappingURL=index.js.map