@ekodb/ekodb-client 0.2.1 → 0.3.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/dist/client.d.ts CHANGED
@@ -7,6 +7,15 @@ import { Schema, SchemaBuilder, CollectionMetadata } from "./schema";
7
7
  export interface Record {
8
8
  [key: string]: any;
9
9
  }
10
+ /**
11
+ * Serialization format for client-server communication
12
+ */
13
+ export declare enum SerializationFormat {
14
+ /** JSON format (default, human-readable) */
15
+ Json = "Json",
16
+ /** MessagePack format (binary, 2-3x faster) */
17
+ MessagePack = "MessagePack"
18
+ }
10
19
  /**
11
20
  * Rate limit information from the server
12
21
  */
@@ -32,6 +41,8 @@ export interface ClientConfig {
32
41
  maxRetries?: number;
33
42
  /** Request timeout in milliseconds (default: 30000) */
34
43
  timeout?: number;
44
+ /** Serialization format (default: MessagePack for best performance, use Json for debugging) */
45
+ format?: SerializationFormat;
35
46
  }
36
47
  /**
37
48
  * Rate limit error
@@ -155,6 +166,7 @@ export declare class EkoDBClient {
155
166
  private shouldRetry;
156
167
  private maxRetries;
157
168
  private timeout;
169
+ private format;
158
170
  private rateLimitInfo;
159
171
  constructor(config: string | ClientConfig, apiKey?: string);
160
172
  /**
@@ -181,6 +193,12 @@ export declare class EkoDBClient {
181
193
  * Sleep for a specified number of seconds
182
194
  */
183
195
  private sleep;
196
+ /**
197
+ * Helper to determine if a path should use JSON
198
+ * Only CRUD operations (insert/update/delete/batch) use MessagePack
199
+ * Everything else uses JSON for compatibility
200
+ */
201
+ private shouldUseJSON;
184
202
  /**
185
203
  * Make an HTTP request to the ekoDB API with retry logic
186
204
  */
@@ -227,18 +245,19 @@ export declare class EkoDBClient {
227
245
  /**
228
246
  * Batch insert multiple documents
229
247
  */
230
- batchInsert(collection: string, records: Record[]): Promise<Record[]>;
248
+ batchInsert(collection: string, records: Record[], bypassRipple?: boolean): Promise<BatchOperationResult>;
231
249
  /**
232
250
  * Batch update multiple documents
233
251
  */
234
252
  batchUpdate(collection: string, updates: Array<{
235
253
  id: string;
236
254
  data: Record;
237
- }>): Promise<Record[]>;
255
+ bypassRipple?: boolean;
256
+ }>): Promise<BatchOperationResult>;
238
257
  /**
239
258
  * Batch delete multiple documents
240
259
  */
241
- batchDelete(collection: string, ids: string[]): Promise<number>;
260
+ batchDelete(collection: string, ids: string[], bypassRipple?: boolean): Promise<BatchOperationResult>;
242
261
  /**
243
262
  * Set a key-value pair
244
263
  */
package/dist/client.js CHANGED
@@ -36,10 +36,21 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  };
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.WebSocketClient = exports.EkoDBClient = exports.MergeStrategy = exports.RateLimitError = void 0;
39
+ exports.WebSocketClient = exports.EkoDBClient = exports.MergeStrategy = exports.RateLimitError = exports.SerializationFormat = void 0;
40
+ const msgpack_1 = require("@msgpack/msgpack");
40
41
  const query_builder_1 = require("./query-builder");
41
42
  const search_1 = require("./search");
42
43
  const schema_1 = require("./schema");
44
+ /**
45
+ * Serialization format for client-server communication
46
+ */
47
+ var SerializationFormat;
48
+ (function (SerializationFormat) {
49
+ /** JSON format (default, human-readable) */
50
+ SerializationFormat["Json"] = "Json";
51
+ /** MessagePack format (binary, 2-3x faster) */
52
+ SerializationFormat["MessagePack"] = "MessagePack";
53
+ })(SerializationFormat || (exports.SerializationFormat = SerializationFormat = {}));
43
54
  /**
44
55
  * Rate limit error
45
56
  */
@@ -68,6 +79,7 @@ class EkoDBClient {
68
79
  this.shouldRetry = true;
69
80
  this.maxRetries = 3;
70
81
  this.timeout = 30000;
82
+ this.format = SerializationFormat.MessagePack; // Default to MessagePack for 2-3x performance
71
83
  }
72
84
  else {
73
85
  this.baseURL = config.baseURL;
@@ -75,6 +87,7 @@ class EkoDBClient {
75
87
  this.shouldRetry = config.shouldRetry ?? true;
76
88
  this.maxRetries = config.maxRetries ?? 3;
77
89
  this.timeout = config.timeout ?? 30000;
90
+ this.format = config.format ?? SerializationFormat.MessagePack; // Default to MessagePack for 2-3x performance
78
91
  }
79
92
  }
