# @warkypublic/resolvespec-js v1.0.0 TypeScript client library for ResolveSpec APIs. Supports body-based REST, header-based REST, and WebSocket protocols. Aligns with Go backend types. ## Clients | Client | Protocol | Singleton Factory | |---|---|---| | `ResolveSpecClient` | REST (body JSON) | `getResolveSpecClient(config)` | | `HeaderSpecClient` | REST (HTTP headers) | `getHeaderSpecClient(config)` | | `WebSocketClient` | WebSocket | `getWebSocketClient(config)` | Singleton factories cache instances keyed by URL. ## Config ```typescript interface ClientConfig { baseUrl: string; token?: string; // Bearer token } interface WebSocketClientConfig { url: string; reconnect?: boolean; reconnectInterval?: number; maxReconnectAttempts?: number; heartbeatInterval?: number; debug?: boolean; } ``` ## ResolveSpecClient (Body-Based REST) ```typescript import { ResolveSpecClient, getResolveSpecClient } from '@warkypublic/resolvespec-js'; const client = new ResolveSpecClient({ baseUrl: 'http://localhost:3000', token: 'your-token' }); // CRUD - signature: (schema, entity, id?, options?) await client.read('public', 'users', undefined, { columns: ['id', 'name'], limit: 10 }); await client.read('public', 'users', 42); // by ID await client.create('public', 'users', { name: 'New' }); // create await client.update('public', 'users', { name: 'Updated' }, 42); // update await client.delete('public', 'users', 42); // delete await client.getMetadata('public', 'users'); // table metadata ``` **Method signatures:** - `read(schema, entity, id?: number|string|string[], options?): Promise>` - `create(schema, entity, data: any|any[], options?): Promise>` - `update(schema, entity, data: any|any[], id?: number|string|string[], options?): Promise>` - `delete(schema, entity, id: number|string): Promise>` - `getMetadata(schema, entity): Promise>` ## HeaderSpecClient (Header-Based REST) ```typescript import { HeaderSpecClient, getHeaderSpecClient } from '@warkypublic/resolvespec-js'; const client = new HeaderSpecClient({ baseUrl: 'http://localhost:3000', token: 'your-token' }); // CRUD - HTTP methods: GET=read, POST=create, PUT=update, DELETE=delete await client.read('public', 'users', undefined, { columns: ['id', 'name'], limit: 50 }); await client.create('public', 'users', { name: 'New' }); await client.update('public', 'users', '42', { name: 'Updated' }); await client.delete('public', 'users', '42'); ``` **Method signatures:** - `read(schema, entity, id?: string, options?): Promise>` - `create(schema, entity, data, options?): Promise>` - `update(schema, entity, id: string, data, options?): Promise>` - `delete(schema, entity, id: string): Promise>` ### 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'; buildHeaders({ columns: ['id', 'name'], limit: 10 }); // => { 'X-Select-Fields': 'id,name', 'X-Limit': '10' } encodeHeaderValue('complex value'); // 'ZIP_...' (base64 encoded) decodeHeaderValue(encoded); // original string ``` ## WebSocketClient ```typescript import { WebSocketClient, getWebSocketClient } from '@warkypublic/resolvespec-js'; const ws = new WebSocketClient({ url: 'ws://localhost:8080/ws', reconnect: true, heartbeatInterval: 30000 }); await ws.connect(); // CRUD await ws.read('users', { schema: 'public', limit: 10, filters: [...], columns: [...] }); await ws.create('users', { name: 'New' }, { schema: 'public' }); await ws.update('users', '1', { name: 'Updated' }, { schema: 'public' }); await ws.delete('users', '1', { schema: 'public' }); await ws.meta('users', { schema: 'public' }); // Subscriptions const subId = await ws.subscribe('users', (notification) => { ... }, { schema: 'public', filters: [...] }); await ws.unsubscribe(subId); ws.getSubscriptions(); // Connection ws.getState(); // 'connecting' | 'connected' | 'disconnecting' | 'disconnected' | 'reconnecting' ws.isConnected(); ws.disconnect(); // Events ws.on('connect', () => {}); ws.on('disconnect', (event: CloseEvent) => {}); ws.on('error', (error: Error) => {}); ws.on('message', (message: WSMessage) => {}); ws.on('stateChange', (state: ConnectionState) => {}); ws.off('connect'); ``` ## Options (Query Parameters) ```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; } ``` ### FilterOption ```typescript 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 ``` ### SortOption ```typescript interface SortOption { column: string; direction: 'asc' | 'desc' | 'ASC' | 'DESC'; } ``` ### PreloadOption ```typescript interface PreloadOption { relation: string; table_name?: string; columns?: string[]; omit_columns?: string[]; sort?: SortOption[]; filters?: FilterOption[]; where?: string; limit?: number; offset?: number; updatable?: boolean; computed_ql?: Record; recursive?: boolean; primary_key?: string; related_key?: string; foreign_key?: string; recursive_child_key?: string; sql_joins?: string[]; join_aliases?: string[]; } ``` ### Other Types ```typescript interface ComputedColumn { name: string; expression: string; } interface CustomOperator { name: string; sql: string; } interface Parameter { name: string; value: string; sequence?: number; } ``` ## Response Types ```typescript interface APIResponse { success: boolean; data: T; metadata?: Metadata; error?: APIError; } interface APIError { code: string; message: string; details?: any; detail?: string; } interface Metadata { total: number; count: number; filtered: number; limit: number; offset: number; row_number?: number; } interface TableMetadata { schema: string; table: string; columns: Column[]; relations: string[]; } interface Column { name: string; type: string; is_nullable: boolean; is_primary: boolean; is_unique: boolean; has_index: boolean; } ``` ## WebSocket Message Types ```typescript type MessageType = 'request' | 'response' | 'notification' | 'subscription' | 'error' | 'ping' | 'pong'; type WSOperation = 'read' | 'create' | 'update' | 'delete' | 'subscribe' | 'unsubscribe' | 'meta'; interface WSMessage { id?: string; type: MessageType; operation?: WSOperation; schema?: string; entity?: string; record_id?: string; data?: any; options?: WSOptions; subscription_id?: string; success?: boolean; error?: WSErrorInfo; metadata?: Record; timestamp?: string; } interface WSNotificationMessage { type: 'notification'; operation: WSOperation; subscription_id: string; schema?: string; entity: string; data: any; timestamp: string; } ``` ## Dependencies - Runtime: `uuid` - Peer: none - Node >= 18