import "./chunk-AQ2CLRU3.js";

// ../@emulators/stripe/dist/index.js
import { AsyncLocalStorage } from "async_hooks";
import { readFileSync } from "fs";
import { fileURLToPath } from "url";
import { dirname, join } from "path";
import { randomBytes } from "crypto";
var tenantContext = new AsyncLocalStorage();
function createErrorHandler(documentationUrl) {
  return async (c, next) => {
    if (documentationUrl) {
      c.set("docsUrl", documentationUrl);
    }
    await next();
  };
}
var errorHandler = createErrorHandler();
var isDebug = typeof process !== "undefined" && (process.env.DEBUG === "1" || process.env.DEBUG === "true" || process.env.EMULATE_DEBUG === "1");
function authLax() {
  const v = process.env.EMULATE_AUTH_LAX;
  return v === "1" || v === "true";
}
function authFlagEnabled(...flags) {
  return flags.some((f) => {
    const v = process.env[f];
    return v === "1" || v === "true";
  });
}
function requireAuthWhen(...flags) {
  return async (c, next) => {
    if (!authLax() && authFlagEnabled(...flags) && !c.get("authUser")) {
      const docsUrl = c.get("docsUrl") ?? "https://github.com/Vinniai/agent-emulate#readme";
      return c.json({ message: "Requires authentication", documentation_url: docsUrl }, 401);
    }
    await next();
  };
}
var __dirname = dirname(fileURLToPath(import.meta.url));
var FONTS = {
  "geist-sans.woff2": readFileSync(join(__dirname, "fonts", "geist-sans.woff2")),
  "GeistPixel-Square.woff2": readFileSync(join(__dirname, "fonts", "GeistPixel-Square.woff2"))
};
var FAVICON = readFileSync(join(__dirname, "fonts", "favicon.ico"));
var DEFAULT_THEME = {
  slug: "default",
  name: "Emulator",
  scheme: "dark",
  bg: "oklch(0.145 0 0)",
  surface: "oklch(0.205 0 0)",
  border: "oklch(1 0 0 / 10%)",
  text: "oklch(0.985 0 0)",
  muted: "oklch(0.708 0 0)",
  accent: "oklch(0.922 0 0)",
  accentText: "oklch(0.205 0 0)",
  font: "'Geist',-apple-system,BlinkMacSystemFont,sans-serif",
  radius: "0.625rem"
};
var THEMES = {
  google: {
    slug: "google",
    name: "Google",
    scheme: "light",
    bg: "#ffffff",
    surface: "#ffffff",
    border: "#dadce0",
    text: "#202124",
    muted: "#5f6368",
    accent: "#1a73e8",
    accentText: "#ffffff",
    font: "'Google Sans',Roboto,Arial,sans-serif",
    radius: "4px"
  },
  microsoft: {
    slug: "microsoft",
    name: "Microsoft",
    scheme: "light",
    bg: "#ffffff",
    surface: "#ffffff",
    border: "#d1d1d1",
    text: "#1b1b1b",
    muted: "#605e5c",
    accent: "#0067b8",
    accentText: "#ffffff",
    font: "'Segoe UI',-apple-system,sans-serif",
    radius: "2px"
  },
  apple: {
    slug: "apple",
    name: "Apple",
    scheme: "light",
    bg: "#ffffff",
    surface: "#ffffff",
    border: "#d2d2d7",
    text: "#1d1d1f",
    muted: "#6e6e73",
    accent: "#0066cc",
    accentText: "#ffffff",
    font: "-apple-system,'SF Pro Text',Helvetica,sans-serif",
    radius: "12px"
  },
  github: {
    slug: "github",
    name: "GitHub",
    scheme: "light",
    bg: "#ffffff",
    surface: "#ffffff",
    border: "#d1d9e0",
    text: "#1f2328",
    muted: "#59636e",
    accent: "#1f883d",
    accentText: "#ffffff",
    font: "-apple-system,'Mona Sans','Segoe UI',Helvetica,sans-serif",
    radius: "6px"
  },
  okta: {
    slug: "okta",
    name: "Okta",
    scheme: "light",
    bg: "#f9f9f9",
    surface: "#ffffff",
    border: "#e1e1e1",
    text: "#1d1d21",
    muted: "#6e6e78",
    accent: "#3f59e4",
    accentText: "#ffffff",
    font: "'Aeonik',-apple-system,sans-serif",
    radius: "4px"
  },
  slack: {
    slug: "slack",
    name: "Slack",
    scheme: "light",
    bg: "#ffffff",
    surface: "#ffffff",
    border: "#dddddd",
    text: "#1d1c1d",
    muted: "#616061",
    accent: "#4a154b",
    accentText: "#ffffff",
    font: "Lato,-apple-system,sans-serif",
    radius: "8px"
  },
  salesforce: {
    slug: "salesforce",
    name: "Salesforce",
    scheme: "light",
    bg: "#f4f6f9",
    surface: "#ffffff",
    border: "#c9c9c9",
    text: "#080707",
    muted: "#54698d",
    accent: "#0070d2",
    accentText: "#ffffff",
    font: "'Salesforce Sans',-apple-system,sans-serif",
    radius: "4px"
  },
  workos: {
    slug: "workos",
    name: "WorkOS",
    scheme: "light",
    bg: "#ffffff",
    surface: "#ffffff",
    border: "#e4e4e7",
    text: "#18181b",
    muted: "#6b7280",
    accent: "#6363f1",
    accentText: "#ffffff",
    font: "'Inter',-apple-system,sans-serif",
    radius: "8px"
  },
  clerk: {
    slug: "clerk",
    name: "Clerk",
    scheme: "light",
    bg: "#ffffff",
    surface: "#ffffff",
    border: "#eeeef0",
    text: "#212126",
    muted: "#747686",
    accent: "#6c47ff",
    accentText: "#ffffff",
    font: "'Inter',-apple-system,sans-serif",
    radius: "8px"
  },
  hubspot: {
    slug: "hubspot",
    name: "HubSpot",
    scheme: "light",
    bg: "#f5f8fa",
    surface: "#ffffff",
    border: "#cbd6e2",
    text: "#33475b",
    muted: "#516f90",
    accent: "#ff7a59",
    accentText: "#ffffff",
    font: "'Lexend Deca',-apple-system,sans-serif",
    radius: "6px"
  },
  stripe: {
    slug: "stripe",
    name: "Stripe",
    scheme: "light",
    bg: "#ffffff",
    surface: "#ffffff",
    border: "#e6e6e6",
    text: "#1a1f36",
    muted: "#697386",
    accent: "#635bff",
    accentText: "#ffffff",
    font: "-apple-system,'Helvetica Neue',sans-serif",
    radius: "8px"
  },
  vercel: {
    slug: "vercel",
    name: "Vercel",
    scheme: "light",
    bg: "#ffffff",
    surface: "#ffffff",
    border: "#eaeaea",
    text: "#171717",
    muted: "#888888",
    accent: "#171717",
    accentText: "#ffffff",
    font: "'Geist',-apple-system,sans-serif",
    radius: "6px"
  },
  resend: {
    slug: "resend",
    name: "Resend",
    scheme: "dark",
    bg: "#000000",
    surface: "#0c0c0c",
    border: "#262626",
    text: "#fafafa",
    muted: "#a3a3a3",
    accent: "#ffffff",
    accentText: "#000000",
    font: "-apple-system,'Inter',sans-serif",
    radius: "8px"
  },
  nango: {
    slug: "nango",
    name: "Nango",
    scheme: "light",
    bg: "#ffffff",
    surface: "#ffffff",
    border: "#e5e7eb",
    text: "#111827",
    muted: "#6b7280",
    accent: "#467df4",
    accentText: "#ffffff",
    font: "'Inter',-apple-system,sans-serif",
    radius: "8px"
  },
  simpro: {
    slug: "simpro",
    name: "Simpro",
    scheme: "light",
    bg: "#ffffff",
    surface: "#ffffff",
    border: "#dde3ea",
    text: "#1f2d3d",
    muted: "#5b6b7b",
    accent: "#0a8a3f",
    accentText: "#ffffff",
    font: "-apple-system,'Open Sans',sans-serif",
    radius: "4px"
  }
};
function getTheme(service) {
  if (!service) return DEFAULT_THEME;
  return THEMES[service.toLowerCase()] ?? DEFAULT_THEME;
}
function themeOverrideCss(theme) {
  if (theme.slug === "default") return "";
  return `
body{background:${theme.bg};color:${theme.text};font-family:${theme.font};}
.emu-bar{background:${theme.scheme === "dark" ? "#000" : "#1b1b1f"};border-bottom-color:rgba(255,255,255,.08);color:#ffd166;}
.emu-bar-title{color:#fff;font-family:${theme.font};}
.emu-bar-links a{color:#bdbdbd;}
.emu-bar-links a:hover{color:#fff;}
.content-inner{background:${theme.surface};border:1px solid ${theme.border};border-radius:${theme.radius};padding:32px 28px;box-shadow:0 1px 3px rgba(0,0,0,.08);}
.card-title{color:${theme.text};font-family:${theme.font};font-weight:600;}
.card-subtitle{color:${theme.muted};}
.user-btn{background:${theme.surface};border:1px solid ${theme.border};color:${theme.text};border-radius:${theme.radius};}
.user-btn:hover{border-color:${theme.accent};background:${theme.scheme === "dark" ? "rgba(255,255,255,.04)" : "rgba(0,0,0,.02)"};}
.avatar{background:${theme.accent};color:${theme.accentText};font-family:${theme.font};}
.user-login{color:${theme.text};}
.user-meta{color:${theme.muted};}
.user-email{color:${theme.muted};opacity:.8;}
.powered-by{color:${theme.muted};}
.powered-by a{color:${theme.accent};}
.error-title{font-family:${theme.font};}
.error-msg{color:${theme.muted};}
`.trim();
}
var google = `<svg viewBox="0 0 48 48" width="42" height="42" aria-hidden="true"><path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/><path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/><path fill="#FBBC05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"/><path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/></svg>`;
var microsoft = `<svg viewBox="0 0 23 23" width="36" height="36" aria-hidden="true"><path fill="#f25022" d="M1 1h10v10H1z"/><path fill="#7fba00" d="M12 1h10v10H12z"/><path fill="#00a4ef" d="M1 12h10v10H1z"/><path fill="#ffb900" d="M12 12h10v10H12z"/></svg>`;
var apple = `<svg viewBox="0 0 24 24" width="40" height="40" fill="#000" aria-hidden="true"><path d="M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701"/></svg>`;
var github = `<svg viewBox="0 0 24 24" width="40" height="40" fill="#1f2328" aria-hidden="true"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>`;
var okta = `<svg viewBox="0 0 24 24" width="38" height="38" fill="#00297a" aria-hidden="true"><path d="M12 0C5.385 0 0 5.385 0 12s5.385 12 12 12 12-5.385 12-12S18.615 0 12 0zm0 18c-3.309 0-6-2.691-6-6s2.691-6 6-6 6 2.691 6 6-2.691 6-6 6z"/></svg>`;
var slack = `<svg viewBox="0 0 122.8 122.8" width="36" height="36" aria-hidden="true"><path d="M25.8 77.6c0 7.1-5.8 12.9-12.9 12.9S0 84.7 0 77.6s5.8-12.9 12.9-12.9h12.9v12.9zm6.5 0c0-7.1 5.8-12.9 12.9-12.9s12.9 5.8 12.9 12.9v32.3c0 7.1-5.8 12.9-12.9 12.9s-12.9-5.8-12.9-12.9V77.6z" fill="#36C5F0"/><path d="M45.2 25.8c-7.1 0-12.9-5.8-12.9-12.9S38.1 0 45.2 0s12.9 5.8 12.9 12.9v12.9H45.2zm0 6.5c7.1 0 12.9 5.8 12.9 12.9s-5.8 12.9-12.9 12.9H12.9C5.8 58.1 0 52.3 0 45.2s5.8-12.9 12.9-12.9h32.3z" fill="#2EB67D"/><path d="M97 45.2c0-7.1 5.8-12.9 12.9-12.9s12.9 5.8 12.9 12.9-5.8 12.9-12.9 12.9H97V45.2zm-6.5 0c0 7.1-5.8 12.9-12.9 12.9s-12.9-5.8-12.9-12.9V12.9C64.7 5.8 70.5 0 77.6 0s12.9 5.8 12.9 12.9v32.3z" fill="#ECB22E"/><path d="M77.6 97c7.1 0 12.9 5.8 12.9 12.9s-5.8 12.9-12.9 12.9-12.9-5.8-12.9-12.9V97h12.9zm0-6.5c-7.1 0-12.9-5.8-12.9-12.9s5.8-12.9 12.9-12.9h32.3c7.1 0 12.9 5.8 12.9 12.9s-5.8 12.9-12.9 12.9H77.6z" fill="#E01E5A"/></svg>`;
var salesforce = `<svg viewBox="0 0 24 24" width="46" height="46" fill="#00A1E0" aria-hidden="true"><path d="M9.9 6.2c.72-.78 1.79-1.3 2.96-1.3 1.55 0 2.92.86 3.64 2.16.62-.28 1.31-.43 2.03-.43 2.3 0 4.17 1.88 4.17 4.2s-1.87 4.2-4.17 4.2c-.28 0-.56-.03-.83-.08-.62 1.11-1.81 1.86-3.17 1.86-.5 0-.98-.1-1.42-.29-.63 1.4-2.03 2.38-3.66 2.38-1.66 0-3.1-1.01-3.71-2.46-.23.05-.47.07-.71.07C1.55 16.61 0 15.04 0 13.11c0-1.29.69-2.42 1.72-3.04-.13-.39-.2-.81-.2-1.24 0-2.2 1.79-3.99 4-3.99 1.29 0 2.45.62 3.18 1.57.04-.07.08-.14.2-.21z"/></svg>`;
var hubspot = `<svg viewBox="0 0 24 24" width="40" height="40" fill="#FF7A59" aria-hidden="true"><path d="M18 7.32V5.13a1.74 1.74 0 1 0-1.16 0v2.19a5.05 5.05 0 0 0-2.4.98L7.97 3.43a1.95 1.95 0 1 0-.92 1.21l6.32 4.91a5.06 5.06 0 0 0 .08 5.71l-1.92 1.92a1.63 1.63 0 0 0-.47-.07 1.65 1.65 0 1 0 1.65 1.65 1.63 1.63 0 0 0-.07-.47l1.9-1.9a5.06 5.06 0 1 0 3.46-9.07zm-1.55 7.6a2.64 2.64 0 1 1 2.64-2.64 2.64 2.64 0 0 1-2.64 2.64z"/></svg>`;
var vercel = `<svg viewBox="0 0 24 24" width="34" height="34" fill="#000" aria-hidden="true"><path d="M12 2 24 22H0z"/></svg>`;
var clerk = `<svg viewBox="0 0 40 40" width="40" height="40" aria-hidden="true"><rect width="40" height="40" rx="9" fill="#6C47FF"/><circle cx="20" cy="16.5" r="4.2" fill="#fff"/><path d="M13 28a7.6 7.6 0 0 1 14 0z" fill="#fff"/></svg>`;
var workos = `<svg viewBox="0 0 40 40" width="40" height="40" aria-hidden="true"><rect width="40" height="40" rx="9" fill="#6363F1"/><path d="M10 14l3 12 3-9 3 9 3-12" fill="none" stroke="#fff" stroke-width="2.2" stroke-linejoin="round" stroke-linecap="round"/><path d="M25 20a4.5 4.5 0 1 0 4.5 4.5" fill="none" stroke="#fff" stroke-width="2.2" stroke-linecap="round"/></svg>`;
var LOGOS = {
  google,
  microsoft,
  apple,
  github,
  okta,
  slack,
  salesforce,
  hubspot,
  vercel,
  clerk,
  workos
};
function getLogo(service) {
  if (!service) return null;
  return LOGOS[service.toLowerCase()] ?? null;
}
function escapeHtml(s) {
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
}
function escapeAttr(s) {
  return escapeHtml(s).replace(/'/g, "&#39;");
}
var CSS = `
@font-face{
  font-family:'Geist';font-style:normal;font-weight:100 900;font-display:swap;
  src:url('/_emulate/fonts/geist-sans.woff2') format('woff2');
}
:root{
  color-scheme:dark;
  /* ElevenLabs UI \u2014 neutral dark (OKLCH). Values already avoid pure #000/#fff. */
  --bg:oklch(0.145 0 0);
  --fg:oklch(0.985 0 0);
  --card:oklch(0.205 0 0);
  --elevated:oklch(0.269 0 0);
  --muted:oklch(0.269 0 0);
  --muted-fg:oklch(0.708 0 0);
  --faint-fg:oklch(0.556 0 0);
  --border:oklch(1 0 0 / 10%);
  --border-strong:oklch(1 0 0 / 16%);
  --ring:oklch(0.556 0 0);
  --primary:oklch(0.922 0 0);
  --primary-fg:oklch(0.205 0 0);
  --danger:oklch(0.704 0.191 22.216);
  --radius:0.625rem;
  --radius-sm:0.375rem;
  --radius-lg:0.875rem;
  --radius-pill:999px;
  --space-xs:0.25rem;--space-sm:0.5rem;--space-md:0.75rem;
  --space-lg:1rem;--space-xl:1.5rem;--space-2xl:2rem;--space-3xl:3rem;
  --text-xs:0.75rem;--text-sm:0.8125rem;--text-md:0.875rem;
  --text-base:1rem;--text-lg:1.25rem;--text-xl:1.5rem;
  --font-sans:'Geist',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  --font-mono:ui-monospace,'SF Mono',SFMono-Regular,Menlo,Consolas,monospace;
}
*{box-sizing:border-box;margin:0;padding:0}
body{
  font-family:var(--font-sans);font-size:var(--text-md);line-height:1.55;
  background:var(--bg);color:var(--fg);min-height:100vh;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
}
::selection{background:var(--primary);color:var(--primary-fg);}

.emu-bar{
  border-bottom:1px solid var(--border);padding:var(--space-md) var(--space-xl);
  display:flex;align-items:center;gap:var(--space-md);
  font-size:var(--text-sm);color:var(--muted-fg);
}
.emu-bar-title{font-weight:600;color:var(--fg);letter-spacing:-0.01em;}
.emu-badge{
  display:inline-flex;align-items:center;gap:var(--space-xs);
  font-size:var(--text-xs);font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  padding:3px 10px;border-radius:var(--radius-pill);
  background:var(--primary);color:var(--primary-fg);
  white-space:nowrap;flex-shrink:0;
}
.emu-badge::before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor;}
.emu-watermark{
  position:fixed;bottom:var(--space-lg);right:var(--space-lg);z-index:9999;pointer-events:none;
  font-size:var(--text-xs);font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  padding:5px 11px;border-radius:var(--radius-sm);
  background:color-mix(in oklch,var(--bg) 72%,transparent);
  color:var(--muted-fg);border:1px solid var(--border-strong);
  backdrop-filter:blur(6px);
}
.emu-bar-links{margin-left:auto;display:flex;gap:var(--space-xl);}
.emu-bar-links a{
  color:var(--muted-fg);font-size:var(--text-sm);text-decoration:none;transition:color .15s;
}
.emu-bar-links a:hover{color:var(--fg);}
.emu-bar-links a .full{display:inline;}
.emu-bar-links a .short{display:none;}
@media(max-width:600px){
  .emu-bar-links a .full{display:none;}
  .emu-bar-links a .short{display:inline;}
}

.content{
  display:flex;align-items:center;justify-content:center;
  min-height:calc(100vh - 50px);padding:var(--space-2xl) var(--space-lg);
}
.content-inner{width:100%;max-width:420px;}
.brand-logo{display:flex;justify-content:center;margin-bottom:var(--space-xl);}
.brand-logo svg{display:block;}
.card-branded{text-align:center;}
.card-branded .card-title{text-align:center;}
.card-branded .card-subtitle{text-align:center;}
.card-branded .user-form{text-align:left;}
.card-title{
  font-size:var(--text-lg);font-weight:600;letter-spacing:-0.02em;
  margin-bottom:var(--space-xs);color:var(--fg);
}
.card-subtitle{color:var(--muted-fg);font-size:var(--text-sm);margin-bottom:var(--space-xl);line-height:1.5;}
.powered-by{
  position:fixed;bottom:0;left:0;right:0;
  text-align:center;padding:var(--space-md);font-size:var(--text-xs);color:var(--faint-fg);
}
.powered-by a{color:var(--muted-fg);text-decoration:none;transition:color .15s;}
.powered-by a:hover{color:var(--fg);}

.error-title{color:var(--danger);font-size:var(--text-lg);font-weight:600;margin-bottom:var(--space-sm);letter-spacing:-0.01em;}
.error-msg{color:var(--muted-fg);font-size:var(--text-md);line-height:1.5;}
.error-card{text-align:center;}

.user-form{margin-bottom:var(--space-sm);}
.user-form:last-of-type{margin-bottom:0;}
.user-btn{
  width:100%;display:flex;align-items:center;gap:var(--space-md);
  padding:var(--space-md);border:1px solid var(--border);border-radius:var(--radius);
  background:var(--card);color:inherit;cursor:pointer;text-align:left;
  font:inherit;transition:border-color .15s,background .15s;
}
.user-btn:hover{border-color:var(--border-strong);background:var(--elevated);}
.avatar{
  width:36px;height:36px;border-radius:50%;
  background:var(--elevated);color:var(--fg);font-weight:600;font-size:var(--text-md);
  display:flex;align-items:center;justify-content:center;flex-shrink:0;
}
.user-text{min-width:0;}
.user-login{font-weight:600;font-size:var(--text-md);display:block;color:var(--fg);}
.user-meta{color:var(--muted-fg);font-size:var(--text-xs);margin-top:1px;}
.user-email{font-size:var(--text-xs);color:var(--faint-fg);word-break:break-all;margin-top:1px;}

.settings-layout{
  max-width:920px;margin:0 auto;padding:var(--space-3xl) var(--space-xl);
  display:flex;gap:var(--space-3xl);
}
.settings-sidebar{width:200px;flex-shrink:0;}
.settings-sidebar a{
  display:block;padding:6px 10px;border-radius:var(--radius-sm);color:var(--muted-fg);
  text-decoration:none;font-size:var(--text-sm);transition:color .15s,background .15s;
}
.settings-sidebar a:hover{color:var(--fg);background:var(--muted);}
.settings-sidebar a.active{color:var(--fg);font-weight:600;background:var(--muted);}
.settings-main{flex:1;min-width:0;}

.s-card{
  padding:var(--space-xl) 0;margin-bottom:var(--space-lg);border-bottom:1px solid var(--border);
}
.s-card:last-child{border-bottom:none;}
.s-card-header{display:flex;align-items:center;gap:var(--space-md);margin-bottom:var(--space-lg);}
.s-icon{
  width:40px;height:40px;border-radius:var(--radius);
  background:var(--elevated);display:flex;align-items:center;justify-content:center;
  font-size:var(--text-base);font-weight:600;color:var(--muted-fg);flex-shrink:0;
}
.s-title{font-size:var(--text-lg);font-weight:600;color:var(--fg);letter-spacing:-0.01em;}
.s-subtitle{font-size:var(--text-xs);color:var(--muted-fg);margin-top:2px;}
.section-heading{
  font-size:var(--text-md);font-weight:600;margin-bottom:var(--space-md);color:var(--fg);
  display:flex;align-items:center;justify-content:space-between;
}
.perm-list{list-style:none;}
.perm-list li{padding:5px 0;font-size:var(--text-sm);display:flex;align-items:center;gap:var(--space-sm);color:var(--muted-fg);}
.check{color:var(--fg);}
.org-row{
  display:flex;align-items:center;gap:var(--space-sm);padding:7px 0;
  border-bottom:1px solid var(--border);font-size:var(--text-sm);
}
.org-row:last-child{border-bottom:none;}
.org-icon{
  width:22px;height:22px;border-radius:var(--radius-sm);background:var(--elevated);
  display:flex;align-items:center;justify-content:center;
  font-size:var(--text-xs);font-weight:600;color:var(--muted-fg);flex-shrink:0;
}
.org-name{font-weight:600;color:var(--fg);}
.badge{
  font-size:var(--text-xs);padding:2px 8px;border-radius:var(--radius-sm);font-weight:500;
  background:var(--muted);color:var(--muted-fg);border:1px solid var(--border);
}
.badge-granted{background:var(--elevated);color:var(--fg);}
.badge-denied{background:color-mix(in oklch,var(--danger) 18%,var(--bg));color:var(--danger);border-color:color-mix(in oklch,var(--danger) 30%,transparent);}
.badge-requested{background:var(--muted);color:var(--muted-fg);}
.btn-revoke{
  display:inline-block;padding:5px 14px;border-radius:var(--radius-sm);
  border:1px solid var(--border);background:transparent;color:var(--danger);
  font-size:var(--text-xs);font-weight:600;cursor:pointer;transition:border-color .15s;
}
.btn-revoke:hover{border-color:var(--danger);}
.info-text{color:var(--muted-fg);font-size:var(--text-xs);line-height:1.5;margin-top:var(--space-md);}
.app-link{
  display:flex;align-items:center;gap:var(--space-md);padding:var(--space-md);
  border:1px solid var(--border);border-radius:var(--radius);background:var(--card);
  text-decoration:none;color:inherit;margin-bottom:var(--space-sm);transition:border-color .15s,background .15s;
}
.app-link:hover{border-color:var(--border-strong);background:var(--elevated);}
.app-link-name{font-weight:600;font-size:var(--text-md);color:var(--fg);}
.app-link-scopes{font-size:var(--text-xs);color:var(--muted-fg);margin-top:1px;}
.empty{color:var(--muted-fg);text-align:center;padding:var(--space-3xl) 0;font-size:var(--text-md);}

.inspector-layout{max-width:960px;margin:0 auto;padding:var(--space-3xl) var(--space-xl);}
.inspector-tabs{
  display:flex;flex-wrap:wrap;gap:var(--space-xs);margin-bottom:var(--space-2xl);
  padding:var(--space-xs);background:var(--card);border:1px solid var(--border);
  border-radius:var(--radius);width:fit-content;max-width:100%;
}
.inspector-tabs a{
  padding:6px 14px;border-radius:var(--radius-sm);text-decoration:none;
  font-size:var(--text-sm);color:var(--muted-fg);transition:color .15s,background .15s;
}
.inspector-tabs a:hover{color:var(--fg);}
.inspector-tabs a.active{color:var(--fg);font-weight:600;background:var(--elevated);}
.inspector-section{margin-bottom:var(--space-2xl);}
.inspector-section h2{
  font-size:var(--text-lg);font-weight:600;color:var(--fg);
  margin-bottom:var(--space-md);letter-spacing:-0.01em;
}
.inspector-section h3{
  font-size:var(--text-md);font-weight:600;color:var(--muted-fg);
  margin:var(--space-xl) 0 var(--space-sm);
}
.inspector-table{width:100%;border-collapse:collapse;margin-bottom:var(--space-md);}
.inspector-table th,.inspector-table td{
  text-align:left;padding:var(--space-sm) var(--space-md);border-bottom:1px solid var(--border);
  font-size:var(--text-sm);
}
.inspector-table th{color:var(--faint-fg);font-weight:600;font-size:var(--text-xs);text-transform:uppercase;letter-spacing:.06em;}
.inspector-table td{color:var(--fg);}
.inspector-table tbody tr{transition:background .12s;}
.inspector-table tbody tr:hover{background:var(--card);}
.inspector-empty{color:var(--muted-fg);text-align:center;padding:var(--space-xl) 0;font-size:var(--text-sm);}

/* Live activity feed (renderActivityCard) \u2014 token-driven so it stays cohesive
   with the dark control plane instead of the old hard-coded light values. */
.act-pulse{
  width:9px;height:9px;border-radius:50%;background:var(--fg);flex-shrink:0;
  position:relative;
}
.act-pulse::after{
  content:"";position:absolute;inset:-4px;border-radius:50%;
  border:1px solid var(--fg);opacity:.5;animation:act-ping 1.8s ease-out infinite;
}
@keyframes act-ping{0%{transform:scale(.6);opacity:.5}80%,100%{transform:scale(1.6);opacity:0}}
@media(prefers-reduced-motion:reduce){.act-pulse::after{animation:none;}}
.act-status.is-live{color:var(--fg);}
.act-status.is-error{color:var(--danger);}
.act-mono{font-family:var(--font-mono);font-size:var(--text-xs);color:var(--muted-fg);white-space:nowrap;}
.act-fields{font-size:var(--text-xs);color:var(--muted-fg);}
.act-json-btn{
  font:inherit;font-size:var(--text-xs);padding:2px 9px;border:1px solid var(--border);
  border-radius:var(--radius-sm);background:transparent;color:var(--muted-fg);
  cursor:pointer;transition:border-color .15s,color .15s;
}
.act-json-btn:hover{border-color:var(--border-strong);color:var(--fg);}
.act-detail-cell{background:var(--card);padding:var(--space-sm) var(--space-md);}
.act-detail-cell pre{
  margin:0;font-family:var(--font-mono);font-size:var(--text-xs);line-height:1.5;
  color:var(--muted-fg);white-space:pre-wrap;word-break:break-word;max-height:300px;overflow:auto;
}
.inspector-table tbody tr.act-row-new td{background:color-mix(in oklch,var(--elevated) 80%,transparent);transition:background .9s;}

.checkout-layout{display:flex;min-height:calc(100vh - 50px);}
.checkout-summary{
  flex:1;background:var(--card);padding:var(--space-3xl) var(--space-2xl) var(--space-3xl) 10%;
  display:flex;flex-direction:column;justify-content:center;border-right:1px solid var(--border);
}
.checkout-form-side{
  flex:1;background:var(--bg);padding:var(--space-3xl) 10% var(--space-3xl) var(--space-2xl);
  display:flex;flex-direction:column;justify-content:center;
}
.checkout-merchant{display:flex;align-items:center;gap:var(--space-md);margin-bottom:6px;}
.checkout-merchant-name{font-size:var(--text-md);font-weight:600;color:var(--fg);}
.checkout-test-badge{
  font-size:var(--text-xs);font-weight:600;letter-spacing:.04em;text-transform:uppercase;
  background:var(--muted);color:var(--muted-fg);padding:2px 8px;border-radius:var(--radius-sm);
}
.checkout-total{font-size:var(--text-xl);font-weight:700;color:var(--fg);margin:var(--space-sm) 0 var(--space-2xl);letter-spacing:-0.02em;}
.checkout-line-item{display:flex;align-items:center;gap:var(--space-md);padding:var(--space-lg) 0;border-bottom:1px solid var(--border);}
.checkout-line-item:first-child{border-top:1px solid var(--border);}
.checkout-item-icon{
  width:40px;height:40px;border-radius:var(--radius-sm);background:var(--elevated);
  display:flex;align-items:center;justify-content:center;flex-shrink:0;
  font-size:var(--text-md);font-weight:600;color:var(--muted-fg);
}
.checkout-item-details{flex:1;min-width:0;}
.checkout-item-name{font-size:var(--text-md);font-weight:600;color:var(--fg);}
.checkout-item-qty{font-size:var(--text-xs);color:var(--muted-fg);margin-top:2px;}
.checkout-item-price{font-size:var(--text-md);font-weight:600;color:var(--fg);text-align:right;white-space:nowrap;}
.checkout-item-unit{font-size:var(--text-xs);color:var(--muted-fg);text-align:right;margin-top:2px;}
.checkout-totals{margin-top:var(--space-xl);}
.checkout-totals-row{display:flex;justify-content:space-between;padding:6px 0;font-size:var(--text-sm);color:var(--muted-fg);}
.checkout-totals-row.total{border-top:1px solid var(--border);margin-top:var(--space-sm);padding-top:var(--space-lg);font-size:var(--text-md);font-weight:600;color:var(--fg);}
.checkout-form-section{margin-bottom:var(--space-xl);}
.checkout-form-label{font-size:var(--text-sm);font-weight:600;color:var(--fg);margin-bottom:var(--space-sm);display:block;}
.checkout-input{
  width:100%;padding:var(--space-md);border:1px solid var(--border);border-radius:var(--radius-sm);
  background:var(--card);color:var(--fg);font:inherit;font-size:var(--text-md);
  transition:border-color .15s;outline:none;
}
.checkout-input:focus{border-color:var(--ring);}
.checkout-input::placeholder{color:var(--faint-fg);}
.checkout-card-box{border:1px solid var(--border);border-radius:var(--radius-sm);padding:var(--space-lg);background:var(--card);}
.checkout-card-row{display:flex;gap:var(--space-md);margin-top:var(--space-md);}
.checkout-card-row .checkout-input{flex:1;}
.checkout-sim-note{font-size:var(--text-xs);color:var(--muted-fg);margin-top:var(--space-md);text-align:center;font-style:italic;}
.checkout-pay-btn{
  width:100%;padding:var(--space-lg);border:none;border-radius:var(--radius);
  background:var(--primary);color:var(--primary-fg);font:inherit;font-size:var(--text-md);font-weight:600;
  cursor:pointer;transition:opacity .15s;
}
.checkout-pay-btn:hover{opacity:.9;}
.checkout-cancel{text-align:center;margin-top:var(--space-lg);}
.checkout-cancel a{color:var(--muted-fg);text-decoration:none;font-size:var(--text-sm);transition:color .15s;}
.checkout-cancel a:hover{color:var(--fg);}
@media(max-width:768px){
  .checkout-layout{flex-direction:column;}
  .checkout-summary{padding:var(--space-2xl) var(--space-xl);border-right:none;border-bottom:1px solid var(--border);}
  .checkout-form-side{padding:var(--space-2xl) var(--space-xl);}
}
`;
var POWERED_BY = `<div class="powered-by">Powered by <a href="https://github.com/Vinniai/agent-emulate#readme" target="_blank" rel="noopener">agent-emulate</a></div>`;
var WATERMARK = `<div class="emu-watermark" aria-hidden="true">agent-emulate</div>`;
function emuBar(service) {
  const title = service ? `${escapeHtml(service)} Emulator` : "Emulator";
  return `<div class="emu-bar">
  <span class="emu-bar-title">${title}</span>
  <span class="emu-badge" title="This is a local emulator, not the real provider">Emulated</span>
  <nav class="emu-bar-links">
    <a href="https://github.com/Vinniai/agent-emulate/issues" target="_blank" rel="noopener"><span class="full">Report Issue</span><span class="short">Report</span></a>
    <a href="https://github.com/Vinniai/agent-emulate" target="_blank" rel="noopener"><span class="full">Source Code</span><span class="short">Source</span></a>
    <a href="https://github.com/Vinniai/agent-emulate#readme" target="_blank" rel="noopener"><span class="full">Learn More</span><span class="short">Learn</span></a>
  </nav>
</div>`;
}
function head(title, service) {
  const themeCss = themeOverrideCss(getTheme(service));
  const themeStyle = themeCss ? `<style>${themeCss}</style>` : "";
  return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<link rel="icon" href="/_emulate/favicon.ico"/>
<title>${escapeHtml(title)} | agent-emulate</title>
<style>${CSS}</style>${themeStyle}
</head>`;
}
function renderCardPage(title, subtitle, body, service) {
  const logo = getLogo(service);
  const logoHtml = logo ? `<div class="brand-logo">${logo}</div>` : "";
  return `${head(title, service)}
<body>
${emuBar(service)}
<div class="content">
  <div class="content-inner${logo ? " card-branded" : ""}">
    ${logoHtml}
    <div class="card-title">${escapeHtml(title)}</div>
    <div class="card-subtitle">${subtitle}</div>
    ${body}
  </div>
</div>
${POWERED_BY}
${WATERMARK}
</body></html>`;
}
function renderSettingsPage(title, sidebarHtml, bodyHtml, service) {
  return `${head(title, service)}
<body>
${emuBar(service)}
<div class="settings-layout">
  <nav class="settings-sidebar">${sidebarHtml}</nav>
  <div class="settings-main">${bodyHtml}</div>
</div>
${POWERED_BY}
${WATERMARK}
</body></html>`;
}
function renderCheckoutPage(opts, service) {
  const fmt = (cents2, cur) => `$${(cents2 / 100).toFixed(2)} ${cur.toUpperCase()}`;
  const fmtShort = (cents2) => `$${(cents2 / 100).toFixed(2)}`;
  const itemsHtml = opts.lineItems.length > 0 ? opts.lineItems.map((li) => {
    const initial = li.name.charAt(0).toUpperCase();
    const unitNote = li.quantity > 1 ? `<div class="checkout-item-unit">${fmtShort(li.unitPrice)} each</div>` : "";
    return `<div class="checkout-line-item">
  <div class="checkout-item-icon">${escapeHtml(initial)}</div>
  <div class="checkout-item-details">
    <div class="checkout-item-name">${escapeHtml(li.name)}</div>
    <div class="checkout-item-qty">Qty ${li.quantity}</div>
  </div>
  <div>
    <div class="checkout-item-price">${fmtShort(li.totalPrice)}</div>
    ${unitNote}
  </div>
</div>`;
  }).join("") : '<p class="empty">No line items</p>';
  const totalsHtml = `<div class="checkout-totals">
  <div class="checkout-totals-row">
    <span>Subtotal</span><span>${fmtShort(opts.subtotal)}</span>
  </div>
  <div class="checkout-totals-row total">
    <span>Total due</span><span>${fmt(opts.total, opts.currency)}</span>
  </div>
</div>`;
  const cancelHtml = opts.cancelUrl ? `<div class="checkout-cancel"><a href="${escapeAttr(opts.cancelUrl)}">Cancel</a></div>` : "";
  const merchant = opts.merchantName ? escapeHtml(opts.merchantName) : "Checkout";
  return `${head("Checkout", service)}
<body>
${emuBar(service)}
<div class="checkout-layout">
  <div class="checkout-summary">
    <div class="checkout-merchant">
      <span class="checkout-merchant-name">${merchant}</span>
      <span class="checkout-test-badge">Test Mode</span>
    </div>
    <div class="checkout-total">${fmtShort(opts.total)}</div>
    ${itemsHtml}
    ${totalsHtml}
  </div>
  <div class="checkout-form-side">
    <form method="post" action="/checkout/${escapeAttr(opts.sessionId)}/complete">
      <div class="checkout-form-section">
        <label class="checkout-form-label">Email</label>
        <input type="email" name="email" class="checkout-input" placeholder="you@agent-emulate.dev"/>
      </div>
      <div class="checkout-form-section">
        <label class="checkout-form-label">Card information</label>
        <div class="checkout-card-box">
          <input type="text" class="checkout-input" placeholder="1234 1234 1234 1234" disabled/>
          <div class="checkout-card-row">
            <input type="text" class="checkout-input" placeholder="MM / YY" disabled/>
            <input type="text" class="checkout-input" placeholder="CVC" disabled/>
          </div>
        </div>
        <div class="checkout-sim-note">Card fields are simulated. Payment will be auto-approved.</div>
      </div>
      <button type="submit" class="checkout-pay-btn">Pay ${fmtShort(opts.total)}</button>
    </form>
    ${cancelHtml}
  </div>
</div>
${POWERED_BY}
${WATERMARK}
</body></html>`;
}
var RING_SIZE = 200;
var ActivityBus = class {
  buffer = [];
  subscribers = /* @__PURE__ */ new Set();
  publish(event) {
    this.buffer.push(event);
    if (this.buffer.length > RING_SIZE) this.buffer.splice(0, this.buffer.length - RING_SIZE);
    for (const cb of this.subscribers) {
      try {
        cb(event);
      } catch {
      }
    }
  }
  subscribe(cb) {
    this.subscribers.add(cb);
    return () => this.subscribers.delete(cb);
  }
  recent(limit = 50, service) {
    const filtered = service ? this.buffer.filter((e) => e.service === service) : this.buffer;
    return filtered.slice(-limit).reverse();
  }
};
var activityBus = globalThis.__emulate_activity_bus__ ?? (globalThis.__emulate_activity_bus__ = new ActivityBus());
var NUMERIC_KEYS = /* @__PURE__ */ new Set([
  "amount",
  "unit_amount",
  "quantity",
  "amount_total",
  "amount_subtotal",
  "application_fee_amount",
  "transfer_amount"
]);
function stripeId(prefix) {
  return `${prefix}_${randomBytes(12).toString("base64url").slice(0, 24)}`;
}
function toUnixTimestamp(iso) {
  return Math.floor(new Date(iso).getTime() / 1e3);
}
async function parseStripeBody(c) {
  const contentType = c.req.header("Content-Type") ?? "";
  const rawText = await c.req.text();
  if (!rawText) return {};
  if (contentType.includes("application/json")) {
    try {
      const parsed = JSON.parse(rawText);
      return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
    } catch {
      return {};
    }
  }
  const params = new URLSearchParams(rawText);
  const result = {};
  for (const [key, value] of params) {
    if (key.includes("[")) {
      const parts = key.replace(/]/g, "").split("[");
      let target = result;
      for (let i = 0; i < parts.length - 1; i++) {
        const part = parts[i];
        const nextIsIndex = /^\d+$/.test(parts[i + 1]);
        const current = target[part];
        if (current === void 0 || current === null || typeof current !== "object") {
          target[part] = nextIsIndex ? [] : {};
        }
        target = target[part];
      }
      const lastKey = parts[parts.length - 1];
      const num = Number(value);
      const coerced = NUMERIC_KEYS.has(lastKey) && Number.isFinite(num) ? num : value;
      if (Array.isArray(target)) {
        const idx = lastKey === "" ? target.length : parseInt(lastKey, 10);
        target[idx] = coerced;
      } else {
        target[lastKey] = coerced;
      }
    } else {
      const num = Number(value);
      result[key] = NUMERIC_KEYS.has(key) && Number.isFinite(num) ? num : value;
    }
  }
  return result;
}
function stripeError(c, status, type, message, code, param) {
  return c.json(
    {
      error: {
        type,
        message,
        ...code && { code },
        ...param && { param }
      }
    },
    status
  );
}
function stripeList(c, items, url, formatFn) {
  const limit = Math.min(parseInt(c.req.query("limit") ?? "10", 10), 100);
  const startingAfter = c.req.query("starting_after");
  const endingBefore = c.req.query("ending_before");
  const createdGte = c.req.query("created[gte]");
  const createdLte = c.req.query("created[lte]");
  let filtered = items;
  if (createdGte) {
    const gte = parseInt(createdGte, 10);
    filtered = filtered.filter((item) => toUnixTimestamp(item.created_at) >= gte);
  }
  if (createdLte) {
    const lte = parseInt(createdLte, 10);
    filtered = filtered.filter((item) => toUnixTimestamp(item.created_at) <= lte);
  }
  filtered.sort((a, b) => b.id - a.id);
  if (startingAfter) {
    const idx = filtered.findIndex((item) => item.stripe_id === startingAfter);
    if (idx !== -1) {
      filtered = filtered.slice(idx + 1);
    }
  } else if (endingBefore) {
    const idx = filtered.findIndex((item) => item.stripe_id === endingBefore);
    if (idx !== -1) {
      filtered = filtered.slice(0, idx);
      filtered = filtered.slice(-limit);
    }
  }
  const page = filtered.slice(0, limit);
  const hasMore = filtered.length > limit;
  return c.json({
    object: "list",
    url,
    has_more: hasMore,
    data: page.map(formatFn)
  });
}
function applyExpand(obj, expandPaths, resolvers) {
  if (!expandPaths || expandPaths.length === 0) return obj;
  const result = { ...obj };
  for (const path of expandPaths) {
    const resolver = resolvers[path];
    const id = result[path];
    if (resolver && typeof id === "string") {
      const expanded = resolver(id);
      if (expanded) {
        result[path] = expanded;
      }
    }
  }
  return result;
}
function parseExpand(c) {
  const fromQuery = c.req.queries("expand[]") ?? [];
  return fromQuery;
}
function getStripeStore(store) {
  return {
    customers: store.collection("stripe.customers", ["stripe_id", "email"]),
    products: store.collection("stripe.products", ["stripe_id"]),
    prices: store.collection("stripe.prices", ["stripe_id", "product_id", "lookup_key"]),
    paymentIntents: store.collection("stripe.payment_intents", ["stripe_id", "customer_id"]),
    charges: store.collection("stripe.charges", ["stripe_id", "customer_id", "payment_intent_id"]),
    checkoutSessions: store.collection("stripe.checkout_sessions", ["stripe_id", "customer_id"]),
    subscriptions: store.collection("stripe.subscriptions", ["stripe_id", "customer_id"]),
    subscriptionItems: store.collection("stripe.subscription_items", [
      "stripe_id",
      "subscription_id"
    ])
  };
}
function formatCustomer(c) {
  return {
    id: c.stripe_id,
    object: "customer",
    email: c.email,
    name: c.name,
    description: c.description,
    metadata: c.metadata,
    created: toUnixTimestamp(c.created_at),
    livemode: false
  };
}
function formatPaymentIntent(pi) {
  return {
    id: pi.stripe_id,
    object: "payment_intent",
    amount: pi.amount,
    currency: pi.currency,
    status: pi.status,
    customer: pi.customer_id,
    description: pi.description,
    payment_method: pi.payment_method,
    metadata: pi.metadata,
    created: toUnixTimestamp(pi.created_at),
    livemode: false
  };
}
function formatCharge(ch) {
  return {
    id: ch.stripe_id,
    object: "charge",
    amount: ch.amount,
    currency: ch.currency,
    status: ch.status,
    customer: ch.customer_id,
    payment_intent: ch.payment_intent_id,
    description: ch.description,
    metadata: ch.metadata,
    created: toUnixTimestamp(ch.created_at),
    livemode: false
  };
}
function chargeRoutes({ app, store }) {
  const ss = getStripeStore(store);
  const expandResolvers = {
    customer: (id) => {
      const cust = ss.customers.findOneBy("stripe_id", id);
      return cust ? formatCustomer(cust) : void 0;
    },
    payment_intent: (id) => {
      const pi = ss.paymentIntents.findOneBy("stripe_id", id);
      return pi ? formatPaymentIntent(pi) : void 0;
    }
  };
  app.get("/v1/charges/:id", (c) => {
    const charge = ss.charges.findOneBy("stripe_id", c.req.param("id"));
    if (!charge)
      return stripeError(c, 404, "invalid_request_error", `No such charge: '${c.req.param("id")}'`, "resource_missing");
    const expand = parseExpand(c);
    const result = applyExpand(formatCharge(charge), expand, expandResolvers);
    return c.json(result);
  });
  app.get("/v1/charges", (c) => {
    let items = ss.charges.all();
    const customerId = c.req.query("customer");
    const piId = c.req.query("payment_intent");
    if (customerId) items = items.filter((ch) => ch.customer_id === customerId);
    if (piId) items = items.filter((ch) => ch.payment_intent_id === piId);
    return stripeList(c, items, "/v1/charges", formatCharge);
  });
}
var SERVICE_LABEL = "Stripe";
function formatSession(s, baseUrl) {
  return {
    id: s.stripe_id,
    object: "checkout.session",
    mode: s.mode,
    status: s.status,
    payment_status: s.payment_status,
    customer: s.customer_id,
    success_url: s.success_url,
    cancel_url: s.cancel_url,
    metadata: s.metadata,
    created: toUnixTimestamp(s.created_at),
    livemode: false,
    url: s.status === "open" ? `${baseUrl}/checkout/${s.stripe_id}` : null
  };
}
function checkoutSessionRoutes({ app, store, webhooks, baseUrl }) {
  const ss = getStripeStore(store);
  app.post("/v1/checkout/sessions", async (c) => {
    const body = await parseStripeBody(c);
    if (!body.mode)
      return stripeError(c, 400, "invalid_request_error", "Missing required param: mode.", void 0, "mode");
    if (body.customer && !ss.customers.findOneBy("stripe_id", body.customer)) {
      return stripeError(
        c,
        400,
        "invalid_request_error",
        `No such customer: '${body.customer}'`,
        "resource_missing",
        "customer"
      );
    }
    const lineItems = [];
    if (body.line_items) {
      if (!Array.isArray(body.line_items)) {
        return stripeError(c, 400, "invalid_request_error", "line_items must be an array.", void 0, "line_items");
      }
      for (let i = 0; i < body.line_items.length; i++) {
        const li = body.line_items[i];
        if (!li || typeof li !== "object") {
          return stripeError(
            c,
            400,
            "invalid_request_error",
            `Invalid line_items[${i}]: must be an object.`,
            void 0,
            `line_items[${i}]`
          );
        }
        if (!li.price || typeof li.price !== "string") {
          return stripeError(
            c,
            400,
            "invalid_request_error",
            `Missing required param: line_items[${i}][price].`,
            void 0,
            `line_items[${i}][price]`
          );
        }
        if (!ss.prices.findOneBy("stripe_id", li.price)) {
          return stripeError(
            c,
            400,
            "invalid_request_error",
            `No such price: '${li.price}'`,
            "resource_missing",
            `line_items[${i}][price]`
          );
        }
        const qty = typeof li.quantity === "number" ? li.quantity : parseInt(li.quantity, 10);
        if (!Number.isFinite(qty) || qty < 1) {
          return stripeError(
            c,
            400,
            "invalid_request_error",
            `Invalid line_items[${i}][quantity]: must be a positive integer.`,
            void 0,
            `line_items[${i}][quantity]`
          );
        }
        lineItems.push({ price: li.price, quantity: qty });
      }
    }
    const session = ss.checkoutSessions.insert({
      stripe_id: stripeId("cs"),
      mode: body.mode,
      status: "open",
      payment_status: "unpaid",
      customer_id: body.customer ?? null,
      success_url: body.success_url ?? null,
      cancel_url: body.cancel_url ?? null,
      line_items: lineItems,
      metadata: body.metadata ?? {}
    });
    return c.json(formatSession(session, baseUrl), 200);
  });
  app.get("/v1/checkout/sessions/:id", (c) => {
    const session = ss.checkoutSessions.findOneBy("stripe_id", c.req.param("id"));
    if (!session)
      return stripeError(
        c,
        404,
        "invalid_request_error",
        `No such checkout session: '${c.req.param("id")}'`,
        "resource_missing"
      );
    return c.json(formatSession(session, baseUrl));
  });
  app.post("/v1/checkout/sessions/:id/expire", async (c) => {
    const session = ss.checkoutSessions.findOneBy("stripe_id", c.req.param("id"));
    if (!session)
      return stripeError(
        c,
        404,
        "invalid_request_error",
        `No such checkout session: '${c.req.param("id")}'`,
        "resource_missing"
      );
    if (session.status !== "open") {
      return stripeError(
        c,
        400,
        "invalid_request_error",
        "Only open sessions can be expired.",
        "checkout_session_not_open"
      );
    }
    const updated = ss.checkoutSessions.update(session.id, { status: "expired" });
    await webhooks.dispatch(
      "checkout.session.expired",
      void 0,
      { type: "checkout.session.expired", data: { object: formatSession(updated, baseUrl) } },
      "stripe"
    );
    return c.json(formatSession(updated, baseUrl));
  });
  app.get("/v1/checkout/sessions", (c) => {
    let items = ss.checkoutSessions.all();
    const customerId = c.req.query("customer");
    const status = c.req.query("status");
    const paymentStatus = c.req.query("payment_status");
    if (customerId) items = items.filter((s) => s.customer_id === customerId);
    if (status) items = items.filter((s) => s.status === status);
    if (paymentStatus) items = items.filter((s) => s.payment_status === paymentStatus);
    return stripeList(c, items, "/v1/checkout/sessions", (s) => formatSession(s, baseUrl));
  });
  app.get("/checkout/:id", (c) => {
    const session = ss.checkoutSessions.findOneBy("stripe_id", c.req.param("id"));
    if (!session) {
      return c.html(
        renderCardPage(
          "Session Not Found",
          "This checkout session does not exist.",
          '<p class="empty">The session ID is invalid or has been removed.</p>',
          SERVICE_LABEL
        ),
        404
      );
    }
    if (session.status !== "open") {
      return c.html(
        renderCardPage(
          "Session Expired",
          "This checkout session is no longer available.",
          `<p class="empty">Status: ${escapeHtml(session.status)}</p>`,
          SERVICE_LABEL
        )
      );
    }
    const lineItems = session.line_items.map((li) => {
      const priceObj = ss.prices.findOneBy("stripe_id", li.price);
      const product = priceObj ? ss.products.findOneBy("stripe_id", priceObj.product_id) : null;
      const unitPrice = priceObj?.unit_amount ?? 0;
      return {
        name: product?.name ?? li.price,
        quantity: li.quantity,
        unitPrice,
        totalPrice: unitPrice * li.quantity,
        currency: priceObj?.currency ?? "usd"
      };
    });
    const subtotal = lineItems.reduce((sum, li) => sum + li.totalPrice, 0);
    const currency = lineItems.length > 0 ? lineItems[0].currency : "usd";
    return c.html(
      renderCheckoutPage(
        {
          lineItems,
          subtotal,
          total: subtotal,
          currency,
          sessionId: session.stripe_id,
          cancelUrl: session.cancel_url
        },
        SERVICE_LABEL
      )
    );
  });
  app.post("/checkout/:id/complete", async (c) => {
    const session = ss.checkoutSessions.findOneBy("stripe_id", c.req.param("id"));
    if (!session || session.status !== "open") {
      return c.redirect("/checkout/" + c.req.param("id"));
    }
    const updated = ss.checkoutSessions.update(session.id, { status: "complete", payment_status: "paid" });
    await webhooks.dispatch(
      "checkout.session.completed",
      void 0,
      { type: "checkout.session.completed", data: { object: formatSession(updated, baseUrl) } },
      "stripe"
    );
    if (session.success_url) {
      const url = session.success_url.replace("{CHECKOUT_SESSION_ID}", updated.stripe_id);
      return c.redirect(url);
    }
    return c.html(
      renderCardPage(
        "Payment Complete",
        "Your payment was successful.",
        '<p class="empty check">Payment received</p>',
        SERVICE_LABEL
      )
    );
  });
}
function customerSessionRoutes({ app, store }) {
  const ss = getStripeStore(store);
  app.post("/v1/customer_sessions", async (c) => {
    const body = await parseStripeBody(c);
    if (!body.customer)
      return stripeError(c, 400, "invalid_request_error", "Missing required param: customer.", void 0, "customer");
    const customer = ss.customers.findOneBy("stripe_id", body.customer);
    if (!customer)
      return stripeError(
        c,
        400,
        "invalid_request_error",
        `No such customer: '${body.customer}'`,
        "resource_missing",
        "customer"
      );
    return c.json(
      {
        object: "customer_session",
        client_secret: stripeId("cuss_secret"),
        components: body.components ?? {},
        created: Math.floor(Date.now() / 1e3),
        customer: customer.stripe_id,
        expires_at: Math.floor(Date.now() / 1e3) + 1800,
        livemode: false
      },
      200
    );
  });
}
function customerRoutes({ app, store, webhooks }) {
  const ss = getStripeStore(store);
  app.post("/v1/customers", async (c) => {
    const body = await parseStripeBody(c);
    const customer = ss.customers.insert({
      stripe_id: stripeId("cus"),
      email: body.email ?? null,
      name: body.name ?? null,
      description: body.description ?? null,
      metadata: body.metadata ?? {}
    });
    await webhooks.dispatch(
      "customer.created",
      void 0,
      { type: "customer.created", data: { object: formatCustomer(customer) } },
      "stripe"
    );
    return c.json(formatCustomer(customer), 200);
  });
  app.get("/v1/customers/:id", (c) => {
    const customer = ss.customers.findOneBy("stripe_id", c.req.param("id"));
    if (!customer)
      return stripeError(
        c,
        404,
        "invalid_request_error",
        `No such customer: '${c.req.param("id")}'`,
        "resource_missing"
      );
    return c.json(formatCustomer(customer));
  });
  app.post("/v1/customers/:id", async (c) => {
    const customer = ss.customers.findOneBy("stripe_id", c.req.param("id"));
    if (!customer)
      return stripeError(
        c,
        404,
        "invalid_request_error",
        `No such customer: '${c.req.param("id")}'`,
        "resource_missing"
      );
    const body = await parseStripeBody(c);
    const updated = ss.customers.update(customer.id, {
      ...body.email !== void 0 && { email: body.email },
      ...body.name !== void 0 && { name: body.name },
      ...body.description !== void 0 && { description: body.description },
      ...body.metadata !== void 0 && { metadata: body.metadata }
    });
    await webhooks.dispatch(
      "customer.updated",
      void 0,
      { type: "customer.updated", data: { object: formatCustomer(updated) } },
      "stripe"
    );
    return c.json(formatCustomer(updated));
  });
  app.delete("/v1/customers/:id", async (c) => {
    const customer = ss.customers.findOneBy("stripe_id", c.req.param("id"));
    if (!customer)
      return stripeError(
        c,
        404,
        "invalid_request_error",
        `No such customer: '${c.req.param("id")}'`,
        "resource_missing"
      );
    for (const pi of ss.paymentIntents.findBy("customer_id", customer.stripe_id)) {
      ss.paymentIntents.update(pi.id, { customer_id: null });
    }
    for (const ch of ss.charges.findBy("customer_id", customer.stripe_id)) {
      ss.charges.update(ch.id, { customer_id: null });
    }
    for (const cs of ss.checkoutSessions.findBy("customer_id", customer.stripe_id)) {
      ss.checkoutSessions.update(cs.id, { customer_id: null });
    }
    ss.customers.delete(customer.id);
    await webhooks.dispatch(
      "customer.deleted",
      void 0,
      { type: "customer.deleted", data: { object: { ...formatCustomer(customer), deleted: true } } },
      "stripe"
    );
    return c.json({ id: customer.stripe_id, object: "customer", deleted: true });
  });
  app.get("/v1/customers", (c) => {
    let items = ss.customers.all();
    const email = c.req.query("email");
    if (email) items = items.filter((cust) => cust.email === email);
    return stripeList(c, items, "/v1/customers", formatCustomer);
  });
}
function paymentIntentRoutes({ app, store, webhooks }) {
  const ss = getStripeStore(store);
  const expandResolvers = {
    customer: (id) => {
      const cust = ss.customers.findOneBy("stripe_id", id);
      return cust ? formatCustomer(cust) : void 0;
    }
  };
  app.post("/v1/payment_intents", async (c) => {
    const body = await parseStripeBody(c);
    if (!body.amount || !body.currency) {
      return stripeError(
        c,
        400,
        "invalid_request_error",
        "Missing required param: amount and currency are required.",
        void 0,
        "amount"
      );
    }
    if (body.customer && !ss.customers.findOneBy("stripe_id", body.customer)) {
      return stripeError(
        c,
        400,
        "invalid_request_error",
        `No such customer: '${body.customer}'`,
        "resource_missing",
        "customer"
      );
    }
    const status = body.payment_method ? "requires_confirmation" : "requires_payment_method";
    const pi = ss.paymentIntents.insert({
      stripe_id: stripeId("pi"),
      amount: body.amount,
      currency: body.currency.toLowerCase(),
      status,
      customer_id: body.customer ?? null,
      description: body.description ?? null,
      payment_method: body.payment_method ?? null,
      metadata: body.metadata ?? {}
    });
    await webhooks.dispatch(
      "payment_intent.created",
      void 0,
      { type: "payment_intent.created", data: { object: formatPaymentIntent(pi) } },
      "stripe"
    );
    return c.json(formatPaymentIntent(pi), 200);
  });
  app.get("/v1/payment_intents/:id", (c) => {
    const pi = ss.paymentIntents.findOneBy("stripe_id", c.req.param("id"));
    if (!pi)
      return stripeError(
        c,
        404,
        "invalid_request_error",
        `No such payment_intent: '${c.req.param("id")}'`,
        "resource_missing"
      );
    const expand = parseExpand(c);
    const result = applyExpand(formatPaymentIntent(pi), expand, expandResolvers);
    return c.json(result);
  });
  app.post("/v1/payment_intents/:id", async (c) => {
    const pi = ss.paymentIntents.findOneBy("stripe_id", c.req.param("id"));
    if (!pi)
      return stripeError(
        c,
        404,
        "invalid_request_error",
        `No such payment_intent: '${c.req.param("id")}'`,
        "resource_missing"
      );
    const body = await parseStripeBody(c);
    const updates = {};
    if (body.amount !== void 0) updates.amount = body.amount;
    if (body.currency !== void 0) updates.currency = body.currency.toLowerCase();
    if (body.description !== void 0) updates.description = body.description;
    if (body.metadata !== void 0) updates.metadata = body.metadata;
    if (body.payment_method !== void 0) {
      updates.payment_method = body.payment_method;
      if (pi.status === "requires_payment_method") {
        updates.status = "requires_confirmation";
      }
    }
    const updated = ss.paymentIntents.update(pi.id, updates);
    return c.json(formatPaymentIntent(updated));
  });
  app.post("/v1/payment_intents/:id/confirm", async (c) => {
    const pi = ss.paymentIntents.findOneBy("stripe_id", c.req.param("id"));
    if (!pi)
      return stripeError(
        c,
        404,
        "invalid_request_error",
        `No such payment_intent: '${c.req.param("id")}'`,
        "resource_missing"
      );
    const body = await parseStripeBody(c);
    if (pi.status !== "requires_confirmation" && pi.status !== "requires_payment_method") {
      return stripeError(
        c,
        400,
        "invalid_request_error",
        `This PaymentIntent's status is ${pi.status}, which does not allow confirmation.`,
        "payment_intent_unexpected_state"
      );
    }
    if (body.payment_method) {
      ss.paymentIntents.update(pi.id, { payment_method: body.payment_method });
    }
    const updated = ss.paymentIntents.update(pi.id, { status: "succeeded" });
    const charge = ss.charges.insert({
      stripe_id: stripeId("ch"),
      amount: updated.amount,
      currency: updated.currency,
      status: "succeeded",
      customer_id: updated.customer_id,
      payment_intent_id: updated.stripe_id,
      description: updated.description,
      metadata: updated.metadata
    });
    await webhooks.dispatch(
      "payment_intent.succeeded",
      void 0,
      { type: "payment_intent.succeeded", data: { object: formatPaymentIntent(updated) } },
      "stripe"
    );
    await webhooks.dispatch(
      "charge.succeeded",
      void 0,
      {
        type: "charge.succeeded",
        data: {
          object: {
            id: charge.stripe_id,
            object: "charge",
            amount: charge.amount,
            currency: charge.currency,
            status: charge.status
          }
        }
      },
      "stripe"
    );
    return c.json(formatPaymentIntent(updated));
  });
  app.post("/v1/payment_intents/:id/cancel", async (c) => {
    const pi = ss.paymentIntents.findOneBy("stripe_id", c.req.param("id"));
    if (!pi)
      return stripeError(
        c,
        404,
        "invalid_request_error",
        `No such payment_intent: '${c.req.param("id")}'`,
        "resource_missing"
      );
    if (pi.status === "succeeded" || pi.status === "canceled") {
      return stripeError(
        c,
        400,
        "invalid_request_error",
        `This PaymentIntent's status is ${pi.status}, which does not allow cancellation.`,
        "payment_intent_unexpected_state"
      );
    }
    const updated = ss.paymentIntents.update(pi.id, { status: "canceled" });
    await webhooks.dispatch(
      "payment_intent.canceled",
      void 0,
      { type: "payment_intent.canceled", data: { object: formatPaymentIntent(updated) } },
      "stripe"
    );
    return c.json(formatPaymentIntent(updated));
  });
  app.get("/v1/payment_intents", (c) => {
    let items = ss.paymentIntents.all();
    const customerId = c.req.query("customer");
    const status = c.req.query("status");
    if (customerId) items = items.filter((pi) => pi.customer_id === customerId);
    if (status) items = items.filter((pi) => pi.status === status);
    return stripeList(c, items, "/v1/payment_intents", formatPaymentIntent);
  });
}
function paymentMethodRoutes({ app, store }) {
  const ss = getStripeStore(store);
  app.get("/v1/payment_methods", (c) => {
    const customerId = c.req.query("customer");
    if (customerId && !ss.customers.findOneBy("stripe_id", customerId)) {
      return stripeError(
        c,
        400,
        "invalid_request_error",
        `No such customer: '${customerId}'`,
        "resource_missing",
        "customer"
      );
    }
    return c.json(
      {
        object: "list",
        url: "/v1/payment_methods",
        has_more: false,
        data: []
      },
      200
    );
  });
}
function formatPrice(p) {
  return {
    id: p.stripe_id,
    object: "price",
    product: p.product_id,
    currency: p.currency,
    unit_amount: p.unit_amount,
    type: p.type,
    lookup_key: p.lookup_key ?? null,
    recurring: p.recurring ?? null,
    active: p.active,
    metadata: p.metadata,
    created: toUnixTimestamp(p.created_at),
    livemode: false
  };
}
function formatProduct(p) {
  return {
    id: p.stripe_id,
    object: "product",
    name: p.name,
    active: p.active,
    created: toUnixTimestamp(p.created_at),
    livemode: false
  };
}
function priceRoutes({ app, store, webhooks }) {
  const ss = getStripeStore(store);
  const expandResolvers = {
    product: (id) => {
      const prod = ss.products.findOneBy("stripe_id", id);
      return prod ? formatProduct(prod) : void 0;
    }
  };
  app.post("/v1/prices", async (c) => {
    const body = await parseStripeBody(c);
    if (!body.currency || !body.product) {
      return stripeError(
        c,
        400,
        "invalid_request_error",
        "Missing required param: currency and product are required.",
        void 0,
        "currency"
      );
    }
    if (!ss.products.findOneBy("stripe_id", body.product)) {
      return stripeError(
        c,
        400,
        "invalid_request_error",
        `No such product: '${body.product}'`,
        "resource_missing",
        "product"
      );
    }
    const recurring = body.recurring;
    const price = ss.prices.insert({
      stripe_id: stripeId("price"),
      product_id: body.product,
      currency: body.currency.toLowerCase(),
      unit_amount: body.unit_amount ?? null,
      type: recurring ? "recurring" : "one_time",
      lookup_key: body.lookup_key ?? null,
      recurring: recurring ? {
        interval: recurring.interval ?? "month",
        interval_count: recurring.interval_count ?? 1
      } : null,
      active: body.active ?? true,
      metadata: body.metadata ?? {}
    });
    await webhooks.dispatch(
      "price.created",
      void 0,
      { type: "price.created", data: { object: formatPrice(price) } },
      "stripe"
    );
    return c.json(formatPrice(price), 200);
  });
  app.get("/v1/prices/:id", (c) => {
    const price = ss.prices.findOneBy("stripe_id", c.req.param("id"));
    if (!price)
      return stripeError(c, 404, "invalid_request_error", `No such price: '${c.req.param("id")}'`, "resource_missing");
    const expand = parseExpand(c);
    const result = applyExpand(formatPrice(price), expand, expandResolvers);
    return c.json(result);
  });
  app.get("/v1/prices", (c) => {
    let items = ss.prices.all();
    const productId = c.req.query("product");
    const active = c.req.query("active");
    const lookupKey = c.req.query("lookup_keys[]") ?? c.req.query("lookup_key");
    if (productId) items = items.filter((p) => p.product_id === productId);
    if (active !== void 0) items = items.filter((p) => p.active === (active === "true"));
    if (lookupKey) items = items.filter((p) => p.lookup_key === lookupKey);
    return stripeList(c, items, "/v1/prices", formatPrice);
  });
}
function formatProduct2(p) {
  return {
    id: p.stripe_id,
    object: "product",
    name: p.name,
    description: p.description,
    active: p.active,
    metadata: p.metadata,
    created: toUnixTimestamp(p.created_at),
    livemode: false
  };
}
function productRoutes({ app, store, webhooks }) {
  const ss = getStripeStore(store);
  app.post("/v1/products", async (c) => {
    const body = await parseStripeBody(c);
    if (!body.name)
      return stripeError(c, 400, "invalid_request_error", "Missing required param: name.", void 0, "name");
    const product = ss.products.insert({
      stripe_id: stripeId("prod"),
      name: body.name,
      description: body.description ?? null,
      active: body.active ?? true,
      metadata: body.metadata ?? {}
    });
    await webhooks.dispatch(
      "product.created",
      void 0,
      { type: "product.created", data: { object: formatProduct2(product) } },
      "stripe"
    );
    return c.json(formatProduct2(product), 200);
  });
  app.get("/v1/products/:id", (c) => {
    const product = ss.products.findOneBy("stripe_id", c.req.param("id"));
    if (!product)
      return stripeError(
        c,
        404,
        "invalid_request_error",
        `No such product: '${c.req.param("id")}'`,
        "resource_missing"
      );
    return c.json(formatProduct2(product));
  });
  app.get("/v1/products", (c) => {
    let items = ss.products.all();
    const active = c.req.query("active");
    if (active !== void 0) items = items.filter((p) => p.active === (active === "true"));
    return stripeList(c, items, "/v1/products", formatProduct2);
  });
}
function formatSubscriptionItem(si) {
  return {
    id: si.stripe_id,
    object: "subscription_item",
    subscription: si.subscription_id,
    price: {
      id: si.price_id,
      object: "price",
      lookup_key: si.price_lookup_key ?? null
    },
    quantity: si.quantity,
    metadata: si.metadata,
    created: toUnixTimestamp(si.created_at)
  };
}
function formatSubscription(sub, items) {
  const now = Math.floor(Date.now() / 1e3);
  return {
    id: sub.stripe_id,
    object: "subscription",
    customer: sub.customer_id,
    status: sub.status,
    current_period_start: sub.current_period_start,
    current_period_end: sub.current_period_end,
    cancel_at_period_end: sub.cancel_at_period_end,
    canceled_at: sub.canceled_at ?? null,
    trial_start: sub.trial_start ?? null,
    trial_end: sub.trial_end ?? null,
    items: {
      object: "list",
      data: items.map(formatSubscriptionItem),
      has_more: false,
      total_count: items.length,
      url: `/v1/subscription_items?subscription=${sub.stripe_id}`
    },
    metadata: sub.metadata,
    livemode: false,
    created: toUnixTimestamp(sub.created_at),
    // Synthetic invoice fields
    latest_invoice: null,
    default_payment_method: null
  };
}
function subscriptionRoutes({ app, store, webhooks }) {
  const ss = getStripeStore(store);
  app.post("/v1/subscriptions", async (c) => {
    const body = await parseStripeBody(c);
    if (!body.customer) {
      return stripeError(c, 400, "invalid_request_error", "Missing required param: customer.", void 0, "customer");
    }
    const customer = ss.customers.findOneBy("stripe_id", body.customer);
    if (!customer) {
      return stripeError(
        c,
        400,
        "invalid_request_error",
        `No such customer: '${body.customer}'`,
        "resource_missing",
        "customer"
      );
    }
    const itemsInput = body.items;
    if (!itemsInput || itemsInput.length === 0) {
      return stripeError(c, 400, "invalid_request_error", "Missing required param: items.", void 0, "items");
    }
    for (let i = 0; i < itemsInput.length; i++) {
      const item = itemsInput[i];
      if (!item.price) {
        return stripeError(
          c,
          400,
          "invalid_request_error",
          `Missing required param: items[${i}][price].`,
          void 0,
          `items[${i}][price]`
        );
      }
      const price = ss.prices.findOneBy("stripe_id", item.price) ?? ss.prices.findOneBy("lookup_key", item.price);
      if (!price) {
        return stripeError(
          c,
          400,
          "invalid_request_error",
          `No such price: '${item.price}'`,
          "resource_missing",
          `items[${i}][price]`
        );
      }
    }
    const now = Math.floor(Date.now() / 1e3);
    const periodEnd = now + 30 * 24 * 3600;
    const trialEnd = body.trial_end ? parseInt(body.trial_end, 10) : void 0;
    const status = trialEnd && trialEnd > now ? "trialing" : "active";
    const sub = ss.subscriptions.insert({
      stripe_id: stripeId("sub"),
      customer_id: customer.stripe_id,
      status,
      current_period_start: now,
      current_period_end: periodEnd,
      cancel_at_period_end: false,
      canceled_at: null,
      trial_start: status === "trialing" ? now : null,
      trial_end: trialEnd ?? null,
      metadata: body.metadata ?? {}
    });
    const createdItems = [];
    for (const itemInput of itemsInput) {
      const price = ss.prices.findOneBy("stripe_id", itemInput.price);
      const si = ss.subscriptionItems.insert({
        stripe_id: stripeId("si"),
        subscription_id: sub.stripe_id,
        price_id: itemInput.price,
        price_lookup_key: price?.lookup_key ?? null,
        quantity: itemInput.quantity ?? 1,
        metadata: {}
      });
      createdItems.push(si);
    }
    const formatted = formatSubscription(sub, createdItems);
    await webhooks.dispatch(
      "customer.subscription.created",
      void 0,
      { type: "customer.subscription.created", data: { object: formatted } },
      "stripe"
    );
    if (status === "active") {
      await webhooks.dispatch(
        "invoice.payment_succeeded",
        void 0,
        {
          type: "invoice.payment_succeeded",
          data: {
            object: {
              id: stripeId("in"),
              object: "invoice",
              customer: customer.stripe_id,
              subscription: sub.stripe_id,
              status: "paid",
              amount_paid: createdItems.reduce((sum, si) => {
                const price = ss.prices.findOneBy("stripe_id", si.price_id);
                return sum + (price?.unit_amount ?? 0) * si.quantity;
              }, 0),
              currency: createdItems[0] ? ss.prices.findOneBy("stripe_id", createdItems[0].price_id)?.currency ?? "usd" : "usd",
              metadata: {}
            }
          }
        },
        "stripe"
      );
    }
    return c.json(formatted, 200);
  });
  app.get("/v1/subscriptions/:id", (c) => {
    const sub = ss.subscriptions.findOneBy("stripe_id", c.req.param("id"));
    if (!sub) {
      return stripeError(
        c,
        404,
        "invalid_request_error",
        `No such subscription: '${c.req.param("id")}'`,
        "resource_missing"
      );
    }
    const items = ss.subscriptionItems.findBy("subscription_id", sub.stripe_id);
    return c.json(formatSubscription(sub, items));
  });
  app.post("/v1/subscriptions/:id", async (c) => {
    const sub = ss.subscriptions.findOneBy("stripe_id", c.req.param("id"));
    if (!sub) {
      return stripeError(
        c,
        404,
        "invalid_request_error",
        `No such subscription: '${c.req.param("id")}'`,
        "resource_missing"
      );
    }
    if (sub.status === "canceled") {
      return stripeError(
        c,
        400,
        "invalid_request_error",
        "Cannot update a canceled subscription.",
        "subscription_canceled"
      );
    }
    const body = await parseStripeBody(c);
    const patch = {};
    if (body.cancel_at_period_end !== void 0) {
      patch.cancel_at_period_end = body.cancel_at_period_end === "true" || body.cancel_at_period_end === true;
    }
    if (body.metadata !== void 0) {
      patch.metadata = body.metadata;
    }
    const updated = ss.subscriptions.update(sub.id, patch);
    const items = ss.subscriptionItems.findBy("subscription_id", updated.stripe_id);
    const formatted = formatSubscription(updated, items);
    await webhooks.dispatch(
      "customer.subscription.updated",
      void 0,
      { type: "customer.subscription.updated", data: { object: formatted } },
      "stripe"
    );
    return c.json(formatted);
  });
  app.delete("/v1/subscriptions/:id", async (c) => {
    const sub = ss.subscriptions.findOneBy("stripe_id", c.req.param("id"));
    if (!sub) {
      return stripeError(
        c,
        404,
        "invalid_request_error",
        `No such subscription: '${c.req.param("id")}'`,
        "resource_missing"
      );
    }
    if (sub.status === "canceled") {
      return stripeError(c, 400, "invalid_request_error", "Subscription is already canceled.", "subscription_canceled");
    }
    const now = Math.floor(Date.now() / 1e3);
    const updated = ss.subscriptions.update(sub.id, { status: "canceled", canceled_at: now });
    const items = ss.subscriptionItems.findBy("subscription_id", updated.stripe_id);
    const formatted = formatSubscription(updated, items);
    await webhooks.dispatch(
      "customer.subscription.deleted",
      void 0,
      { type: "customer.subscription.deleted", data: { object: formatted } },
      "stripe"
    );
    return c.json(formatted);
  });
  app.get("/v1/subscriptions", (c) => {
    let subs = ss.subscriptions.all();
    const customerId = c.req.query("customer");
    const status = c.req.query("status");
    if (customerId) subs = subs.filter((s) => s.customer_id === customerId);
    if (status && status !== "all") subs = subs.filter((s) => s.status === status);
    return stripeList(c, subs, "/v1/subscriptions", (sub) => {
      const items = ss.subscriptionItems.findBy("subscription_id", sub.stripe_id);
      return formatSubscription(sub, items);
    });
  });
  app.post("/v1/billing_portal/sessions", async (c) => {
    const body = await parseStripeBody(c);
    const returnUrl = body.return_url ?? "/";
    return c.json({
      id: stripeId("bps"),
      object: "billing_portal.session",
      created: Math.floor(Date.now() / 1e3),
      customer: body.customer ?? null,
      livemode: false,
      locale: "auto",
      on_behalf_of: null,
      return_url: returnUrl,
      url: returnUrl
      // emulator: just redirect back immediately
    });
  });
}
var SERVICE_LABEL2 = "Stripe";
function cents(amount, currency) {
  if (amount === null) return "\u2014";
  return new Intl.NumberFormat("en-US", { style: "currency", currency: currency.toUpperCase() }).format(amount / 100);
}
function unixDate(ts) {
  return new Date(ts * 1e3).toLocaleDateString("en-AU", { day: "2-digit", month: "short", year: "numeric" });
}
function statusBadge(status) {
  const active = ["active", "succeeded", "trialing", "paid", "complete"];
  const warn = ["processing", "open", "past_due", "requires_action", "trialing"];
  const bad = ["canceled", "failed", "incomplete", "expired"];
  const cls = active.includes(status) ? "badge-granted" : bad.includes(status) ? "badge-denied" : "badge-requested";
  return `<span class="badge ${cls}">${escapeHtml(status)}</span>`;
}
function inspectorRoutes(ctx) {
  const { app, store } = ctx;
  const ss = () => getStripeStore(store);
  app.get("/", (c) => {
    const tab = c.req.query("tab") ?? "customers";
    const s = ss();
    const customers = s.customers.all();
    const products = s.products.all();
    const prices = s.prices.all();
    const subscriptions = s.subscriptions.all();
    const paymentIntents = s.paymentIntents.all();
    const charges = s.charges.all();
    const sidebar = `
<a href="/?tab=customers"${tab === "customers" ? ' class="active"' : ""}>Customers (${customers.length})</a>
<a href="/?tab=subscriptions"${tab === "subscriptions" ? ' class="active"' : ""}>Subscriptions (${subscriptions.length})</a>
<a href="/?tab=products"${tab === "products" ? ' class="active"' : ""}>Products (${products.length})</a>
<a href="/?tab=prices"${tab === "prices" ? ' class="active"' : ""}>Prices (${prices.length})</a>
<a href="/?tab=payments"${tab === "payments" ? ' class="active"' : ""}>Payments (${paymentIntents.length})</a>`;
    let bodyHtml = "";
    if (tab === "customers") {
      const rows = customers.length === 0 ? `<tr><td colspan="4" class="inspector-empty">No customers yet.</td></tr>` : customers.map((c2) => {
        const subs = subscriptions.filter((s2) => s2.customer_id === c2.stripe_id);
        const subBadge = subs.length > 0 ? subs.map((s2) => statusBadge(s2.status)).join(" ") : '<span class="badge badge-requested">no subscription</span>';
        return `
<tr>
  <td><span style="color:#33ff00;font-weight:600">${escapeHtml(c2.email ?? "\u2014")}</span><br><span style="color:#1a8c00;font-size:.75rem">${escapeHtml(c2.name ?? "")}</span></td>
  <td><code style="color:#1a8c00;font-size:.75rem">${escapeHtml(c2.stripe_id)}</code></td>
  <td>${subBadge}</td>
</tr>`;
      }).join("");
      bodyHtml = `
<div class="inspector-section">
  <h2>Customers</h2>
  <table class="inspector-table">
    <thead><tr><th>Email / Name</th><th>ID</th><th>Subscriptions</th></tr></thead>
    <tbody>${rows}</tbody>
  </table>
</div>`;
    }
    if (tab === "subscriptions") {
      const subItems = s.subscriptionItems.all();
      const rows = subscriptions.length === 0 ? `<tr><td colspan="5" class="inspector-empty">No subscriptions yet.</td></tr>` : subscriptions.map((sub) => {
        const customer = customers.find((c2) => c2.stripe_id === sub.customer_id);
        const items = subItems.filter((i) => i.subscription_id === sub.stripe_id);
        const priceLabels = items.map((i) => {
          const price = prices.find((p) => p.stripe_id === i.price_id);
          const label = price ? `${cents(price.unit_amount, price.currency)}/${price.recurring?.interval ?? ""}` : i.price_id;
          return `<span style="color:#33ff00">${escapeHtml(label)}</span> \xD7${i.quantity}`;
        }).join(", ") || "\u2014";
        const trialNote = sub.trial_end && sub.trial_end > Date.now() / 1e3 ? `<br><span style="color:#1a8c00;font-size:.75rem">trial ends ${unixDate(sub.trial_end)}</span>` : "";
        return `
<tr>
  <td><code style="color:#1a8c00;font-size:.75rem">${escapeHtml(sub.stripe_id)}</code></td>
  <td><span style="color:#33ff00">${escapeHtml(customer?.email ?? sub.customer_id)}</span></td>
  <td>${statusBadge(sub.status)}${trialNote}</td>
  <td>${priceLabels}</td>
  <td style="color:#1a8c00;font-size:.75rem">${unixDate(sub.current_period_end)}</td>
</tr>`;
      }).join("");
      bodyHtml = `
<div class="inspector-section">
  <h2>Subscriptions</h2>
  <table class="inspector-table">
    <thead><tr><th>ID</th><th>Customer</th><th>Status</th><th>Plan</th><th>Renews</th></tr></thead>
    <tbody>${rows}</tbody>
  </table>
</div>`;
    }
    if (tab === "products") {
      const rows = products.length === 0 ? `<tr><td colspan="3" class="inspector-empty">No products yet.</td></tr>` : products.map((p) => {
        const productPrices = prices.filter((pr) => pr.product_id === p.stripe_id);
        const priceList = productPrices.length === 0 ? "\u2014" : productPrices.map(
          (pr) => `<span style="color:#33ff00">${escapeHtml(cents(pr.unit_amount, pr.currency))}</span> <span style="color:#1a8c00;font-size:.75rem">${pr.recurring ? `/${pr.recurring.interval}` : "one-time"}</span>`
        ).join(", ");
        return `
<tr>
  <td><span style="color:#33ff00;font-weight:600">${escapeHtml(p.name)}</span><br><span style="color:#1a8c00;font-size:.75rem">${escapeHtml(p.description ?? "")}</span></td>
  <td><code style="color:#1a8c00;font-size:.75rem">${escapeHtml(p.stripe_id)}</code></td>
  <td>${p.active ? '<span class="badge badge-granted">active</span>' : '<span class="badge badge-denied">inactive</span>'}</td>
  <td>${priceList}</td>
</tr>`;
      }).join("");
      bodyHtml = `
<div class="inspector-section">
  <h2>Products</h2>
  <table class="inspector-table">
    <thead><tr><th>Name</th><th>ID</th><th>Status</th><th>Prices</th></tr></thead>
    <tbody>${rows}</tbody>
  </table>
</div>`;
    }
    if (tab === "prices") {
      const rows = prices.length === 0 ? `<tr><td colspan="5" class="inspector-empty">No prices yet.</td></tr>` : prices.map((p) => {
        const product = products.find((pr) => pr.stripe_id === p.product_id);
        return `
<tr>
  <td><code style="color:#1a8c00;font-size:.75rem">${escapeHtml(p.stripe_id)}</code></td>
  <td>${escapeHtml(product?.name ?? p.product_id)}</td>
  <td><span style="color:#33ff00;font-weight:600">${cents(p.unit_amount, p.currency)}</span></td>
  <td>${p.recurring ? `<span class="badge badge-granted">${escapeHtml(p.recurring.interval)}ly</span>` : '<span class="badge badge-requested">one-time</span>'}</td>
  <td>${p.lookup_key ? `<code style="color:#1a8c00;font-size:.75rem">${escapeHtml(p.lookup_key)}</code>` : "\u2014"}</td>
</tr>`;
      }).join("");
      bodyHtml = `
<div class="inspector-section">
  <h2>Prices</h2>
  <table class="inspector-table">
    <thead><tr><th>ID</th><th>Product</th><th>Amount</th><th>Billing</th><th>Lookup Key</th></tr></thead>
    <tbody>${rows}</tbody>
  </table>
</div>`;
    }
    if (tab === "payments") {
      const rows = paymentIntents.length === 0 ? `<tr><td colspan="5" class="inspector-empty">No payment intents yet.</td></tr>` : paymentIntents.map((pi) => {
        const customer = customers.find((c2) => c2.stripe_id === pi.customer_id);
        const charge = charges.find((ch) => ch.payment_intent_id === pi.stripe_id);
        return `
<tr>
  <td><code style="color:#1a8c00;font-size:.75rem">${escapeHtml(pi.stripe_id)}</code></td>
  <td><span style="color:#33ff00;font-weight:600">${cents(pi.amount, pi.currency)}</span></td>
  <td>${escapeHtml(customer?.email ?? pi.customer_id ?? "\u2014")}</td>
  <td>${statusBadge(pi.status)}</td>
  <td>${charge ? statusBadge(charge.status) : "\u2014"}</td>
</tr>`;
      }).join("");
      bodyHtml = `
<div class="inspector-section">
  <h2>Payment Intents</h2>
  <table class="inspector-table">
    <thead><tr><th>ID</th><th>Amount</th><th>Customer</th><th>Intent Status</th><th>Charge</th></tr></thead>
    <tbody>${rows}</tbody>
  </table>
</div>`;
    }
    const stats = `${customers.length} customers \xB7 ${subscriptions.length} subscriptions \xB7 ${paymentIntents.length} payments`;
    return c.html(
      renderSettingsPage(
        "Stripe Inspector",
        sidebar,
        `<div class="s-card">
  <div class="s-card-header">
    <div class="s-icon">$</div>
    <div>
      <div class="s-title">Stripe Payments</div>
      <div class="s-subtitle">${stats}</div>
    </div>
  </div>
  ${bodyHtml}
</div>`,
        SERVICE_LABEL2
      )
    );
  });
}
function seedDefaults(store, _baseUrl) {
  const ss = getStripeStore(store);
  const existing = ss.customers.findOneBy("email", "test@agent-emulate.dev");
  if (!existing) {
    ss.customers.insert({
      stripe_id: stripeId("cus"),
      email: "test@agent-emulate.dev",
      name: "Test Customer",
      description: null,
      metadata: {}
    });
  }
}
function seedFromConfig(store, _baseUrl, config, webhooks) {
  const ss = getStripeStore(store);
  if (config.customers) {
    for (const c of config.customers) {
      if (c.email) {
        const existing = ss.customers.findOneBy("email", c.email);
        if (existing) continue;
      }
      ss.customers.insert({
        stripe_id: c.id ?? stripeId("cus"),
        email: c.email ?? null,
        name: c.name ?? null,
        description: c.description ?? null,
        metadata: {}
      });
    }
  }
  if (config.products) {
    for (const p of config.products) {
      const existingProd = ss.products.all().find((pr) => pr.name === p.name);
      const product = existingProd ?? ss.products.insert({
        stripe_id: p.id ?? stripeId("prod"),
        name: p.name,
        description: p.description ?? null,
        active: true,
        metadata: {}
      });
      const matchingPrices = config.prices?.filter((pr) => pr.product_name === p.name) ?? [];
      for (const pr of matchingPrices) {
        if (pr.lookup_key) {
          const existingPrice = ss.prices.findOneBy("lookup_key", pr.lookup_key);
          if (existingPrice) continue;
        }
        ss.prices.insert({
          stripe_id: pr.id ?? stripeId("price"),
          product_id: product.stripe_id,
          currency: pr.currency.toLowerCase(),
          unit_amount: pr.unit_amount,
          type: pr.recurring ? "recurring" : "one_time",
          lookup_key: pr.lookup_key ?? null,
          recurring: pr.recurring ? {
            interval: pr.recurring.interval,
            interval_count: pr.recurring.interval_count ?? 1
          } : null,
          active: true,
          metadata: {}
        });
      }
    }
  }
  if (config.subscriptions) {
    const now = Math.floor(Date.now() / 1e3);
    for (const sub of config.subscriptions) {
      const customer = ss.customers.findOneBy("email", sub.customer_email);
      if (!customer) continue;
      const price = ss.prices.findOneBy("lookup_key", sub.price_lookup_key);
      if (!price) continue;
      const existingSub = ss.subscriptions.findBy("customer_id", customer.stripe_id).find((s) => s.status !== "canceled");
      if (existingSub) continue;
      const subscription = ss.subscriptions.insert({
        stripe_id: stripeId("sub"),
        customer_id: customer.stripe_id,
        status: sub.status ?? "active",
        current_period_start: now,
        current_period_end: now + 30 * 24 * 3600,
        cancel_at_period_end: false,
        canceled_at: null,
        trial_start: null,
        trial_end: null,
        metadata: sub.metadata ?? {}
      });
      ss.subscriptionItems.insert({
        stripe_id: stripeId("si"),
        subscription_id: subscription.stripe_id,
        price_id: price.stripe_id,
        price_lookup_key: price.lookup_key,
        quantity: 1,
        metadata: {}
      });
    }
  }
  if (config.webhooks && webhooks) {
    for (const wh of config.webhooks) {
      webhooks.register({
        url: wh.url,
        events: wh.events,
        active: true,
        secret: wh.secret,
        owner: "stripe"
      });
    }
  }
}
var stripePlugin = {
  name: "stripe",
  register(app, store, webhooks, baseUrl, tokenMap) {
    const ctx = { app, store, webhooks, baseUrl, tokenMap };
    app.get("/health", (c) => c.json({ status: "ok", service: "stripe" }));
    inspectorRoutes(ctx);
    app.use("/v1/*", requireAuthWhen("EMULATE_STRIPE_REQUIRE_AUTH", "EMULATE_REQUIRE_AUTH"));
    customerRoutes(ctx);
    paymentMethodRoutes(ctx);
    paymentIntentRoutes(ctx);
    chargeRoutes(ctx);
    productRoutes(ctx);
    priceRoutes(ctx);
    checkoutSessionRoutes(ctx);
    customerSessionRoutes(ctx);
    subscriptionRoutes(ctx);
  },
  seed(store, baseUrl) {
    seedDefaults(store, baseUrl);
  }
};
var index_default = stripePlugin;
export {
  index_default as default,
  getStripeStore,
  seedFromConfig,
  stripePlugin
};
//# sourceMappingURL=dist-JUJAT6CZ.js.map