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
8.1 KiB
8.1 KiB
@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
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)
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)
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
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
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)
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
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
interface SortOption {
column: string;
direction: 'asc' | 'desc' | 'ASC' | 'DESC';
}
PreloadOption
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
interface ComputedColumn { name: string; expression: string; }
interface CustomOperator { name: string; sql: string; }
interface Parameter { name: string; value: string; sequence?: number; }
Response Types
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
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