@ekodb/ekodb-client 0.18.2 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -269,7 +269,6 @@ const joinResults = await client.find("users", multiQuery);
269
269
  - `.contains(field, value)` - String contains
270
270
  - `.startsWith(field, value)` - String starts with
271
271
  - `.endsWith(field, value)` - String ends with
272
- - `.regex(field, pattern)` - Regex match
273
272
  - `.sortAsc(field)` / `.sortDesc(field)` - Sorting
274
273
  - `.limit(n)` / `.skip(n)` - Pagination
275
274
  - `.join(joinConfig)` - Add join configuration
package/dist/client.d.ts CHANGED
@@ -258,6 +258,22 @@ export interface ChatModels {
258
258
  anthropic: string[];
259
259
  perplexity: string[];
260
260
  }
261
+ /**
262
+ * Request to compact a chat session's history on demand.
263
+ */
264
+ export interface CompactChatRequest {
265
+ keep_recent?: number;
266
+ }
267
+ /**
268
+ * Result of an on-demand chat history compaction.
269
+ */
270
+ export interface CompactChatResponse {
271
+ folded: number;
272
+ kept_recent: number;
273
+ summary_chars: number;
274
+ summary_message_id: string | null;
275
+ already_compact: boolean;
276
+ }
261
277
  /**
262
278
  * Request to generate embeddings
263
279
  */
@@ -358,6 +374,20 @@ export declare class EkoDBClient {
358
374
  * Sleep for a specified number of seconds
359
375
  */
360
376
  private sleep;
377
+ /**
378
+ * Parse a `Retry-After` header into a non-negative delay in seconds.
379
+ *
380
+ * Per RFC 9110 the value is either delay-seconds (an integer) or an
381
+ * HTTP-date. Anything that doesn't resolve to a finite, non-negative number
382
+ * (missing header, garbage, a past date) falls back to `defaultSecs`.
383
+ */
384
+ private parseRetryAfter;
385
+ /**
386
+ * Backoff delay (in seconds) for a 0-indexed retry attempt: a capped
387
+ * exponential schedule (0.2s → 5s) with full jitter, so concurrent clients
388
+ * don't retry in lockstep. Returns a value in [d/2, d].
389
+ */
390
+ private backoffSeconds;
361
391
  /**
362
392
  * Helper to determine if a path should use JSON
363
393
  * Only CRUD operations (insert/update/delete/batch) use MessagePack
@@ -866,6 +896,17 @@ export declare class EkoDBClient {
866
896
  * Toggle the "forgotten" status of a message
867
897
  */
868
898
  toggleForgottenMessage(sessionId: string, messageId: string, forgotten: boolean): Promise<void>;
899
+ /**
900
+ * Compact a chat session's history on demand.
901
+ *
902
+ * Folds older messages into a summary while preserving the most recent
903
+ * messages verbatim, reducing context size for long-running sessions.
904
+ *
905
+ * @param chatId - Chat session ID
906
+ * @param keepRecent - Number of recent messages to preserve verbatim (optional)
907
+ * @returns Compaction result with counts and the summary message ID
908
+ */
909
+ compactChat(chatId: string, keepRecent?: number): Promise<CompactChatResponse>;
869
910
  /**
870
911
  * Merge multiple chat sessions into one
871
912
  */
@@ -1063,9 +1104,17 @@ export declare class EkoDBClient {
1063
1104
  filterValue?: string;
1064
1105
  }): EventStream<MutationNotification>;
1065
1106
  /**
1066
- * Create a WebSocket client
1107
+ * Create a WebSocket client.
1108
+ *
1109
+ * The token is supplied as a provider bound to this client's
1110
+ * {@link getToken}, so every (re)connect re-evaluates (and proactively
1111
+ * refreshes) the auth token instead of snapshotting it once. This means a
1112
+ * reconnect after a token rotation uses the current token.
1113
+ *
1114
+ * @param wsURL - The WebSocket URL (e.g. `wss://host`); `/api/ws` is appended if absent.
1115
+ * @param options - Optional reconnect/timeout tunables.
1067
1116
  */
1068
- websocket(wsURL: string): WebSocketClient;
1117
+ websocket(wsURL: string, options?: WebSocketClientOptions): WebSocketClient;
1069
1118
  /**
1070
1119
  * Generate embeddings for a single text
1071
1120
  *
@@ -1209,6 +1258,35 @@ export interface SubscribeOptions {
1209
1258
  filterField?: string;
1210
1259
  filterValue?: string;
1211
1260
  }
1261
+ /**
1262
+ * A token provider: either a static token string, or a (possibly async)
1263
+ * function that returns a fresh token. When a function is supplied it is
1264
+ * re-invoked on every (re)connect, so a rotated/refreshed token is always
1265
+ * used for the new socket instead of a stale snapshot captured once.
1266
+ */
1267
+ export type TokenProvider = string | (() => string | null | Promise<string | null>);
1268
+ /** Tunables for the WebSocket client's reconnect + request-timeout behavior. */
1269
+ export interface WebSocketClientOptions {
1270
+ /**
1271
+ * Auto-reconnect after an unexpected socket close/error (not an explicit
1272
+ * `close()`/unsubscribe). Defaults to true.
1273
+ */
1274
+ autoReconnect?: boolean;
1275
+ /** Initial backoff delay in ms before the first reconnect attempt. Default 200. */
1276
+ reconnectInitialDelayMs?: number;
1277
+ /** Maximum backoff delay in ms (the cap for exponential growth). Default 5000. */
1278
+ reconnectMaxDelayMs?: number;
1279
+ /**
1280
+ * Maximum number of consecutive reconnect attempts before giving up.
1281
+ * 0 or undefined means unlimited. Default unlimited.
1282
+ */
1283
+ reconnectMaxAttempts?: number;
1284
+ /**
1285
+ * Per-request timeout in ms for request/response WS calls. If no response
1286
+ * arrives in this window the pending promise rejects. Default 30000.
1287
+ */
1288
+ requestTimeoutMs?: number;
1289
+ }
1212
1290
  /** EventEmitter-like interface for subscriptions and chat streams. */
