import { createCimplifyClient } from './chunk-NWUEWUX2.mjs';
import { PKCE_COOKIE, decodePkceState, PKCE_COOKIE_PATH, DEFAULT_ISSUER } from './chunk-7BM56JET.mjs';
import './chunk-TD3AY34U.mjs';
export { CimplifyError } from './chunk-AMZXALF6.mjs';
import './chunk-3G6RQLXK.mjs';
import { cache } from 'react';
import { jwtVerify, createRemoteJWKSet } from 'jose';

var DEFAULT_PROD_URL = "https://storefronts.cimplify.io";
var DEFAULT_DEV_URL = "http://127.0.0.1:8787";
function defaultBaseUrl() {
  const proc = globalThis.process;
  return proc?.env?.NODE_ENV === "development" ? DEFAULT_DEV_URL : DEFAULT_PROD_URL;
}
function readEnv(...keys) {
  const env = globalThis.process?.env;
  if (!env) return void 0;
  for (const k of keys) {
    const v = env[k]?.trim();
    if (v) return v;
  }
  return void 0;
}
var getServerClient = cache((opts = {}) => {
  const baseUrl = opts.apiUrl ?? readEnv("CIMPLIFY_API_URL", "NEXT_PUBLIC_CIMPLIFY_API_URL") ?? defaultBaseUrl();
  const publicKey = opts.secretKey ?? readEnv("CIMPLIFY_SECRET_KEY", "NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY") ?? "mock-dev";
  const client = createCimplifyClient({
    baseUrl,
    publicKey,
    suppressPublicKeyWarning: true
  });
  if (opts.locationId) {
    client.setLocationId(opts.locationId);
  }
  if (opts.accessToken) {
    client.setAccessToken(opts.accessToken);
  }
  return client;
});

// src/server/tags.ts
var tags = {
  products: () => "cimplify:products",
  product: (id) => `cimplify:product:${id}`,
  categories: () => "cimplify:categories",
  category: (id) => `cimplify:category:${id}`,
  categoryProducts: (id) => `cimplify:category:${id}:products`,
  collections: () => "cimplify:collections",
  collection: (id) => `cimplify:collection:${id}`,
  collectionProducts: (id) => `cimplify:collection:${id}:products`,
  business: () => "cimplify:business",
  brand: () => "cimplify:brand",
  locations: () => "cimplify:locations",
  location: (id) => `cimplify:location:${id}`,
  locale: () => "cimplify:locale",
  pricing: () => "cimplify:pricing",
  // Product-level tag (e.g. "vegan", "bestseller"), not "cache tag".
  tag: (name) => `cimplify:tag:${name}`,
  addons: () => "cimplify:addons",
  addon: (id) => `cimplify:addon:${id}`,
  subscriptions: () => "cimplify:subscriptions",
  subscription: (id) => `cimplify:subscription:${id}`,
  stock: () => "cimplify:stock",
  stockFor: (productId) => `cimplify:stock:${productId}`,
  orders: (customerId) => `cimplify:orders:${customerId}`,
  order: (id) => `cimplify:order:${id}`
};

// src/server/cache-life.ts
var CACHE_LIFE_DEFAULT = "max";
var CACHE_LIFE_PROBE = "seconds";

