AkurAI Build
Menu

Public repository / 8b66e18ce8cb51ca3e4124f89fd1429a1d4792f3

AkurAI-Mail / src/framework/api.js

Latest change 3af23b0e937520000e1aa43c7e2eb9de2675abd1 - OLI-248: webmail list and reader as framework islands on a signals store by Ólafur Búi Ólafsson

// api.js — the webmail API client (plain ESM). Mirrors the SvelteKit-era src/lib/api.ts:
// same-origin fetch, a 401 sends the browser to the IdP and never settles.

/**
 * The IdP login route, returning to `returnTo` afterwards.
 * @param {string} returnTo
 */
export function loginUrl(returnTo) {
  return `/auth/login?return_to=${encodeURIComponent(returnTo)}`;
}

/**
 * @typedef {(input: RequestInfo | URL, init?: RequestInit) => Promise<Response>} FetchLike
 * @typedef {{ fetch?: FetchLike, navigate?: (url: string) => void, location?: { pathname: string } }} ApiOptions
 */

/**
 * @param {ApiOptions} [options]
 */
export function createApi({ fetch: doFetch = (...args) => fetch(...args), navigate = (url) => { window.location.href = url; }, location = window.location } = {}) {
  /**
   * @param {string} path
   * @param {RequestInit} [init]
   * @returns {Promise<any>}
   */
  async function api(path, init) {
    const res = await doFetch(path, { credentials: "same-origin", ...init });
    if (res.status === 401) {
      navigate(loginUrl(location.pathname));
      // The page is navigating away; never settle so callers do not render a half-loaded state.
      return new Promise(() => {});
    }
    return res.json();
  }

  /**
   * @param {string} path
   * @param {unknown} body
   */
  const post = (path, body) => api(path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });

  return {
    api,
    post,
    /** @param {Record<string, string>} params */
    mailbox: (params) => doFetch(`/api/webmail?${new URLSearchParams(params)}`, { credentials: "same-origin" }),
    /** @param {Record<string, unknown>} body */
    webmail: (body) => doFetch("/api/webmail", { method: "POST", credentials: "same-origin", headers: { "content-type": "application/json" }, body: JSON.stringify(body) }),
  };
}