# @warkypublic/resolvespec-js

> TypeScript client library for ResolveSpec REST, HeaderSpec, and WebSocket APIs

Latest version **1.0.2** (published 2026-09-23) · MIT license · 0 weekly downloads

## Install

```sh
npm install @warkypublic/resolvespec-js
pnpm add @warkypublic/resolvespec-js
yarn add @warkypublic/resolvespec-js
bun add @warkypublic/resolvespec-js
```

## Health

**Score 70/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 1.0.2 |
| Published | 2026-09-23 |
| First published | 2026-02-15 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >=18 |
| Dependencies | 2 |
| Unpacked size | 57.2 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 3 |
| Author | Hein |
| Maintainers | warky |
| Keywords | resolvespec, headerspec, websocket, rest-client, typescript, api-client |

## Links

- npm: https://www.npmjs.com/package/@warkypublic/resolvespec-js
- Repository: https://github.com/bitechdev/ResolveSpec
- Homepage: https://github.com/bitechdev/ResolveSpec#readme
- Issues: https://github.com/bitechdev/ResolveSpec/issues
- npm.io page: https://npm.io/package/@warkypublic/resolvespec-js

## Dependencies (2)

- [uuid](https://npm.io/package/uuid.md) ^14.0.2
- [@warkypublic/artemis-kit](https://npm.io/package/@warkypublic/artemis-kit.md) ^1.0.10

## Alternatives

- [@opentelemetry/exporter-zipkin](https://npm.io/package/@opentelemetry/exporter-zipkin.md) — 14.8M weekly downloads
- [pusher-js](https://npm.io/package/pusher-js.md) — 2.0M weekly downloads
- [browserify](https://npm.io/package/browserify.md) — 1.7M weekly downloads
- [sqs-consumer](https://npm.io/package/sqs-consumer.md) — 1.7M weekly downloads
- [@sanity/eventsource](https://npm.io/package/@sanity/eventsource.md) — 930.8K weekly downloads

## Recent versions

- 1.0.2 (latest) — 2026-09-23
- 1.0.1 — 2026-02-15
- 1.0.0 — 2026-02-15

## README

# ResolveSpec JS

TypeScript client library for ResolveSpec APIs. Supports body-based REST, header-based REST, and WebSocket protocols.

## Install

```bash
pnpm add @warkypublic/resolvespec-js
```

## Clients

| Client | Protocol | Singleton Factory |
| --- | --- | --- |
| `ResolveSpecClient` | REST (body-based) | `getResolveSpecClient(config)` |
| `HeaderSpecClient` | REST (header-based) | `getHeaderSpecClient(config)` |
| `WebSocketClient` | WebSocket | `getWebSocketClient(config)` |

All clients use the class pattern. Singleton factories return cached instances keyed by URL.

## REST Client (Body-Based)

Options sent in JSON request body. Maps to Go `pkg/resolvespec`.

```typescript
import { ResolveSpecClient, getResolveSpecClient } from '@warkypublic/resolvespec-js';

// Class instantiation
const client = new ResolveSpecClient({ baseUrl: 'http://localhost:3000', token: 'your-token' });

// Or singleton factory (returns cached instance per baseUrl and effective headers)
const client = getResolveSpecClient({ baseUrl: 'http://localhost:3000', token: 'your-token' });

// Read with filters, sort, pagination
const result = await client.read('public', 'users', undefined, {
  columns: ['id', 'name', 'email'],
  filters: [{ column: 'status', operator: 'eq', value: 'active' }],
  sort: [{ column: 'name', direction: 'asc' }],
  limit: 10,
  offset: 0,
  preload: [{ relation: 'Posts', columns: ['id', 'title'] }],
});

// Read by ID
const user = await client.read('public', 'users', 42);

// Create
const created = await client.create('public', 'users', { name: 'New User' });

// Update
await client.update('public', 'users', { name: 'Updated' }, 42);

// Delete
await client.delete('public', 'users', 42);

// Metadata
const meta = await client.getMetadata('public', 'users');
```

## HeaderSpec Client (Header-Based)

Options sent via HTTP headers. Maps to Go `pkg/restheadspec`.

```typescript
import { HeaderSpecClient, getHeaderSpecClient } from '@warkypublic/resolvespec-js';

const client = new HeaderSpecClient({ baseUrl: 'http://localhost:3000', token: 'your-token' });
// Or: const client = getHeaderSpecClient({ baseUrl: 'http://localhost:3000', token: 'your-token' });

// GET with options as headers
const result = await client.read('public', 'users', undefined, {
  columns: ['id', 'name'],
  filters: [
    { column: 'status', operator: 'eq', value: 'active' },
    { column: 'age', operator: 'gte', value: 18, logic_operator: 'AND' },
  ],
  sort: [{ column: 'name', direction: 'asc' }],
  limit: 50,
  preload: [{ relation: 'Department', columns: ['id', 'name'] }],
});

// POST create
await client.create('public', 'users', { name: 'New User' });

// PUT update
await client.update('public', 'users', '42', { name: 'Updated' });

// DELETE
await client.delete('public', 'users', '42');
```

### Header Mapping

| Header | Options Field | Format |
| --- | --- | --- |
| `X-Select-Fields` | `columns` | comma-separated |
| `X-Not-Select-Fields` | `omit_columns` | comma-separated |
| `X-FieldFilter-{col}` | `filters` (eq, AND) | value |
| `X-SearchOp-{op}-{col}` | `filters` (AND) | value |
| `X-SearchOr-{op}-{col}` | `filters` (OR) | value |
| `X-Sort` | `sort` | `+col` asc, `-col` desc |
| `X-Limit` / `X-Offset` | `limit` / `offset` | number |
| `X-Cursor-Forward` | `cursor_forward` | string |
| `X-Cursor-Backward` | `cursor_backward` | string |
| `X-Preload` | `preload` | `Rel:col1,col2` pipe-separated |
| `X-Fetch-RowNumber` | `fetch_row_number` | string |
| `X-CQL-SEL-{col}` | `computedColumns` | expression |
| `X-Custom-SQL-W` | `customOperators` | SQL AND-joined |

### Utility Functions

```typescript
import { buildHeaders, encodeHeaderValue, decodeHeaderValue } from '@warkypublic/resolvespec-js';

const headers = buildHeaders({ columns: ['id', 'name'], limit: 10 });
// => { 'X-Select-Fields': 'id,name', 'X-Limit': '10' }

const encoded = encodeHeaderValue('complex value');  // 'ZIP_...'
const decoded = decodeHeaderValue(encoded);           // 'complex value'
```

## WebSocket Client

Real-time CRUD with subscriptions. Maps to Go `pkg/websocketspec`.

```typescript
import { WebSocketClient, getWebSocketClient } from '@warkypublic/resolvespec-js';

const ws = new WebSocketClient({
  url: 'ws://localhost:8080/ws',
  reconnect: true,
  heartbeatInterval: 30000,
});
// Or: const ws = getWebSocketClient({ url: 'ws://localhost:8080/ws' });

await ws.connect();

// CRUD
const users = await ws.read('users', { schema: 'public', limit: 10 });
const created = await ws.create('users', { name: 'New' }, { schema: 'public' });
await ws.update('users', '1', { name: 'Updated' });
await ws.delete('users', '1');

// Subscribe to changes
const subId = await ws.subscribe('users', (notification) => {
  console.log(notification.operation, notification.data);
});

// Unsubscribe
await ws.unsubscribe(subId);

// Events
ws.on('connect', () => console.log('connected'));
ws.on('disconnect', () => console.log('disconnected'));
ws.on('error', (err) => console.error(err));

ws.disconnect();
```

## Types

All types align with Go `pkg/common/types.go`.

### Key Types

```typescript
interface Options {
  columns?: string[];
  omit_columns?: string[];
  filters?: FilterOption[];
  sort?: SortOption[];
  limit?: number;
  offset?: number;
  preload?: PreloadOption[];
  customOperators?: CustomOperator[];
  computedColumns?: ComputedColumn[];
  parameters?: Parameter[];
  cursor_forward?: string;
  cursor_backward?: string;
  fetch_row_number?: string;
}

interface FilterOption {
  column: string;
  operator: Operator | string;
  value: any;
  logic_operator?: 'AND' | 'OR';
}

// Operators: eq, neq, gt, gte, lt, lte, like, ilike, in,
//            contains, startswith, endswith, between,
//            between_inclusive, is_null, is_not_null

interface APIResponse<T> {
  success: boolean;
  data: T;
  metadata?: Metadata;
  error?: APIError;
}
```

## Build

```bash
pnpm install
pnpm run build    # dist/index.js (ES) + dist/index.cjs (CJS) + .d.ts
pnpm run test     # vitest
pnpm run lint     # eslint
```

## License

MIT

### Custom HTTP headers

Both `ResolveSpecClient` and `HeaderSpecClient` (including their factory functions)
accept `headers` in `ClientConfig` and send them on every HTTP request:

```typescript
const client = new ResolveSpecClient({
  baseUrl: 'http://localhost:3000',
  token: 'your-token',
  headers: { 'X-Tenant': 'acme' },
});
```

Header names are merged case-insensitively. Custom headers override the default
`Content-Type`; a supplied `token` overrides custom `Authorization`, and HeaderSpec
query options override matching custom query headers. Without a token, custom
`Authorization` is preserved. Configuration is copied at construction; create or
retrieve a client with new configuration to change headers. Factory clients are
cached by URL and effective headers, keeping different tenants and tokens separate.

Grid adapters must forward `dataSourceOptions.headers` to this `headers` option.

---
_Source: https://npm.io/package/@warkypublic/resolvespec-js · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
