---
url: /shared.md
---
# Shared

`@xsai/shared` holds the HTTP options, request helper, and error types that every xsAI package uses.
Adapters send requests through `sendRequest`, so every package fails in the same way.
A non-success status throws `HttpError` with `status`, `body`, and `headers`.
A rejected `fetch` throws an `XSAIError` with the code `network-error`, and the original error is in `cause`.

## Handle an HTTP error

```sh
pnpm add @xsai/shared
```

```ts
import { HttpError, sendRequest } from '@xsai/shared'

try {
  await sendRequest({ method: 'GET', path: 'models' }, {
    baseURL: 'https://example.com/v1/',
    fetch: async () => new Response('Try again later', { status: 429 }),
  })
}
catch (error) {
  if (HttpError.isInstance(error))
    console.log(error.status, error.body)
  else
    throw error
}
```

This example replaces `fetch` with a local response, so it needs no service.
It prints `429 Try again later`.

Use `HttpError.isInstance(error)` and `XSAIError.isInstance(error)` to narrow an unknown error.
xsAI does not retry requests.
For a rate limit, read `retry-after` from `error.headers` and wait before you call again.
Read request IDs from `error.headers` too, because your service support team needs them to trace a failure.

## Reference

Import these exports from `@xsai/shared`.
The adapters use them, and you need them when you write an adapter or customize requests.

### HTTP options

`HttpOptions` requires `baseURL: string | URL` and `model: string`.
It has the optional fields `apiKey`, `headers`, and `fetch`.
Model catalogs and `sendRequest` use `Omit<HttpOptions, 'model'>`.

`apiKey` becomes a bearer token in `sendRequest`, and an adapter can use another header instead.
`headers` is a record of strings, and undefined values are left out.
`fetch` has the type `(request: Request) => Promise<Response>`.
It receives a complete `Request`.

`requestURL(path, baseURL)` adds a trailing slash to the root and then resolves the path.
Absolute paths and absolute URLs follow the normal URL rules.

### sendRequest

`sendRequest(options, httpOptions)` makes one request and returns a `Response` with a body.
It does not retry.

| Field | Description |
| --- | --- |
| `path` | Required. A path or URL. |
| `method` | `GET` or `POST`. Defaults to `POST`. |
| `body` | `FormData` or an object. Objects are sent as JSON. |
| `headers` | Replaces all headers that the helper builds. |
| `signal` | An `AbortSignal`. |

Without `headers`, the helper merges `httpOptions.headers`, the authentication header, and the content type.
For `FormData`, it removes `Content-Type` so the runtime can add the multipart boundary.

| Condition | Result |
| --- | --- |
| Non-success status | Throws `HttpError` after it reads the body. |
| Success with no body | Throws `invalid-response`. |
| `fetch` rejects | Throws `network-error`, with the original error in `cause`. |
| Signal aborted | Throws the abort reason. |

JSON parsing and later stream errors happen outside this helper.

### Errors

`XSAIError(code, message, options?)` extends `Error` with a typed `code`.
`XSAIError.isInstance(error)` narrows an unknown value.
Packages add their own codes by extending `XSAIErrorCauseMap`.

| Code | Meaning |
| --- | --- |
| `http-error` | A non-success HTTP status. |
| `network-error` | `fetch` rejected. A cause is required. |
| `invalid-input` | The operation cannot accept the input. |
| `invalid-response` | The response breaks the contract of the operation. |
| `truncated-stream` | A required terminal event is missing. |
| `decision-refusal` | A decision model refused a question. `@xsai/decide` adds this code. |
| `model-error` | A text adapter reports a model failure. |
| `protocol-error` | A text stream breaks its protocol. |

`HttpError({ status, body, headers })` extends `XSAIError<'http-error'>`.
Its message is `HTTP {status}: {body}`, and its fields hold the status, the text body, and the `Headers`.

### SSE and utility types

`EventSourceParserStream` and `EventSourceMessage` are re-exported from `eventsource-parser/stream`.
Pipe decoded SSE text through the stream to get parsed messages.
It does not translate provider events into xsAI events.

`Promisable<T>` is `T | Promise<T>`.
Custom models use it for functions that return synchronously or asynchronously.