// src/server/revalidate.ts
var DEFAULT_PROFILE = CACHE_LIFE_DEFAULT;
var _revalidateTag = null;
async function getRevalidateTag() {
  if (_revalidateTag) return _revalidateTag;
  const specifier = "next/cache";
  const mod = await import(
    /* webpackIgnore: true */
    /* @vite-ignore */
    specifier
  );
  _revalidateTag = mod.revalidateTag;
  return _revalidateTag;
}
async function revalidate(profile, ...tagList) {
  const fn = await getRevalidateTag();
  for (const t of tagList) fn(t, profile);
}
async function revalidateProducts(profile = DEFAULT_PROFILE) {
  return revalidate(profile, tags.products());
}
async function revalidateProduct(id, profile = DEFAULT_PROFILE) {
  return revalidate(profile, tags.product(id), tags.products());
}
async function revalidateCategories(profile = DEFAULT_PROFILE) {
  return revalidate(profile, tags.categories());
}
async function revalidateCategory(id, profile = DEFAULT_PROFILE) {
  return revalidate(profile, tags.category(id), tags.categoryProducts(id), tags.categories());
}
async function revalidateCollections(profile = DEFAULT_PROFILE) {
  return revalidate(profile, tags.collections());
}
async function revalidateCollection(id, profile = DEFAULT_PROFILE) {
  return revalidate(
    profile,
    tags.collection(id),
    tags.collectionProducts(id),
    tags.collections()
  );
}
async function revalidateBusiness(profile = DEFAULT_PROFILE) {
  return revalidate(profile, tags.business());
}
async function revalidateBrand(profile = DEFAULT_PROFILE) {
  return revalidate(profile, tags.brand());
}
async function revalidateLocations(profile = DEFAULT_PROFILE) {
  return revalidate(profile, tags.locations());
}
async function revalidateLocation(id, profile = DEFAULT_PROFILE) {
  return revalidate(profile, tags.location(id), tags.locations());
}
async function revalidatePricing(profile = DEFAULT_PROFILE) {
  return revalidate(profile, tags.pricing(), tags.products());
}
async function revalidateAddOns(profile = DEFAULT_PROFILE) {
  return revalidate(profile, tags.addons());
}
async function revalidateAddOn(id, profile = DEFAULT_PROFILE) {
  return revalidate(profile, tags.addon(id), tags.addons());
}
async function revalidateSubscriptions(profile = DEFAULT_PROFILE) {
  return revalidate(profile, tags.subscriptions());
}
async function revalidateSubscription(id, profile = DEFAULT_PROFILE) {
  return revalidate(profile, tags.subscription(id), tags.subscriptions());
}
async function revalidateStock(productId, profile = DEFAULT_PROFILE) {
  return productId ? revalidate(profile, tags.stockFor(productId), tags.stock()) : revalidate(profile, tags.stock());
}
async function revalidateByTag(tag, profile = DEFAULT_PROFILE) {
  return revalidate(profile, tag);
}

