@ekodb/ekodb-client 0.5.0 → 0.6.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.
@@ -5,12 +5,12 @@ export interface Script {
5
5
  label: string;
6
6
  name: string;
7
7
  description?: string;
8
- version: string;
8
+ version?: string;
9
9
  parameters: {
10
10
  [key: string]: ParameterDefinition;
11
11
  };
12
12
  functions: FunctionStageConfig[];
13
- tags: string[];
13
+ tags?: string[];
14
14
  created_at?: string;
15
15
  updated_at?: string;
16
16
  }
@@ -129,6 +129,51 @@ export type FunctionStageConfig = {
129
129
  input_field: string;
130
130
  output_field: string;
131
131
  model?: string;
132
+ } | {
133
+ type: "FindById";
134
+ collection: string;
135
+ record_id: string;
136
+ } | {
137
+ type: "FindOne";
138
+ collection: string;
139
+ key: string;
140
+ value: any;
141
+ } | {
142
+ type: "If";
143
+ condition: ScriptCondition;
144
+ then_functions: FunctionStageConfig[];
145
+ else_functions?: FunctionStageConfig[];
146
+ } | {
147
+ type: "ForEach";
148
+ functions: FunctionStageConfig[];
149
+ } | {
150
+ type: "CallFunction";
151
+ function_label: string;
152
+ params?: Record<string, any>;
153
+ } | {
154
+ type: "FindOneAndUpdate";
155
+ collection: string;
156
+ record_id: string;
157
+ updates: Record<string, any>;
158
+ bypass_ripple?: boolean;
159
+ ttl?: number;
160
+ } | {
161
+ type: "UpdateWithAction";
162
+ collection: string;
163
+ record_id: string;
164
+ action: string;
165
+ field: string;
166
+ value: any;
167
+ bypass_ripple?: boolean;
168
+ } | {
169
+ type: "CreateSavepoint";
170
+ name: string;
171
+ } | {
172
+ type: "RollbackToSavepoint";
173
+ name: string;
174
+ } | {
175
+ type: "ReleaseSavepoint";
176
+ name: string;
132
177
  };
