# Core (https://dimah-s3.vercel.app/docs/core)



# @dimah-s3/core [#dimah-s3core]

Shared protocol types, the isomorphic HTTP client, and pure helpers used by `@dimah-s3/server` and `@dimah-s3/react`.

## What lives here [#what-lives-here]

* `S3_API_ROUTES` / `S3_API_BASE_PATH` — route path SSOT
* `createS3Client` — browser/isomorphic client implementing `S3Api` (+ client plugins). Exposes `$ERROR_CODES`, `$fetch`, `$Infer`. `baseURL` wins over `basePath`. Upload and download `expiresIn` default to **600** seconds (`S3_DEFAULT_EXPIRES_IN`) on the server.
* `APIError` — better-call class (`status`, `body`); JSON `{ message, code?, params? }`. Use `APIError.from(status, S3_ERROR_CODES.FORBIDDEN)` or `isAPIError`. Catalog: [Errors](https://dimah-s3.vercel.app/docs/server/errors).
* `s3FetchErrorSchema` — Zod schema for that JSON (better-fetch `errorSchema`)
* Client plugin helpers — `defineClientPlugin`, `createS3Fetch`, `pluginPath`
* Pure file helpers — `validateFile`, `formatFileSize`, `buildContentDisposition`, …

The browser client (`createS3Client`) uses nested calls
(`api.download({ route, key })`, `api.multipart.init`). The server `s3.api`
is the better-call map (`s3.api.download({ query })`, plus nested aliases
`s3.api.multipart.init`).
Both share these paths (under `basePath`, default `/api/s3`):

| Constant             | Method | Path                          |
| -------------------- | ------ | ----------------------------- |
| `upload`             | POST   | `/presign/upload`             |
| `uploadConfirm`      | POST   | `/presign/upload/confirm`     |
| `download`           | GET    | `/presign/download`           |
| `delete`             | DELETE | `/delete`                     |
| `multipartInit`      | POST   | `/presign/multipart/init`     |
| `multipartPart`      | POST   | `/presign/multipart/part`     |
| `multipartListParts` | GET    | `/presign/multipart/parts`    |
| `multipartComplete`  | POST   | `/presign/multipart/complete` |
| `multipartAbort`     | POST   | `/presign/multipart/abort`    |

## Quick start [#quick-start]

```ts
import { createS3Client } from "@dimah-s3/core";
import { dbClient } from "@dimah-s3/db/client";

export const api = createS3Client({
  basePath: "/api/s3",
  credentials: "include",
  plugins: [dbClient()],
});

await api.upload({
  route: "avatar",
  fileName: "avatar.png",
  contentType: "image/png",
  fileSize: 1024,
});
await api.db.listObjects({ limit: 20, offset: 0 });
```

For React apps prefer `createS3Client` from `@dimah-s3/react` — same options, plus a bound `Provider` / typed `useApi` on the client object.

## Client plugins [#client-plugins]

```ts
import { defineClientPlugin, pluginPath } from "@dimah-s3/core";

export function myClient() {
  return defineClientPlugin({
    id: "my",
    getActions: ($fetch) => ({
      ping: () => $fetch(pluginPath("my", "ping"), { method: "GET" }),
    }),
  });
}
```

Paths must match the server plugin endpoint (e.g. `createS3Endpoint("/my/ping", …)`).

## Validation [#validation]

`validateFile` returns `{ code, message, params? }` (or `null`). On the client, `useFormatValidateFileError` maps `code` to a UI string.
