refactor(types): reorganize SearchCondition and AdvancedSearchState interfaces refactor(filterPresets): streamline useFilterPresets hook and localStorage handling refactor(filtering): clean up ColumnFilterButton and ColumnFilterPopover components refactor(loading): separate GriddyLoadingOverlay from GriddyLoadingSkeleton refactor(searchHistory): enhance useSearchHistory hook with persistence refactor(index): update exports for adapters and core components refactor(rendering): improve EditableCell and TableCell components for clarity refactor(rendering): enhance TableHeader and VirtualBody components for better readability
264 lines
8.1 KiB
Markdown
264 lines
8.1 KiB
Markdown
# @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<T>(schema, entity, id?: number|string|string[], options?): Promise<APIResponse<T>>`
|
|
- `create<T>(schema, entity, data: any|any[], options?): Promise<APIResponse<T>>`
|
|
- `update<T>(schema, entity, data: any|any[], id?: number|string|string[], options?): Promise<APIResponse<T>>`
|
|
- `delete(schema, entity, id: number|string): Promise<APIResponse<void>>`
|
|
- `getMetadata(schema, entity): Promise<APIResponse<TableMetadata>>`
|
|
|
|
## 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<T>(schema, entity, id?: string, options?): Promise<APIResponse<T>>`
|
|
- `create<T>(schema, entity, data, options?): Promise<APIResponse<T>>`
|
|
- `update<T>(schema, entity, id: string, data, options?): Promise<APIResponse<T>>`
|
|
- `delete(schema, entity, id: string): Promise<APIResponse<void>>`
|
|
|
|
### 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<string, string>;
|
|
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<T = any> {
|
|
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<string, any>; 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
|