@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.
- package/dist/{chunk-M2MR6C55.js → chunk-3Q7CPJBA.js} +68 -18
- package/dist/chunk-3Q7CPJBA.js.map +1 -0
- package/dist/chunk-7KXJLHGA.cjs +160 -0
- package/dist/chunk-7KXJLHGA.cjs.map +1 -0
- package/dist/chunk-PXS2DPVL.js +158 -0
- package/dist/chunk-PXS2DPVL.js.map +1 -0
- package/dist/{chunk-3VKRKDPL.cjs → chunk-VAA2KKCH.cjs} +68 -18
- package/dist/chunk-VAA2KKCH.cjs.map +1 -0
- package/dist/{client-Ca8Otk-R.d.cts → client-BSO263Uv.d.cts} +91 -6
- package/dist/{client-Ca8Otk-R.d.ts → client-BSO263Uv.d.ts} +91 -6
- package/dist/index.cjs +5 -5
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/next/index.cjs +7 -6
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +1 -1
- package/dist/next/index.d.ts +1 -1
- package/dist/next/index.js +5 -4
- package/dist/next/index.js.map +1 -1
- package/dist/{pixel-manager-CIZKghfx.d.ts → pixel-manager-BcL95odX.d.ts} +1 -1
- package/dist/{pixel-manager-CIR16DXY.d.cts → pixel-manager-DJ9m2FaQ.d.cts} +1 -1
- package/dist/react/index.cjs +1503 -56
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +71 -9
- package/dist/react/index.d.ts +71 -9
- package/dist/react/index.js +1497 -55
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-3VKRKDPL.cjs.map +0 -1
- package/dist/chunk-BTGOSNMP.cjs +0 -95
- package/dist/chunk-BTGOSNMP.cjs.map +0 -1
- package/dist/chunk-M2MR6C55.js.map +0 -1
- package/dist/chunk-NLH3W6JA.js +0 -93
- 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
|
|
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
|
|
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<
|
|
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
|
|
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
|
|
4
|
-
var
|
|
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
|
|
10
|
+
get: function () { return chunk7KXJLHGA_cjs.PixelManager; }
|
|
11
11
|
});
|
|
12
12
|
Object.defineProperty(exports, "WhaleClient", {
|
|
13
13
|
enumerable: true,
|
|
14
|
-
get: function () { return
|
|
14
|
+
get: function () { return chunkVAA2KKCH_cjs.WhaleClient; }
|
|
15
15
|
});
|
|
16
16
|
Object.defineProperty(exports, "resilientSend", {
|
|
17
17
|
enumerable: true,
|
|
18
|
-
get: function () { return
|
|
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,
|
|
2
|
-
export { P as PixelManager } from './pixel-manager-
|
|
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,
|
|
2
|
-
export { P as PixelManager } from './pixel-manager-
|
|
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-
|
|
2
|
-
export { WhaleClient, resilientSend } from './chunk-
|
|
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
|
package/dist/next/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
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
|
|
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
|
-
|
|
84
|
-
|
|
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 =
|
|
90
|
-
const s =
|
|
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
|
}
|
package/dist/next/index.cjs.map
CHANGED
|
@@ -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"]}
|
package/dist/next/index.d.cts
CHANGED
package/dist/next/index.d.ts
CHANGED
package/dist/next/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WhaleClient } from '../chunk-
|
|
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
|
-
|
|
82
|
-
|
|
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(
|
|
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
|
};
|
package/dist/next/index.js.map
CHANGED
|
@@ -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"]}
|