@neowhale/storefront 0.2.13 → 0.2.20

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.
Files changed (35) hide show
  1. package/dist/{chunk-M2MR6C55.js → chunk-3Q7CPJBA.js} +68 -18
  2. package/dist/chunk-3Q7CPJBA.js.map +1 -0
  3. package/dist/chunk-7KXJLHGA.cjs +160 -0
  4. package/dist/chunk-7KXJLHGA.cjs.map +1 -0
  5. package/dist/chunk-PXS2DPVL.js +158 -0
  6. package/dist/chunk-PXS2DPVL.js.map +1 -0
  7. package/dist/{chunk-3VKRKDPL.cjs → chunk-VAA2KKCH.cjs} +68 -18
  8. package/dist/chunk-VAA2KKCH.cjs.map +1 -0
  9. package/dist/{client-Ca8Otk-R.d.cts → client-BSO263Uv.d.cts} +91 -6
  10. package/dist/{client-Ca8Otk-R.d.ts → client-BSO263Uv.d.ts} +91 -6
  11. package/dist/index.cjs +5 -5
  12. package/dist/index.d.cts +2 -2
  13. package/dist/index.d.ts +2 -2
  14. package/dist/index.js +2 -2
  15. package/dist/next/index.cjs +7 -6
  16. package/dist/next/index.cjs.map +1 -1
  17. package/dist/next/index.d.cts +1 -1
  18. package/dist/next/index.d.ts +1 -1
  19. package/dist/next/index.js +5 -4
  20. package/dist/next/index.js.map +1 -1
  21. package/dist/{pixel-manager-CIZKghfx.d.ts → pixel-manager-BcL95odX.d.ts} +1 -1
  22. package/dist/{pixel-manager-CIR16DXY.d.cts → pixel-manager-DJ9m2FaQ.d.cts} +1 -1
  23. package/dist/react/index.cjs +1503 -56
  24. package/dist/react/index.cjs.map +1 -1
  25. package/dist/react/index.d.cts +71 -9
  26. package/dist/react/index.d.ts +71 -9
  27. package/dist/react/index.js +1497 -55
  28. package/dist/react/index.js.map +1 -1
  29. package/package.json +1 -1
  30. package/dist/chunk-3VKRKDPL.cjs.map +0 -1
  31. package/dist/chunk-BTGOSNMP.cjs +0 -95
  32. package/dist/chunk-BTGOSNMP.cjs.map +0 -1
  33. package/dist/chunk-M2MR6C55.js.map +0 -1
  34. package/dist/chunk-NLH3W6JA.js +0 -93
  35. package/dist/chunk-NLH3W6JA.js.map +0 -1
@@ -1,6 +1,7 @@
1
1
  interface PixelConfig {
2
2
  provider: string;
3
3
  pixel_id: string;
4
+ measurement_id?: string;
4
5
  }