// src/server/update.ts
var _updateTag = null;
var _refresh = null;
async function getUpdateTag() {
  if (_updateTag) return _updateTag;
  const specifier = "next/cache";
  const mod = await import(
    /* webpackIgnore: true */
    /* @vite-ignore */
    specifier
  );
  if (!mod.updateTag) {
    throw new Error("updateTag is not exported from next/cache \u2014 requires Next 16+");
  }
  _updateTag = mod.updateTag;
  return _updateTag;
}
async function getRefresh() {
  if (_refresh) return _refresh;
  const specifier = "next/cache";
  const mod = await import(
    /* webpackIgnore: true */
    /* @vite-ignore */
    specifier
  );
  if (!mod.refresh) {
    throw new Error("refresh is not exported from next/cache \u2014 requires Next 16+");
  }
  _refresh = mod.refresh;
  return _refresh;
}
async function update(...tagList) {
  const fn = await getUpdateTag();
  for (const t of tagList) fn(t);
}
async function updateProducts() {
  return update(tags.products());
}
async function updateProduct(id) {
  return update(tags.product(id), tags.products());
}
async function updateCategories() {
  return update(tags.categories());
}
async function updateCategory(id) {
  return update(tags.category(id), tags.categoryProducts(id), tags.categories());
}
async function updateCollections() {
  return update(tags.collections());
}
async function updateCollection(id) {
  return update(tags.collection(id), tags.collectionProducts(id), tags.collections());
}
async function updateBusiness() {
  return update(tags.business());
}
async function updateBrand() {
  return update(tags.brand());
}
async function updateLocations() {
  return update(tags.locations());
}
async function updateLocation(id) {
  return update(tags.location(id), tags.locations());
}
async function updatePricing() {
  return update(tags.pricing(), tags.products());
}
async function updateAddOns() {
  return update(tags.addons());
}
async function updateAddOn(id) {
  return update(tags.addon(id), tags.addons());
}
async function updateSubscriptions() {
  return update(tags.subscriptions());
}
async function updateSubscription(id) {
  return update(tags.subscription(id), tags.subscriptions());
}
async function updateStock(productId) {
  return productId ? update(tags.stockFor(productId), tags.stock()) : update(tags.stock());
}
async function updateByTag(tag) {
  return update(tag);
}
async function refreshPage() {
  const fn = await getRefresh();
  fn();
}
var SESSION_COOKIE = "cimplify_session";
var ACCESS_TOKEN_COOKIE = "cimplify_access";
var REFRESH_TOKEN_COOKIE = "cimplify_refresh";
var REFRESH_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 30;
var ACCESS_REFRESH_SKEW_SECONDS = 30;
var HEADER_LOCATION = "Location";
var PARAM_CODE = "code";
var PARAM_STATE = "state";
var PARAM_ERROR = "error";
var QUERY_AUTH_ERROR = "cimplify_auth_error";
var discoveryCache = /* @__PURE__ */ new Map();
var jwksCache = /* @__PURE__ */ new Map();
function issuerBase(cfg) {
  return (cfg.issuer ?? cfg.authUrl ?? DEFAULT_ISSUER).replace(/\/$/, "");
}
function cookieName(cfg) {
  return cfg.cookieName ?? SESSION_COOKIE;
}
function accessTokenCookieName(cfg) {
  return cfg.accessTokenCookieName ?? ACCESS_TOKEN_COOKIE;
}
function refreshTokenCookieName(cfg) {
  return cfg.refreshTokenCookieName ?? REFRESH_TOKEN_COOKIE;
}
async function discovery(cfg) {
  const base = issuerBase(cfg);
  let p = discoveryCache.get(base);
  if (!p) {
    p = fetch(`${base}/.well-known/openid-configuration`, {
      next: { revalidate: 3600 }
    }).then((r) => {
      if (!r.ok) throw new Error(`oidc discovery failed: ${r.status}`);
      return r.json();
    });
    discoveryCache.set(base, p);
  }
  return p;
}
async function jwks(cfg) {
  const doc = await discovery(cfg);
  let set = jwksCache.get(doc.jwks_uri);
  if (!set) {
    set = createRemoteJWKSet(new URL(doc.jwks_uri));
    jwksCache.set(doc.jwks_uri, set);
  }
  return { set, issuer: doc.issuer };
}
async function exchangeCode(opts) {
  const doc = await discovery(opts);
  const body = new URLSearchParams({
    grant_type: "authorization_code",
    code: opts.code,
    code_verifier: opts.codeVerifier,
    redirect_uri: opts.redirectUri,
    client_id: opts.clientId
  });
  const res = await fetch(doc.token_endpoint, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: body.toString()
  });
  if (!res.ok) {
    const text = await res.text().catch(() => "");
    throw new Error(`token exchange failed (${res.status}): ${text}`);
  }
  const payload = await res.json();
  return {
    accessToken: payload.access_token,
    idToken: payload.id_token,
    refreshToken: payload.refresh_token,
    expiresIn: payload.expires_in,
    scope: payload.scope
  };
}
async function verifyIdToken(cfg, idToken) {
  const { set, issuer } = await jwks(cfg);
  return jwtVerify(idToken, set, {
    issuer,
    audience: cfg.clientId
  });
}
function payloadToSession(payload) {
  return {
    sub: String(payload.sub ?? ""),
    name: payload.name,
    email: payload.email,
    emailVerified: payload.email_verified,
    phoneNumber: payload.phone_number,
    phoneNumberVerified: payload.phone_number_verified,
    exp: Number(payload.exp ?? 0),
    iat: Number(payload.iat ?? 0)
  };
}
function buildSessionCookie(cfg, idToken, maxAgeSeconds) {
  const name = cookieName(cfg);
  const parts = [
    `${name}=${idToken}`,
    "Path=/",
    "HttpOnly",
    "Secure",
    "SameSite=Lax",
    `Max-Age=${maxAgeSeconds}`
  ];
  if (cfg.cookieDomain) parts.push(`Domain=${cfg.cookieDomain}`);
  return parts.join("; ");
}
function buildSignoutCookie(cfg) {
  const name = cookieName(cfg);
  const parts = [`${name}=`, "Path=/", "HttpOnly", "Secure", "SameSite=Lax", "Max-Age=0"];
  if (cfg.cookieDomain) parts.push(`Domain=${cfg.cookieDomain}`);
  return parts.join("; ");
}
function buildAccessTokenCookie(cfg, accessToken, maxAgeSeconds) {
  const parts = [
    `${accessTokenCookieName(cfg)}=${accessToken}`,
    "Path=/",
    "HttpOnly",
    "Secure",
    "SameSite=Lax",
    `Max-Age=${maxAgeSeconds}`
  ];
  if (cfg.cookieDomain) parts.push(`Domain=${cfg.cookieDomain}`);
  return parts.join("; ");
}
function buildRefreshTokenCookie(cfg, refreshToken, maxAgeSeconds) {
  const parts = [
    `${refreshTokenCookieName(cfg)}=${refreshToken}`,
    "Path=/",
    "HttpOnly",
    "Secure",
    "SameSite=Lax",
    `Max-Age=${maxAgeSeconds}`
  ];
  if (cfg.cookieDomain) parts.push(`Domain=${cfg.cookieDomain}`);
  return parts.join("; ");
}
function buildSignoutCookies(cfg) {
  const clear = (name) => {
    const parts = [`${name}=`, "Path=/", "HttpOnly", "Secure", "SameSite=Lax", "Max-Age=0"];
    if (cfg.cookieDomain) parts.push(`Domain=${cfg.cookieDomain}`);
    return parts.join("; ");
  };
  return [
    clear(cookieName(cfg)),
    clear(accessTokenCookieName(cfg)),
    clear(refreshTokenCookieName(cfg))
  ];
}
function readCookie(name, cookieHeader) {
  if (!cookieHeader) return null;
  const match = cookieHeader.split(/;\s*/).find((p) => p.startsWith(`${name}=`));
  if (!match) return null;
  return match.slice(name.length + 1) || null;
}
function getRefreshTokenFromCookieHeader(cfg, cookieHeader) {
  return readCookie(refreshTokenCookieName(cfg), cookieHeader);
}
function getAccessTokenFromCookieHeader(cfg, cookieHeader) {
  if (!cookieHeader) return null;
  const name = accessTokenCookieName(cfg);
  const match = cookieHeader.split(/;\s*/).find((p) => p.startsWith(`${name}=`));
  if (!match) return null;
  const token = match.slice(name.length + 1);
  return token || null;
}
function isSameOriginCallback(req, redirectUri) {
  const site = req.headers.get("sec-fetch-site");
  if (site) return site === "same-origin";
  const origin = req.headers.get("origin");
  if (!origin) return false;
  try {
    return origin === new URL(redirectUri).origin;
  } catch {
    return false;
  }
}
async function handleOidcCallback(req, opts) {
  if (!isSameOriginCallback(req, opts.redirectUri)) {
    return Response.json({ error: "forbidden_origin" }, { status: 403 });
  }
  let body;
  try {
    body = await req.json();
  } catch {
    return Response.json({ error: "invalid_json" }, { status: 400 });
  }
  if (!body.code || !body.codeVerifier) {
    return Response.json({ error: "missing_code_or_verifier" }, { status: 400 });
  }
  let tokens;
  try {
    tokens = await exchangeCode({
      ...opts,
      code: body.code,
      codeVerifier: body.codeVerifier
    });
  } catch {
    return Response.json({ error: "exchange_failed" }, { status: 400 });
  }
  if (!tokens.idToken) {
    return Response.json({ error: "missing_id_token" }, { status: 400 });
  }
  try {
    await verifyIdToken(opts, tokens.idToken);
  } catch {
    return Response.json({ error: "id_token_invalid" }, { status: 401 });
  }
  const headers = new Headers({ "Content-Type": "application/json" });
  headers.append("Set-Cookie", buildSessionCookie(opts, tokens.idToken, tokens.expiresIn));
  headers.append("Set-Cookie", buildAccessTokenCookie(opts, tokens.accessToken, tokens.expiresIn));
  if (tokens.refreshToken) {
    headers.append(
      "Set-Cookie",
      buildRefreshTokenCookie(opts, tokens.refreshToken, REFRESH_COOKIE_MAX_AGE_SECONDS)
    );
  }
  return new Response(
    JSON.stringify({
      ok: true,
      accessToken: tokens.accessToken,
      expiresIn: tokens.expiresIn,
      scope: tokens.scope
    }),
    { status: 200, headers }
  );
}
function readPkceCookie(cookieHeader) {
  if (!cookieHeader) return null;
  const match = cookieHeader.split(/;\s*/).find((p) => p.startsWith(`${PKCE_COOKIE}=`));
  if (!match) return null;
  return decodePkceState(match.slice(PKCE_COOKIE.length + 1));
}
function buildPkceClearCookie() {
  return `${PKCE_COOKIE}=; Path=${PKCE_COOKIE_PATH}; Max-Age=0; Secure; SameSite=Lax`;
}
function safeReturnPath(returnTo, fallback) {
  if (returnTo && returnTo.startsWith("/") && !returnTo.startsWith("//")) return returnTo;
  return fallback;
}
function withAuthError(path, reason) {
  const sep = path.includes("?") ? "&" : "?";
  return `${path}${sep}${QUERY_AUTH_ERROR}=${encodeURIComponent(reason)}`;
}
async function handleRedirectCallback(req, opts) {
  const url = new URL(req.url);
  const code = url.searchParams.get(PARAM_CODE);
  const urlState = url.searchParams.get(PARAM_STATE);
  const authError = url.searchParams.get(PARAM_ERROR);
  const pkce = readPkceCookie(req.headers.get("cookie"));
  const returnTo = safeReturnPath(pkce?.returnTo, opts.defaultReturnTo ?? "/");
  const fail = (reason) => {
    const headers2 = new Headers({ [HEADER_LOCATION]: withAuthError(returnTo, reason) });
    headers2.append("Set-Cookie", buildPkceClearCookie());
    return new Response(null, { status: 302, headers: headers2 });
  };
  if (authError) return fail(authError);
  if (!code || !pkce) return fail("missing_code");
  if (!urlState || urlState !== pkce.state) return fail("state_mismatch");
  let tokens;
  try {
    tokens = await exchangeCode({ ...opts, code, codeVerifier: pkce.verifier });
  } catch {
    return fail("exchange_failed");
  }
  if (!tokens.idToken) return fail("missing_id_token");
  try {
    await verifyIdToken(opts, tokens.idToken);
  } catch {
    return fail("id_token_invalid");
  }
  const headers = new Headers({ [HEADER_LOCATION]: returnTo });
  headers.append("Set-Cookie", buildSessionCookie(opts, tokens.idToken, tokens.expiresIn));
  headers.append("Set-Cookie", buildAccessTokenCookie(opts, tokens.accessToken, tokens.expiresIn));
  if (tokens.refreshToken) {
    headers.append(
      "Set-Cookie",
      buildRefreshTokenCookie(opts, tokens.refreshToken, REFRESH_COOKIE_MAX_AGE_SECONDS)
    );
  }
  headers.append("Set-Cookie", buildPkceClearCookie());
  return new Response(null, { status: 302, headers });
}
async function handleSessionRequest(req, cfg) {
  const session = await getSessionFromCookieHeader(cfg, req.headers.get("cookie"));
  if (!session) {
    return new Response(JSON.stringify({ sub: null }), {
      status: 200,
      headers: { "Content-Type": "application/json", "Cache-Control": "no-store" }
    });
  }
  return new Response(JSON.stringify(session), {
    status: 200,
    headers: { "Content-Type": "application/json", "Cache-Control": "no-store" }
  });
}
async function getSessionFromCookieHeader(cfg, cookieHeader) {
  if (!cookieHeader) return null;
  const name = cookieName(cfg);
  const match = cookieHeader.split(/;\s*/).find((p) => p.startsWith(`${name}=`));
  if (!match) return null;
  const token = match.slice(name.length + 1);
  if (!token) return null;
  try {
    const { payload } = await verifyIdToken(cfg, token);
    return payloadToSession(payload);
  } catch {
    return null;
  }
}
async function refreshTokens(cfg, refreshToken) {
  const doc = await discovery(cfg);
  const body = new URLSearchParams({
    grant_type: "refresh_token",
    refresh_token: refreshToken,
    client_id: cfg.clientId
  });
  const res = await fetch(doc.token_endpoint, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: body.toString()
  });
  if (!res.ok) {
    const text = await res.text().catch(() => "");
    throw new Error(`token refresh failed (${res.status}): ${text}`);
  }
  const payload = await res.json();
  return {
    accessToken: payload.access_token,
    idToken: payload.id_token,
    refreshToken: payload.refresh_token,
    expiresIn: payload.expires_in,
    scope: payload.scope
  };
}
function decodeBase64Url(segment) {
  const b64 = segment.replace(/-/g, "+").replace(/_/g, "/");
  const pad = b64.length % 4 ? "=".repeat(4 - b64.length % 4) : "";
  return atob(b64 + pad);
}
function accessTokenExpiry(token) {
  if (!token) return null;
  const parts = token.split(".");
  if (parts.length < 2) return null;
  try {
    const payload = JSON.parse(decodeBase64Url(parts[1]));
    return typeof payload.exp === "number" ? payload.exp : null;
  } catch {
    return null;
  }
}
var refreshInflight = /* @__PURE__ */ new Map();
function dedupedRefresh(cfg, refreshToken) {
  const existing = refreshInflight.get(refreshToken);
  if (existing) return existing;
  const p = refreshTokens(cfg, refreshToken).finally(() => refreshInflight.delete(refreshToken));
  refreshInflight.set(refreshToken, p);
  return p;
}
function clearedResult(cfg) {
  return {
    outcome: "cleared",
    setCookies: buildSignoutCookies(cfg),
    cookies: [
      { name: cookieName(cfg), value: "" },
      { name: accessTokenCookieName(cfg), value: "" },
      { name: refreshTokenCookieName(cfg), value: "" }
    ]
  };
}
async function handleTokenRefresh(req, cfg) {
  const cookieHeader = req.headers.get("cookie");
  const refreshToken = getRefreshTokenFromCookieHeader(cfg, cookieHeader);
  if (!refreshToken) return { outcome: "noop", setCookies: [], cookies: [] };
  const access = getAccessTokenFromCookieHeader(cfg, cookieHeader);
  const exp = accessTokenExpiry(access);
  const now = Math.floor(Date.now() / 1e3);
  if (access && exp !== null && exp - now > ACCESS_REFRESH_SKEW_SECONDS) {
    return { outcome: "noop", setCookies: [], cookies: [] };
  }
  let tokens;
  try {
    tokens = await dedupedRefresh(cfg, refreshToken);
  } catch {
    return clearedResult(cfg);
  }
  if (!tokens.idToken) return clearedResult(cfg);
  const setCookies = [
    buildSessionCookie(cfg, tokens.idToken, tokens.expiresIn),
    buildAccessTokenCookie(cfg, tokens.accessToken, tokens.expiresIn)
  ];
  const cookies = [
    { name: cookieName(cfg), value: tokens.idToken },
    { name: accessTokenCookieName(cfg), value: tokens.accessToken }
  ];
  if (tokens.refreshToken) {
    setCookies.push(
      buildRefreshTokenCookie(cfg, tokens.refreshToken, REFRESH_COOKIE_MAX_AGE_SECONDS)
    );
    cookies.push({ name: refreshTokenCookieName(cfg), value: tokens.refreshToken });
  }
  return { outcome: "refreshed", setCookies, cookies };
}

export { CACHE_LIFE_DEFAULT, CACHE_LIFE_PROBE, buildAccessTokenCookie, buildRefreshTokenCookie, buildSessionCookie, buildSignoutCookie, buildSignoutCookies, exchangeCode, getAccessTokenFromCookieHeader, getRefreshTokenFromCookieHeader, getServerClient, getSessionFromCookieHeader, handleOidcCallback, handleRedirectCallback, handleSessionRequest, handleTokenRefresh, refreshPage, refreshTokens, revalidateAddOn, revalidateAddOns, revalidateBrand, revalidateBusiness, revalidateByTag, revalidateCategories, revalidateCategory, revalidateCollection, revalidateCollections, revalidateLocation, revalidateLocations, revalidatePricing, revalidateProduct, revalidateProducts, revalidateStock, revalidateSubscription, revalidateSubscriptions, tags, updateAddOn, updateAddOns, updateBrand, updateBusiness, updateByTag, updateCategories, updateCategory, updateCollection, updateCollections, updateLocation, updateLocations, updatePricing, updateProduct, updateProducts, updateStock, updateSubscription, updateSubscriptions, verifyIdToken };
