{"version":3,"file":"site-client.mjs","names":[],"sources":["../../../src/services/auth/site-client.ts"],"sourcesContent":["import { Context, Layer } from \"effect\";\n\n/**\n * The ONE site HTTP client (ticket #423): every CLI→site call — better-auth\n * `/api/auth/*` routes, oRPC `/api/rpc/*` routes, and plain account reads —\n * goes through here, with error parsing that understands every payload shape\n * the server emits. Previously three fetch styles hand-parsed three shapes;\n * the oRPC envelope (`{ json: { defined, code, message, data } }`) was the\n * one nobody parsed, so the server's FORBIDDEN detail was lost behind a bare\n * \"Deploy key could not be created (403)\".\n */\n\nexport interface SiteResponse {\n  status: number;\n  json: unknown;\n  setCookie: string | null;\n}\n\n/** Extract the human detail from any error body shape the server emits —\n * undefined when nothing recognizable is present. */\nexport function parseSiteErrorDetail(status: number, json: unknown): string | undefined {\n  if (json === null || json === undefined || typeof json !== \"object\") return undefined;\n  const body = json as Record<string, unknown>;\n\n  // oRPC envelope: the error object rides in `json` (success payloads ride\n  // there too), carrying { defined, code, message, data }\n  const inner = typeof body.json === \"object\" && body.json !== null ? body.json : body;\n  const innerBody = inner as Record<string, unknown>;\n\n  if (typeof innerBody.message === \"string\") return innerBody.message;\n  if (\n    typeof body.error === \"object\" &&\n    body.error !== null &&\n    !Array.isArray(body.error) &&\n    typeof (body.error as Record<string, unknown>).message === \"string\"\n  ) {\n    return (body.error as { message: string }).message;\n  }\n  if (Array.isArray(body.error)) {\n    const first = body.error[0] as Record<string, unknown> | undefined;\n    if (first && typeof first.message === \"string\") return first.message;\n  }\n  void status;\n  return undefined;\n}\n\nexport interface SiteClientShape {\n  /** POST to a better-auth `/api/auth/*` route (origin header included for\n   * cookie-bearing requests). */\n  authPost: (\n    siteUrl: string,\n    path: string,\n    body: unknown,\n    cookiePair?: string,\n    extraHeaders?: Record<string, string>,\n    signal?: AbortSignal,\n  ) => Promise<SiteResponse>;\n  /** POST to an oRPC `/api/rpc/*` route — the input is wrapped in the RPC\n   * `json` envelope; errors parse through `parseSiteErrorDetail`. */\n  rpcPost: (\n    siteUrl: string,\n    path: string,\n    input: unknown,\n    cookiePair?: string,\n    extraHeaders?: Record<string, string>,\n  ) => Promise<SiteResponse>;\n  /** Plain GET returning parsed JSON (account resolution). */\n  getJson: (url: string, cookiePair?: string) => Promise<unknown>;\n}\n\nexport class SiteClient extends Context.Service<SiteClient, SiteClientShape>()(\n  \"everything-dev/auth/site-client\",\n) {\n  static readonly Live = Layer.succeed(\n    this,\n    SiteClient.of({\n      authPost: (siteUrl, path, body, cookiePair, extraHeaders, signal) =>\n        authPost(siteUrl, path, body, cookiePair, extraHeaders, signal),\n      rpcPost: (siteUrl, path, input, cookiePair, extraHeaders) =>\n        rpcPost(siteUrl, path, input, cookiePair, extraHeaders),\n      getJson: (url, cookiePair) => getJson(url, cookiePair),\n    }),\n  );\n}\n\nexport async function authPost(\n  siteUrl: string,\n  path: string,\n  body: unknown,\n  cookiePair?: string,\n  extraHeaders?: Record<string, string>,\n  signal?: AbortSignal,\n): Promise<SiteResponse> {\n  // Better Auth's origin check demands an Origin on cookie-bearing\n  // requests — present the site's own origin, exactly as a browser would.\n  const origin = new URL(siteUrl).origin;\n  const headers: Record<string, string> = {\n    \"content-type\": \"application/json\",\n    origin,\n    ...extraHeaders,\n  };\n  if (cookiePair) headers.cookie = cookiePair;\n  const response = await fetch(`${siteUrl.replace(/\\/$/, \"\")}/api/auth${path}`, {\n    method: \"POST\",\n    headers,\n    body: JSON.stringify(body),\n    signal,\n  });\n  return {\n    status: response.status,\n    json: await response.json().catch(() => ({})),\n    setCookie: response.headers.get(\"set-cookie\"),\n  };\n}\n\nexport async function rpcPost(\n  siteUrl: string,\n  path: string,\n  input: unknown,\n  cookiePair?: string,\n  extraHeaders?: Record<string, string>,\n): Promise<SiteResponse> {\n  const origin = new URL(siteUrl).origin;\n  const headers: Record<string, string> = {\n    \"content-type\": \"application/json\",\n    origin,\n    ...(extraHeaders ?? {}),\n  };\n  if (cookiePair) headers.cookie = cookiePair;\n  const response = await fetch(`${siteUrl.replace(/\\/$/, \"\")}${path}`, {\n    method: \"POST\",\n    headers,\n    body: JSON.stringify({ json: input }),\n  });\n  return {\n    status: response.status,\n    json: await response.json().catch(() => ({})),\n    setCookie: response.headers.get(\"set-cookie\"),\n  };\n}\n\nexport async function getJson(url: string, cookiePair?: string): Promise<unknown> {\n  const response = await fetch(url, {\n    headers: cookiePair ? { cookie: cookiePair } : {},\n  });\n  return (await response.json().catch(() => ({}))) as unknown;\n}\n"],"mappings":";;;;;AAoBA,SAAgB,qBAAqB,QAAgB,MAAmC;CACtF,IAAI,SAAS,QAAQ,SAAS,UAAa,OAAO,SAAS,UAAU,OAAO;CAC5E,MAAM,OAAO;CAKb,MAAM,YADQ,OAAO,KAAK,SAAS,YAAY,KAAK,SAAS,OAAO,KAAK,OAAO;CAGhF,IAAI,OAAO,UAAU,YAAY,UAAU,OAAO,UAAU;CAC5D,IACE,OAAO,KAAK,UAAU,YACtB,KAAK,UAAU,QACf,CAAC,MAAM,QAAQ,KAAK,KAAK,KACzB,OAAQ,KAAK,MAAkC,YAAY,UAE3D,OAAQ,KAAK,MAA8B;CAE7C,IAAI,MAAM,QAAQ,KAAK,KAAK,GAAG;EAC7B,MAAM,QAAQ,KAAK,MAAM;EACzB,IAAI,SAAS,OAAO,MAAM,YAAY,UAAU,OAAO,MAAM;CAC/D;AAGF;AA0BA,IAAa,aAAb,MAAa,mBAAmB,QAAQ,QAAqC,CAAC,CAC5E,iCACF,CAAC,CAAC;CACA,OAAgB,OAAO,MAAM,QAC3B,MACA,WAAW,GAAG;EACZ,WAAW,SAAS,MAAM,MAAM,YAAY,cAAc,WACxD,SAAS,SAAS,MAAM,MAAM,YAAY,cAAc,MAAM;EAChE,UAAU,SAAS,MAAM,OAAO,YAAY,iBAC1C,QAAQ,SAAS,MAAM,OAAO,YAAY,YAAY;EACxD,UAAU,KAAK,eAAe,QAAQ,KAAK,UAAU;CACvD,CAAC,CACH;AACF;AAEA,eAAsB,SACpB,SACA,MACA,MACA,YACA,cACA,QACuB;CAIvB,MAAM,UAAkC;EACtC,gBAAgB;EAChB,QAHa,IAAI,IAAI,OAAO,CAAC,CAAC;EAI9B,GAAG;CACL;CACA,IAAI,YAAY,QAAQ,SAAS;CACjC,MAAM,WAAW,MAAM,MAAM,GAAG,QAAQ,QAAQ,OAAO,EAAE,EAAE,WAAW,QAAQ;EAC5E,QAAQ;EACR;EACA,MAAM,KAAK,UAAU,IAAI;EACzB;CACF,CAAC;CACD,OAAO;EACL,QAAQ,SAAS;EACjB,MAAM,MAAM,SAAS,KAAK,CAAC,CAAC,aAAa,CAAC,EAAE;EAC5C,WAAW,SAAS,QAAQ,IAAI,YAAY;CAC9C;AACF;AAEA,eAAsB,QACpB,SACA,MACA,OACA,YACA,cACuB;CAEvB,MAAM,UAAkC;EACtC,gBAAgB;EAChB,QAHa,IAAI,IAAI,OAAO,CAAC,CAAC;EAI9B,GAAI,gBAAgB,CAAC;CACvB;CACA,IAAI,YAAY,QAAQ,SAAS;CACjC,MAAM,WAAW,MAAM,MAAM,GAAG,QAAQ,QAAQ,OAAO,EAAE,IAAI,QAAQ;EACnE,QAAQ;EACR;EACA,MAAM,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;CACtC,CAAC;CACD,OAAO;EACL,QAAQ,SAAS;EACjB,MAAM,MAAM,SAAS,KAAK,CAAC,CAAC,aAAa,CAAC,EAAE;EAC5C,WAAW,SAAS,QAAQ,IAAI,YAAY;CAC9C;AACF;AAEA,eAAsB,QAAQ,KAAa,YAAuC;CAIhF,OAAQ,OAAM,MAHS,MAAM,KAAK,EAChC,SAAS,aAAa,EAAE,QAAQ,WAAW,IAAI,CAAC,EAClD,CAAC,EACqB,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,EAAE;AAChD"}