@neowhale/storefront 0.2.12 → 0.2.19

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-7KXJLHGA.cjs +160 -0
  2. package/dist/chunk-7KXJLHGA.cjs.map +1 -0
  3. package/dist/{chunk-3VKRKDPL.cjs → chunk-CQCCXDUS.cjs} +52 -11
  4. package/dist/chunk-CQCCXDUS.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-M2MR6C55.js → chunk-XHWAUMWS.js} +52 -11
  8. package/dist/chunk-XHWAUMWS.js.map +1 -0
  9. package/dist/{client-T1eA5Xs-.d.cts → client-D1XVKpFt.d.cts} +78 -4
  10. package/dist/{client-T1eA5Xs-.d.ts → client-D1XVKpFt.d.ts} +78 -4
  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-1C6-h9Rp.d.ts → pixel-manager-C6PAp7vQ.d.ts} +1 -1
  22. package/dist/{pixel-manager-BHE1nZ90.d.cts → pixel-manager-DZwpn_x2.d.cts} +1 -1
  23. package/dist/react/index.cjs +1290 -56
  24. package/dist/react/index.cjs.map +1 -1
  25. package/dist/react/index.d.cts +54 -7
  26. package/dist/react/index.d.ts +54 -7
  27. package/dist/react/index.js +1284 -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 +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":";;;;;;AAIO,IAAM,eAAA,GAAkB;AAAA,EAC7B;AAAA,IACE,GAAA,EAAK,wBAAA;AAAA,IACL,KAAA,EAAO;AAAA,GACT;AAAA,EACA;AAAA,IACE,GAAA,EAAK,2BAAA;AAAA,IACL,KAAA,EAAO;AAAA,GACT;AAAA,EACA;AAAA,IACE,GAAA,EAAK,wBAAA;AAAA,IACL,KAAA,EAAO;AAAA,GACT;AAAA,EACA;AAAA,IACE,GAAA,EAAK,iBAAA;AAAA,IACL,KAAA,EAAO;AAAA,GACT;AAAA,EACA;AAAA,IACE,GAAA,EAAK,kBAAA;AAAA,IACL,KAAA,EAAO;AAAA,GACT;AAAA,EACA;AAAA,IACE,GAAA,EAAK,iBAAA;AAAA,IACL,KAAA,EAAO;AAAA,GACT;AAAA,EACA;AAAA,IACE,GAAA,EAAK,oBAAA;AAAA,IACL,KAAA,EAAO;AAAA;AAEX;AAWO,SAAS,oBACd,KAAA,EACgF;AAChF,EAAA,MAAM,aAAa,KAAA,GAAQ,CAAC,GAAG,eAAA,EAAiB,GAAG,KAAK,CAAA,GAAI,eAAA;AAC5D,EAAA,OAAO,YAAY;AAAA,IACjB;AAAA,MACE,MAAA,EAAQ,SAAA;AAAA,MACR,OAAA,EAAS;AAAA;AACX,GACF;AACF;;;ACxCO,SAAS,mBAAA,CACd,UAAA,GAAa,+BAAA,EACb,SAAA,GAAY,SAAA,EAC6B;AACzC,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,GAAG,SAAS,CAAA,OAAA,CAAA;AAAA,IACpB,WAAA,EAAa,GAAG,UAAU,CAAA,OAAA;AAAA,GAC5B;AACF;;;ACfO,SAAS,mBAAmB,MAAA,EAAsD;AACvF,EAAA,OAAO,IAAIA,6BAAA,CAAY;AAAA,IACrB,OAAA,EAAS,QAAQ,OAAA,IAAW,OAAA,CAAQ,IAAI,oBAAA,IAAwB,OAAA,CAAQ,IAAI,0BAAA,IAA8B,EAAA;AAAA,IAC1G,MAAA,EAAQ,QAAQ,MAAA,IAAU,OAAA,CAAQ,IAAI,mBAAA,IAAuB,OAAA,CAAQ,IAAI,yBAAA,IAA6B,EAAA;AAAA,IACtG,UAAA,EAAY,QAAQ,UAAA,IAAc,OAAA,CAAQ,IAAI,mBAAA,IAAuB,OAAA,CAAQ,IAAI,6BAAA,IAAiC,+BAAA;AAAA,IAClH,WAAW,MAAA,EAAQ;AAAA,GACpB,CAAA;AACH;AAMA,eAAsB,eAAe,OAAA,EAOd;AACrB,EAAA,MAAM,MAAA,GAAS,OAAA,EAAS,MAAA,IAAU,kBAAA,EAAmB;AACrD,EAAA,OAAO,OAAO,cAAA,CAAe;AAAA,IAC3B,MAAA,EAAQ,WAAA;AAAA,IACR,UAAA,EAAY,SAAS,UAAA,IAAc,EAAA;AAAA,IACnC,QAAQ,OAAA,EAAS;AAAA,GAClB,CAAA;AACH;;;AChCA,IAAM,cAAA,GAAiB,CAAC,EAAA,EAAI,EAAA,EAAI,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,IAAA,EAAM,IAAA,EAAM,IAAI,CAAA;AAEzE,SAAS,UAAU,CAAA,EAAmB;AACpC,EAAA,KAAA,MAAW,MAAM,cAAA,EAAgB;AAC/B,IAAA,IAAI,EAAA,IAAM,GAAG,OAAO,EAAA;AAAA,EACtB;AACA,EAAA,OAAO,cAAA,CAAe,cAAA,CAAe,MAAA,GAAS,CAAC,CAAA;AACjD;AAsBO,SAAS,kBAAkB,MAAA,EAKQ;AACxC,EAAA,OAAO,CAAC,EAAE,GAAA,EAAK,KAAA,EAAO,SAAQ,KAAiC;AAE7D,IAAA,MAAM,QAAA,GAAW,GAAA,CAAI,OAAA,CAAQ,YAAA,EAAc,EAAE,CAAA;AAE7C,IAAA,IAAI,CAAC,QAAA,CAAS,QAAA,CAAS,MAAA,CAAO,YAAY,CAAA,EAAG;AAC3C,MAAA,OAAO,QAAA;AAAA,IACT;AAEA,IAAA,MAAM,CAAA,GAAI,MAAA,CAAO,SAAA,CAAU,KAAK,CAAC,CAAA;AACjC,IAAA,MAAM,CAAA,GAAI,MAAA,CAAO,OAAA,IAAW,EAAE,CAAA;AAC9B,IAAA,MAAM,CAAA,GAAI,MAAA;AACV,IAAA,MAAM,OAAA,GAAUA,6BAAA,CAAY,eAAA,CAAgB,QAAQ,CAAA;AACpD,IAAA,MAAM,CAAA,GAAIA,8BAAY,SAAA,CAAU,MAAA,CAAO,eAAe,OAAA,EAAS,CAAA,EAAG,GAAG,CAAC,CAAA;AAEtE,IAAA,OAAO,CAAA,EAAG,MAAA,CAAO,UAAU,CAAA,WAAA,EAAc,OAAO,OAAO,CAAA,WAAA,EAAc,OAAO,CAAA,GAAA,EAAM,CAAC,CAAA,GAAA,EAAM,CAAC,CAAA,GAAA,EAAM,CAAC,MAAM,CAAC,CAAA,CAAA;AAAA,EAC1G,CAAA;AACF;ACjCO,SAAS,qBAAqB,OAAA,EAIlC;AACD,EAAA,MAAM,EAAE,cAAA,EAAgB,SAAA,EAAW,UAAA,GAAa,iBAAgB,GAAI,OAAA;AAEpE,EAAA,OAAO,SAAS,WAAW,OAAA,EAAsB;AAC/C,IAAA,MAAM,EAAE,QAAA,EAAS,GAAI,OAAA,CAAQ,OAAA;AAG7B,IAAA,IAAI,aAAa,SAAA,IAAa,QAAA,CAAS,WAAW,CAAA,EAAG,SAAS,GAAG,CAAA,EAAG;AAClE,MAAA,OAAOC,oBAAa,IAAA,EAAK;AAAA,IAC3B;AAGA,IAAA,MAAM,cAAc,cAAA,CAAe,IAAA;AAAA,MACjC,CAAC,MAAM,QAAA,KAAa,CAAA,IAAK,SAAS,UAAA,CAAW,CAAA,EAAG,CAAC,CAAA,CAAA,CAAG;AAAA,KACtD;AAEA,IAAA,IAAI,CAAC,WAAA,EAAa;AAChB,MAAA,OAAOA,oBAAa,IAAA,EAAK;AAAA,IAC3B;AAGA,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,UAAU,CAAA,EAAG,KAAA;AAE/C,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,OAAA,CAAQ,KAAA,EAAM;AAClC,MAAA,GAAA,CAAI,QAAA,GAAW,SAAA;AACf,MAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,UAAA,EAAY,QAAQ,CAAA;AACzC,MAAA,OAAOA,mBAAA,CAAa,SAAS,GAAG,CAAA;AAAA,IAClC;AAEA,IAAA,OAAOA,oBAAa,IAAA,EAAK;AAAA,EAC3B,CAAA;AACF","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-T1eA5Xs-.cjs';
1
+ import { F as WhaleStorefrontConfig, W as WhaleClient, r as Product } from '../client-D1XVKpFt.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-T1eA5Xs-.js';
1
+ import { F as WhaleStorefrontConfig, W as WhaleClient, r as Product } from '../client-D1XVKpFt.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-XHWAUMWS.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-T1eA5Xs-.js';
1
+ import { p as PixelConfig } from './client-D1XVKpFt.js';
2
2
 
3
3
  declare class PixelManager {
4
4
  private providers;
@@ -1,4 +1,4 @@
1
- import { p as PixelConfig } from './client-T1eA5Xs-.cjs';
1
+ import { p as PixelConfig } from './client-D1XVKpFt.cjs';
2
2
 
3
3
  declare class PixelManager {
4
4
  private providers;