5
6
  interface StorefrontConfig {
6
7
  object: 'storefront_config';
@@ -84,6 +85,7 @@ interface LoyaltyAccount {
84
85
  points_balance: number;
85
86
  tier: string;
86
87
  lifetime_points: number;
88
+ redemption_mode?: 'cash' | 'rewards_only';
87
89
  }
88
90
  interface LoyaltyReward {
89
91
  id: string;
@@ -92,6 +94,8 @@ interface LoyaltyReward {
92
94
  points_cost: number;
93
95
  reward_type: string;
94
96
  is_active: boolean;
97
+ reward_category?: string;
98
+ reward_product_tier?: string;
95
99
  }
96
100
  interface LoyaltyTransaction {
97
101
  id: string;
@@ -119,13 +123,19 @@ interface ShippingRate {
119
123
  estimated_days_min: number | null;
120
124
  estimated_days_max: number | null;
121
125
  }
122
- interface CouponValidation {
126
+ interface DealValidation {
123
127
  valid: boolean;
124
128
  code: string;
129
+ deal_id?: string;
130
+ name?: string;
125
131
  discount_type: string | null;
126
132
  discount_value: number | null;
133
+ apply_to?: string;
134
+ badge_text?: string | null;
127
135
  message: string | null;
128
136
  }
137
+ /** @deprecated Use DealValidation instead */
138
+ type CouponValidation = DealValidation;
129
139
  interface CheckoutSession {
130
140
  id: string;
131
141
  cart_id: string;
@@ -365,7 +375,7 @@ interface QRLandingData {
365
375
  }
366
376
  interface LandingSection {
367
377
  id: string;
368
- type: 'hero' | 'text' | 'image' | 'video' | 'gallery' | 'cta' | 'stats' | 'product_card' | 'coa_viewer' | 'social_links' | 'divider' | 'custom';
378
+ type: 'hero' | 'text' | 'image' | 'video' | 'gallery' | 'cta' | 'stats' | 'product_card' | 'coa_viewer' | 'social_links' | 'divider' | 'lead_capture' | 'custom';
369
379
  content: Record<string, unknown>;
370
380
  order: number;
371
381
  config?: Record<string, unknown>;
@@ -401,6 +411,29 @@ interface LandingPageRenderData {
401
411
  document_name: string;
402
412
  } | null;
403
413
  }
414
+ interface ReferralEnrollment {
415
+ object: 'referral_enrollment';
416
+ affiliate_id: string;
417
+ referral_code: string;
418
+ share_url: string;
419
+ qr_code_id: string;
420
+ wallet_pass_id: string;
421
+ }
422
+ interface ReferralStatus {
423
+ object: 'referral_status';
424
+ enrolled: boolean;
425
+ referral_code: string | null;
426
+ share_url: string | null;
427
+ total_referrals: number;
428
+ pending_referrals: number;
429
+ points_earned: number;
430
+ wallet_pass_id: string | null;
431
+ referred_by: {
432
+ affiliate_id: string;
433
+ referral_code: string;
434
+ referrer_name: string;
435
+ } | null;
436
+ }
404
437
 
405
438
  /**
406
439
  * Extended query methods and static utilities for WhaleClient.
@@ -447,7 +480,7 @@ declare class WhaleClient {
447
480
  }): Promise<CartItem>;
448
481
  updateCartItem(cartId: string, itemId: string, quantity: number): Promise<Cart>;
449
482
  removeCartItem(cartId: string, itemId: string): Promise<void>;
450
- checkout(cartId: string, customerEmail?: string, payment?: PaymentData): Promise<Order>;
483
+ checkout(cartId: string, customerEmail?: string, payment?: PaymentData, referralCode?: string): Promise<Order>;
451
484
  findCustomer(query: string): Promise<Customer[]>;
452
485
  getCustomer(id: string): Promise<Customer>;
453
486
  createCustomer(data: {
@@ -456,6 +489,12 @@ declare class WhaleClient {
456
489
  email: string;
457
490
  phone?: string;
458
491
  }): Promise<Customer>;
492
+ updateProfile(customerId: string, data: {
493
+ first_name: string;
494
+ last_name: string;
495
+ phone?: string;
496
+ date_of_birth?: string;
497
+ }): Promise<Customer>;
459
498
  listOrders(params?: {
460
499
  customer_id?: string;
461
500
  limit?: number;
@@ -474,17 +513,37 @@ declare class WhaleClient {
474
513
  /** Fetch landing page data by slug (public, no auth needed). */
475
514
  fetchLandingPage(slug: string): Promise<LandingPageRenderData>;
476
515
  createSession(params: {
516
+ visitor_id?: string;
477
517
  user_agent?: string;
478
518
  referrer?: string;
519
+ page_url?: string;
520
+ device?: string;
521
+ utm_source?: string;
522
+ utm_medium?: string;
523
+ utm_campaign?: string;
524
+ utm_content?: string;
525
+ utm_term?: string;
526
+ gclid?: string;
527
+ fbclid?: string;
479
528
  }): Promise<StorefrontSession>;
480
529
  updateSession(sessionId: string, params: {
481
530
  last_active_at?: string;
482
531
  customer_id?: string;
532
+ customer_first_name?: string;
533
+ customer_last_name?: string;
534
+ cart_id?: string;
535
+ cart_item_count?: number;
536
+ cart_total?: number;
537
+ order_id?: string;
538
+ fingerprint_id?: string;
539
+ status?: string;
540
+ current_page?: string;
483
541
  }): Promise<StorefrontSession>;
484
542
  trackEvent(params: {
485
543
  session_id: string;
486
544
  event_type: EventType;
487
545
  event_data?: Record<string, unknown>;
546
+ visitor_id?: string;
488
547
  }): Promise<void>;
489
548
  createCheckoutSession(params: {
490
549
  cart_id: string;
@@ -493,6 +552,9 @@ declare class WhaleClient {
493
552
  billing_address?: Address;
494
553
  shipping_method_id?: string;
495
554
  coupon_code?: string;
555
+ referral_code?: string;
556
+ loyalty_reward_id?: string;
557
+ selected_product_id?: string;
496
558
  }): Promise<CheckoutSession>;
497
559
  getCheckoutSession(sessionId: string): Promise<CheckoutSession>;
498
560
  updateCheckoutSession(sessionId: string, params: {
@@ -502,7 +564,10 @@ declare class WhaleClient {
502
564
  shipping_method_id?: string;
503
565
  coupon_code?: string;
504
566
  }): Promise<CheckoutSession>;
505
- completeCheckout(sessionId: string, payment?: PaymentData): Promise<Order>;
567
+ completeCheckout(sessionId: string, payment?: PaymentData, opts?: {
568
+ loyalty_reward_id?: string;
569
+ selected_product_id?: string;
570
+ }): Promise<Order>;
506
571
  searchProducts(params: {
507
572
  query: string;
508
573
  category_id?: string;
@@ -521,6 +586,11 @@ declare class WhaleClient {
521
586
  limit?: number;
522
587
  starting_after?: string;
523
588
  }): Promise<ListResponse<LoyaltyTransaction>>;
589
+ listLoyaltyProducts(params: {
590
+ category: string;
591
+ location_id: string;
592
+ tier?: string;
593
+ }): Promise<ListResponse<Product>>;
524
594
  redeemLoyaltyReward(customerId: string, rewardId: string): Promise<{
525
595
  success: boolean;
526
596
  points_remaining: number;
@@ -558,13 +628,28 @@ declare class WhaleClient {
558
628
  }): Promise<{
559
629
  data: ShippingRate[];
560
630
  }>;
631
+ validateDeal(code: string, params?: {
632
+ cart_id?: string;
633
+ }): Promise<DealValidation>;
634
+ applyDeal(cartId: string, code: string): Promise<Cart>;
635
+ removeDeal(cartId: string): Promise<Cart>;
636
+ /** @deprecated Use validateDeal instead */
561
637
  validateCoupon(code: string, params?: {
562
638
  cart_id?: string;
563
- }): Promise<CouponValidation>;
639
+ }): Promise<DealValidation>;
640
+ /** @deprecated Use applyDeal instead */
564
641
  applyCoupon(cartId: string, code: string): Promise<Cart>;
642
+ /** @deprecated Use removeDeal instead */
565
643
  removeCoupon(cartId: string): Promise<Cart>;
644
+ enrollReferral(customerId: string): Promise<ReferralEnrollment>;
645
+ getReferralStatus(customerId: string): Promise<ReferralStatus>;
646
+ attributeReferral(customerId: string, referralCode: string): Promise<{
647
+ success: boolean;
648
+ affiliate_id?: string;
649
+ error?: string;
650
+ }>;
566
651
  static encodeBase64Url: typeof encodeBase64Url;
567
652
  static signMedia: typeof signMedia;
568
653
  }
569
654
 
570
- export { type Address as A, type StorefrontSession as B, type Cart as C, type WhaleStorefrontConfig as D, type EventType as E, type WishlistItem as F, type LandingPageConfig as L, type Order as O, type PaymentData as P, type QRLandingData as Q, type Recommendation as R, type SendCodeResponse as S, type TaxBreakdown as T, type VerifyCodeResponse as V, WhaleClient as W, type CartItem as a, type Category as b, type CategoryTreeNode as c, type CheckoutSession as d, type CouponValidation as e, type Customer as f, type CustomerAnalytics as g, type LandingPageRenderData as h, type LandingSection as i, type ListResponse as j, type Location as k, type LoyaltyAccount as l, type LoyaltyReward as m, type LoyaltyTransaction as n, type OrderItem as o, type PixelConfig as p, type PricingTier as q, type Product as r, type ProductVariation as s, type QRLandingPage as t, type QRLandingStore as u, type Review as v, type ReviewSummary as w, type ShippingMethod as x, type ShippingRate as y, type StorefrontConfig as z };
655
+ export { type Address as A, type StorefrontSession as B, type Cart as C, type DealValidation as D, type EventType as E, type WhaleStorefrontConfig as F, type WishlistItem as G, type ReferralStatus as H, type ReferralEnrollment as I, type LandingPageConfig as L, type Order as O, type PaymentData as P, type QRLandingData as Q, type Recommendation as R, type SendCodeResponse as S, type TaxBreakdown as T, type VerifyCodeResponse as V, WhaleClient as W, type CartItem as a, type Category as b, type CategoryTreeNode as c, type CheckoutSession as d, type CouponValidation as e, type Customer as f, type CustomerAnalytics as g, type LandingPageRenderData as h, type LandingSection as i, type ListResponse as j, type Location as k, type LoyaltyAccount as l, type LoyaltyReward as m, type LoyaltyTransaction as n, type OrderItem as o, type PixelConfig as p, type PricingTier as q, type Product as r, type ProductVariation as s, type QRLandingPage as t, type QRLandingStore as u, type Review as v, type ReviewSummary as w, type ShippingMethod as x, type ShippingRate as y, type StorefrontConfig as z };
package/dist/index.cjs CHANGED
@@ -1,21 +1,21 @@
1
1
  'use strict';
2
2
 
3
- var chunkBTGOSNMP_cjs = require('./chunk-BTGOSNMP.cjs');
4
- var chunk3VKRKDPL_cjs = require('./chunk-3VKRKDPL.cjs');
3
+ var chunk7KXJLHGA_cjs = require('./chunk-7KXJLHGA.cjs');
4
+ var chunkVAA2KKCH_cjs = require('./chunk-VAA2KKCH.cjs');
5
5
 
6
6
 
7
7
 
8
8
  Object.defineProperty(exports, "PixelManager", {
9
9
  enumerable: true,
10
- get: function () { return chunkBTGOSNMP_cjs.PixelManager; }
10
+ get: function () { return chunk7KXJLHGA_cjs.PixelManager; }
11
11
  });
12
12
  Object.defineProperty(exports, "WhaleClient", {
13
13
  enumerable: true,
14
- get: function () { return chunk3VKRKDPL_cjs.WhaleClient; }
14
+ get: function () { return chunkVAA2KKCH_cjs.WhaleClient; }
15
15
  });
16
16
  Object.defineProperty(exports, "resilientSend", {
17
17
  enumerable: true,
18
- get: function () { return chunk3VKRKDPL_cjs.resilientSend; }
18
+ get: function () { return chunkVAA2KKCH_cjs.resilientSend; }
19
19
  });
20
20
  //# sourceMappingURL=index.cjs.map
21
21
  //# sourceMappingURL=index.cjs.map
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- export { A as Address, C as Cart, a as CartItem, b as Category, c as CategoryTreeNode, d as CheckoutSession, e as CouponValidation, f as Customer, g as CustomerAnalytics, E as EventType, L as LandingPageConfig, h as LandingPageRenderData, i as LandingSection, j as ListResponse, k as Location, l as LoyaltyAccount, m as LoyaltyReward, n as LoyaltyTransaction, O as Order, o as OrderItem, P as PaymentData, p as PixelConfig, q as PricingTier, r as Product, s as ProductVariation, Q as QRLandingData, t as QRLandingPage, u as QRLandingStore, R as Recommendation, v as Review, w as ReviewSummary, S as SendCodeResponse, x as ShippingMethod, y as ShippingRate, z as StorefrontConfig, B as StorefrontSession, T as TaxBreakdown, V as VerifyCodeResponse, W as WhaleClient, D as WhaleStorefrontConfig, F as WishlistItem } from './client-Ca8Otk-R.cjs';
2
- export { P as PixelManager } from './pixel-manager-CIR16DXY.cjs';
1
+ export { A as Address, C as Cart, a as CartItem, b as Category, c as CategoryTreeNode, d as CheckoutSession, e as CouponValidation, f as Customer, g as CustomerAnalytics, D as DealValidation, E as EventType, L as LandingPageConfig, h as LandingPageRenderData, i as LandingSection, j as ListResponse, k as Location, l as LoyaltyAccount, m as LoyaltyReward, n as LoyaltyTransaction, O as Order, o as OrderItem, P as PaymentData, p as PixelConfig, q as PricingTier, r as Product, s as ProductVariation, Q as QRLandingData, t as QRLandingPage, u as QRLandingStore, R as Recommendation, v as Review, w as ReviewSummary, S as SendCodeResponse, x as ShippingMethod, y as ShippingRate, z as StorefrontConfig, B as StorefrontSession, T as TaxBreakdown, V as VerifyCodeResponse, W as WhaleClient, F as WhaleStorefrontConfig, G as WishlistItem } from './client-BSO263Uv.cjs';
2
+ export { P as PixelManager } from './pixel-manager-DJ9m2FaQ.cjs';
3
3
 
4
4
  /**
5
5
  * Resilient HTTP sender for analytics/event payloads.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { A as Address, C as Cart, a as CartItem, b as Category, c as CategoryTreeNode, d as CheckoutSession, e as CouponValidation, f as Customer, g as CustomerAnalytics, E as EventType, L as LandingPageConfig, h as LandingPageRenderData, i as LandingSection, j as ListResponse, k as Location, l as LoyaltyAccount, m as LoyaltyReward, n as LoyaltyTransaction, O as Order, o as OrderItem, P as PaymentData, p as PixelConfig, q as PricingTier, r as Product, s as ProductVariation, Q as QRLandingData, t as QRLandingPage, u as QRLandingStore, R as Recommendation, v as Review, w as ReviewSummary, S as SendCodeResponse, x as ShippingMethod, y as ShippingRate, z as StorefrontConfig, B as StorefrontSession, T as TaxBreakdown, V as VerifyCodeResponse, W as WhaleClient, D as WhaleStorefrontConfig, F as WishlistItem } from './client-Ca8Otk-R.js';
2
- export { P as PixelManager } from './pixel-manager-CIZKghfx.js';
1
+ export { A as Address, C as Cart, a as CartItem, b as Category, c as CategoryTreeNode, d as CheckoutSession, e as CouponValidation, f as Customer, g as CustomerAnalytics, D as DealValidation, E as EventType, L as LandingPageConfig, h as LandingPageRenderData, i as LandingSection, j as ListResponse, k as Location, l as LoyaltyAccount, m as LoyaltyReward, n as LoyaltyTransaction, O as Order, o as OrderItem, P as PaymentData, p as PixelConfig, q as PricingTier, r as Product, s as ProductVariation, Q as QRLandingData, t as QRLandingPage, u as QRLandingStore, R as Recommendation, v as Review, w as ReviewSummary, S as SendCodeResponse, x as ShippingMethod, y as ShippingRate, z as StorefrontConfig, B as StorefrontSession, T as TaxBreakdown, V as VerifyCodeResponse, W as WhaleClient, F as WhaleStorefrontConfig, G as WishlistItem } from './client-BSO263Uv.js';
2
+ export { P as PixelManager } from './pixel-manager-BcL95odX.js';
3
3
 
4
4
  /**
5
5
  * Resilient HTTP sender for analytics/event payloads.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { PixelManager } from './chunk-NLH3W6JA.js';
2
- export { WhaleClient, resilientSend } from './chunk-M2MR6C55.js';
1
+ export { PixelManager } from './chunk-PXS2DPVL.js';
2
+ export { WhaleClient, resilientSend } from './chunk-3Q7CPJBA.js';
3
3
  //# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunk3VKRKDPL_cjs = require('../chunk-3VKRKDPL.cjs');
3
+ var chunkVAA2KKCH_cjs = require('../chunk-VAA2KKCH.cjs');
4
4
  var server = require('next/server');
5
5
 
6
6
  // src/next/headers.ts
@@ -54,7 +54,7 @@ function whaleGatewayRewrite(gatewayUrl = "https://whale-gateway.fly.dev", proxy
54
54
 
55
55
  // src/next/server.ts
56
56
  function createServerClient(config) {
57
- return new chunk3VKRKDPL_cjs.WhaleClient({
57
+ return new chunkVAA2KKCH_cjs.WhaleClient({
58
58
  storeId: config?.storeId || process.env.NEXT_PUBLIC_STORE_ID || process.env.NEXT_PUBLIC_WHALE_STORE_ID || "",
59
59
  apiKey: config?.apiKey || process.env.NEXT_PUBLIC_API_KEY || process.env.NEXT_PUBLIC_WHALE_API_KEY || "",
60
60
  gatewayUrl: config?.gatewayUrl || process.env.NEXT_PUBLIC_API_URL || process.env.NEXT_PUBLIC_WHALE_GATEWAY_URL || "https://whale-gateway.fly.dev",
@@ -80,14 +80,15 @@ function snapWidth(w) {
80
80
  }
81
81
  function createImageLoader(config) {
82
82
  return ({ src, width, quality }) => {
83
- if (!src.includes(config.supabaseHost)) {
84
- return src;
83
+ const cleanSrc = src.replace(/[?&]_r=\d+/, "");
84
+ if (!cleanSrc.includes(config.supabaseHost)) {
85
+ return cleanSrc;
85
86
  }
86
87
  const w = String(snapWidth(width));
87
88
  const q = String(quality || 80);
88
89
  const f = "webp";
89
- const encoded = chunk3VKRKDPL_cjs.WhaleClient.encodeBase64Url(src);
90
- const s = chunk3VKRKDPL_cjs.WhaleClient.signMedia(config.signingSecret, encoded, w, q, f);
90
+ const encoded = chunkVAA2KKCH_cjs.WhaleClient.encodeBase64Url(cleanSrc);
91
+ const s = chunkVAA2KKCH_cjs.WhaleClient.signMedia(config.signingSecret, encoded, w, q, f);
91
92
  return `${config.gatewayUrl}/v1/stores/${config.storeId}/media?url=${encoded}&w=${w}&q=${q}&f=${f}&s=${s}`;
92
93
  };
93
94
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/next/headers.ts","../../src/next/rewrite.ts","../../src/next/server.ts","../../src/next/image-loader.ts","../../src/next/middleware.ts"],"names":["WhaleClient","NextResponse"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Security headers for Next.js storefronts.\n */\n\nexport const securityHeaders = [\n {\n key: 'X-DNS-Prefetch-Control',\n value: 'on',\n },\n {\n key: 'Strict-Transport-Security',\n value: 'max-age=63072000; includeSubDomains; preload',\n },\n {\n key: 'X-Content-Type-Options',\n value: 'nosniff',\n },\n {\n key: 'X-Frame-Options',\n value: 'SAMEORIGIN',\n },\n {\n key: 'X-XSS-Protection',\n value: '1; mode=block',\n },\n {\n key: 'Referrer-Policy',\n value: 'strict-origin-when-cross-origin',\n },\n {\n key: 'Permissions-Policy',\n value: 'camera=(), microphone=(), geolocation=(self), interest-cohort=()',\n },\n]\n\n/**\n * Returns a Next.js `headers()` config with security headers applied to all routes.\n * Use in next.config.ts:\n *\n * ```ts\n * import { withSecurityHeaders } from '@neowhale/storefront/next'\n * export default { headers: withSecurityHeaders() }\n * ```\n */\nexport function withSecurityHeaders(\n extra?: { key: string; value: string }[]\n): () => Promise<{ source: string; headers: { key: string; value: string }[] }[]> {\n const allHeaders = extra ? [...securityHeaders, ...extra] : securityHeaders\n return async () => [\n {\n source: '/:path*',\n headers: allHeaders,\n },\n ]\n}\n","/**\n * Gateway rewrite rule for Next.js.\n * Proxies client-side /api/gw/* requests to whale-gateway to avoid CORS.\n *\n * Usage in next.config.ts:\n * ```ts\n * import { whaleGatewayRewrite } from '@neowhale/storefront/next'\n * export default {\n * async rewrites() {\n * return [whaleGatewayRewrite()]\n * }\n * }\n * ```\n */\nexport function whaleGatewayRewrite(\n gatewayUrl = 'https://whale-gateway.fly.dev',\n proxyPath = '/api/gw'\n): { source: string; destination: string } {\n return {\n source: `${proxyPath}/:path*`,\n destination: `${gatewayUrl}/:path*`,\n }\n}\n","import { WhaleClient } from '../client.js'\nimport type { Product, WhaleStorefrontConfig } from '../types.js'\n\n/**\n * Creates a server-side WhaleClient.\n * Reads from env vars by default — override with explicit config.\n */\nexport function createServerClient(config?: Partial<WhaleStorefrontConfig>): WhaleClient {\n return new WhaleClient({\n storeId: config?.storeId || process.env.NEXT_PUBLIC_STORE_ID || process.env.NEXT_PUBLIC_WHALE_STORE_ID || '',\n apiKey: config?.apiKey || process.env.NEXT_PUBLIC_API_KEY || process.env.NEXT_PUBLIC_WHALE_API_KEY || '',\n gatewayUrl: config?.gatewayUrl || process.env.NEXT_PUBLIC_API_URL || process.env.NEXT_PUBLIC_WHALE_GATEWAY_URL || 'https://whale-gateway.fly.dev',\n proxyPath: config?.proxyPath,\n })\n}\n\n/**\n * Server-side: fetch all published products with ISR caching.\n * Drop-in replacement for Flora's `getAllProducts()`.\n */\nexport async function getAllProducts(options?: {\n /** Revalidate interval in seconds. Defaults to 60. */\n revalidate?: number\n /** Filter function to exclude products (e.g. hidden categories, out of stock) */\n filter?: (product: Product) => boolean\n /** Override client config */\n client?: WhaleClient\n}): Promise<Product[]> {\n const client = options?.client ?? createServerClient()\n return client.getAllProducts({\n status: 'published',\n revalidate: options?.revalidate ?? 60,\n filter: options?.filter,\n })\n}\n","import { WhaleClient } from '../client.js'\n\nconst ALLOWED_WIDTHS = [64, 96, 128, 256, 384, 640, 828, 1080, 1280, 1920]\n\nfunction snapWidth(w: number): number {\n for (const aw of ALLOWED_WIDTHS) {\n if (aw >= w) return aw\n }\n return ALLOWED_WIDTHS[ALLOWED_WIDTHS.length - 1]\n}\n\ninterface ImageLoaderParams {\n src: string\n width: number\n quality?: number\n}\n\n/**\n * Creates a Next.js custom image loader that proxies Supabase images through gateway.\n *\n * Usage in a loader file (e.g. src/lib/image-loader.ts):\n * ```ts\n * import { createImageLoader } from '@neowhale/storefront/next'\n * export default createImageLoader({\n * storeId: process.env.NEXT_PUBLIC_STORE_ID!,\n * gatewayUrl: 'https://whale-gateway.fly.dev',\n * supabaseHost: 'your-project.supabase.co',\n * signingSecret: process.env.NEXT_PUBLIC_MEDIA_SIGNING_SECRET!,\n * })\n * ```\n */\nexport function createImageLoader(config: {\n storeId: string\n gatewayUrl: string\n supabaseHost: string\n signingSecret: string\n}): (params: ImageLoaderParams) => string {\n return ({ src, width, quality }: ImageLoaderParams): string => {\n if (!src.includes(config.supabaseHost)) {\n return src\n }\n\n const w = String(snapWidth(width))\n const q = String(quality || 80)\n const f = 'webp'\n const encoded = WhaleClient.encodeBase64Url(src)\n const s = WhaleClient.signMedia(config.signingSecret, encoded, w, q, f)\n\n return `${config.gatewayUrl}/v1/stores/${config.storeId}/media?url=${encoded}&w=${w}&q=${q}&f=${f}&s=${s}`\n }\n}\n","import type { NextRequest } from 'next/server'\nimport { NextResponse } from 'next/server'\n\n/**\n * Creates a Next.js middleware that protects routes requiring authentication.\n *\n * Checks for a session token cookie or localStorage indicator.\n * Since middleware runs on the edge and can't access localStorage,\n * this checks for a cookie-based token instead.\n *\n * Usage in middleware.ts:\n * ```ts\n * import { createAuthMiddleware } from '@neowhale/storefront/next'\n * export const middleware = createAuthMiddleware({\n * protectedPaths: ['/account'],\n * loginPath: '/account',\n * })\n * export const config = { matcher: ['/account/:path*'] }\n * ```\n */\nexport function createAuthMiddleware(options: {\n protectedPaths: string[]\n loginPath: string\n cookieName?: string\n}) {\n const { protectedPaths, loginPath, cookieName = 'whale-session' } = options\n\n return function middleware(request: NextRequest) {\n const { pathname } = request.nextUrl\n\n // Never gate the login path itself — prevents redirect loops\n if (pathname === loginPath || pathname.startsWith(`${loginPath}/`)) {\n return NextResponse.next()\n }\n\n // Check if this is a protected path\n const isProtected = protectedPaths.some(\n (p) => pathname === p || pathname.startsWith(`${p}/`)\n )\n\n if (!isProtected) {\n return NextResponse.next()\n }\n\n // Check for session cookie\n const token = request.cookies.get(cookieName)?.value\n\n if (!token) {\n const url = request.nextUrl.clone()\n url.pathname = loginPath\n url.searchParams.set('redirect', pathname)\n return NextResponse.redirect(url)\n }\n\n return NextResponse.next()\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/next/headers.ts","../../src/next/rewrite.ts","../../src/next/server.ts","../../src/next/image-loader.ts","../../src/next/middleware.ts"],"names":["WhaleClient","NextResponse"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Security headers for Next.js storefronts.\n */\n\nexport const securityHeaders = [\n {\n key: 'X-DNS-Prefetch-Control',\n value: 'on',\n },\n {\n key: 'Strict-Transport-Security',\n value: 'max-age=63072000; includeSubDomains; preload',\n },\n {\n key: 'X-Content-Type-Options',\n value: 'nosniff',\n },\n {\n key: 'X-Frame-Options',\n value: 'SAMEORIGIN',\n },\n {\n key: 'X-XSS-Protection',\n value: '1; mode=block',\n },\n {\n key: 'Referrer-Policy',\n value: 'strict-origin-when-cross-origin',\n },\n {\n key: 'Permissions-Policy',\n value: 'camera=(), microphone=(), geolocation=(self), interest-cohort=()',\n },\n]\n\n/**\n * Returns a Next.js `headers()` config with security headers applied to all routes.\n * Use in next.config.ts:\n *\n * ```ts\n * import { withSecurityHeaders } from '@neowhale/storefront/next'\n * export default { headers: withSecurityHeaders() }\n * ```\n */\nexport function withSecurityHeaders(\n extra?: { key: string; value: string }[]\n): () => Promise<{ source: string; headers: { key: string; value: string }[] }[]> {\n const allHeaders = extra ? [...securityHeaders, ...extra] : securityHeaders\n return async () => [\n {\n source: '/:path*',\n headers: allHeaders,\n },\n ]\n}\n","/**\n * Gateway rewrite rule for Next.js.\n * Proxies client-side /api/gw/* requests to whale-gateway to avoid CORS.\n *\n * Usage in next.config.ts:\n * ```ts\n * import { whaleGatewayRewrite } from '@neowhale/storefront/next'\n * export default {\n * async rewrites() {\n * return [whaleGatewayRewrite()]\n * }\n * }\n * ```\n */\nexport function whaleGatewayRewrite(\n gatewayUrl = 'https://whale-gateway.fly.dev',\n proxyPath = '/api/gw'\n): { source: string; destination: string } {\n return {\n source: `${proxyPath}/:path*`,\n destination: `${gatewayUrl}/:path*`,\n }\n}\n","import { WhaleClient } from '../client.js'\nimport type { Product, WhaleStorefrontConfig } from '../types.js'\n\n/**\n * Creates a server-side WhaleClient.\n * Reads from env vars by default — override with explicit config.\n */\nexport function createServerClient(config?: Partial<WhaleStorefrontConfig>): WhaleClient {\n return new WhaleClient({\n storeId: config?.storeId || process.env.NEXT_PUBLIC_STORE_ID || process.env.NEXT_PUBLIC_WHALE_STORE_ID || '',\n apiKey: config?.apiKey || process.env.NEXT_PUBLIC_API_KEY || process.env.NEXT_PUBLIC_WHALE_API_KEY || '',\n gatewayUrl: config?.gatewayUrl || process.env.NEXT_PUBLIC_API_URL || process.env.NEXT_PUBLIC_WHALE_GATEWAY_URL || 'https://whale-gateway.fly.dev',\n proxyPath: config?.proxyPath,\n })\n}\n\n/**\n * Server-side: fetch all published products with ISR caching.\n * Drop-in replacement for Flora's `getAllProducts()`.\n */\nexport async function getAllProducts(options?: {\n /** Revalidate interval in seconds. Defaults to 60. */\n revalidate?: number\n /** Filter function to exclude products (e.g. hidden categories, out of stock) */\n filter?: (product: Product) => boolean\n /** Override client config */\n client?: WhaleClient\n}): Promise<Product[]> {\n const client = options?.client ?? createServerClient()\n return client.getAllProducts({\n status: 'published',\n revalidate: options?.revalidate ?? 60,\n filter: options?.filter,\n })\n}\n","import { WhaleClient } from '../client.js'\n\nconst ALLOWED_WIDTHS = [64, 96, 128, 256, 384, 640, 828, 1080, 1280, 1920]\n\nfunction snapWidth(w: number): number {\n for (const aw of ALLOWED_WIDTHS) {\n if (aw >= w) return aw\n }\n return ALLOWED_WIDTHS[ALLOWED_WIDTHS.length - 1]\n}\n\ninterface ImageLoaderParams {\n src: string\n width: number\n quality?: number\n}\n\n/**\n * Creates a Next.js custom image loader that proxies Supabase images through gateway.\n *\n * Usage in a loader file (e.g. src/lib/image-loader.ts):\n * ```ts\n * import { createImageLoader } from '@neowhale/storefront/next'\n * export default createImageLoader({\n * storeId: process.env.NEXT_PUBLIC_STORE_ID!,\n * gatewayUrl: 'https://whale-gateway.fly.dev',\n * supabaseHost: 'your-project.supabase.co',\n * signingSecret: process.env.NEXT_PUBLIC_MEDIA_SIGNING_SECRET!,\n * })\n * ```\n */\nexport function createImageLoader(config: {\n storeId: string\n gatewayUrl: string\n supabaseHost: string\n signingSecret: string\n}): (params: ImageLoaderParams) => string {\n return ({ src, width, quality }: ImageLoaderParams): string => {\n // Strip retry cache-bust params (_r=N) so retries hit the same gateway cache key\n const cleanSrc = src.replace(/[?&]_r=\\d+/, '')\n\n if (!cleanSrc.includes(config.supabaseHost)) {\n return cleanSrc\n }\n\n const w = String(snapWidth(width))\n const q = String(quality || 80)\n const f = 'webp'\n const encoded = WhaleClient.encodeBase64Url(cleanSrc)\n const s = WhaleClient.signMedia(config.signingSecret, encoded, w, q, f)\n\n return `${config.gatewayUrl}/v1/stores/${config.storeId}/media?url=${encoded}&w=${w}&q=${q}&f=${f}&s=${s}`\n }\n}\n","import type { NextRequest } from 'next/server'\nimport { NextResponse } from 'next/server'\n\n/**\n * Creates a Next.js middleware that protects routes requiring authentication.\n *\n * Checks for a session token cookie or localStorage indicator.\n * Since middleware runs on the edge and can't access localStorage,\n * this checks for a cookie-based token instead.\n *\n * Usage in middleware.ts:\n * ```ts\n * import { createAuthMiddleware } from '@neowhale/storefront/next'\n * export const middleware = createAuthMiddleware({\n * protectedPaths: ['/account'],\n * loginPath: '/account',\n * })\n * export const config = { matcher: ['/account/:path*'] }\n * ```\n */\nexport function createAuthMiddleware(options: {\n protectedPaths: string[]\n loginPath: string\n cookieName?: string\n}) {\n const { protectedPaths, loginPath, cookieName = 'whale-session' } = options\n\n return function middleware(request: NextRequest) {\n const { pathname } = request.nextUrl\n\n // Never gate the login path itself — prevents redirect loops\n if (pathname === loginPath || pathname.startsWith(`${loginPath}/`)) {\n return NextResponse.next()\n }\n\n // Check if this is a protected path\n const isProtected = protectedPaths.some(\n (p) => pathname === p || pathname.startsWith(`${p}/`)\n )\n\n if (!isProtected) {\n return NextResponse.next()\n }\n\n // Check for session cookie\n const token = request.cookies.get(cookieName)?.value\n\n if (!token) {\n const url = request.nextUrl.clone()\n url.pathname = loginPath\n url.searchParams.set('redirect', pathname)\n return NextResponse.redirect(url)\n }\n\n return NextResponse.next()\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { D as WhaleStorefrontConfig, W as WhaleClient, r as Product } from '../client-Ca8Otk-R.cjs';
1
+ import { F as WhaleStorefrontConfig, W as WhaleClient, r as Product } from '../client-BSO263Uv.cjs';
2
2
  import { NextRequest, NextResponse } from 'next/server';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { D as WhaleStorefrontConfig, W as WhaleClient, r as Product } from '../client-Ca8Otk-R.js';
1
+ import { F as WhaleStorefrontConfig, W as WhaleClient, r as Product } from '../client-BSO263Uv.js';
2
2
  import { NextRequest, NextResponse } from 'next/server';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { WhaleClient } from '../chunk-M2MR6C55.js';
1
+ import { WhaleClient } from '../chunk-3Q7CPJBA.js';
2
2
  import { NextResponse } from 'next/server';
3
3
 
4
4
  // src/next/headers.ts
@@ -78,13 +78,14 @@ function snapWidth(w) {
78
78
  }
79
79
  function createImageLoader(config) {
80
80
  return ({ src, width, quality }) => {
81
- if (!src.includes(config.supabaseHost)) {
82
- return src;
81
+ const cleanSrc = src.replace(/[?&]_r=\d+/, "");
82
+ if (!cleanSrc.includes(config.supabaseHost)) {
83
+ return cleanSrc;
83
84
  }
84
85
  const w = String(snapWidth(width));
85
86
  const q = String(quality || 80);
86
87
  const f = "webp";
87
- const encoded = WhaleClient.encodeBase64Url(src);
88
+ const encoded = WhaleClient.encodeBase64Url(cleanSrc);
88
89
  const s = WhaleClient.signMedia(config.signingSecret, encoded, w, q, f);
89
90
  return `${config.gatewayUrl}/v1/stores/${config.storeId}/media?url=${encoded}&w=${w}&q=${q}&f=${f}&s=${s}`;
90
91
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/next/headers.ts","../../src/next/rewrite.ts","../../src/next/server.ts","../../src/next/image-loader.ts","../../src/next/middleware.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Security headers for Next.js storefronts.\n */\n\nexport const securityHeaders = [\n {\n key: 'X-DNS-Prefetch-Control',\n value: 'on',\n },\n {\n key: 'Strict-Transport-Security',\n value: 'max-age=63072000; includeSubDomains; preload',\n },\n {\n key: 'X-Content-Type-Options',\n value: 'nosniff',\n },\n {\n key: 'X-Frame-Options',\n value: 'SAMEORIGIN',\n },\n {\n key: 'X-XSS-Protection',\n value: '1; mode=block',\n },\n {\n key: 'Referrer-Policy',\n value: 'strict-origin-when-cross-origin',\n },\n {\n key: 'Permissions-Policy',\n value: 'camera=(), microphone=(), geolocation=(self), interest-cohort=()',\n },\n]\n\n/**\n * Returns a Next.js `headers()` config with security headers applied to all routes.\n * Use in next.config.ts:\n *\n * ```ts\n * import { withSecurityHeaders } from '@neowhale/storefront/next'\n * export default { headers: withSecurityHeaders() }\n * ```\n */\nexport function withSecurityHeaders(\n extra?: { key: string; value: string }[]\n): () => Promise<{ source: string; headers: { key: string; value: string }[] }[]> {\n const allHeaders = extra ? [...securityHeaders, ...extra] : securityHeaders\n return async () => [\n {\n source: '/:path*',\n headers: allHeaders,\n },\n ]\n}\n","/**\n * Gateway rewrite rule for Next.js.\n * Proxies client-side /api/gw/* requests to whale-gateway to avoid CORS.\n *\n * Usage in next.config.ts:\n * ```ts\n * import { whaleGatewayRewrite } from '@neowhale/storefront/next'\n * export default {\n * async rewrites() {\n * return [whaleGatewayRewrite()]\n * }\n * }\n * ```\n */\nexport function whaleGatewayRewrite(\n gatewayUrl = 'https://whale-gateway.fly.dev',\n proxyPath = '/api/gw'\n): { source: string; destination: string } {\n return {\n source: `${proxyPath}/:path*`,\n destination: `${gatewayUrl}/:path*`,\n }\n}\n","import { WhaleClient } from '../client.js'\nimport type { Product, WhaleStorefrontConfig } from '../types.js'\n\n/**\n * Creates a server-side WhaleClient.\n * Reads from env vars by default — override with explicit config.\n */\nexport function createServerClient(config?: Partial<WhaleStorefrontConfig>): WhaleClient {\n return new WhaleClient({\n storeId: config?.storeId || process.env.NEXT_PUBLIC_STORE_ID || process.env.NEXT_PUBLIC_WHALE_STORE_ID || '',\n apiKey: config?.apiKey || process.env.NEXT_PUBLIC_API_KEY || process.env.NEXT_PUBLIC_WHALE_API_KEY || '',\n gatewayUrl: config?.gatewayUrl || process.env.NEXT_PUBLIC_API_URL || process.env.NEXT_PUBLIC_WHALE_GATEWAY_URL || 'https://whale-gateway.fly.dev',\n proxyPath: config?.proxyPath,\n })\n}\n\n/**\n * Server-side: fetch all published products with ISR caching.\n * Drop-in replacement for Flora's `getAllProducts()`.\n */\nexport async function getAllProducts(options?: {\n /** Revalidate interval in seconds. Defaults to 60. */\n revalidate?: number\n /** Filter function to exclude products (e.g. hidden categories, out of stock) */\n filter?: (product: Product) => boolean\n /** Override client config */\n client?: WhaleClient\n}): Promise<Product[]> {\n const client = options?.client ?? createServerClient()\n return client.getAllProducts({\n status: 'published',\n revalidate: options?.revalidate ?? 60,\n filter: options?.filter,\n })\n}\n","import { WhaleClient } from '../client.js'\n\nconst ALLOWED_WIDTHS = [64, 96, 128, 256, 384, 640, 828, 1080, 1280, 1920]\n\nfunction snapWidth(w: number): number {\n for (const aw of ALLOWED_WIDTHS) {\n if (aw >= w) return aw\n }\n return ALLOWED_WIDTHS[ALLOWED_WIDTHS.length - 1]\n}\n\ninterface ImageLoaderParams {\n src: string\n width: number\n quality?: number\n}\n\n/**\n * Creates a Next.js custom image loader that proxies Supabase images through gateway.\n *\n * Usage in a loader file (e.g. src/lib/image-loader.ts):\n * ```ts\n * import { createImageLoader } from '@neowhale/storefront/next'\n * export default createImageLoader({\n * storeId: process.env.NEXT_PUBLIC_STORE_ID!,\n * gatewayUrl: 'https://whale-gateway.fly.dev',\n * supabaseHost: 'your-project.supabase.co',\n * signingSecret: process.env.NEXT_PUBLIC_MEDIA_SIGNING_SECRET!,\n * })\n * ```\n */\nexport function createImageLoader(config: {\n storeId: string\n gatewayUrl: string\n supabaseHost: string\n signingSecret: string\n}): (params: ImageLoaderParams) => string {\n return ({ src, width, quality }: ImageLoaderParams): string => {\n if (!src.includes(config.supabaseHost)) {\n return src\n }\n\n const w = String(snapWidth(width))\n const q = String(quality || 80)\n const f = 'webp'\n const encoded = WhaleClient.encodeBase64Url(src)\n const s = WhaleClient.signMedia(config.signingSecret, encoded, w, q, f)\n\n return `${config.gatewayUrl}/v1/stores/${config.storeId}/media?url=${encoded}&w=${w}&q=${q}&f=${f}&s=${s}`\n }\n}\n","import type { NextRequest } from 'next/server'\nimport { NextResponse } from 'next/server'\n\n/**\n * Creates a Next.js middleware that protects routes requiring authentication.\n *\n * Checks for a session token cookie or localStorage indicator.\n * Since middleware runs on the edge and can't access localStorage,\n * this checks for a cookie-based token instead.\n *\n * Usage in middleware.ts:\n * ```ts\n * import { createAuthMiddleware } from '@neowhale/storefront/next'\n * export const middleware = createAuthMiddleware({\n * protectedPaths: ['/account'],\n * loginPath: '/account',\n * })\n * export const config = { matcher: ['/account/:path*'] }\n * ```\n */\nexport function createAuthMiddleware(options: {\n protectedPaths: string[]\n loginPath: string\n cookieName?: string\n}) {\n const { protectedPaths, loginPath, cookieName = 'whale-session' } = options\n\n return function middleware(request: NextRequest) {\n const { pathname } = request.nextUrl\n\n // Never gate the login path itself — prevents redirect loops\n if (pathname === loginPath || pathname.startsWith(`${loginPath}/`)) {\n return NextResponse.next()\n }\n\n // Check if this is a protected path\n const isProtected = protectedPaths.some(\n (p) => pathname === p || pathname.startsWith(`${p}/`)\n )\n\n if (!isProtected) {\n return NextResponse.next()\n }\n\n // Check for session cookie\n const token = request.cookies.get(cookieName)?.value\n\n if (!token) {\n const url = request.nextUrl.clone()\n url.pathname = loginPath\n url.searchParams.set('redirect', pathname)\n return NextResponse.redirect(url)\n }\n\n return NextResponse.next()\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/next/headers.ts","../../src/next/rewrite.ts","../../src/next/server.ts","../../src/next/image-loader.ts","../../src/next/middleware.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Security headers for Next.js storefronts.\n */\n\nexport const securityHeaders = [\n {\n key: 'X-DNS-Prefetch-Control',\n value: 'on',\n },\n {\n key: 'Strict-Transport-Security',\n value: 'max-age=63072000; includeSubDomains; preload',\n },\n {\n key: 'X-Content-Type-Options',\n value: 'nosniff',\n },\n {\n key: 'X-Frame-Options',\n value: 'SAMEORIGIN',\n },\n {\n key: 'X-XSS-Protection',\n value: '1; mode=block',\n },\n {\n key: 'Referrer-Policy',\n value: 'strict-origin-when-cross-origin',\n },\n {\n key: 'Permissions-Policy',\n value: 'camera=(), microphone=(), geolocation=(self), interest-cohort=()',\n },\n]\n\n/**\n * Returns a Next.js `headers()` config with security headers applied to all routes.\n * Use in next.config.ts:\n *\n * ```ts\n * import { withSecurityHeaders } from '@neowhale/storefront/next'\n * export default { headers: withSecurityHeaders() }\n * ```\n */\nexport function withSecurityHeaders(\n extra?: { key: string; value: string }[]\n): () => Promise<{ source: string; headers: { key: string; value: string }[] }[]> {\n const allHeaders = extra ? [...securityHeaders, ...extra] : securityHeaders\n return async () => [\n {\n source: '/:path*',\n headers: allHeaders,\n },\n ]\n}\n","/**\n * Gateway rewrite rule for Next.js.\n * Proxies client-side /api/gw/* requests to whale-gateway to avoid CORS.\n *\n * Usage in next.config.ts:\n * ```ts\n * import { whaleGatewayRewrite } from '@neowhale/storefront/next'\n * export default {\n * async rewrites() {\n * return [whaleGatewayRewrite()]\n * }\n * }\n * ```\n */\nexport function whaleGatewayRewrite(\n gatewayUrl = 'https://whale-gateway.fly.dev',\n proxyPath = '/api/gw'\n): { source: string; destination: string } {\n return {\n source: `${proxyPath}/:path*`,\n destination: `${gatewayUrl}/:path*`,\n }\n}\n","import { WhaleClient } from '../client.js'\nimport type { Product, WhaleStorefrontConfig } from '../types.js'\n\n/**\n * Creates a server-side WhaleClient.\n * Reads from env vars by default — override with explicit config.\n */\nexport function createServerClient(config?: Partial<WhaleStorefrontConfig>): WhaleClient {\n return new WhaleClient({\n storeId: config?.storeId || process.env.NEXT_PUBLIC_STORE_ID || process.env.NEXT_PUBLIC_WHALE_STORE_ID || '',\n apiKey: config?.apiKey || process.env.NEXT_PUBLIC_API_KEY || process.env.NEXT_PUBLIC_WHALE_API_KEY || '',\n gatewayUrl: config?.gatewayUrl || process.env.NEXT_PUBLIC_API_URL || process.env.NEXT_PUBLIC_WHALE_GATEWAY_URL || 'https://whale-gateway.fly.dev',\n proxyPath: config?.proxyPath,\n })\n}\n\n/**\n * Server-side: fetch all published products with ISR caching.\n * Drop-in replacement for Flora's `getAllProducts()`.\n */\nexport async function getAllProducts(options?: {\n /** Revalidate interval in seconds. Defaults to 60. */\n revalidate?: number\n /** Filter function to exclude products (e.g. hidden categories, out of stock) */\n filter?: (product: Product) => boolean\n /** Override client config */\n client?: WhaleClient\n}): Promise<Product[]> {\n const client = options?.client ?? createServerClient()\n return client.getAllProducts({\n status: 'published',\n revalidate: options?.revalidate ?? 60,\n filter: options?.filter,\n })\n}\n","import { WhaleClient } from '../client.js'\n\nconst ALLOWED_WIDTHS = [64, 96, 128, 256, 384, 640, 828, 1080, 1280, 1920]\n\nfunction snapWidth(w: number): number {\n for (const aw of ALLOWED_WIDTHS) {\n if (aw >= w) return aw\n }\n return ALLOWED_WIDTHS[ALLOWED_WIDTHS.length - 1]\n}\n\ninterface ImageLoaderParams {\n src: string\n width: number\n quality?: number\n}\n\n/**\n * Creates a Next.js custom image loader that proxies Supabase images through gateway.\n *\n * Usage in a loader file (e.g. src/lib/image-loader.ts):\n * ```ts\n * import { createImageLoader } from '@neowhale/storefront/next'\n * export default createImageLoader({\n * storeId: process.env.NEXT_PUBLIC_STORE_ID!,\n * gatewayUrl: 'https://whale-gateway.fly.dev',\n * supabaseHost: 'your-project.supabase.co',\n * signingSecret: process.env.NEXT_PUBLIC_MEDIA_SIGNING_SECRET!,\n * })\n * ```\n */\nexport function createImageLoader(config: {\n storeId: string\n gatewayUrl: string\n supabaseHost: string\n signingSecret: string\n}): (params: ImageLoaderParams) => string {\n return ({ src, width, quality }: ImageLoaderParams): string => {\n // Strip retry cache-bust params (_r=N) so retries hit the same gateway cache key\n const cleanSrc = src.replace(/[?&]_r=\\d+/, '')\n\n if (!cleanSrc.includes(config.supabaseHost)) {\n return cleanSrc\n }\n\n const w = String(snapWidth(width))\n const q = String(quality || 80)\n const f = 'webp'\n const encoded = WhaleClient.encodeBase64Url(cleanSrc)\n const s = WhaleClient.signMedia(config.signingSecret, encoded, w, q, f)\n\n return `${config.gatewayUrl}/v1/stores/${config.storeId}/media?url=${encoded}&w=${w}&q=${q}&f=${f}&s=${s}`\n }\n}\n","import type { NextRequest } from 'next/server'\nimport { NextResponse } from 'next/server'\n\n/**\n * Creates a Next.js middleware that protects routes requiring authentication.\n *\n * Checks for a session token cookie or localStorage indicator.\n * Since middleware runs on the edge and can't access localStorage,\n * this checks for a cookie-based token instead.\n *\n * Usage in middleware.ts:\n * ```ts\n * import { createAuthMiddleware } from '@neowhale/storefront/next'\n * export const middleware = createAuthMiddleware({\n * protectedPaths: ['/account'],\n * loginPath: '/account',\n * })\n * export const config = { matcher: ['/account/:path*'] }\n * ```\n */\nexport function createAuthMiddleware(options: {\n protectedPaths: string[]\n loginPath: string\n cookieName?: string\n}) {\n const { protectedPaths, loginPath, cookieName = 'whale-session' } = options\n\n return function middleware(request: NextRequest) {\n const { pathname } = request.nextUrl\n\n // Never gate the login path itself — prevents redirect loops\n if (pathname === loginPath || pathname.startsWith(`${loginPath}/`)) {\n return NextResponse.next()\n }\n\n // Check if this is a protected path\n const isProtected = protectedPaths.some(\n (p) => pathname === p || pathname.startsWith(`${p}/`)\n )\n\n if (!isProtected) {\n return NextResponse.next()\n }\n\n // Check for session cookie\n const token = request.cookies.get(cookieName)?.value\n\n if (!token) {\n const url = request.nextUrl.clone()\n url.pathname = loginPath\n url.searchParams.set('redirect', pathname)\n return NextResponse.redirect(url)\n }\n\n return NextResponse.next()\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { p as PixelConfig } from './client-Ca8Otk-R.js';
1
+ import { p as PixelConfig } from './client-BSO263Uv.js';
2
2
 
3
3
  declare class PixelManager {
4
4
  private providers;
@@ -1,4 +1,4 @@
1
- import { p as PixelConfig } from './client-Ca8Otk-R.cjs';
1
+ import { p as PixelConfig } from './client-BSO263Uv.cjs';
2
2
 
3
3
  declare class PixelManager {
4
4
  private providers;