133
178
  export interface ChatMessage {
134
179
  role: string;
@@ -148,6 +193,34 @@ export interface SortFieldConfig {
148
193
  field: string;
149
194
  ascending: boolean;
150
195
  }
196
+ export type ScriptCondition = {
197
+ type: "FieldEquals";
198
+ field: string;
199
+ value: any;
200
+ } | {
201
+ type: "FieldExists";
202
+ field: string;
203
+ } | {
204
+ type: "HasRecords";
205
+ } | {
206
+ type: "CountEquals";
207
+ count: number;
208
+ } | {
209
+ type: "CountGreaterThan";
210
+ count: number;
211
+ } | {
212
+ type: "CountLessThan";
213
+ count: number;
214
+ } | {
215
+ type: "And";
216
+ conditions: ScriptCondition[];
217
+ } | {
218
+ type: "Or";
219
+ conditions: ScriptCondition[];
220
+ } | {
221
+ type: "Not";
222
+ condition: ScriptCondition;
223
+ };
151
224
  export interface FunctionResult {
152
225
  records: Record<string, any>[];
153
226
  stats: FunctionStats;
@@ -192,4 +265,14 @@ export declare const Stage: {
192
265
  hybridSearch: (collection: string, query_text: string, query_vector?: number[], limit?: number) => FunctionStageConfig;
193
266
  chat: (messages: ChatMessage[], model?: string, temperature?: number, max_tokens?: number) => FunctionStageConfig;
194
267
  embed: (input_field: string, output_field: string, model?: string) => FunctionStageConfig;
268
+ findById: (collection: string, record_id: string) => FunctionStageConfig;
269
+ findOne: (collection: string, key: string, value: any) => FunctionStageConfig;
270
+ if: (condition: ScriptCondition, thenFunctions: FunctionStageConfig[], elseFunctions?: FunctionStageConfig[]) => FunctionStageConfig;
271
+ forEach: (functions: FunctionStageConfig[]) => FunctionStageConfig;
272
+ callFunction: (function_label: string, params?: Record<string, any>) => FunctionStageConfig;
273
+ findOneAndUpdate: (collection: string, record_id: string, updates: Record<string, any>, bypassRipple?: boolean, ttl?: number) => FunctionStageConfig;
274
+ updateWithAction: (collection: string, record_id: string, action: string, field: string, value: any, bypassRipple?: boolean) => FunctionStageConfig;
275
+ createSavepoint: (name: string) => FunctionStageConfig;
276
+ rollbackToSavepoint: (name: string) => FunctionStageConfig;
277
+ releaseSavepoint: (name: string) => FunctionStageConfig;
195
278
  };
package/dist/functions.js CHANGED
@@ -151,4 +151,59 @@ exports.Stage = {
151
151
  output_field,
152
152
  model,
153
153
  }),
154
+ findById: (collection, record_id) => ({
155
+ type: "FindById",
156
+ collection,
157
+ record_id,
158
+ }),
159
+ findOne: (collection, key, value) => ({
160
+ type: "FindOne",
161
+ collection,
162
+ key,
163
+ value,
164
+ }),
165
+ if: (condition, thenFunctions, elseFunctions) => ({
166
+ type: "If",
167
+ condition,
168
+ then_functions: thenFunctions,
169
+ else_functions: elseFunctions,
170
+ }),
171
+ forEach: (functions) => ({
172
+ type: "ForEach",
173
+ functions,
174
+ }),
175
+ callFunction: (function_label, params) => ({
176
+ type: "CallFunction",
177
+ function_label,
178
+ params,
179
+ }),
180
+ findOneAndUpdate: (collection, record_id, updates, bypassRipple = false, ttl) => ({
181
+ type: "FindOneAndUpdate",
182
+ collection,
183
+ record_id,
184
+ updates,
185
+ bypass_ripple: bypassRipple,
186
+ ttl,
187
+ }),
188
+ updateWithAction: (collection, record_id, action, field, value, bypassRipple = false) => ({
189
+ type: "UpdateWithAction",
190
+ collection,
191
+ record_id,
192
+ action,
193
+ field,
194
+ value,
195
+ bypass_ripple: bypassRipple,
196
+ }),
197
+ createSavepoint: (name) => ({
198
+ type: "CreateSavepoint",
199
+ name,
200
+ }),
201
+ rollbackToSavepoint: (name) => ({
202
+ type: "RollbackToSavepoint",
203
+ name,
204
+ }),
205
+ releaseSavepoint: (name) => ({
206
+ type: "ReleaseSavepoint",
207
+ name,
208
+ }),
154
209
  };
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ 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
8
  export type { SearchQuery, SearchResult, SearchResponse } from "./search";
8
9
  export type { Schema, FieldTypeSchema, IndexConfig, CollectionMetadata, } from "./schema";
9
10
  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.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.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; } });
