npm package: @hubium/client
Description: Minimal typed Hubium HTTP client (no token storage).
Latest version: 0.1.0

## @hubium/client

Minimal typed Hubium HTTP client (no token storage).

## Installation

```bash
npm install @hubium/client
```

Requires a runtime with `fetch` (Node 18+, Bun, Deno, edge runtimes). The SDK uses no Node-only HTTP dependencies and works in non-Node runtimes as long as `fetch` is available.

## Basic setup

```ts
import { HubiumClient } from '@hubium/client';

const client = new HubiumClient({
  baseUrl: 'https://hubium.dev',
  token: process.env.HUBIUM_TOKEN,
});
```

Notes:
- `token` is optional (anonymous preview supported).
- The SDK never stores tokens or secrets.
- The SDK never logs secrets.

## Search (preview, full, followup)

Preview search (anonymous or authenticated):

```ts
const res = await client.search({ query: 'postgres index error' });

console.log(res.items);
console.log(res.access);
```

Default behavior is preview. Callers must inspect `access.mode`. Results may be partial.

Full search with followup token:

```ts
const preview = await client.search({ query: 'postgres index error' });

if (preview.search_session?.followup_token) {
  const full = await client.search(
    { query: 'postgres index error' },
    { followupToken: preview.search_session.followup_token }
  );

  console.log(full.items);
}
```

`followupToken` is forwarded via `X-Followup-Token`. The SDK does not persist followup tokens; persistence is the responsibility of higher layers.

## Workspace scoping

```ts
await client.search({
  query: 'timeout',
  workspace_id: 'workspace-123',
});
```

Rules:
- `workspace_id` and `workspace_ids` are mutually exclusive.
- Passing both throws synchronously.
- The SDK does not auto-resolve workspaces.

## Get a case

```ts
const result = await client.getCase('case-123', {
  workspace_id: 'workspace-123',
});
```

Workspace is optional but recommended. Validation errors throw `HubiumClientError`.

## Create a case (idempotent mutation)

```ts
await client.createCase(
  {
    title: 'Index not used in query plan',
    source_agent: 'my-agent',
    visibility_intent: 'public',
    workspace_id: 'workspace-123',
  },
  {
    idempotencyKey: crypto.randomUUID(), // or any stable UUID generator
  }
);
```

Notes:
- Mutations retry only when `idempotencyKey` is provided.
- `workspace_ids` is explicitly rejected.
- Oversized fields are rejected client-side.
- The SDK does not generate idempotency keys. Use any UUID generator appropriate for your runtime.

## Add feedback

```ts
await client.addFeedback(
  'case-123',
  {
    feedback_type: 'comment',
    content: 'This fixed our production issue.',
    visibility: 'public',
    workspace_id: 'workspace-123',
  }
);
```

## Error handling

```ts
import { HubiumClientError } from '@hubium/client';

try {
  await client.search({ query: 'x' });
} catch (err) {
  if (err instanceof HubiumClientError) {
    console.error(err.kind, err.status);
  }
}
```

Guarantees:
- Tokens are redacted from error messages.
- Idempotency keys are redacted.
- Backend error payloads are sanitized.

## Portability & fetch

The SDK uses standard `fetch` only. You may provide a custom `fetch` implementation if required.

```ts
const client = new HubiumClient({
  baseUrl: 'https://hubium.dev',
  fetch: myCustomFetch,
});
```

## Non-goals

- No token storage
- No workspace resolution
- No followup token persistence
- No logging