1213
1291
  export declare class EventStream<_T = unknown> {
1214
1292
  private listeners;
@@ -1253,24 +1331,67 @@ export declare class SchemaCache {
1253
1331
  export declare function extractRecordId(record: Record, extraCandidates?: string[]): string | undefined;
1254
1332
  export declare class WebSocketClient {
1255
1333
  private wsURL;
1256
- private token;
1334
+ private tokenProvider;
1257
1335
  private ws;
1258
1336
  private dispatcherRunning;
1259
1337
  private schemaCache;
1338
+ private autoReconnect;
1339
+ private reconnectInitialDelayMs;
1340
+ private reconnectMaxDelayMs;
1341
+ private reconnectMaxAttempts;
1342
+ private requestTimeoutMs;
1343
+ /** Set while close() is in progress so the close handler doesn't reconnect. */
1344
+ private closed;
1345
+ private reconnectAttempts;
1346
+ private reconnecting;
1347
+ private connectPromise;
1260
1348
  private pendingRequests;
1261
1349
  private subscriptions;
1350
+ /** Bookkeeping so subscriptions can be replayed on reconnect. */
1351
+ private subscriptionParams;
1262
1352
  private chatStreams;
1263
1353
  private registerToolsAck;
1264
- constructor(wsURL: string, token: string);
1354
+ /**
1355
+ * @param wsURL - WebSocket URL; `/api/ws` is appended if absent.
1356
+ * @param token - A static token string OR a {@link TokenProvider} function
1357
+ * re-evaluated on every (re)connect (so a refreshed token is used after a drop).
1358
+ * @param options - Optional reconnect/timeout tunables.
1359
+ */
1360
+ constructor(wsURL: string, token: TokenProvider, options?: WebSocketClientOptions);
1265
1361
  private messageCounter;
1266
1362
  private genMessageId;
1267
1363
  /**
1268
- * Connect and start the dispatcher.
1364
+ * Compute the capped exponential backoff (with jitter) for a reconnect
1365
+ * attempt. attempt 0 -> ~initial, growing x2 each time up to the max cap.
1366
+ * Jitter is +/-25% to avoid thundering-herd reconnect storms.
1367
+ * @internal exposed for testing
1368
+ */
1369
+ computeBackoff(attempt: number): number;
1370
+ /**
1371
+ * Connect and start the dispatcher. Re-evaluates the token provider so the
1372
+ * current/refreshed token is used for this socket.
1269
1373
  */
1270
1374
  private ensureConnected;
1375
+ private openSocket;
1271
1376
  private spawnDispatcher;
1377
+ /**
1378
+ * Reject in-flight requests and tear down the dead socket. If the close was
1379
+ * unexpected (not an explicit `close()`) and auto-reconnect is enabled,
1380
+ * schedule a reconnect that re-sends the active subscriptions.
1381
+ */
1382
+ private handleDisconnect;
1383
+ /**
1384
+ * Reconnect with capped exponential backoff + jitter, then re-send the
1385
+ * subscribe messages for every active subscription so the SAME EventStream
1386
+ * keeps delivering mutations after a transient drop.
1387
+ */
1388
+ private scheduleReconnect;
1389
+ /** Re-send Subscribe frames for every tracked subscription after a reconnect. */
1390
+ private resubscribeAll;
1272
1391
  private routeMessage;
1273
1392
  private sendRequest;
1393
+ /** Resolve/reject a pending request, clearing its timeout timer. */
1394
+ private settlePending;
1274
1395
  /**
1275
1396
  * Find all records in a collection via WebSocket.
1276
1397
  */
@@ -1280,6 +1401,11 @@ export declare class WebSocketClient {
1280
1401
  * Returns an EventStream that emits "mutation" events.
1281
1402
  */
1282
1403
  subscribe(collection: string, options?: SubscribeOptions): Promise<EventStream<MutationNotification>>;
1404
+ /**
1405
+ * Unsubscribe from a collection's mutation notifications. This is an
1406
+ * intentional teardown, so the subscription is NOT replayed on reconnect.
1407
+ */
1408
+ unsubscribe(collection: string): void;
1283
1409
  /**
1284
1410
  * Send a chat message and receive a streaming response.
1285
1411
  * Returns an EventStream that emits "event" with ChatStreamEvent objects.
@@ -1342,6 +1468,10 @@ export declare class WebSocketClient {
1342
1468
  deleteCollection(name: string): Promise<void>;
1343
1469
  /**
1344
1470
  * Close the WebSocket connection.
1471
+ *
1472
+ * This is an INTENTIONAL close: it disables auto-reconnect, rejects any
1473
+ * in-flight requests, and tears down all subscriptions/chat streams so
1474
+ * nothing is replayed afterward.
1345
1475
  */
1346
1476
  close(): void;
1347
1477
  }