@@ -22,3 +22,17 @@ Object.defineProperty(exports, "JoinBuilder", { enumerable: true, get: function
22
22
  var functions_1 = require("./functions");
23
23
  Object.defineProperty(exports, "Stage", { enumerable: true, get: function () { return functions_1.Stage; } });
24
24
  Object.defineProperty(exports, "ChatMessage", { enumerable: true, get: function () { return functions_1.ChatMessage; } });
25
+ var utils_1 = require("./utils");
26
+ Object.defineProperty(exports, "getValue", { enumerable: true, get: function () { return utils_1.getValue; } });
27
+ Object.defineProperty(exports, "getValues", { enumerable: true, get: function () { return utils_1.getValues; } });
28
+ Object.defineProperty(exports, "extractRecord", { enumerable: true, get: function () { return utils_1.extractRecord; } });
29
+ Object.defineProperty(exports, "getDateTimeValue", { enumerable: true, get: function () { return utils_1.getDateTimeValue; } });
30
+ Object.defineProperty(exports, "getUUIDValue", { enumerable: true, get: function () { return utils_1.getUUIDValue; } });
31
+ Object.defineProperty(exports, "getDecimalValue", { enumerable: true, get: function () { return utils_1.getDecimalValue; } });
32
+ Object.defineProperty(exports, "getDurationValue", { enumerable: true, get: function () { return utils_1.getDurationValue; } });
33
+ Object.defineProperty(exports, "getBytesValue", { enumerable: true, get: function () { return utils_1.getBytesValue; } });
34
+ Object.defineProperty(exports, "getBinaryValue", { enumerable: true, get: function () { return utils_1.getBinaryValue; } });
35
+ Object.defineProperty(exports, "getArrayValue", { enumerable: true, get: function () { return utils_1.getArrayValue; } });
36
+ Object.defineProperty(exports, "getSetValue", { enumerable: true, get: function () { return utils_1.getSetValue; } });
37
+ Object.defineProperty(exports, "getVectorValue", { enumerable: true, get: function () { return utils_1.getVectorValue; } });
38
+ Object.defineProperty(exports, "getObjectValue", { enumerable: true, get: function () { return utils_1.getObjectValue; } });
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Utility functions for working with ekoDB records
3
+ */
4
+ /**
5
+ * Extract the value from an ekoDB field object.
6
+ * ekoDB returns fields as { type: string, value: any } objects.
7
+ * This helper safely extracts the value or returns the input if it's not a field object.
8
+ *
9
+ * @param field - The field value from ekoDB (may be { type, value } or a plain value)
10
+ * @returns The extracted value
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const user = await client.findByID('users', userId);
15
+ * const email = getValue(user.email); // Extracts string from { type: 'String', value: 'user@example.com' }
16
+ * const age = getValue(user.age); // Extracts number from { type: 'Integer', value: 25 }
17
+ * ```
18
+ */
19
+ export declare function getValue<T = any>(field: any): T;
20
+ /**
21
+ * Extract values from multiple fields in a record.
22
+ * Useful for processing entire records returned from ekoDB.
23
+ *
24
+ * @param record - The record object from ekoDB
25
+ * @param fields - Array of field names to extract values from
26
+ * @returns Object with extracted values
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const user = await client.findByID('users', userId);
31
+ * const { email, first_name, status } = getValues(user, ['email', 'first_name', 'status']);
32
+ * ```
33
+ */
34
+ export declare function getValues<T extends Record<string, any>>(record: any, fields: (keyof T)[]): Partial<T>;
35
+ /**
36
+ * Extract a Date value from an ekoDB DateTime field
37
+ */
38
+ export declare function getDateTimeValue(field: any): Date | null;
39
+ /**
40
+ * Extract a UUID string from an ekoDB UUID field
41
+ */
42
+ export declare function getUUIDValue(field: any): string | null;
43
+ /**
44
+ * Extract a number from an ekoDB Decimal field
45
+ */
46
+ export declare function getDecimalValue(field: any): number | null;
47
+ /**
48
+ * Extract a number (milliseconds) from an ekoDB Duration field
49
+ */
50
+ export declare function getDurationValue(field: any): number | null;
51
+ /**
52
+ * Extract a Uint8Array from an ekoDB Bytes field
53
+ */
54
+ export declare function getBytesValue(field: any): Uint8Array | null;
55
+ /**
56
+ * Extract a Uint8Array from an ekoDB Binary field
57
+ */
58
+ export declare function getBinaryValue(field: any): Uint8Array | null;
59
+ /**
60
+ * Extract an array from an ekoDB Array field
61
+ */
62
+ export declare function getArrayValue<T = any>(field: any): T[] | null;
63
+ /**
64
+ * Extract an array from an ekoDB Set field
65
+ */
66
+ export declare function getSetValue<T = any>(field: any): T[] | null;
67
+ /**
68
+ * Extract an array from an ekoDB Vector field
69
+ */
70
+ export declare function getVectorValue(field: any): number[] | null;
71
+ /**
72
+ * Extract an object from an ekoDB Object field
73
+ */
74
+ export declare function getObjectValue<T = any>(field: any): T | null;
75
+ /**
76
+ * Transform an entire record by extracting all field values.
77
+ * Preserves the 'id' field and extracts values from all other fields.
78
+ *
79
+ * @param record - The record object from ekoDB
80
+ * @returns Object with all field values extracted
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * const user = await client.findByID('users', userId);
85
+ * const plainUser = extractRecord(user);
86
+ * // { id: '123', email: 'user@example.com', first_name: 'John', ... }
87
+ * ```
88
+ */
89
+ export declare function extractRecord<T extends Record<string, any>>(record: any): T;
package/dist/utils.js ADDED
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ /**
3
+ * Utility functions for working with ekoDB records
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getValue = getValue;
7
+ exports.getValues = getValues;
8
+ exports.getDateTimeValue = getDateTimeValue;
9
+ exports.getUUIDValue = getUUIDValue;
10
+ exports.getDecimalValue = getDecimalValue;
11
+ exports.getDurationValue = getDurationValue;
12
+ exports.getBytesValue = getBytesValue;
13
+ exports.getBinaryValue = getBinaryValue;
14
+ exports.getArrayValue = getArrayValue;
15
+ exports.getSetValue = getSetValue;
16
+ exports.getVectorValue = getVectorValue;
17
+ exports.getObjectValue = getObjectValue;
18
+ exports.extractRecord = extractRecord;
19
+ /**
20
+ * Extract the value from an ekoDB field object.
21
+ * ekoDB returns fields as { type: string, value: any } objects.
22
+ * This helper safely extracts the value or returns the input if it's not a field object.
23
+ *
24
+ * @param field - The field value from ekoDB (may be { type, value } or a plain value)
25
+ * @returns The extracted value
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const user = await client.findByID('users', userId);
30
+ * const email = getValue(user.email); // Extracts string from { type: 'String', value: 'user@example.com' }
31
+ * const age = getValue(user.age); // Extracts number from { type: 'Integer', value: 25 }
32
+ * ```
33
+ */
34
+ function getValue(field) {
35
+ if (field && typeof field === "object" && "value" in field) {
36
+ return field.value;
37
+ }
38
+ return field;
39
+ }
40
+ /**
41
+ * Extract values from multiple fields in a record.
42
+ * Useful for processing entire records returned from ekoDB.
43
+ *
44
+ * @param record - The record object from ekoDB
45
+ * @param fields - Array of field names to extract values from
46
+ * @returns Object with extracted values
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * const user = await client.findByID('users', userId);
51
+ * const { email, first_name, status } = getValues(user, ['email', 'first_name', 'status']);
52
+ * ```
53
+ */
54
+ function getValues(record, fields) {
55
+ const result = {};
56
+ for (const field of fields) {
57
+ result[field] = getValue(record[field]);
58
+ }
59
+ return result;
60
+ }
61
+ /**
62
+ * Extract a Date value from an ekoDB DateTime field
63
+ */
64
+ function getDateTimeValue(field) {
65
+ const val = getValue(field);
66
+ if (val instanceof Date)
67
+ return val;
68
+ if (typeof val === "string") {
69
+ const date = new Date(val);
70
+ return isNaN(date.getTime()) ? null : date;
71
+ }
72
+ return null;
73
+ }
74
+ /**
75
+ * Extract a UUID string from an ekoDB UUID field
76
+ */
77
+ function getUUIDValue(field) {
78
+ const val = getValue(field);
79
+ return typeof val === "string" ? val : null;
80
+ }
81
+ /**
82
+ * Extract a number from an ekoDB Decimal field
83
+ */
84
+ function getDecimalValue(field) {
85
+ const val = getValue(field);
86
+ if (typeof val === "number")
87
+ return val;
88
+ if (typeof val === "string") {
89
+ const num = parseFloat(val);
90
+ return isNaN(num) ? null : num;
91
+ }
92
+ return null;
93
+ }
94
+ /**
95
+ * Extract a number (milliseconds) from an ekoDB Duration field
96
+ */
97
+ function getDurationValue(field) {
98
+ const val = getValue(field);
99
+ if (typeof val === "number")
100
+ return val;
101
+ if (typeof val === "object" && val.secs !== undefined) {
102
+ return val.secs * 1000 + val.nanos / 1000000;
103
+ }
104
+ return null;
105
+ }
106
+ /**
107
+ * Extract a Uint8Array from an ekoDB Bytes field
108
+ */
109
+ function getBytesValue(field) {
110
+ const val = getValue(field);
111
+ if (val instanceof Uint8Array)
112
+ return val;
113
+ if (Array.isArray(val))
114
+ return new Uint8Array(val);
115
+ if (typeof val === "string") {
116
+ // Assume base64 encoded
117
+ try {
118
+ const binary = atob(val);
119
+ const bytes = new Uint8Array(binary.length);
120
+ for (let i = 0; i < binary.length; i++) {
121
+ bytes[i] = binary.charCodeAt(i);
122
+ }
123
+ return bytes;
124
+ }
125
+ catch {
126
+ return null;
127
+ }
128
+ }
129
+ return null;
130
+ }
131
+ /**
132
+ * Extract a Uint8Array from an ekoDB Binary field
133
+ */
134
+ function getBinaryValue(field) {
135
+ return getBytesValue(field);
136
+ }
137
+ /**
138
+ * Extract an array from an ekoDB Array field
139
+ */
140
+ function getArrayValue(field) {
141
+ const val = getValue(field);
142
+ return Array.isArray(val) ? val : null;
143
+ }
144
+ /**
145
+ * Extract an array from an ekoDB Set field
146
+ */
147
+ function getSetValue(field) {
148
+ const val = getValue(field);
149
+ return Array.isArray(val) ? val : null;
150
+ }
151
+ /**
152
+ * Extract an array from an ekoDB Vector field
153
+ */
154
+ function getVectorValue(field) {
155
+ const val = getValue(field);
156
+ if (Array.isArray(val)) {
157
+ return val
158
+ .map((v) => (typeof v === "number" ? v : parseFloat(v)))
159
+ .filter((v) => !isNaN(v));
160
+ }
161
+ return null;
162
+ }
163
+ /**
164
+ * Extract an object from an ekoDB Object field
165
+ */
166
+ function getObjectValue(field) {
167
+ const val = getValue(field);
168
+ return val && typeof val === "object" && !Array.isArray(val) ? val : null;
169
+ }
170
+ /**
171
+ * Transform an entire record by extracting all field values.
172
+ * Preserves the 'id' field and extracts values from all other fields.
173
+ *
174
+ * @param record - The record object from ekoDB
175
+ * @returns Object with all field values extracted
176
+ *
177
+ * @example
178
+ * ```typescript
179
+ * const user = await client.findByID('users', userId);
180
+ * const plainUser = extractRecord(user);
181
+ * // { id: '123', email: 'user@example.com', first_name: 'John', ... }
182
+ * ```
183
+ */
184
+ function extractRecord(record) {
185
+ if (!record || typeof record !== "object") {
186
+ return record;
187
+ }
188
+ const result = {};
189
+ for (const [key, value] of Object.entries(record)) {
190
+ if (key === "id") {
191
+ result[key] = value;
192
+ }
193
+ else {
194
+ result[key] = getValue(value);
195
+ }
196
+ }
197
+ return result;
198
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekodb/ekodb-client",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "Official TypeScript/JavaScript client for ekoDB",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/functions.ts CHANGED
@@ -6,10 +6,10 @@ export interface Script {
6
6
  label: string;
7
7
  name: string;
8
8
  description?: string;
9
- version: string;
9
+ version?: string;
10
10
  parameters: { [key: string]: ParameterDefinition };
11
11
  functions: FunctionStageConfig[];
12
- tags: string[];
12
+ tags?: string[];
13
13
  created_at?: string;
14
14
  updated_at?: string;
15
15
  }
@@ -132,6 +132,61 @@ export type FunctionStageConfig =
132
132
  input_field: string;
133
133
  output_field: string;
134
134
  model?: string;
135
+ }
136
+ | {
137
+ type: "FindById";
138
+ collection: string;
139
+ record_id: string;
140
+ }
141
+ | {
142
+ type: "FindOne";
143
+ collection: string;
144
+ key: string;
145
+ value: any;
146
+ }
147
+ | {
148
+ type: "If";
149
+ condition: ScriptCondition;
150
+ then_functions: FunctionStageConfig[];
151
+ else_functions?: FunctionStageConfig[];
152
+ }
153
+ | {
154
+ type: "ForEach";
155
+ functions: FunctionStageConfig[];
156
+ }
157
+ | {
158
+ type: "CallFunction";
159
+ function_label: string;
160
+ params?: Record<string, any>;
161
+ }
162
+ | {
163
+ type: "FindOneAndUpdate";
164
+ collection: string;
165
+ record_id: string;
166
+ updates: Record<string, any>;
167
+ bypass_ripple?: boolean;
168
+ ttl?: number;
169
+ }
170
+ | {
171
+ type: "UpdateWithAction";
172
+ collection: string;
173
+ record_id: string;
174
+ action: string;
175
+ field: string;
176
+ value: any;
177
+ bypass_ripple?: boolean;
178
+ }
179
+ | {
180
+ type: "CreateSavepoint";
181
+ name: string;
182
+ }
183
+ | {
184
+ type: "RollbackToSavepoint";
185
+ name: string;
186
+ }
187
+ | {
188
+ type: "ReleaseSavepoint";
189
+ name: string;
135
190
  };
136
191
 
137
192
  export interface ChatMessage {
@@ -173,6 +228,17 @@ export interface SortFieldConfig {
173
228
  ascending: boolean;
174
229
  }
175
230
 
231
+ export type ScriptCondition =
232
+ | { type: "FieldEquals"; field: string; value: any }
233
+ | { type: "FieldExists"; field: string }
234
+ | { type: "HasRecords" }
235
+ | { type: "CountEquals"; count: number }
236
+ | { type: "CountGreaterThan"; count: number }
237
+ | { type: "CountLessThan"; count: number }
238
+ | { type: "And"; conditions: ScriptCondition[] }
239
+ | { type: "Or"; conditions: ScriptCondition[] }
240
+ | { type: "Not"; condition: ScriptCondition };
241
+
176
242
  export interface FunctionResult {
177
243
  records: Record<string, any>[];
178
244
  stats: FunctionStats;
@@ -417,4 +483,93 @@ export const Stage = {
417
483
  output_field,
418
484
  model,
419
485
  }),
486
+
487
+ findById: (collection: string, record_id: string): FunctionStageConfig => ({
488
+ type: "FindById",
489
+ collection,
490
+ record_id,
491
+ }),
492
+
493
+ findOne: (
494
+ collection: string,
495
+ key: string,
496
+ value: any,
497
+ ): FunctionStageConfig => ({
498
+ type: "FindOne",
499
+ collection,
500
+ key,
501
+ value,
502
+ }),
503
+
504
+ if: (
505
+ condition: ScriptCondition,
506
+ thenFunctions: FunctionStageConfig[],
507
+ elseFunctions?: FunctionStageConfig[],
508
+ ): FunctionStageConfig => ({
509
+ type: "If",
510
+ condition,
511
+ then_functions: thenFunctions,
512
+ else_functions: elseFunctions,
513
+ }),
514
+
515
+ forEach: (functions: FunctionStageConfig[]): FunctionStageConfig => ({
516
+ type: "ForEach",
517
+ functions,
518
+ }),
519
+
520
+ callFunction: (
521
+ function_label: string,
522
+ params?: Record<string, any>,
523
+ ): FunctionStageConfig => ({
524
+ type: "CallFunction",
525
+ function_label,
526
+ params,
527
+ }),
528
+
529
+ findOneAndUpdate: (
530
+ collection: string,
531
+ record_id: string,
532
+ updates: Record<string, any>,
533
+ bypassRipple = false,
534
+ ttl?: number,
535
+ ): FunctionStageConfig => ({
536
+ type: "FindOneAndUpdate",
537
+ collection,
538
+ record_id,
539
+ updates,
540
+ bypass_ripple: bypassRipple,
541
+ ttl,
542
+ }),
543
+
544
+ updateWithAction: (
545
+ collection: string,
546
+ record_id: string,
547
+ action: string,
548
+ field: string,
549
+ value: any,
550
+ bypassRipple = false,
551
+ ): FunctionStageConfig => ({
552
+ type: "UpdateWithAction",
553
+ collection,
554
+ record_id,
555
+ action,
556
+ field,
557
+ value,
558
+ bypass_ripple: bypassRipple,
559
+ }),
560
+
561
+ createSavepoint: (name: string): FunctionStageConfig => ({
562
+ type: "CreateSavepoint",
563
+ name,
564
+ }),
565
+
566
+ rollbackToSavepoint: (name: string): FunctionStageConfig => ({
567
+ type: "RollbackToSavepoint",
568
+ name,
569
+ }),
570
+
571
+ releaseSavepoint: (name: string): FunctionStageConfig => ({
572
+ type: "ReleaseSavepoint",
573
+ name,
574
+ }),
420
575
  };
package/src/index.ts CHANGED
@@ -15,6 +15,21 @@ export {
15
15
  } from "./schema";
16
16
  export { JoinBuilder } from "./join";
17
17
  export { Stage, ChatMessage } from "./functions";
18
+ export {
19
+ getValue,
20
+ getValues,
21
+ extractRecord,
22
+ getDateTimeValue,
23
+ getUUIDValue,
24
+ getDecimalValue,
25
+ getDurationValue,
26
+ getBytesValue,
27
+ getBinaryValue,
28
+ getArrayValue,
29
+ getSetValue,
30
+ getVectorValue,
31
+ getObjectValue,
32
+ } from "./utils";
18
33
  export type { SearchQuery, SearchResult, SearchResponse } from "./search";
19
34
  export type {
20
35
  Schema,
package/src/utils.ts ADDED
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Utility functions for working with ekoDB records
3
+ */
4
+
5
+ /**
6
+ * Extract the value from an ekoDB field object.
7
+ * ekoDB returns fields as { type: string, value: any } objects.
8
+ * This helper safely extracts the value or returns the input if it's not a field object.
9
+ *
10
+ * @param field - The field value from ekoDB (may be { type, value } or a plain value)
11
+ * @returns The extracted value
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const user = await client.findByID('users', userId);
16
+ * const email = getValue(user.email); // Extracts string from { type: 'String', value: 'user@example.com' }
17
+ * const age = getValue(user.age); // Extracts number from { type: 'Integer', value: 25 }
18
+ * ```
19
+ */
20
+ export function getValue<T = any>(field: any): T {
21
+ if (field && typeof field === "object" && "value" in field) {
22
+ return field.value as T;
23
+ }
24
+ return field as T;
25
+ }
26
+
27
+ /**
28
+ * Extract values from multiple fields in a record.
29
+ * Useful for processing entire records returned from ekoDB.
30
+ *
31
+ * @param record - The record object from ekoDB
32
+ * @param fields - Array of field names to extract values from
33
+ * @returns Object with extracted values
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const user = await client.findByID('users', userId);
38
+ * const { email, first_name, status } = getValues(user, ['email', 'first_name', 'status']);
39
+ * ```
40
+ */
41
+ export function getValues<T extends Record<string, any>>(
42
+ record: any,
43
+ fields: (keyof T)[],
44
+ ): Partial<T> {
45
+ const result: any = {};
46
+ for (const field of fields) {
47
+ result[field] = getValue(record[field]);
48
+ }
49
+ return result;
50
+ }
51
+
52
+ /**
53
+ * Extract a Date value from an ekoDB DateTime field
54
+ */
55
+ export function getDateTimeValue(field: any): Date | null {
56
+ const val = getValue(field);
57
+ if (val instanceof Date) return val;
58
+ if (typeof val === "string") {
59
+ const date = new Date(val);
60
+ return isNaN(date.getTime()) ? null : date;
61
+ }
62
+ return null;
63
+ }
64
+
65
+ /**
66
+ * Extract a UUID string from an ekoDB UUID field
67
+ */
68
+ export function getUUIDValue(field: any): string | null {
69
+ const val = getValue(field);
70
+ return typeof val === "string" ? val : null;
71
+ }
72
+
73
+ /**
74
+ * Extract a number from an ekoDB Decimal field
75
+ */
76
+ export function getDecimalValue(field: any): number | null {
77
+ const val = getValue(field);
78
+ if (typeof val === "number") return val;
79
+ if (typeof val === "string") {
80
+ const num = parseFloat(val);
81
+ return isNaN(num) ? null : num;
82
+ }
83
+ return null;
84
+ }
85
+
86
+ /**
87
+ * Extract a number (milliseconds) from an ekoDB Duration field
88
+ */
89
+ export function getDurationValue(field: any): number | null {
90
+ const val = getValue(field);
91
+ if (typeof val === "number") return val;
92
+ if (typeof val === "object" && val.secs !== undefined) {
93
+ return val.secs * 1000 + val.nanos / 1_000_000;
94
+ }
95
+ return null;
96
+ }
97
+
98
+ /**
99
+ * Extract a Uint8Array from an ekoDB Bytes field
100
+ */
101
+ export function getBytesValue(field: any): Uint8Array | null {
102
+ const val = getValue(field);
103
+ if (val instanceof Uint8Array) return val;
104
+ if (Array.isArray(val)) return new Uint8Array(val);
105
+ if (typeof val === "string") {
106
+ // Assume base64 encoded
107
+ try {
108
+ const binary = atob(val);
109
+ const bytes = new Uint8Array(binary.length);
110
+ for (let i = 0; i < binary.length; i++) {
111
+ bytes[i] = binary.charCodeAt(i);
112
+ }
113
+ return bytes;
114
+ } catch {
115
+ return null;
116
+ }
117
+ }
118
+ return null;
119
+ }
120
+
121
+ /**
122
+ * Extract a Uint8Array from an ekoDB Binary field
123
+ */
124
+ export function getBinaryValue(field: any): Uint8Array | null {
125
+ return getBytesValue(field);
126
+ }
127
+
128
+ /**
129
+ * Extract an array from an ekoDB Array field
130
+ */
131
+ export function getArrayValue<T = any>(field: any): T[] | null {
132
+ const val = getValue(field);
133
+ return Array.isArray(val) ? val : null;
134
+ }
135
+
136
+ /**
137
+ * Extract an array from an ekoDB Set field
138
+ */
139
+ export function getSetValue<T = any>(field: any): T[] | null {
140
+ const val = getValue(field);
141
+ return Array.isArray(val) ? val : null;
142
+ }
143
+
144
+ /**
145
+ * Extract an array from an ekoDB Vector field
146
+ */
147
+ export function getVectorValue(field: any): number[] | null {
148
+ const val = getValue(field);
149
+ if (Array.isArray(val)) {
150
+ return val
151
+ .map((v) => (typeof v === "number" ? v : parseFloat(v)))
152
+ .filter((v) => !isNaN(v));
153
+ }
154
+ return null;
155
+ }
156
+
157
+ /**
158
+ * Extract an object from an ekoDB Object field
159
+ */
160
+ export function getObjectValue<T = any>(field: any): T | null {
161
+ const val = getValue(field);
162
+ return val && typeof val === "object" && !Array.isArray(val) ? val : null;
163
+ }
164
+
165
+ /**
166
+ * Transform an entire record by extracting all field values.
167
+ * Preserves the 'id' field and extracts values from all other fields.
168
+ *
169
+ * @param record - The record object from ekoDB
170
+ * @returns Object with all field values extracted
171
+ *
172
+ * @example
173
+ * ```typescript
174
+ * const user = await client.findByID('users', userId);
175
+ * const plainUser = extractRecord(user);
176
+ * // { id: '123', email: 'user@example.com', first_name: 'John', ... }
177
+ * ```
178
+ */
179
+ export function extractRecord<T extends Record<string, any>>(record: any): T {
180
+ if (!record || typeof record !== "object") {
181
+ return record;
182
+ }
183
+
184
+ const result: any = {};
185
+ for (const [key, value] of Object.entries(record)) {
186
+ if (key === "id") {
187
+ result[key] = value;
188
+ } else {
189
+ result[key] = getValue(value);
190
+ }
191
+ }
192
+ return result as T;
193
+ }