@sinequa/atomic

A custom transport

Replacing what actually sends the requests — for retries, tracing, or a host that owns the network.

Two levels of replacement, for two different needs:

ReplaceWhen
auth.fetchThe fetch functionTests, a polyfill, a wrapper around the platform's own
httpAdapterThe whole send stepA host SDK owns the network — SharePoint being the built-in case

Both are configuration, not subclassing: hand in a function or an object and the library uses it.

Wrapping fetch

The lighter option. It stays a fetch, so everything downstream is unchanged — you see every request and every response.

Trace and retry, without touching the library
src/traced-fetch.ts
import { createAtomicClient } from "@sinequa/atomic";

/**
 * A fetch that logs each request and retries once on a network failure.
 *
 * Note what it must not do: retry a 4xx or a 5xx. A 401 is the recovery hook's
 * business, and a 500 usually means the request itself is wrong — replaying it
 * only doubles the load.
 */
function tracedFetch(base: typeof globalThis.fetch): typeof globalThis.fetch {
  return async (input, init) => {
    const started = performance.now();

    try {
      const response = await base(input, init);
      console.debug(`${init?.method ?? "GET"} ${String(input)} → ${response.status}`,
        `${Math.round(performance.now() - started)}ms`);
      return response;
    } catch (error) {
      // A transport-level failure only: DNS, connection reset, offline.
      console.warn("network failure, retrying once:", error);
      return base(input, init);
    }
  };
}

export const client = createAtomicClient({
  app: "<your-app>",
  backendUrl: "https://<your-sinequa-server>",
  auth: {
    fetch: tracedFetch(globalThis.fetch.bind(globalThis)),
  },
});

Replacing the send step

httpAdapter goes further: the library builds the URL, the headers and the body, then hands the whole thing to you. The contract is two types:

the contract
interface HttpAdapterRequest {
  method: string;
  headers: Headers;
  body?: string;
  signal?: AbortSignal;
}

interface HttpAdapter {
  fetch(url: string, request: HttpAdapterRequest): Promise<AadHttpResponseLike | Response>;
}

Return a real Response and everything downstream behaves normally. The union's other member exists for the SharePoint SDK, whose responses are not Response instances.

an adapter over a host SDK
import type { HttpAdapter } from "@sinequa/atomic";

const adapter: HttpAdapter = {
  async fetch(url, { method, headers, body, signal }) {
    // Whatever the host gives you, as long as it resolves to a Response.
    return hostSdk.request({ url, method, headers, body, signal });
  },
};

const client = createAtomicClient({ app: "<your-app>", httpAdapter: adapter });

An adapter changes how the auth mode resolves

A configured transport is taken as evidence that requests authenticate themselves, so an ambiguous mode settles as sso and noAutoAuthentication follows. That is right for SharePoint and wrong for an adapter you added for logging or retries.

If your adapter does not authenticate, declare authMode explicitly.

Pitfalls

What's next

On this page