@neowhale/storefront 0.1.2 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/chunk-BTGOSNMP.cjs +95 -0
- package/dist/chunk-BTGOSNMP.cjs.map +1 -0
- package/dist/chunk-NLH3W6JA.js +93 -0
- package/dist/chunk-NLH3W6JA.js.map +1 -0
- package/dist/chunk-OP4LOUCV.cjs +291 -0
- package/dist/chunk-OP4LOUCV.cjs.map +1 -0
- package/dist/chunk-QIIKQ7DN.js +288 -0
- package/dist/chunk-QIIKQ7DN.js.map +1 -0
- package/dist/client-y0V1x0px.d.cts +305 -0
- package/dist/client-y0V1x0px.d.ts +305 -0
- package/dist/index.cjs +11 -2
- package/dist/index.d.cts +14 -267
- package/dist/index.d.ts +14 -267
- package/dist/index.js +2 -1
- package/dist/next/index.cjs +10 -7
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +1 -1
- package/dist/next/index.d.ts +1 -1
- package/dist/next/index.js +7 -4
- package/dist/next/index.js.map +1 -1
- package/dist/pixel-manager-CYtRIGo0.d.ts +10 -0
- package/dist/pixel-manager-Db6Czwr2.d.cts +10 -0
- package/dist/react/index.cjs +75 -14
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +25 -3
- package/dist/react/index.d.ts +25 -3
- package/dist/react/index.js +75 -15
- package/dist/react/index.js.map +1 -1
- package/package.json +8 -1
- package/dist/chunk-PR4PUHVN.js +0 -273
- package/dist/chunk-PR4PUHVN.js.map +0 -1
- package/dist/chunk-XMLH3TLA.cjs +0 -275
- package/dist/chunk-XMLH3TLA.cjs.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,269 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
unit: string;
|
|
4
|
-
label: string;
|
|
5
|
-
quantity: number;
|
|
6
|
-
sort_order: number;
|
|
7
|
-
default_price: number;
|
|
8
|
-
}
|
|
9
|
-
interface ProductVariation {
|
|
10
|
-
id: string;
|
|
11
|
-
product_id: string;
|
|
12
|
-
name: string;
|
|
13
|
-
sku: string | null;
|
|
14
|
-
price: number;
|
|
15
|
-
stock_quantity: number;
|
|
16
|
-
attributes: Record<string, string>;
|
|
17
|
-
}
|
|
18
|
-
interface Product {
|
|
19
|
-
id: string;
|
|
20
|
-
name: string;
|
|
21
|
-
slug: string;
|
|
22
|
-
sku: string | null;
|
|
23
|
-
description: string | null;
|
|
24
|
-
status: string;
|
|
25
|
-
type: string;
|
|
26
|
-
primary_category_id: string;
|
|
27
|
-
featured_image: string | null;
|
|
28
|
-
image_gallery: string[];
|
|
29
|
-
pricing_data: PricingTier[] | {
|
|
30
|
-
mode?: string;
|
|
31
|
-
tiers?: PricingTier[];
|
|
32
|
-
} | null;
|
|
33
|
-
custom_fields: Record<string, string | null>;
|
|
34
|
-
stock_quantity?: number;
|
|
35
|
-
}
|
|
36
|
-
interface Category {
|
|
37
|
-
id: string;
|
|
38
|
-
name: string;
|
|
39
|
-
slug: string;
|
|
40
|
-
}
|
|
41
|
-
interface CartItem {
|
|
42
|
-
id: string;
|
|
43
|
-
product_id: string;
|
|
44
|
-
product_name: string;
|
|
45
|
-
image_url: string | null;
|
|
46
|
-
quantity: number;
|
|
47
|
-
unit_price: number;
|
|
48
|
-
tier_label: string | null;
|
|
49
|
-
line_total: number;
|
|
50
|
-
}
|
|
51
|
-
interface TaxBreakdown {
|
|
52
|
-
name: string;
|
|
53
|
-
rate: number;
|
|
54
|
-
type: string;
|
|
55
|
-
rate_decimal: number;
|
|
56
|
-
}
|
|
57
|
-
interface Cart {
|
|
58
|
-
id: string;
|
|
59
|
-
items: CartItem[];
|
|
60
|
-
item_count: number;
|
|
61
|
-
subtotal: number;
|
|
62
|
-
tax_rate: number;
|
|
63
|
-
tax_amount: number;
|
|
64
|
-
total: number;
|
|
65
|
-
tax_breakdown: TaxBreakdown[];
|
|
66
|
-
discount_amount: number;
|
|
67
|
-
customer_email: string | null;
|
|
68
|
-
}
|
|
69
|
-
interface Order {
|
|
70
|
-
id: string;
|
|
71
|
-
order_number: string;
|
|
72
|
-
status: string;
|
|
73
|
-
total_amount: number;
|
|
74
|
-
subtotal: number;
|
|
75
|
-
tax_amount: number;
|
|
76
|
-
discount_amount: number;
|
|
77
|
-
item_count?: number;
|
|
78
|
-
created_at: string;
|
|
79
|
-
payment_status?: string;
|
|
80
|
-
fulfillment_status?: string;
|
|
81
|
-
items?: OrderItem[];
|
|
82
|
-
}
|
|
83
|
-
interface OrderItem {
|
|
84
|
-
id: string;
|
|
85
|
-
product_name: string;
|
|
86
|
-
quantity: number;
|
|
87
|
-
unit_price?: number;
|
|
88
|
-
cost_per_unit?: number;
|
|
89
|
-
line_total: number;
|
|
90
|
-
}
|
|
91
|
-
interface Customer {
|
|
92
|
-
id: string;
|
|
93
|
-
first_name: string;
|
|
94
|
-
last_name: string;
|
|
95
|
-
email: string | null;
|
|
96
|
-
phone: string | null;
|
|
97
|
-
loyalty_points: number;
|
|
98
|
-
loyalty_tier: string;
|
|
99
|
-
total_spent: number;
|
|
100
|
-
total_orders: number;
|
|
101
|
-
lifetime_value?: number;
|
|
102
|
-
street_address?: string | null;
|
|
103
|
-
city?: string | null;
|
|
104
|
-
state?: string | null;
|
|
105
|
-
postal_code?: string | null;
|
|
106
|
-
date_of_birth?: string | null;
|
|
107
|
-
created_at?: string;
|
|
108
|
-
is_staff?: boolean;
|
|
109
|
-
email_consent?: boolean;
|
|
110
|
-
sms_consent?: boolean;
|
|
111
|
-
}
|
|
112
|
-
interface Address {
|
|
113
|
-
firstName: string;
|
|
114
|
-
lastName: string;
|
|
115
|
-
address: string;
|
|
116
|
-
city: string;
|
|
117
|
-
state: string;
|
|
118
|
-
zip: string;
|
|
119
|
-
country: string;
|
|
120
|
-
}
|
|
121
|
-
interface PaymentData {
|
|
122
|
-
payment_method: 'card' | 'cash';
|
|
123
|
-
opaque_data?: {
|
|
124
|
-
dataDescriptor: string;
|
|
125
|
-
dataValue: string;
|
|
126
|
-
};
|
|
127
|
-
billTo?: Address;
|
|
128
|
-
shipTo?: Address;
|
|
129
|
-
}
|
|
130
|
-
interface CustomerAnalytics {
|
|
131
|
-
customer_id: string;
|
|
132
|
-
customer_name: string;
|
|
133
|
-
total_orders: number;
|
|
134
|
-
lifetime_revenue: number;
|
|
135
|
-
avg_order_value: number;
|
|
136
|
-
ltv_tier: string;
|
|
137
|
-
rfm_segment: string;
|
|
138
|
-
churn_risk: string;
|
|
139
|
-
last_order_date: string;
|
|
140
|
-
recency_days: number;
|
|
141
|
-
}
|
|
142
|
-
interface Location {
|
|
143
|
-
id: string;
|
|
144
|
-
name: string;
|
|
145
|
-
address?: string;
|
|
146
|
-
city?: string;
|
|
147
|
-
state?: string;
|
|
148
|
-
zip?: string;
|
|
149
|
-
phone?: string;
|
|
150
|
-
is_active?: boolean;
|
|
151
|
-
}
|
|
152
|
-
interface SendCodeResponse {
|
|
153
|
-
sent: boolean;
|
|
154
|
-
}
|
|
155
|
-
interface VerifyCodeResponse {
|
|
156
|
-
object: string;
|
|
157
|
-
token_hash: string;
|
|
158
|
-
needs_profile: boolean;
|
|
159
|
-
customer: Customer | null;
|
|
160
|
-
}
|
|
161
|
-
interface StorefrontSession {
|
|
162
|
-
id: string;
|
|
163
|
-
store_id: string;
|
|
164
|
-
customer_id?: string;
|
|
165
|
-
started_at: string;
|
|
166
|
-
last_active_at: string;
|
|
167
|
-
}
|
|
168
|
-
interface ListResponse<T> {
|
|
169
|
-
object: 'list';
|
|
170
|
-
data: T[];
|
|
171
|
-
has_more: boolean;
|
|
172
|
-
cursors?: {
|
|
173
|
-
before?: string;
|
|
174
|
-
after?: string;
|
|
175
|
-
};
|
|
176
|
-
url?: string;
|
|
177
|
-
}
|
|
178
|
-
interface WhaleStorefrontConfig {
|
|
179
|
-
/** Store UUID */
|
|
180
|
-
storeId: string;
|
|
181
|
-
/** API key (wk_live_... or wk_test_...) */
|
|
182
|
-
apiKey: string;
|
|
183
|
-
/** Gateway base URL. Defaults to https://whale-gateway.fly.dev */
|
|
184
|
-
gatewayUrl?: string;
|
|
185
|
-
/** Client-side proxy path. Defaults to /api/gw */
|
|
186
|
-
proxyPath?: string;
|
|
187
|
-
/** Media signing secret for image/video proxy */
|
|
188
|
-
mediaSigningSecret?: string;
|
|
189
|
-
/** Supabase host for media URL detection */
|
|
190
|
-
supabaseHost?: string;
|
|
191
|
-
/** localStorage key prefix. Defaults to "whale" */
|
|
192
|
-
storagePrefix?: string;
|
|
193
|
-
/** Analytics session TTL in ms. Defaults to 30 minutes */
|
|
194
|
-
sessionTtl?: number;
|
|
195
|
-
/** Enable debug logging */
|
|
196
|
-
debug?: boolean;
|
|
197
|
-
}
|
|
198
|
-
type EventType = 'page_view' | 'product_view' | 'add_to_cart' | 'remove_from_cart' | 'begin_checkout' | 'purchase' | 'category_view' | 'search';
|
|
1
|
+
export { A as Address, C as Cart, a as CartItem, b as Category, c as Customer, d as CustomerAnalytics, E as EventType, L as ListResponse, e as Location, O as Order, f as OrderItem, P as PaymentData, g as PixelConfig, h as PricingTier, i as Product, j as ProductVariation, S as SendCodeResponse, k as StorefrontConfig, l as StorefrontSession, T as TaxBreakdown, V as VerifyCodeResponse, W as WhaleClient, m as WhaleStorefrontConfig } from './client-y0V1x0px.js';
|
|
2
|
+
export { P as PixelManager } from './pixel-manager-CYtRIGo0.js';
|
|
199
3
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
listProducts(params?: {
|
|
212
|
-
limit?: number;
|
|
213
|
-
starting_after?: string;
|
|
214
|
-
status?: string;
|
|
215
|
-
}): Promise<ListResponse<Product>>;
|
|
216
|
-
getProduct(id: string): Promise<Product>;
|
|
217
|
-
getAllProducts(options?: {
|
|
218
|
-
status?: string;
|
|
219
|
-
maxPages?: number;
|
|
220
|
-
revalidate?: number;
|
|
221
|
-
filter?: (product: Product) => boolean;
|
|
222
|
-
}): Promise<Product[]>;
|
|
223
|
-
createCart(customerEmail?: string): Promise<Cart>;
|
|
224
|
-
getCart(cartId: string): Promise<Cart>;
|
|
225
|
-
addToCart(cartId: string, productId: string, quantity: number, options?: {
|
|
226
|
-
tier?: string;
|
|
227
|
-
unitPrice?: number;
|
|
228
|
-
}): Promise<CartItem>;
|
|
229
|
-
updateCartItem(cartId: string, itemId: string, quantity: number): Promise<Cart>;
|
|
230
|
-
removeCartItem(cartId: string, itemId: string): Promise<void>;
|
|
231
|
-
checkout(cartId: string, customerEmail?: string, payment?: PaymentData): Promise<Order>;
|
|
232
|
-
findCustomer(query: string): Promise<Customer[]>;
|
|
233
|
-
getCustomer(id: string): Promise<Customer>;
|
|
234
|
-
createCustomer(data: {
|
|
235
|
-
first_name: string;
|
|
236
|
-
last_name: string;
|
|
237
|
-
email: string;
|
|
238
|
-
phone?: string;
|
|
239
|
-
}): Promise<Customer>;
|
|
240
|
-
listOrders(params?: {
|
|
241
|
-
customer_id?: string;
|
|
242
|
-
limit?: number;
|
|
243
|
-
starting_after?: string;
|
|
244
|
-
}): Promise<ListResponse<Order>>;
|
|
245
|
-
getOrder(id: string): Promise<Order>;
|
|
246
|
-
getCustomerOrders(customerId: string): Promise<Order[]>;
|
|
247
|
-
sendCode(email: string): Promise<SendCodeResponse>;
|
|
248
|
-
verifyCode(email: string, code: string): Promise<VerifyCodeResponse>;
|
|
249
|
-
getCustomerAnalytics(customerId: string, customerName?: string): Promise<CustomerAnalytics | null>;
|
|
250
|
-
listLocations(): Promise<ListResponse<Location>>;
|
|
251
|
-
getCOAEmbedUrl(productId: string): string;
|
|
252
|
-
createSession(params: {
|
|
253
|
-
user_agent?: string;
|
|
254
|
-
referrer?: string;
|
|
255
|
-
}): Promise<StorefrontSession>;
|
|
256
|
-
updateSession(sessionId: string, params: {
|
|
257
|
-
last_active_at?: string;
|
|
258
|
-
customer_id?: string;
|
|
259
|
-
}): Promise<StorefrontSession>;
|
|
260
|
-
trackEvent(params: {
|
|
261
|
-
session_id: string;
|
|
262
|
-
event_type: EventType;
|
|
263
|
-
event_data?: Record<string, unknown>;
|
|
264
|
-
}): Promise<void>;
|
|
265
|
-
static encodeBase64Url(url: string): string;
|
|
266
|
-
static signMedia(signingSecret: string, encodedUrl: string, w: string, q: string, f: string): string;
|
|
267
|
-
}
|
|
4
|
+
/**
|
|
5
|
+
* Resilient HTTP sender for analytics/event payloads.
|
|
6
|
+
*
|
|
7
|
+
* Retries transient (5xx / network) failures with linear back-off,
|
|
8
|
+
* then falls back to navigator.sendBeacon so the event still lands
|
|
9
|
+
* even when the tab is being unloaded.
|
|
10
|
+
*
|
|
11
|
+
* 4xx responses are NOT retried — they indicate a client error that
|
|
12
|
+
* won't resolve by resending.
|
|
13
|
+
*/
|
|
14
|
+
declare function resilientSend(url: string, payload: object, headers: Record<string, string>): Promise<void>;
|
|
268
15
|
|
|
269
|
-
export {
|
|
16
|
+
export { resilientSend };
|
package/dist/index.js
CHANGED
package/dist/next/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkOP4LOUCV_cjs = require('../chunk-OP4LOUCV.cjs');
|
|
4
4
|
var server = require('next/server');
|
|
5
5
|
|
|
6
6
|
// src/next/headers.ts
|
|
@@ -54,10 +54,10 @@ 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
|
|
58
|
-
storeId: config?.storeId || process.env.NEXT_PUBLIC_STORE_ID || "",
|
|
59
|
-
apiKey: config?.apiKey || process.env.NEXT_PUBLIC_API_KEY || "",
|
|
60
|
-
gatewayUrl: config?.gatewayUrl || process.env.NEXT_PUBLIC_API_URL || "https://whale-gateway.fly.dev",
|
|
57
|
+
return new chunkOP4LOUCV_cjs.WhaleClient({
|
|
58
|
+
storeId: config?.storeId || process.env.NEXT_PUBLIC_STORE_ID || process.env.NEXT_PUBLIC_WHALE_STORE_ID || "",
|
|
59
|
+
apiKey: config?.apiKey || process.env.NEXT_PUBLIC_API_KEY || process.env.NEXT_PUBLIC_WHALE_API_KEY || "",
|
|
60
|
+
gatewayUrl: config?.gatewayUrl || process.env.NEXT_PUBLIC_API_URL || process.env.NEXT_PUBLIC_WHALE_GATEWAY_URL || "https://whale-gateway.fly.dev",
|
|
61
61
|
proxyPath: config?.proxyPath
|
|
62
62
|
});
|
|
63
63
|
}
|
|
@@ -86,8 +86,8 @@ function createImageLoader(config) {
|
|
|
86
86
|
const w = String(snapWidth(width));
|
|
87
87
|
const q = String(quality || 80);
|
|
88
88
|
const f = "webp";
|
|
89
|
-
const encoded =
|
|
90
|
-
const s =
|
|
89
|
+
const encoded = chunkOP4LOUCV_cjs.WhaleClient.encodeBase64Url(src);
|
|
90
|
+
const s = chunkOP4LOUCV_cjs.WhaleClient.signMedia(config.signingSecret, encoded, w, q, f);
|
|
91
91
|
return `${config.gatewayUrl}/v1/stores/${config.storeId}/media?url=${encoded}&w=${w}&q=${q}&f=${f}&s=${s}`;
|
|
92
92
|
};
|
|
93
93
|
}
|
|
@@ -95,6 +95,9 @@ function createAuthMiddleware(options) {
|
|
|
95
95
|
const { protectedPaths, loginPath, cookieName = "whale-session" } = options;
|
|
96
96
|
return function middleware(request) {
|
|
97
97
|
const { pathname } = request.nextUrl;
|
|
98
|
+
if (pathname === loginPath || pathname.startsWith(`${loginPath}/`)) {
|
|
99
|
+
return server.NextResponse.next();
|
|
100
|
+
}
|
|
98
101
|
const isProtected = protectedPaths.some(
|
|
99
102
|
(p) => pathname === p || pathname.startsWith(`${p}/`)
|
|
100
103
|
);
|
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 || '',\n apiKey: config?.apiKey || process.env.NEXT_PUBLIC_API_KEY || '',\n gatewayUrl: config?.gatewayUrl || process.env.NEXT_PUBLIC_API_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 // 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 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"]}
|
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-QIIKQ7DN.js';
|
|
2
2
|
import { NextResponse } from 'next/server';
|
|
3
3
|
|
|
4
4
|
// src/next/headers.ts
|
|
@@ -53,9 +53,9 @@ function whaleGatewayRewrite(gatewayUrl = "https://whale-gateway.fly.dev", proxy
|
|
|
53
53
|
// src/next/server.ts
|
|
54
54
|
function createServerClient(config) {
|
|
55
55
|
return new WhaleClient({
|
|
56
|
-
storeId: config?.storeId || process.env.NEXT_PUBLIC_STORE_ID || "",
|
|
57
|
-
apiKey: config?.apiKey || process.env.NEXT_PUBLIC_API_KEY || "",
|
|
58
|
-
gatewayUrl: config?.gatewayUrl || process.env.NEXT_PUBLIC_API_URL || "https://whale-gateway.fly.dev",
|
|
56
|
+
storeId: config?.storeId || process.env.NEXT_PUBLIC_STORE_ID || process.env.NEXT_PUBLIC_WHALE_STORE_ID || "",
|
|
57
|
+
apiKey: config?.apiKey || process.env.NEXT_PUBLIC_API_KEY || process.env.NEXT_PUBLIC_WHALE_API_KEY || "",
|
|
58
|
+
gatewayUrl: config?.gatewayUrl || process.env.NEXT_PUBLIC_API_URL || process.env.NEXT_PUBLIC_WHALE_GATEWAY_URL || "https://whale-gateway.fly.dev",
|
|
59
59
|
proxyPath: config?.proxyPath
|
|
60
60
|
});
|
|
61
61
|
}
|
|
@@ -93,6 +93,9 @@ function createAuthMiddleware(options) {
|
|
|
93
93
|
const { protectedPaths, loginPath, cookieName = "whale-session" } = options;
|
|
94
94
|
return function middleware(request) {
|
|
95
95
|
const { pathname } = request.nextUrl;
|
|
96
|
+
if (pathname === loginPath || pathname.startsWith(`${loginPath}/`)) {
|
|
97
|
+
return NextResponse.next();
|
|
98
|
+
}
|
|
96
99
|
const isProtected = protectedPaths.some(
|
|
97
100
|
(p) => pathname === p || pathname.startsWith(`${p}/`)
|
|
98
101
|
);
|
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 || '',\n apiKey: config?.apiKey || process.env.NEXT_PUBLIC_API_KEY || '',\n gatewayUrl: config?.gatewayUrl || process.env.NEXT_PUBLIC_API_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 // 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 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"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { g as PixelConfig } from './client-y0V1x0px.js';
|
|
2
|
+
|
|
3
|
+
declare class PixelManager {
|
|
4
|
+
private providers;
|
|
5
|
+
constructor(configs: PixelConfig[]);
|
|
6
|
+
initialize(): Promise<void>;
|
|
7
|
+
track(event: string, params?: Record<string, unknown>): void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export { PixelManager as P };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { g as PixelConfig } from './client-y0V1x0px.cjs';
|
|
2
|
+
|
|
3
|
+
declare class PixelManager {
|
|
4
|
+
private providers;
|
|
5
|
+
constructor(configs: PixelConfig[]);
|
|
6
|
+
initialize(): Promise<void>;
|
|
7
|
+
track(event: string, params?: Record<string, unknown>): void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export { PixelManager as P };
|