80
93
  /**
@@ -138,29 +151,72 @@ class EkoDBClient {
138
151
  sleep(seconds) {
139
152
  return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
140
153
  }
154
+ /**
155
+ * Helper to determine if a path should use JSON
156
+ * Only CRUD operations (insert/update/delete/batch) use MessagePack
157
+ * Everything else uses JSON for compatibility
158
+ */
159
+ shouldUseJSON(path) {
160
+ // ONLY these operations support MessagePack
161
+ const msgpackPaths = [
162
+ "/api/insert/",
163
+ "/api/batch_insert/",
164
+ "/api/update/",
165
+ "/api/batch_update/",
166
+ "/api/delete/",
167
+ "/api/batch_delete/",
168
+ ];
169
+ // Check if path starts with any MessagePack-supported operation
170
+ for (const prefix of msgpackPaths) {
171
+ if (path.startsWith(prefix)) {
172
+ return false; // Use MessagePack
173
+ }
174
+ }
175
+ // Everything else uses JSON
176
+ return true;
177
+ }
141
178
  /**
142
179
  * Make an HTTP request to the ekoDB API with retry logic
143
180
  */
144
- async makeRequest(method, path, data, attempt = 0) {
181
+ async makeRequest(method, path, data, attempt = 0, forceJson = false) {
145
182
  if (!this.token) {
146
183
  await this.refreshToken();
147
184
  }
185
+ // Determine content type and serialization based on path
186
+ // Only CRUD operations support MessagePack, everything else uses JSON
187
+ const shouldForceJson = forceJson || this.shouldUseJSON(path);
188
+ const isMessagePack = !shouldForceJson && this.format === SerializationFormat.MessagePack;
189
+ const contentType = isMessagePack
190
+ ? "application/msgpack"
191
+ : "application/json";
192
+ // Note: Modern fetch API automatically handles gzip/deflate decompression
193
+ // when server sends Content-Encoding header. No additional configuration needed.
148
194
  const options = {
149
195
  method,
150
196
  headers: {
151
197
  Authorization: `Bearer ${this.token}`,
152
- "Content-Type": "application/json",
198
+ "Content-Type": contentType,
199
+ Accept: contentType,
153
200
  },
154
201
  };
155
202
  if (data) {
156
- options.body = JSON.stringify(data);
203
+ options.body = isMessagePack
204
+ ? (0, msgpack_1.encode)(data)
205
+ : JSON.stringify(data);
157
206
  }
158
207
  try {
159
208
  const response = await fetch(`${this.baseURL}${path}`, options);
160
209
  // Extract rate limit info from successful responses
161
210
  if (response.ok) {
162
211
  this.extractRateLimitInfo(response);
163
- return response.json();
212
+ // Deserialize based on format
213
+ if (isMessagePack) {
214
+ const buffer = await response.arrayBuffer();
215
+ return (0, msgpack_1.decode)(new Uint8Array(buffer));
216
+ }
217
+ else {
218
+ return response.json();
219
+ }
164
220
  }
165
221
  // Handle rate limiting (429)
166
222
  if (response.status === 429) {
@@ -168,7 +224,7 @@ class EkoDBClient {
168
224
  if (this.shouldRetry && attempt < this.maxRetries) {
169
225
  console.log(`Rate limited. Retrying after ${retryAfter} seconds...`);
170
226
  await this.sleep(retryAfter);
171
- return this.makeRequest(method, path, data, attempt + 1);
227
+ return this.makeRequest(method, path, data, attempt + 1, forceJson);
172
228
  }
173
229
  throw new RateLimitError(retryAfter);
174
230
  }
@@ -179,7 +235,7 @@ class EkoDBClient {
179
235
  const retryDelay = 10;
180
236
  console.log(`Service unavailable. Retrying after ${retryDelay} seconds...`);
181
237
  await this.sleep(retryDelay);
182
- return this.makeRequest(method, path, data, attempt + 1);
238
+ return this.makeRequest(method, path, data, attempt + 1, forceJson);
183
239
  }
184
240
  // Handle other errors
185
241
  const text = await response.text();
@@ -193,7 +249,7 @@ class EkoDBClient {
193
249
  const retryDelay = 3;
194
250
  console.log(`Network error. Retrying after ${retryDelay} seconds...`);
195
251
  await this.sleep(retryDelay);
196
- return this.makeRequest(method, path, data, attempt + 1);
252
+ return this.makeRequest(method, path, data, attempt + 1, forceJson);
197
253
  }
198
254
  throw error;
199
255
  }
@@ -255,57 +311,65 @@ class EkoDBClient {
255
311
  /**
256
312
  * Batch insert multiple documents
257
313
  */
258
- async batchInsert(collection, records) {
259
- const inserts = records.map((data) => ({ data }));
260
- const result = await this.makeRequest("POST", `/api/batch/insert/${collection}`, { inserts });
261
- return result.successful.map((id) => ({ id }));
314
+ async batchInsert(collection, records, bypassRipple) {
315
+ const inserts = records.map((data) => ({
316
+ data,
317
+ bypass_ripple: bypassRipple,
318
+ }));
319
+ return this.makeRequest("POST", `/api/batch/insert/${collection}`, { inserts });
262
320
  }
263
321
  /**
264
322
  * Batch update multiple documents
265
323
  */
266
324
  async batchUpdate(collection, updates) {
267
- const result = await this.makeRequest("PUT", `/api/batch/update/${collection}`, { updates });
268
- return result.successful.map((id) => ({ id }));
325
+ const formattedUpdates = updates.map((u) => ({
326
+ id: u.id,
327
+ data: u.data,
328
+ bypass_ripple: u.bypassRipple,
329
+ }));
330
+ return this.makeRequest("PUT", `/api/batch/update/${collection}`, { updates: formattedUpdates });
269
331
  }
270
332
  /**
271
333
  * Batch delete multiple documents
272
334
  */
273
- async batchDelete(collection, ids) {
274
- const deletes = ids.map((id) => ({ id }));
275
- const result = await this.makeRequest("DELETE", `/api/batch/delete/${collection}`, { deletes });
276
- return result.successful.length;
335
+ async batchDelete(collection, ids, bypassRipple) {
336
+ const deletes = ids.map((id) => ({
337
+ id: id,
338
+ bypass_ripple: bypassRipple,
339
+ }));
340
+ return this.makeRequest("DELETE", `/api/batch/delete/${collection}`, { deletes });
277
341
  }
278
342
  /**
279
343
  * Set a key-value pair
280
344
  */
281
345
  async kvSet(key, value) {
282
- await this.makeRequest("POST", `/api/kv/set/${encodeURIComponent(key)}`, { value });
346
+ await this.makeRequest("POST", `/api/kv/set/${encodeURIComponent(key)}`, { value }, 0, true);
283
347
  }
284
348
  /**
285
349
  * Get a value by key
286
350
  */
287
351
  async kvGet(key) {
288
- const result = await this.makeRequest("GET", `/api/kv/get/${encodeURIComponent(key)}`);
352
+ const result = await this.makeRequest("GET", `/api/kv/get/${encodeURIComponent(key)}`, undefined, 0, true);
289
353
  return result.value;
290
354
  }
291
355
  /**
292
356
  * Delete a key
293
357
  */
294
358
  async kvDelete(key) {
295
- await this.makeRequest("DELETE", `/api/kv/delete/${encodeURIComponent(key)}`);
359
+ await this.makeRequest("DELETE", `/api/kv/delete/${encodeURIComponent(key)}`, undefined, 0, true);
296
360
  }
297
361
  /**
298
362
  * List all collections
299
363
  */
300
364
  async listCollections() {
301
- const result = await this.makeRequest("GET", "/api/collections");
365
+ const result = await this.makeRequest("GET", "/api/collections", undefined, 0, true);
302
366
  return result.collections;
303
367
  }
304
368
  /**
305
369
  * Delete a collection
306
370
  */
307
371
  async deleteCollection(collection) {
308
- await this.makeRequest("DELETE", `/api/collections/${collection}`);
372
+ await this.makeRequest("DELETE", `/api/collections/${collection}`, undefined, 0, true);
309
373
  }
310
374
  /**
311
375
  * Create a collection with schema
@@ -325,7 +389,7 @@ class EkoDBClient {
325
389
  */
326
390
  async createCollection(collection, schema) {
327
391
  const schemaObj = schema instanceof schema_1.SchemaBuilder ? schema.build() : schema;
328
- await this.makeRequest("POST", `/api/collections/${collection}`, schemaObj);
392
+ await this.makeRequest("POST", `/api/collections/${collection}`, schemaObj, 0, true);
329
393
  }
330
394
  /**
331
395
  * Get collection metadata and schema
@@ -334,7 +398,7 @@ class EkoDBClient {
334
398
  * @returns Collection metadata including schema and analytics
335
399
  */
336
400
  async getCollection(collection) {
337
- return this.makeRequest("GET", `/api/collections/${collection}`);
401
+ return this.makeRequest("GET", `/api/collections/${collection}`, undefined, 0, true);
338
402
  }
339
403
  /**
340
404
  * Get collection schema
@@ -383,26 +447,26 @@ class EkoDBClient {
383
447
  const queryObj = searchQuery instanceof search_1.SearchQueryBuilder
384
448
  ? searchQuery.build()
385
449
  : searchQuery;
386
- return this.makeRequest("POST", `/api/search/${collection}`, queryObj);
450
+ return this.makeRequest("POST", `/api/search/${collection}`, queryObj, 0, true);
387
451
  }
388
452
  // ========== Chat Methods ==========
389
453
  /**
390
454
  * Create a new chat session
391
455
  */
392
456
  async createChatSession(request) {
393
- return this.makeRequest("POST", "/api/chat", request);
457
+ return this.makeRequest("POST", "/api/chat", request, 0, true);
394
458
  }
395
459
  /**
396
460
  * Send a message in an existing chat session
397
461
  */
398
462
  async chatMessage(sessionId, request) {
399
- return this.makeRequest("POST", `/api/chat/${sessionId}/messages`, request);
463
+ return this.makeRequest("POST", `/api/chat/${sessionId}/messages`, request, 0, true);
400
464
  }
401
465
  /**
402
466
  * Get a chat session by ID
403
467
  */
404
468
  async getChatSession(sessionId) {
405
- return this.makeRequest("GET", `/api/chat/${sessionId}`);
469
+ return this.makeRequest("GET", `/api/chat/${sessionId}`, undefined, 0, true);
406
470
  }
407
471
  /**
408
472
  * List all chat sessions
@@ -417,7 +481,7 @@ class EkoDBClient {
417
481
  params.append("sort", query.sort);
418
482
  const queryString = params.toString();
419
483
  const path = queryString ? `/api/chat?${queryString}` : "/api/chat";
420
- return this.makeRequest("GET", path);
484
+ return this.makeRequest("GET", path, undefined, 0, true);
421
485
  }
422
486
  /**
423
487
  * Get messages from a chat session
@@ -434,55 +498,55 @@ class EkoDBClient {
434
498
  const path = queryString
435
499
  ? `/api/chat/${sessionId}/messages?${queryString}`
436
500
  : `/api/chat/${sessionId}/messages`;
437
- return this.makeRequest("GET", path);
501
+ return this.makeRequest("GET", path, undefined, 0, true);
438
502
  }
439
503
  /**
440
504
  * Update a chat session
441
505
  */
442
506
  async updateChatSession(sessionId, request) {
443
- return this.makeRequest("PUT", `/api/chat/${sessionId}`, request);
507
+ return this.makeRequest("PUT", `/api/chat/${sessionId}`, request, 0, true);
444
508
  }
445
509
  /**
446
510
  * Branch a chat session
447
511
  */
448
512
  async branchChatSession(request) {
449
- return this.makeRequest("POST", "/api/chat/branch", request);
513
+ return this.makeRequest("POST", "/api/chat/branch", request, 0, true);
450
514
  }
451
515
  /**
452
516
  * Delete a chat session
453
517
  */
454
518
  async deleteChatSession(sessionId) {
455
- await this.makeRequest("DELETE", `/api/chat/${sessionId}`);
519
+ await this.makeRequest("DELETE", `/api/chat/${sessionId}`, undefined, 0, true);
456
520
  }
457
521
  /**
458
522
  * Regenerate an AI response message
459
523
  */
460
524
  async regenerateMessage(sessionId, messageId) {
461
- return this.makeRequest("POST", `/api/chat/${sessionId}/messages/${messageId}/regenerate`);
525
+ return this.makeRequest("POST", `/api/chat/${sessionId}/messages/${messageId}/regenerate`, undefined, 0, true);
462
526
  }
463
527
  /**
464
528
  * Update a specific message
465
529
  */
466
530
  async updateChatMessage(sessionId, messageId, content) {
467
- await this.makeRequest("PUT", `/api/chat/${sessionId}/messages/${messageId}`, { content });
531
+ await this.makeRequest("PUT", `/api/chat/${sessionId}/messages/${messageId}`, { content }, 0, true);
468
532
  }
469
533
  /**
470
534
  * Delete a specific message
471
535
  */
472
536
  async deleteChatMessage(sessionId, messageId) {
473
- await this.makeRequest("DELETE", `/api/chat/${sessionId}/messages/${messageId}`);
537
+ await this.makeRequest("DELETE", `/api/chat/${sessionId}/messages/${messageId}`, undefined, 0, true);
474
538
  }
475
539
  /**
476
540
  * Toggle the "forgotten" status of a message
477
541
  */
478
542
  async toggleForgottenMessage(sessionId, messageId, forgotten) {
479
- await this.makeRequest("PATCH", `/api/chat/${sessionId}/messages/${messageId}/forgotten`, { forgotten });
543
+ await this.makeRequest("PATCH", `/api/chat/${sessionId}/messages/${messageId}/forgotten`, { forgotten }, 0, true);
480
544
  }
481
545
  /**
482
546
  * Merge multiple chat sessions into one
483
547
  */
484
548
  async mergeChatSessions(request) {
485
- return this.makeRequest("POST", "/api/chat/merge", request);
549
+ return this.makeRequest("POST", "/api/chat/merge", request, 0, true);
486
550
  }
487
551
  /**
488
552
  * Create a WebSocket client
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { EkoDBClient, WebSocketClient, MergeStrategy, RateLimitError, } from "./client";
1
+ export { EkoDBClient, WebSocketClient, SerializationFormat, MergeStrategy, RateLimitError, } from "./client";
2
2
  export { QueryBuilder, SortOrder } from "./query-builder";
3
3
  export { SearchQueryBuilder } from "./search";
4
4
  export { SchemaBuilder, FieldTypeSchemaBuilder, VectorIndexAlgorithm, DistanceMetric, } from "./schema";
package/dist/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.JoinBuilder = exports.DistanceMetric = exports.VectorIndexAlgorithm = exports.FieldTypeSchemaBuilder = exports.SchemaBuilder = exports.SearchQueryBuilder = exports.SortOrder = exports.QueryBuilder = exports.RateLimitError = exports.MergeStrategy = exports.WebSocketClient = exports.EkoDBClient = void 0;
3
+ exports.JoinBuilder = exports.DistanceMetric = exports.VectorIndexAlgorithm = exports.FieldTypeSchemaBuilder = exports.SchemaBuilder = exports.SearchQueryBuilder = exports.SortOrder = exports.QueryBuilder = exports.RateLimitError = exports.MergeStrategy = exports.SerializationFormat = exports.WebSocketClient = exports.EkoDBClient = void 0;
4
4
  var client_1 = require("./client");
5
5
  Object.defineProperty(exports, "EkoDBClient", { enumerable: true, get: function () { return client_1.EkoDBClient; } });
6
6
  Object.defineProperty(exports, "WebSocketClient", { enumerable: true, get: function () { return client_1.WebSocketClient; } });
7
+ Object.defineProperty(exports, "SerializationFormat", { enumerable: true, get: function () { return client_1.SerializationFormat; } });
7
8
  Object.defineProperty(exports, "MergeStrategy", { enumerable: true, get: function () { return client_1.MergeStrategy; } });
8
9
  Object.defineProperty(exports, "RateLimitError", { enumerable: true, get: function () { return client_1.RateLimitError; } });
9
10
  var query_builder_1 = require("./query-builder");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekodb/ekodb-client",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Official TypeScript/JavaScript client for ekoDB",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -22,6 +22,7 @@
22
22
  "typescript": "^5.3.0"
23
23
  },
24
24
  "dependencies": {
25
+ "@msgpack/msgpack": "^3.0.0",
25
26
  "ws": "^8.16.0"
26
27
  }
27
28
  }
package/src/client.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  * ekoDB TypeScript Client
3
3
  */
4
4
 
5
+ import { encode, decode } from "@msgpack/msgpack";
5
6
  import { QueryBuilder, Query as QueryBuilderQuery } from "./query-builder";
6
7
  import { SearchQuery, SearchQueryBuilder, SearchResponse } from "./search";
7
8
  import { Schema, SchemaBuilder, CollectionMetadata } from "./schema";
@@ -10,6 +11,16 @@ export interface Record {
10
11
  [key: string]: any;
11
12
  }
12
13
 
14
+ /**
15
+ * Serialization format for client-server communication
16
+ */
17
+ export enum SerializationFormat {
18
+ /** JSON format (default, human-readable) */
19
+ Json = "Json",
20
+ /** MessagePack format (binary, 2-3x faster) */
21
+ MessagePack = "MessagePack",
22
+ }
23
+
13
24
  /**
14
25
  * Rate limit information from the server
15
26
  */
@@ -36,6 +47,8 @@ export interface ClientConfig {
36
47
  maxRetries?: number;
37
48
  /** Request timeout in milliseconds (default: 30000) */
38
49
  timeout?: number;
50
+ /** Serialization format (default: MessagePack for best performance, use Json for debugging) */
51
+ format?: SerializationFormat;
39
52
  }
40
53
 
41
54
  /**
@@ -184,6 +197,7 @@ export class EkoDBClient {
184
197
  private shouldRetry: boolean;
185
198
  private maxRetries: number;
186
199
  private timeout: number;
200
+ private format: SerializationFormat;
187
201
  private rateLimitInfo: RateLimitInfo | null = null;
188
202
 
189
203
  constructor(config: string | ClientConfig, apiKey?: string) {
@@ -194,12 +208,14 @@ export class EkoDBClient {
194
208
  this.shouldRetry = true;
195
209
  this.maxRetries = 3;
196
210
  this.timeout = 30000;
211
+ this.format = SerializationFormat.MessagePack; // Default to MessagePack for 2-3x performance
197
212
  } else {
198
213
  this.baseURL = config.baseURL;
199
214
  this.apiKey = config.apiKey;
200
215
  this.shouldRetry = config.shouldRetry ?? true;
201
216
  this.maxRetries = config.maxRetries ?? 3;
202
217
  this.timeout = config.timeout ?? 30000;
218
+ this.format = config.format ?? SerializationFormat.MessagePack; // Default to MessagePack for 2-3x performance
203
219
  }
204
220
  }
205
221
 
@@ -275,6 +291,33 @@ export class EkoDBClient {
275
291
  return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
276
292
  }
277
293
 
294
+ /**
295
+ * Helper to determine if a path should use JSON
296
+ * Only CRUD operations (insert/update/delete/batch) use MessagePack
297
+ * Everything else uses JSON for compatibility
298
+ */
299
+ private shouldUseJSON(path: string): boolean {
300
+ // ONLY these operations support MessagePack
301
+ const msgpackPaths = [
302
+ "/api/insert/",
303
+ "/api/batch_insert/",
304
+ "/api/update/",
305
+ "/api/batch_update/",
306
+ "/api/delete/",
307
+ "/api/batch_delete/",
308
+ ];
309
+
310
+ // Check if path starts with any MessagePack-supported operation
311
+ for (const prefix of msgpackPaths) {
312
+ if (path.startsWith(prefix)) {
313
+ return false; // Use MessagePack
314
+ }
315
+ }
316
+
317
+ // Everything else uses JSON
318
+ return true;
319
+ }
320
+
278
321
  /**
279
322
  * Make an HTTP request to the ekoDB API with retry logic
280
323
  */
@@ -283,21 +326,36 @@ export class EkoDBClient {
283
326
  path: string,
284
327
  data?: any,
285
328
  attempt: number = 0,
329
+ forceJson: boolean = false,
286
330
  ): Promise<T> {
287
331
  if (!this.token) {
288
332
  await this.refreshToken();
289
333
  }
290
334
 
335
+ // Determine content type and serialization based on path
336
+ // Only CRUD operations support MessagePack, everything else uses JSON
337
+ const shouldForceJson = forceJson || this.shouldUseJSON(path);
338
+ const isMessagePack =
339
+ !shouldForceJson && this.format === SerializationFormat.MessagePack;
340
+ const contentType = isMessagePack
341
+ ? "application/msgpack"
342
+ : "application/json";
343
+
344
+ // Note: Modern fetch API automatically handles gzip/deflate decompression
345
+ // when server sends Content-Encoding header. No additional configuration needed.
291
346
  const options: RequestInit = {
292
347
  method,
293
348
  headers: {
294
349
  Authorization: `Bearer ${this.token}`,
295
- "Content-Type": "application/json",
350
+ "Content-Type": contentType,
351
+ Accept: contentType,
296
352
  },
297
353
  };
298
354
 
299
355
  if (data) {
300
- options.body = JSON.stringify(data);
356
+ options.body = isMessagePack
357
+ ? (encode(data) as any)
358
+ : JSON.stringify(data);
301
359
  }
302
360
 
303
361
  try {
@@ -306,7 +364,14 @@ export class EkoDBClient {
306
364
  // Extract rate limit info from successful responses
307
365
  if (response.ok) {
308
366
  this.extractRateLimitInfo(response);
309
- return response.json() as Promise<T>;
367
+
368
+ // Deserialize based on format
369
+ if (isMessagePack) {
370
+ const buffer = await response.arrayBuffer();
371
+ return decode(new Uint8Array(buffer)) as T;
372
+ } else {
373
+ return response.json() as Promise<T>;
374
+ }
310
375
  }
311
376
 
312
377
  // Handle rate limiting (429)
@@ -319,7 +384,13 @@ export class EkoDBClient {
319
384
  if (this.shouldRetry && attempt < this.maxRetries) {
320
385
  console.log(`Rate limited. Retrying after ${retryAfter} seconds...`);
321
386
  await this.sleep(retryAfter);
322
- return this.makeRequest<T>(method, path, data, attempt + 1);
387
+ return this.makeRequest<T>(
388
+ method,
389
+ path,
390
+ data,
391
+ attempt + 1,
392
+ forceJson,
393
+ );
323
394
  }
324
395
 
325
396
  throw new RateLimitError(retryAfter);
@@ -336,7 +407,7 @@ export class EkoDBClient {
336
407
  `Service unavailable. Retrying after ${retryDelay} seconds...`,
337
408
  );
338
409
  await this.sleep(retryDelay);
339
- return this.makeRequest<T>(method, path, data, attempt + 1);
410
+ return this.makeRequest<T>(method, path, data, attempt + 1, forceJson);
340
411
  }
341
412
 
342
413
  // Handle other errors
@@ -352,7 +423,7 @@ export class EkoDBClient {
352
423
  const retryDelay = 3;
353
424
  console.log(`Network error. Retrying after ${retryDelay} seconds...`);
354
425
  await this.sleep(retryDelay);
355
- return this.makeRequest<T>(method, path, data, attempt + 1);
426
+ return this.makeRequest<T>(method, path, data, attempt + 1, forceJson);
356
427
  }
357
428
 
358
429
  throw error;
@@ -440,14 +511,20 @@ export class EkoDBClient {
440
511
  /**
441
512
  * Batch insert multiple documents
442
513
  */
443
- async batchInsert(collection: string, records: Record[]): Promise<Record[]> {
444
- const inserts = records.map((data) => ({ data }));
445
- const result = await this.makeRequest<BatchOperationResult>(
514
+ async batchInsert(
515
+ collection: string,
516
+ records: Record[],
517
+ bypassRipple?: boolean,
518
+ ): Promise<BatchOperationResult> {
519
+ const inserts = records.map((data) => ({
520
+ data,
521
+ bypass_ripple: bypassRipple,
522
+ }));
523
+ return this.makeRequest<BatchOperationResult>(
446
524
  "POST",
447
525
  `/api/batch/insert/${collection}`,
448
526
  { inserts },
449
527
  );
450
- return result.successful.map((id) => ({ id }));
451
528
  }
452
529
 
453
530
  /**
@@ -455,27 +532,37 @@ export class EkoDBClient {
455
532
  */
456
533
  async batchUpdate(
457
534
  collection: string,
458
- updates: Array<{ id: string; data: Record }>,
459
- ): Promise<Record[]> {
460
- const result = await this.makeRequest<BatchOperationResult>(
535
+ updates: Array<{ id: string; data: Record; bypassRipple?: boolean }>,
536
+ ): Promise<BatchOperationResult> {
537
+ const formattedUpdates = updates.map((u) => ({
538
+ id: u.id,
539
+ data: u.data,
540
+ bypass_ripple: u.bypassRipple,
541
+ }));
542
+ return this.makeRequest<BatchOperationResult>(
461
543
  "PUT",
462
544
  `/api/batch/update/${collection}`,
463
- { updates },
545
+ { updates: formattedUpdates },
464
546
  );
465
- return result.successful.map((id) => ({ id }));
466
547
  }
467
548
 
468
549
  /**
469
550
  * Batch delete multiple documents
470
551
  */
471
- async batchDelete(collection: string, ids: string[]): Promise<number> {
472
- const deletes = ids.map((id) => ({ id }));
473
- const result = await this.makeRequest<BatchOperationResult>(
552
+ async batchDelete(
553
+ collection: string,
554
+ ids: string[],
555
+ bypassRipple?: boolean,
556
+ ): Promise<BatchOperationResult> {
557
+ const deletes = ids.map((id) => ({
558
+ id: id,
559
+ bypass_ripple: bypassRipple,
560
+ }));
561
+ return this.makeRequest<BatchOperationResult>(
474
562
  "DELETE",
475
563
  `/api/batch/delete/${collection}`,
476
564
  { deletes },
477
565
  );
478
- return result.successful.length;
479
566
  }
480
567
 
481
568
  /**
@@ -486,6 +573,8 @@ export class EkoDBClient {
486
573
  "POST",
487
574
  `/api/kv/set/${encodeURIComponent(key)}`,
488
575
  { value },
576
+ 0,
577
+ true, // Force JSON for KV operations
489
578
  );
490
579
  }
491
580
 
@@ -496,6 +585,9 @@ export class EkoDBClient {
496
585
  const result = await this.makeRequest<{ value: any }>(
497
586
  "GET",
498
587
  `/api/kv/get/${encodeURIComponent(key)}`,
588
+ undefined,
589
+ 0,
590
+ true, // Force JSON for KV operations
499
591
  );
500
592
  return result.value;
501
593
  }
@@ -507,6 +599,9 @@ export class EkoDBClient {
507
599
  await this.makeRequest<void>(
508
600
  "DELETE",
509
601
  `/api/kv/delete/${encodeURIComponent(key)}`,
602
+ undefined,
603
+ 0,
604
+ true, // Force JSON for KV operations
510
605
  );
511
606
  }
512
607
 
@@ -517,6 +612,9 @@ export class EkoDBClient {
517
612
  const result = await this.makeRequest<{ collections: string[] }>(
518
613
  "GET",
519
614
  "/api/collections",
615
+ undefined,
616
+ 0,
617
+ true, // Force JSON for metadata operations
520
618
  );
521
619
  return result.collections;
522
620
  }
@@ -525,7 +623,13 @@ export class EkoDBClient {
525
623
  * Delete a collection
526
624
  */
527
625
  async deleteCollection(collection: string): Promise<void> {
528
- await this.makeRequest<void>("DELETE", `/api/collections/${collection}`);
626
+ await this.makeRequest<void>(
627
+ "DELETE",
628
+ `/api/collections/${collection}`,
629
+ undefined,
630
+ 0,
631
+ true, // Force JSON for metadata operations
632
+ );
529
633
  }
530
634
 
531
635
  /**
@@ -553,6 +657,8 @@ export class EkoDBClient {
553
657
  "POST",
554
658
  `/api/collections/${collection}`,
555
659
  schemaObj,
660
+ 0,
661
+ true, // Force JSON for metadata operations
556
662
  );
557
663
  }
558
664
 
@@ -566,6 +672,9 @@ export class EkoDBClient {
566
672
  return this.makeRequest<CollectionMetadata>(
567
673
  "GET",
568
674
  `/api/collections/${collection}`,
675
+ undefined,
676
+ 0,
677
+ true, // Force JSON for metadata operations
569
678
  );
570
679
  }
571
680
 
@@ -625,6 +734,8 @@ export class EkoDBClient {
625
734
  "POST",
626
735
  `/api/search/${collection}`,
627
736
  queryObj,
737
+ 0,
738
+ true, // Force JSON for search operations
628
739
  );
629
740
  }
630
741
 
@@ -636,7 +747,13 @@ export class EkoDBClient {
636
747
  async createChatSession(
637
748
  request: CreateChatSessionRequest,
638
749
  ): Promise<ChatResponse> {
639
- return this.makeRequest<ChatResponse>("POST", "/api/chat", request);
750
+ return this.makeRequest<ChatResponse>(
751
+ "POST",
752
+ "/api/chat",
753
+ request,
754
+ 0,
755
+ true, // Force JSON for chat operations
756
+ );
640
757
  }
641
758
 
642
759
  /**
@@ -650,6 +767,8 @@ export class EkoDBClient {
650
767
  "POST",
651
768
  `/api/chat/${sessionId}/messages`,
652
769
  request,
770
+ 0,
771
+ true, // Force JSON for chat operations
653
772
  );
654
773
  }
655
774
 
@@ -660,6 +779,9 @@ export class EkoDBClient {
660
779
  return this.makeRequest<ChatSessionResponse>(
661
780
  "GET",
662
781
  `/api/chat/${sessionId}`,
782
+ undefined,
783
+ 0,
784
+ true, // Force JSON for chat operations
663
785
  );
664
786
  }
665
787
 
@@ -676,7 +798,13 @@ export class EkoDBClient {
676
798
 
677
799
  const queryString = params.toString();
678
800
  const path = queryString ? `/api/chat?${queryString}` : "/api/chat";
679
- return this.makeRequest<ListSessionsResponse>("GET", path);
801
+ return this.makeRequest<ListSessionsResponse>(
802
+ "GET",
803
+ path,
804
+ undefined,
805
+ 0,
806
+ true, // Force JSON for chat operations
807
+ );
680
808
  }
681
809
 
682
810
  /**
@@ -695,7 +823,13 @@ export class EkoDBClient {
695
823
  const path = queryString
696
824
  ? `/api/chat/${sessionId}/messages?${queryString}`
697
825
  : `/api/chat/${sessionId}/messages`;
698
- return this.makeRequest<GetMessagesResponse>("GET", path);
826
+ return this.makeRequest<GetMessagesResponse>(
827
+ "GET",
828
+ path,
829
+ undefined,
830
+ 0,
831
+ true, // Force JSON for chat operations
832
+ );
699
833
  }
700
834
 
701
835
  /**
@@ -709,6 +843,8 @@ export class EkoDBClient {
709
843
  "PUT",
710
844
  `/api/chat/${sessionId}`,
711
845
  request,
846
+ 0,
847
+ true, // Force JSON for chat operations
712
848
  );
713
849
  }
714
850
 
@@ -718,14 +854,26 @@ export class EkoDBClient {
718
854
  async branchChatSession(
719
855
  request: CreateChatSessionRequest,
720
856
  ): Promise<ChatResponse> {
721
- return this.makeRequest<ChatResponse>("POST", "/api/chat/branch", request);
857
+ return this.makeRequest<ChatResponse>(
858
+ "POST",
859
+ "/api/chat/branch",
860
+ request,
861
+ 0,
862
+ true, // Force JSON for chat operations
863
+ );
722
864
  }
723
865
 
724
866
  /**
725
867
  * Delete a chat session
726
868
  */
727
869
  async deleteChatSession(sessionId: string): Promise<void> {
728
- await this.makeRequest<void>("DELETE", `/api/chat/${sessionId}`);
870
+ await this.makeRequest<void>(
871
+ "DELETE",
872
+ `/api/chat/${sessionId}`,
873
+ undefined,
874
+ 0,
875
+ true, // Force JSON for chat operations
876
+ );
729
877
  }
730
878
 
731
879
  /**
@@ -738,6 +886,9 @@ export class EkoDBClient {
738
886
  return this.makeRequest<ChatResponse>(
739
887
  "POST",
740
888
  `/api/chat/${sessionId}/messages/${messageId}/regenerate`,
889
+ undefined,
890
+ 0,
891
+ true, // Force JSON for chat operations
741
892
  );
742
893
  }
743
894
 
@@ -753,6 +904,8 @@ export class EkoDBClient {
753
904
  "PUT",
754
905
  `/api/chat/${sessionId}/messages/${messageId}`,
755
906
  { content },
907
+ 0,
908
+ true, // Force JSON for chat operations
756
909
  );
757
910
  }
758
911
 
@@ -763,6 +916,9 @@ export class EkoDBClient {
763
916
  await this.makeRequest<void>(
764
917
  "DELETE",
765
918
  `/api/chat/${sessionId}/messages/${messageId}`,
919
+ undefined,
920
+ 0,
921
+ true, // Force JSON for chat operations
766
922
  );
767
923
  }
768
924
 
@@ -778,6 +934,8 @@ export class EkoDBClient {
778
934
  "PATCH",
779
935
  `/api/chat/${sessionId}/messages/${messageId}/forgotten`,
780
936
  { forgotten },
937
+ 0,
938
+ true, // Force JSON for chat operations
781
939
  );
782
940
  }
783
941
 
@@ -791,6 +949,8 @@ export class EkoDBClient {
791
949
  "POST",
792
950
  "/api/chat/merge",
793
951
  request,
952
+ 0,
953
+ true, // Force JSON for chat operations
794
954
  );
795
955
  }
796
956
 
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export {
2
2
  EkoDBClient,
3
3
  WebSocketClient,
4
+ SerializationFormat,
4
5
  MergeStrategy,
5
6
  RateLimitError,
6
7
  } from "./client";