@ekodb/ekodb-client 0.6.1 → 0.7.1

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
@@ -206,6 +206,9 @@ export declare class EkoDBClient {
206
206
  private makeRequest;
207
207
  /**
208
208
  * Insert a document into a collection
209
+ * @param collection - Collection name
210
+ * @param record - Document to insert
211
+ * @param ttl - Optional TTL: duration string ("1h", "30m"), seconds ("3600"), or ISO8601 timestamp
209
212
  */
210
213
  insert(collection: string, record: Record, ttl?: string): Promise<Record>;
211
214
  /**
@@ -234,7 +237,7 @@ export declare class EkoDBClient {
234
237
  /**
235
238
  * Find a document by ID
236
239
  */
237
- findByID(collection: string, id: string): Promise<Record>;
240
+ findById(collection: string, id: string): Promise<Record>;
238
241
  /**
239
242
  * Update a document
240
243
  */
@@ -260,9 +263,12 @@ export declare class EkoDBClient {
260
263
  */
261
264
  batchDelete(collection: string, ids: string[], bypassRipple?: boolean): Promise<BatchOperationResult>;
262
265
  /**
263
- * Set a key-value pair
266
+ * Set a key-value pair with optional TTL
267
+ * @param key - The key to set
268
+ * @param value - The value to store
269
+ * @param ttl - Optional TTL in seconds
264
270
  */
265
- kvSet(key: string, value: any): Promise<void>;
271
+ kvSet(key: string, value: any, ttl?: number): Promise<void>;
266
272
  /**
267
273
  * Get a value by key
268
274
  */
@@ -271,6 +277,53 @@ export declare class EkoDBClient {
271
277
  * Delete a key
272
278
  */
273
279
  kvDelete(key: string): Promise<void>;
280
+ /**
281
+ * Check if a key exists
282
+ * @param key - The key to check
283
+ * @returns true if the key exists, false otherwise
284
+ */
285
+ kvExists(key: string): Promise<boolean>;
286
+ /**
287
+ * Query/find KV entries with pattern matching
288
+ * @param options - Query options including pattern and include_expired
289
+ * @returns Array of matching records
290
+ */
291
+ kvFind(options?: {
292
+ pattern?: string;
293
+ include_expired?: boolean;
294
+ }): Promise<any[]>;
295
+ /**
296
+ * Alias for kvFind - query KV store with pattern
297
+ */
298
+ kvQuery(options?: {
299
+ pattern?: string;
300
+ include_expired?: boolean;
301
+ }): Promise<any[]>;
302
+ /**
303
+ * Begin a new transaction
304
+ * @param isolationLevel - Transaction isolation level (default: "ReadCommitted")
305
+ * @returns Transaction ID
306
+ */
307
+ beginTransaction(isolationLevel?: string): Promise<string>;
308
+ /**
309
+ * Get transaction status
310
+ * @param transactionId - The transaction ID
311
+ * @returns Transaction status object
312
+ */
313
+ getTransactionStatus(transactionId: string): Promise<{
314
+ state: string;
315
+ operations_count: number;
316
+ }>;
317
+ /**
318
+ * Commit a transaction
319
+ * @param transactionId - The transaction ID to commit
320
+ */
321
+ commitTransaction(transactionId: string): Promise<void>;
322
+ /**
323
+ * Rollback a transaction
324
+ * @param transactionId - The transaction ID to rollback
325
+ */
326
+ rollbackTransaction(transactionId: string): Promise<void>;
274
327
  /**
275
328
  * List all collections
276
329
  */
package/dist/client.js CHANGED
@@ -263,11 +263,14 @@ class EkoDBClient {
263
263
  }
264
264
  /**
265
265
  * Insert a document into a collection
266
+ * @param collection - Collection name
267
+ * @param record - Document to insert
268
+ * @param ttl - Optional TTL: duration string ("1h", "30m"), seconds ("3600"), or ISO8601 timestamp
266
269
  */
267
270
  async insert(collection, record, ttl) {
268
271
  const data = { ...record };
269
272
  if (ttl) {
270
- data.ttl_duration = ttl;
273
+ data.ttl = ttl;
271
274
  }
272
275
  return this.makeRequest("POST", `/api/insert/${collection}`, data);
273
276
  }
@@ -300,7 +303,7 @@ class EkoDBClient {
300
303
  /**
301
304
  * Find a document by ID
302
305
  */
303
- async findByID(collection, id) {
306
+ async findById(collection, id) {
304
307
  return this.makeRequest("GET", `/api/find/${collection}/${id}`);
305
308
  }
306
309
  /**
@@ -347,10 +350,17 @@ class EkoDBClient {
347
350
  return this.makeRequest("DELETE", `/api/batch/delete/${collection}`, { deletes });
348
351
  }
349
352
  /**
350
- * Set a key-value pair
353
+ * Set a key-value pair with optional TTL
354
+ * @param key - The key to set
355
+ * @param value - The value to store
356
+ * @param ttl - Optional TTL in seconds
351
357
  */
352
- async kvSet(key, value) {
353
- await this.makeRequest("POST", `/api/kv/set/${encodeURIComponent(key)}`, { value }, 0, true);
358
+ async kvSet(key, value, ttl) {
359
+ const body = { value };
360
+ if (ttl !== undefined) {
361
+ body.ttl = ttl;
362
+ }
363
+ await this.makeRequest("POST", `/api/kv/set/${encodeURIComponent(key)}`, body, 0, true);
354
364
  }
355
365
  /**
356
366
  * Get a value by key
@@ -365,6 +375,69 @@ class EkoDBClient {
365
375
  async kvDelete(key) {
366
376
  await this.makeRequest("DELETE", `/api/kv/delete/${encodeURIComponent(key)}`, undefined, 0, true);
367
377
  }
378
+ /**
379
+ * Check if a key exists
380
+ * @param key - The key to check
381
+ * @returns true if the key exists, false otherwise
382
+ */
383
+ async kvExists(key) {
384
+ try {
385
+ const result = await this.kvGet(key);
386
+ return result !== null && result !== undefined;
387
+ }
388
+ catch {
389
+ return false;
390
+ }
391
+ }
392
+ /**
393
+ * Query/find KV entries with pattern matching
394
+ * @param options - Query options including pattern and include_expired
395
+ * @returns Array of matching records
396
+ */
397
+ async kvFind(options) {
398
+ const result = await this.makeRequest("POST", "/api/kv/find", options || {}, 0, true);
399
+ return result;
400
+ }
401
+ /**
402
+ * Alias for kvFind - query KV store with pattern
403
+ */
404
+ async kvQuery(options) {
405
+ return this.kvFind(options);
406
+ }
407
+ // ============================================================================
408
+ // Transaction Operations
409
+ // ============================================================================
410
+ /**
411
+ * Begin a new transaction
412
+ * @param isolationLevel - Transaction isolation level (default: "ReadCommitted")
413
+ * @returns Transaction ID
414
+ */
415
+ async beginTransaction(isolationLevel = "ReadCommitted") {
416
+ const result = await this.makeRequest("POST", "/api/transactions", { isolation_level: isolationLevel }, 0, true);
417
+ return result.transaction_id;
418
+ }
419
+ /**
420
+ * Get transaction status
421
+ * @param transactionId - The transaction ID
422
+ * @returns Transaction status object
423
+ */
424
+ async getTransactionStatus(transactionId) {
425
+ return this.makeRequest("GET", `/api/transactions/${encodeURIComponent(transactionId)}`, undefined, 0, true);
426
+ }
427
+ /**
428
+ * Commit a transaction
429
+ * @param transactionId - The transaction ID to commit
430
+ */
431
+ async commitTransaction(transactionId) {
432
+ await this.makeRequest("POST", `/api/transactions/${encodeURIComponent(transactionId)}/commit`, undefined, 0, true);
433
+ }
434
+ /**
435
+ * Rollback a transaction
436
+ * @param transactionId - The transaction ID to rollback
437
+ */
438
+ async rollbackTransaction(transactionId) {
439
+ await this.makeRequest("POST", `/api/transactions/${encodeURIComponent(transactionId)}/rollback`, undefined, 0, true);
440
+ }
368
441
  /**
369
442
  * List all collections
370
443
  */
@@ -174,6 +174,26 @@ export type FunctionStageConfig = {
174
174
  } | {
175
175
  type: "ReleaseSavepoint";
176
176
  name: string;
177
+ } | {
178
+ type: "KvGet";
179
+ key: string;
180
+ output_field?: string;
181
+ } | {
182
+ type: "KvSet";
183
+ key: string;
184
+ value: any;
185
+ ttl?: number;
186
+ } | {
187
+ type: "KvDelete";
188
+ key: string;
189
+ } | {
190
+ type: "KvExists";
191
+ key: string;
192
+ output_field?: string;
193
+ } | {
194
+ type: "KvQuery";
195
+ pattern?: string;
196
+ include_expired?: boolean;
177
197
  };
178
198
  export interface ChatMessage {
179
199
  role: string;
@@ -275,4 +295,9 @@ export declare const Stage: {
275
295
  createSavepoint: (name: string) => FunctionStageConfig;
276
296
  rollbackToSavepoint: (name: string) => FunctionStageConfig;
277
297
  releaseSavepoint: (name: string) => FunctionStageConfig;
298
+ kvGet: (key: string, output_field?: string) => FunctionStageConfig;
299
+ kvSet: (key: string, value: any, ttl?: number) => FunctionStageConfig;
300
+ kvDelete: (key: string) => FunctionStageConfig;
301
+ kvExists: (key: string, output_field?: string) => FunctionStageConfig;
302
+ kvQuery: (pattern?: string, include_expired?: boolean) => FunctionStageConfig;
278
303
  };
package/dist/functions.js CHANGED
@@ -206,4 +206,30 @@ exports.Stage = {
206
206
  type: "ReleaseSavepoint",
207
207
  name,
208
208
  }),
209
+ // KV Store operations - faster than collection lookups for simple key-value data
210
+ kvGet: (key, output_field) => ({
211
+ type: "KvGet",
212
+ key,
213
+ output_field,
214
+ }),
215
+ kvSet: (key, value, ttl) => ({
216
+ type: "KvSet",
217
+ key,
218
+ value,
219
+ ttl,
220
+ }),
221
+ kvDelete: (key) => ({
222
+ type: "KvDelete",
223
+ key,
224
+ }),
225
+ kvExists: (key, output_field) => ({
226
+ type: "KvExists",
227
+ key,
228
+ output_field,
229
+ }),
230
+ kvQuery: (pattern, include_expired) => ({
231
+ type: "KvQuery",
232
+ pattern,
233
+ include_expired,
234
+ }),
209
235
  };
package/dist/index.d.ts CHANGED
@@ -4,7 +4,8 @@ export { SearchQueryBuilder } from "./search";
4
4
  export { SchemaBuilder, FieldTypeSchemaBuilder, VectorIndexAlgorithm, DistanceMetric, } from "./schema";
5
5
  export { JoinBuilder } from "./join";
6
6
  export { Stage, ChatMessage } from "./functions";
7
- export { getValue, getValues, extractRecord, getDateTimeValue, getUUIDValue, getDecimalValue, getDurationValue, getBytesValue, getBinaryValue, getArrayValue, getSetValue, getVectorValue, getObjectValue, } from "./utils";
7
+ export { getValue, getValues, extractRecord, getDateTimeValue, getUUIDValue, getDecimalValue, getDurationValue, getBytesValue, getBinaryValue, getArrayValue, getSetValue, getVectorValue, getObjectValue, Field, } from "./utils";
8
+ export type { WrappedFieldValue } from "./utils";
8
9
  export type { SearchQuery, SearchResult, SearchResponse } from "./search";
9
10
  export type { Schema, FieldTypeSchema, IndexConfig, CollectionMetadata, } from "./schema";
10
11
  export type { JoinConfig } from "./join";
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getObjectValue = exports.getVectorValue = exports.getSetValue = exports.getArrayValue = exports.getBinaryValue = exports.getBytesValue = exports.getDurationValue = exports.getDecimalValue = exports.getUUIDValue = exports.getDateTimeValue = exports.extractRecord = exports.getValues = exports.getValue = exports.ChatMessage = exports.Stage = 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;
3
+ exports.Field = exports.getObjectValue = exports.getVectorValue = exports.getSetValue = exports.getArrayValue = exports.getBinaryValue = exports.getBytesValue = exports.getDurationValue = exports.getDecimalValue = exports.getUUIDValue = exports.getDateTimeValue = exports.extractRecord = exports.getValues = exports.getValue = exports.ChatMessage = exports.Stage = 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; } });
@@ -36,3 +36,4 @@ Object.defineProperty(exports, "getArrayValue", { enumerable: true, get: functio
36
36
  Object.defineProperty(exports, "getSetValue", { enumerable: true, get: function () { return utils_1.getSetValue; } });
37
37
  Object.defineProperty(exports, "getVectorValue", { enumerable: true, get: function () { return utils_1.getVectorValue; } });
38
38
  Object.defineProperty(exports, "getObjectValue", { enumerable: true, get: function () { return utils_1.getObjectValue; } });
39
+ Object.defineProperty(exports, "Field", { enumerable: true, get: function () { return utils_1.Field; } });
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Unit tests for ekoDB TypeScript client QueryBuilder
3
+ */
4
+ export {};
@@ -0,0 +1,318 @@
1
+ "use strict";
2
+ /**
3
+ * Unit tests for ekoDB TypeScript client QueryBuilder
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const vitest_1 = require("vitest");
7
+ const query_builder_1 = require("./query-builder");
8
+ // ============================================================================
9
+ // Basic Tests
10
+ // ============================================================================
11
+ (0, vitest_1.describe)("QueryBuilder basics", () => {
12
+ (0, vitest_1.it)("creates empty query builder", () => {
13
+ const qb = new query_builder_1.QueryBuilder();
14
+ (0, vitest_1.expect)(qb).toBeInstanceOf(query_builder_1.QueryBuilder);
15
+ });
16
+ (0, vitest_1.it)("builds empty query", () => {
17
+ const query = new query_builder_1.QueryBuilder().build();
18
+ (0, vitest_1.expect)(query).toEqual({});
19
+ });
20
+ });
21
+ // ============================================================================
22
+ // Equality Operators Tests
23
+ // ============================================================================
24
+ (0, vitest_1.describe)("QueryBuilder equality operators", () => {
25
+ (0, vitest_1.it)("builds eq filter", () => {
26
+ const query = new query_builder_1.QueryBuilder().eq("status", "active").build();
27
+ (0, vitest_1.expect)(query.filter).toEqual({
28
+ type: "Condition",
29
+ content: {
30
+ field: "status",
31
+ operator: "Eq",
32
+ value: "active",
33
+ },
34
+ });
35
+ });
36
+ (0, vitest_1.it)("builds ne filter", () => {
37
+ const query = new query_builder_1.QueryBuilder().ne("status", "deleted").build();
38
+ (0, vitest_1.expect)(query.filter.content.operator).toBe("Ne");
39
+ });
40
+ });
41
+ // ============================================================================
42
+ // Comparison Operators Tests
43
+ // ============================================================================
44
+ (0, vitest_1.describe)("QueryBuilder comparison operators", () => {
45
+ (0, vitest_1.it)("builds gt filter", () => {
46
+ const query = new query_builder_1.QueryBuilder().gt("age", 18).build();
47
+ (0, vitest_1.expect)(query.filter.content.operator).toBe("Gt");
48
+ (0, vitest_1.expect)(query.filter.content.value).toBe(18);
49
+ });
50
+ (0, vitest_1.it)("builds gte filter", () => {
51
+ const query = new query_builder_1.QueryBuilder().gte("score", 80).build();
52
+ (0, vitest_1.expect)(query.filter.content.operator).toBe("Gte");
53
+ });
54
+ (0, vitest_1.it)("builds lt filter", () => {
55
+ const query = new query_builder_1.QueryBuilder().lt("price", 100.5).build();
56
+ (0, vitest_1.expect)(query.filter.content.operator).toBe("Lt");
57
+ });
58
+ (0, vitest_1.it)("builds lte filter", () => {
59
+ const query = new query_builder_1.QueryBuilder().lte("count", 1000).build();
60
+ (0, vitest_1.expect)(query.filter.content.operator).toBe("Lte");
61
+ });
62
+ });
63
+ // ============================================================================
64
+ // Array Operators Tests
65
+ // ============================================================================
66
+ (0, vitest_1.describe)("QueryBuilder array operators", () => {
67
+ (0, vitest_1.it)("builds in filter", () => {
68
+ const query = new query_builder_1.QueryBuilder()
69
+ .in("status", ["active", "pending"])
70
+ .build();
71
+ (0, vitest_1.expect)(query.filter.content.operator).toBe("In");
72
+ (0, vitest_1.expect)(query.filter.content.value).toEqual(["active", "pending"]);
73
+ });
74
+ (0, vitest_1.it)("builds nin filter", () => {
75
+ const query = new query_builder_1.QueryBuilder()
76
+ .nin("role", ["blocked", "deleted"])
77
+ .build();
78
+ (0, vitest_1.expect)(query.filter.content.operator).toBe("NotIn");
79
+ });
80
+ });
81
+ // ============================================================================
82
+ // String Operators Tests
83
+ // ============================================================================
84
+ (0, vitest_1.describe)("QueryBuilder string operators", () => {
85
+ (0, vitest_1.it)("builds contains filter", () => {
86
+ const query = new query_builder_1.QueryBuilder().contains("email", "@example.com").build();
87
+ (0, vitest_1.expect)(query.filter.content.operator).toBe("Contains");
88
+ (0, vitest_1.expect)(query.filter.content.value).toBe("@example.com");
89
+ });
90
+ (0, vitest_1.it)("builds startsWith filter", () => {
91
+ const query = new query_builder_1.QueryBuilder().startsWith("name", "John").build();
92
+ (0, vitest_1.expect)(query.filter.content.operator).toBe("StartsWith");
93
+ });
94
+ (0, vitest_1.it)("builds endsWith filter", () => {
95
+ const query = new query_builder_1.QueryBuilder().endsWith("filename", ".pdf").build();
96
+ (0, vitest_1.expect)(query.filter.content.operator).toBe("EndsWith");
97
+ });
98
+ (0, vitest_1.it)("builds regex filter", () => {
99
+ const query = new query_builder_1.QueryBuilder().regex("phone", "^\\+1").build();
100
+ (0, vitest_1.expect)(query.filter.content.operator).toBe("Regex");
101
+ });
102
+ });
103
+ // ============================================================================
104
+ // Logical Operators Tests
105
+ // ============================================================================
106
+ (0, vitest_1.describe)("QueryBuilder logical operators", () => {
107
+ (0, vitest_1.it)("builds and filter", () => {
108
+ const conditions = [
109
+ {
110
+ type: "Condition",
111
+ content: { field: "status", operator: "Eq", value: "active" },
112
+ },
113
+ {
114
+ type: "Condition",
115
+ content: { field: "age", operator: "Gt", value: 18 },
116
+ },
117
+ ];
118
+ const query = new query_builder_1.QueryBuilder().and(conditions).build();
119
+ (0, vitest_1.expect)(query.filter.type).toBe("Logical");
120
+ (0, vitest_1.expect)(query.filter.content.operator).toBe("And");
121
+ (0, vitest_1.expect)(query.filter.content.expressions).toHaveLength(2);
122
+ });
123
+ (0, vitest_1.it)("builds or filter", () => {
124
+ const conditions = [
125
+ {
126
+ type: "Condition",
127
+ content: { field: "role", operator: "Eq", value: "admin" },
128
+ },
129
+ {
130
+ type: "Condition",
131
+ content: { field: "role", operator: "Eq", value: "super_admin" },
132
+ },
133
+ ];
134
+ const query = new query_builder_1.QueryBuilder().or(conditions).build();
135
+ (0, vitest_1.expect)(query.filter.content.operator).toBe("Or");
136
+ });
137
+ (0, vitest_1.it)("builds not filter", () => {
138
+ const condition = {
139
+ type: "Condition",
140
+ content: { field: "deleted", operator: "Eq", value: true },
141
+ };
142
+ const query = new query_builder_1.QueryBuilder().not(condition).build();
143
+ (0, vitest_1.expect)(query.filter.content.operator).toBe("Not");
144
+ });
145
+ });
146
+ // ============================================================================
147
+ // Multiple Filters (Auto AND) Tests
148
+ // ============================================================================
149
+ (0, vitest_1.describe)("QueryBuilder multiple filters", () => {
150
+ (0, vitest_1.it)("combines multiple filters with AND logic", () => {
151
+ const query = new query_builder_1.QueryBuilder()
152
+ .eq("status", "active")
153
+ .gt("age", 18)
154
+ .contains("email", "@company.com")
155
+ .build();
156
+ (0, vitest_1.expect)(query.filter.type).toBe("Logical");
157
+ (0, vitest_1.expect)(query.filter.content.operator).toBe("And");
158
+ (0, vitest_1.expect)(query.filter.content.expressions).toHaveLength(3);
159
+ });
160
+ });
161
+ // ============================================================================
162
+ // Sorting Tests
163
+ // ============================================================================
164
+ (0, vitest_1.describe)("QueryBuilder sorting", () => {
165
+ (0, vitest_1.it)("builds ascending sort", () => {
166
+ const query = new query_builder_1.QueryBuilder().sortAsc("name").build();
167
+ (0, vitest_1.expect)(query.sort).toHaveLength(1);
168
+ (0, vitest_1.expect)(query.sort[0].field).toBe("name");
169
+ (0, vitest_1.expect)(query.sort[0].ascending).toBe(true);
170
+ });
171
+ (0, vitest_1.it)("builds descending sort", () => {
172
+ const query = new query_builder_1.QueryBuilder().sortDesc("created_at").build();
173
+ (0, vitest_1.expect)(query.sort[0].ascending).toBe(false);
174
+ });
175
+ (0, vitest_1.it)("builds multiple sorts", () => {
176
+ const query = new query_builder_1.QueryBuilder()
177
+ .sortDesc("created_at")
178
+ .sortAsc("name")
179
+ .build();
180
+ (0, vitest_1.expect)(query.sort).toHaveLength(2);
181
+ (0, vitest_1.expect)(query.sort[0].field).toBe("created_at");
182
+ (0, vitest_1.expect)(query.sort[0].ascending).toBe(false);
183
+ (0, vitest_1.expect)(query.sort[1].field).toBe("name");
184
+ (0, vitest_1.expect)(query.sort[1].ascending).toBe(true);
185
+ });
186
+ });
187
+ // ============================================================================
188
+ // Pagination Tests
189
+ // ============================================================================
190
+ (0, vitest_1.describe)("QueryBuilder pagination", () => {
191
+ (0, vitest_1.it)("builds limit", () => {
192
+ const query = new query_builder_1.QueryBuilder().limit(10).build();
193
+ (0, vitest_1.expect)(query.limit).toBe(10);
194
+ });
195
+ (0, vitest_1.it)("builds skip", () => {
196
+ const query = new query_builder_1.QueryBuilder().skip(20).build();
197
+ (0, vitest_1.expect)(query.skip).toBe(20);
198
+ });
199
+ (0, vitest_1.it)("builds page (convenience method)", () => {
200
+ // Page 2 with 20 items per page = skip 40
201
+ const query = new query_builder_1.QueryBuilder().page(2, 20).build();
202
+ (0, vitest_1.expect)(query.limit).toBe(20);
203
+ (0, vitest_1.expect)(query.skip).toBe(40);
204
+ });
205
+ (0, vitest_1.it)("builds page 0", () => {
206
+ const query = new query_builder_1.QueryBuilder().page(0, 10).build();
207
+ (0, vitest_1.expect)(query.skip).toBe(0);
208
+ (0, vitest_1.expect)(query.limit).toBe(10);
209
+ });
210
+ });
211
+ // ============================================================================
212
+ // Join Tests
213
+ // ============================================================================
214
+ (0, vitest_1.describe)("QueryBuilder join", () => {
215
+ (0, vitest_1.it)("builds join configuration", () => {
216
+ const joinConfig = {
217
+ collections: ["users"],
218
+ local_field: "user_id",
219
+ foreign_field: "id",
220
+ as_field: "user",
221
+ };
222
+ const query = new query_builder_1.QueryBuilder().join(joinConfig).build();
223
+ (0, vitest_1.expect)(query.join).toEqual(joinConfig);
224
+ });
225
+ });
226
+ // ============================================================================
227
+ // Bypass Flags Tests
228
+ // ============================================================================
229
+ (0, vitest_1.describe)("QueryBuilder bypass flags", () => {
230
+ (0, vitest_1.it)("builds bypass_cache true", () => {
231
+ const query = new query_builder_1.QueryBuilder().bypassCache(true).build();
232
+ (0, vitest_1.expect)(query.bypass_cache).toBe(true);
233
+ });
234
+ (0, vitest_1.it)("builds bypass_cache false (not included)", () => {
235
+ const query = new query_builder_1.QueryBuilder().bypassCache(false).build();
236
+ (0, vitest_1.expect)(query.bypass_cache).toBeUndefined();
237
+ });
238
+ (0, vitest_1.it)("builds bypass_cache default (true)", () => {
239
+ const query = new query_builder_1.QueryBuilder().bypassCache().build();
240
+ (0, vitest_1.expect)(query.bypass_cache).toBe(true);
241
+ });
242
+ (0, vitest_1.it)("builds bypass_ripple", () => {
243
+ const query = new query_builder_1.QueryBuilder().bypassRipple(true).build();
244
+ (0, vitest_1.expect)(query.bypass_ripple).toBe(true);
245
+ });
246
+ });
247
+ // ============================================================================
248
+ // Chaining Tests
249
+ // ============================================================================
250
+ (0, vitest_1.describe)("QueryBuilder chaining", () => {
251
+ (0, vitest_1.it)("supports full method chaining", () => {
252
+ const query = new query_builder_1.QueryBuilder()
253
+ .eq("status", "active")
254
+ .gt("age", 18)
255
+ .sortDesc("created_at")
256
+ .sortAsc("name")
257
+ .limit(10)
258
+ .skip(20)
259
+ .bypassCache(true)
260
+ .build();
261
+ // Check filter exists
262
+ (0, vitest_1.expect)(query.filter).toBeDefined();
263
+ // Check sort exists
264
+ (0, vitest_1.expect)(query.sort).toHaveLength(2);
265
+ // Check pagination
266
+ (0, vitest_1.expect)(query.limit).toBe(10);
267
+ (0, vitest_1.expect)(query.skip).toBe(20);
268
+ // Check bypass flag
269
+ (0, vitest_1.expect)(query.bypass_cache).toBe(true);
270
+ });
271
+ (0, vitest_1.it)("returns this for method chaining", () => {
272
+ const qb = new query_builder_1.QueryBuilder();
273
+ (0, vitest_1.expect)(qb.eq("a", 1)).toBe(qb);
274
+ (0, vitest_1.expect)(qb.ne("b", 2)).toBe(qb);
275
+ (0, vitest_1.expect)(qb.gt("c", 3)).toBe(qb);
276
+ (0, vitest_1.expect)(qb.gte("d", 4)).toBe(qb);
277
+ (0, vitest_1.expect)(qb.lt("e", 5)).toBe(qb);
278
+ (0, vitest_1.expect)(qb.lte("f", 6)).toBe(qb);
279
+ (0, vitest_1.expect)(qb.in("g", [7])).toBe(qb);
280
+ (0, vitest_1.expect)(qb.nin("h", [8])).toBe(qb);
281
+ (0, vitest_1.expect)(qb.contains("i", "j")).toBe(qb);
282
+ (0, vitest_1.expect)(qb.startsWith("k", "l")).toBe(qb);
283
+ (0, vitest_1.expect)(qb.endsWith("m", "n")).toBe(qb);
284
+ (0, vitest_1.expect)(qb.regex("o", "p")).toBe(qb);
285
+ (0, vitest_1.expect)(qb.sortAsc("q")).toBe(qb);
286
+ (0, vitest_1.expect)(qb.sortDesc("r")).toBe(qb);
287
+ (0, vitest_1.expect)(qb.limit(1)).toBe(qb);
288
+ (0, vitest_1.expect)(qb.skip(0)).toBe(qb);
289
+ (0, vitest_1.expect)(qb.bypassCache()).toBe(qb);
290
+ (0, vitest_1.expect)(qb.bypassRipple()).toBe(qb);
291
+ });
292
+ });
293
+ // ============================================================================
294
+ // Raw Filter Tests
295
+ // ============================================================================
296
+ (0, vitest_1.describe)("QueryBuilder rawFilter", () => {
297
+ (0, vitest_1.it)("adds raw filter expression", () => {
298
+ const rawFilter = {
299
+ type: "Condition",
300
+ content: {
301
+ field: "custom",
302
+ operator: "CustomOp",
303
+ value: "custom_value",
304
+ },
305
+ };
306
+ const query = new query_builder_1.QueryBuilder().rawFilter(rawFilter).build();
307
+ (0, vitest_1.expect)(query.filter).toEqual(rawFilter);
308
+ });
309
+ });
310
+ // ============================================================================
311
+ // SortOrder Enum Tests
312
+ // ============================================================================
313
+ (0, vitest_1.describe)("SortOrder enum", () => {
314
+ (0, vitest_1.it)("has correct values", () => {
315
+ (0, vitest_1.expect)(query_builder_1.SortOrder.Asc).toBe("asc");
316
+ (0, vitest_1.expect)(query_builder_1.SortOrder.Desc).toBe("desc");
317
+ });
318
+ });