<p align="center">
  <img src="https://warmhub.ai/logo-light.svg" width="200" alt="WarmHub logo">
</p>

# @warmhub/sdk-ts

TypeScript SDK for WarmHub.

📖 **Full docs:** [docs.warmhub.ai/sdk/overview/](https://docs.warmhub.ai/sdk/overview/) · **API reference:** [docs.warmhub.ai/sdk-reference/readme/](https://docs.warmhub.ai/sdk-reference/readme/) · **Release notes:** [docs.warmhub.ai/sdk/typescript/changelog/](https://docs.warmhub.ai/sdk/typescript/changelog/)

## Upgrading

`CHANGELOG.md` ships inside this package. It is migration-guide style: every
breaking change is written as what it was, what it is now, and the exact edit
with before/after code. Read it before a version bump.

```bash
# Installed copy, offline:
cat node_modules/@warmhub/sdk-ts/CHANGELOG.md
```

It is also browsable on the npm page under **Code** →
[`CHANGELOG.md`](https://www.npmjs.com/package/@warmhub/sdk-ts?activeTab=code).

## Install

```bash
npm install @warmhub/sdk-ts
```

Node 22 callers verify downloaded repository checkpoints through the isolated
subpath. The verifier consumes caller-supplied bytes and performs no fetch or
WarmHub authentication:

```ts
import { createReadStream } from 'node:fs'
import { verifyRepositoryCheckpointArchive } from '@warmhub/sdk-ts/checkpoint'

const result = await verifyRepositoryCheckpointArchive(
  createReadStream('repository-checkpoint.zip'),
)
```

## Get a Token

The `WH_TOKEN` used below is a personal access token. Mint one with the [WarmHub CLI](https://docs.warmhub.ai/get-started/quickstart/#connect-via-cli):

```bash
wh auth login
wh token create --name my-app
export WH_TOKEN=eyJhbGciOi...
```

See [Personal Access Tokens](https://docs.warmhub.ai/auth/personal-access-tokens/) for scopes, rotation, and CI usage.

## Quickstart

```ts
import { WarmHubClient } from '@warmhub/sdk-ts'

const client = new WarmHubClient({
  auth: { getToken: async () => process.env.WH_TOKEN },
})

// One-time setup. Skip these three calls (or catch CONFLICT) on re-runs.
await client.org.create('acme')
await client.repo.create('acme', 'world', 'Game world')
await client.shape.create('acme', 'world', 'Location', {
  x: 'number',
  y: 'number',
  label: 'string',
})

await client.commit.apply('acme', 'world', 'seed cave', [
  {
    operation: 'add',
    kind: 'thing',
    name: 'Location/cave',
    data: { x: 0, y: 0, label: 'Dark Cave' },
  },
])

const head = await client.thing.head('acme', 'world', { shape: 'Location' })
```

For the connect-and-read flow without setup, see the [Quickstart](https://docs.warmhub.ai/get-started/quickstart/#connect-via-sdk). For reference on shapes, things, and writes, see [Data Modeling](https://docs.warmhub.ai/data-modeling/wrefs/).

## Function Logs

Backend function `console.*` lines are suppressed by default. SDK consumers can
opt into log replay for debugging:

```ts
const client = new WarmHubClient({
  auth: { getToken: async () => process.env.WH_TOKEN },
  functionLogs: 'raw',
})
```

## Entrypoints

- `@warmhub/sdk-ts`: core client, types, errors, `OperationBuilder`

## Error Handling

```ts
import { isRetryable, isWarmHubError } from '@warmhub/sdk-ts'

try {
  await client.repo.get('acme', 'world')
} catch (err) {
  if (isWarmHubError(err) && err.kind === 'NOT_FOUND') {
    // handle missing repo
  }
  if (isRetryable(err)) {
    // retry strategy
  }
  throw err
}
```

## OperationBuilder

`OperationBuilder` is single-use: after a successful `commit()`, the builder
is sealed and cannot be modified or reused.

```ts
import { OperationBuilder } from '@warmhub/sdk-ts'

const ob = new OperationBuilder()
ob.add({ name: 'Location/cave', data: { x: 0, y: 0 } })
const result = await ob.commit({
  client,
  orgName: 'acme',
  repoName: 'world',
  message: 'seed cave',
})
```

## API Docs

Generated API reference is published at [docs.warmhub.ai/sdk-reference/readme/](https://docs.warmhub.ai/sdk-reference/readme/) — the landing page lists every public class, interface, type alias, variable, and function exported from the main `@warmhub/sdk-ts` entrypoint.
