@ekodb/ekodb-client 0.15.2 → 0.17.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/src/functions.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  /**
2
- * Scripts API for ekoDB TypeScript client
2
+ * Functions API for ekoDB TypeScript client
3
3
  */
4
4
 
5
- export interface Script {
5
+ /** A reusable sequence of Functions stored in ekoDB. */
6
+ export interface UserFunction {
7
+ id?: string;
6
8
  label: string;
7
9
  name: string;
8
10
  description?: string;
@@ -146,7 +148,7 @@ export type FunctionStageConfig =
146
148
  }
147
149
  | {
148
150
  type: "If";
149
- condition: ScriptCondition;
151
+ condition: FunctionCondition;
150
152
  then_functions: FunctionStageConfig[];
151
153
  else_functions?: FunctionStageConfig[];
152
154
  }
@@ -224,6 +226,80 @@ export type FunctionStageConfig =
224
226
  timeout_seconds?: number;
225
227
  output_field?: string;
226
228
  collection?: string;
229
+ }
230
+ | {
231
+ /**
232
+ * Bcrypt-hash a plaintext value and add the result to every record in
233
+ * the working data as `output_field`. Requires ekoDB >= 0.41.0.
234
+ */
235
+ type: "BcryptHash";
236
+ plain: string;
237
+ cost?: number;
238
+ output_field: string;
239
+ }
240
+ | {
241
+ /**
242
+ * Verify a plaintext against a bcrypt hash stored on the first record
243
+ * in the working data and write a boolean result into `output_field`.
244
+ * Pair with an `If` stage for login flows. Requires ekoDB >= 0.41.0.
245
+ */
246
+ type: "BcryptVerify";
247
+ plain: string;
248
+ hash_field: string;
249
+ output_field: string;
250
+ }
251
+ | {
252
+ /**
253
+ * Generate a cryptographically-random token and add it to every
254
+ * record in the working data. Requires ekoDB >= 0.41.0.
255
+ */
256
+ type: "RandomToken";
257
+ bytes: number;
258
+ encoding?: "hex" | "base64" | "base64url";
259
+ output_field: string;
260
+ }
261
+ | {
262
+ /**
263
+ * Try/Catch error handling for graceful failure recovery.
264
+ * Executes try_functions, and if any fail, executes catch_functions.
265
+ */
266
+ type: "TryCatch";
267
+ try_functions: FunctionStageConfig[];
268
+ catch_functions: FunctionStageConfig[];
269
+ output_error_field?: string;
270
+ }
271
+ | {
272
+ /**
273
+ * Execute multiple functions in parallel (concurrently).
274
+ * All functions run simultaneously, results are merged.
275
+ */
276
+ type: "Parallel";
277
+ functions: FunctionStageConfig[];
278
+ wait_for_all: boolean;
279
+ }
280
+ | {
281
+ /** Sleep/delay execution for rate limiting or timing control. */
282
+ type: "Sleep";
283
+ duration_ms: string | number;
284
+ }
285
+ | {
286
+ /**
287
+ * Return a shaped response (final output formatting).
288
+ * Constructs the final response object from current execution context.
289
+ */
290
+ type: "Return";
291
+ fields: Record<string, any>;
292
+ status_code?: number;
293
+ }
294
+ | {
295
+ /**
296
+ * Validate data against a JSON schema before processing.
297
+ * Prevents invalid data from corrupting database or causing errors downstream.
298
+ */
299
+ type: "Validate";
300
+ schema: Record<string, any>;
301
+ data_field: string;
302
+ on_error?: FunctionStageConfig[];
227
303
  };
228
304
 
229
305
  export interface ChatMessage {
@@ -265,18 +341,18 @@ export interface SortFieldConfig {
265
341
  ascending: boolean;
266
342
  }
267
343
 
268
- // ScriptCondition uses adjacently-tagged format: { type: "...", value: { ...data } }
344
+ // FunctionCondition uses adjacently-tagged format: { type: "...", value: { ...data } }
269
345
  // Unit variants (HasRecords) have no value field
270
- export type ScriptCondition =
346
+ export type FunctionCondition =
271
347
  | { type: "FieldEquals"; value: { field: string; value: any } }
272
348
  | { type: "FieldExists"; value: { field: string } }
273
349
  | { type: "HasRecords" }
274
350
  | { type: "CountEquals"; value: { count: number } }
275
351
  | { type: "CountGreaterThan"; value: { count: number } }
276
352
  | { type: "CountLessThan"; value: { count: number } }
277
- | { type: "And"; value: { conditions: ScriptCondition[] } }
278
- | { type: "Or"; value: { conditions: ScriptCondition[] } }
279
- | { type: "Not"; value: { condition: ScriptCondition } };
353
+ | { type: "And"; value: { conditions: FunctionCondition[] } }
354
+ | { type: "Or"; value: { conditions: FunctionCondition[] } }
355
+ | { type: "Not"; value: { condition: FunctionCondition } };
280
356
 
281
357
  export interface FunctionResult {
282
358
  records: Record<string, any>[];
@@ -298,8 +374,54 @@ export interface StageStats {
298
374
  execution_time_ms: number;
299
375
  }
300
376
 
377
+ /**
378
+ * Reference a call-time function parameter inside a stored-function stage
379
+ * body (Insert.record, Update.updates, UpdateById.updates,
380
+ * FindOneAndUpdate.updates, BatchInsert.records, or any nested JSON value).
381
+ *
382
+ * Returns the structural placeholder `{"type": "Parameter", "name": "<name>"}`.
383
+ * ekoDB's `resolve_json_parameters` recognizes this shape and substitutes the
384
+ * actual parameter value at execution time, preserving the original FieldType
385
+ * (Binary, DateTime, UUID, Decimal, Duration, Number, Set, Vector) via the
386
+ * `{type,value}` wrapped form. Safe to use for any type.
387
+ *
388
+ * This is the structural alternative to the text-level `"{{name}}"` form;
389
+ * both are accepted but structural placeholders are preferred when the
390
+ * parameter is a whole-object Record or a value whose type would be lost in
391
+ * raw JSON.
392
+ *
393
+ * @example
394
+ * ```ts
395
+ * const createUser: UserFunction = {
396
+ * label: "users_create",
397
+ * name: "Create user",
398
+ * parameters: {
399
+ * record: { required: true },
400
+ * },
401
+ * functions: [
402
+ * Stage.insert("users", Stage.param("record")),
403
+ * ],
404
+ * };
405
+ * ```
406
+ */
407
+ export interface ParameterRef {
408
+ type: "Parameter";
409
+ name: string;
410
+ }
411
+
412
+ export function parameterRef(name: string): ParameterRef {
413
+ return { type: "Parameter", name };
414
+ }
415
+
301
416
  // Stage builder functions
302
417
  export const Stage = {
418
+ /**
419
+ * Shorthand for `parameterRef(name)` — builds the structural placeholder
420
+ * `{"type": "Parameter", "name": name}`. See `parameterRef` for the full
421
+ * explanation and example.
422
+ */
423
+ param: (name: string): ParameterRef => parameterRef(name),
424
+
303
425
  findAll: (collection: string): FunctionStageConfig => ({
304
426
  type: "FindAll",
305
427
  collection,
@@ -342,7 +464,7 @@ export const Stage = {
342
464
 
343
465
  insert: (
344
466
  collection: string,
345
- record: Record<string, any>,
467
+ record: Record<string, any> | ParameterRef,
346
468
  bypassRipple = false,
347
469
  ttl?: number,
348
470
  ): FunctionStageConfig => ({
@@ -356,7 +478,7 @@ export const Stage = {
356
478
  update: (
357
479
  collection: string,
358
480
  filter: Record<string, any>,
359
- updates: Record<string, any>,
481
+ updates: Record<string, any> | ParameterRef,
360
482
  bypassRipple = false,
361
483
  ttl?: number,
362
484
  ): FunctionStageConfig => ({
@@ -371,7 +493,7 @@ export const Stage = {
371
493
  updateById: (
372
494
  collection: string,
373
495
  record_id: string,
374
- updates: Record<string, any>,
496
+ updates: Record<string, any> | ParameterRef,
375
497
  bypassRipple = false,
376
498
  ttl?: number,
377
499
  ): FunctionStageConfig => ({
@@ -541,7 +663,7 @@ export const Stage = {
541
663
  }),
542
664
 
543
665
  if: (
544
- condition: ScriptCondition,
666
+ condition: FunctionCondition,
545
667
  thenFunctions: FunctionStageConfig[],
546
668
  elseFunctions?: FunctionStageConfig[],
547
669
  ): FunctionStageConfig => ({
@@ -568,7 +690,7 @@ export const Stage = {
568
690
  findOneAndUpdate: (
569
691
  collection: string,
570
692
  record_id: string,
571
- updates: Record<string, any>,
693
+ updates: Record<string, any> | ParameterRef,
572
694
  bypassRipple = false,
573
695
  ttl?: number,
574
696
  ): FunctionStageConfig => ({
@@ -682,4 +804,145 @@ export const Stage = {
682
804
  output_field,
683
805
  collection,
684
806
  }),
807
+
808
+ /**
809
+ * Bcrypt-hash a plaintext value and write the result into every record
810
+ * in the working data as `output_field`. Requires ekoDB >= 0.41.0.
811
+ *
812
+ * @param plain - Plaintext to hash. Typically a `"{{password}}"`
813
+ * placeholder that the substituter replaces with the call-time param
814
+ * before this stage runs.
815
+ * @param output_field - Field name to write the bcrypt hash into.
816
+ * @param cost - bcrypt cost factor (4..=31). Defaults to 12 when undefined.
817
+ */
818
+ bcryptHash: (
819
+ plain: string,
820
+ output_field: string,
821
+ cost?: number,
822
+ ): FunctionStageConfig => ({
823
+ type: "BcryptHash",
824
+ plain,
825
+ cost,
826
+ output_field,
827
+ }),
828
+
829
+ /**
830
+ * Verify a plaintext against a bcrypt hash stored on the first record in
831
+ * the working data. Writes a boolean into `output_field` on every
832
+ * working record. Pair with `Stage.if` to branch on success / failure.
833
+ * Requires ekoDB >= 0.41.0.
834
+ *
835
+ * @param plain - Plaintext to verify (typically `"{{password}}"`).
836
+ * @param hash_field - Name of the field on the current record that
837
+ * holds the stored bcrypt hash (e.g. `"password_hash"`).
838
+ * @param output_field - Field name to write the boolean result into.
839
+ */
840
+ bcryptVerify: (
841
+ plain: string,
842
+ hash_field: string,
843
+ output_field: string,
844
+ ): FunctionStageConfig => ({
845
+ type: "BcryptVerify",
846
+ plain,
847
+ hash_field,
848
+ output_field,
849
+ }),
850
+
851
+ /**
852
+ * Generate a cryptographically-random token and add it to every record
853
+ * in the working data. Requires ekoDB >= 0.41.0.
854
+ *
855
+ * @param bytes - Number of random bytes to draw (1..=1024).
856
+ * @param output_field - Field name to write the encoded token into.
857
+ * @param encoding - `"hex"` (default) | `"base64"` | `"base64url"`.
858
+ */
859
+ randomToken: (
860
+ bytes: number,
861
+ output_field: string,
862
+ encoding?: "hex" | "base64" | "base64url",
863
+ ): FunctionStageConfig => ({
864
+ type: "RandomToken",
865
+ bytes,
866
+ encoding,
867
+ output_field,
868
+ }),
869
+
870
+ /**
871
+ * Try/Catch error handling for graceful failure recovery.
872
+ * Executes tryFunctions, and if any fail, executes catchFunctions.
873
+ *
874
+ * @param tryFunctions - Functions to attempt.
875
+ * @param catchFunctions - Functions to execute on failure.
876
+ * @param outputErrorField - Field name to store error details (default: "error").
877
+ */
878
+ tryCatch: (
879
+ tryFunctions: FunctionStageConfig[],
880
+ catchFunctions: FunctionStageConfig[],
881
+ outputErrorField?: string,
882
+ ): FunctionStageConfig => ({
883
+ type: "TryCatch",
884
+ try_functions: tryFunctions,
885
+ catch_functions: catchFunctions,
886
+ output_error_field: outputErrorField,
887
+ }),
888
+
889
+ /**
890
+ * Execute multiple functions in parallel (concurrently).
891
+ * All functions run simultaneously, results are merged.
892
+ *
893
+ * @param functions - Functions to execute concurrently.
894
+ * @param waitForAll - true = wait for all to complete, false = return on first completion.
895
+ */
896
+ parallel: (
897
+ functions: FunctionStageConfig[],
898
+ waitForAll = true,
899
+ ): FunctionStageConfig => ({
900
+ type: "Parallel",
901
+ functions,
902
+ wait_for_all: waitForAll,
903
+ }),
904
+
905
+ /**
906
+ * Sleep/delay execution for rate limiting or timing control.
907
+ *
908
+ * @param durationMs - Duration in milliseconds: `1000` or `"{{delay_param}}"`.
909
+ */
910
+ sleep: (durationMs: string | number): FunctionStageConfig => ({
911
+ type: "Sleep",
912
+ duration_ms: durationMs,
913
+ }),
914
+
915
+ /**
916
+ * Return a shaped response (final output formatting).
917
+ * Constructs the final response object from current execution context.
918
+ *
919
+ * @param fields - Fields to include in response with `{{param}}` substitution.
920
+ * @param statusCode - HTTP status code (default: 200).
921
+ */
922
+ returnResponse: (
923
+ fields: Record<string, any>,
924
+ statusCode?: number,
925
+ ): FunctionStageConfig => ({
926
+ type: "Return",
927
+ fields,
928
+ status_code: statusCode,
929
+ }),
930
+
931
+ /**
932
+ * Validate data against a JSON schema before processing.
933
+ *
934
+ * @param schema - JSON Schema to validate against.
935
+ * @param dataField - Field containing data to validate.
936
+ * @param onError - Functions to execute on validation failure.
937
+ */
938
+ validate: (
939
+ schema: Record<string, any>,
940
+ dataField: string,
941
+ onError?: FunctionStageConfig[],
942
+ ): FunctionStageConfig => ({
943
+ type: "Validate",
944
+ schema,
945
+ data_field: dataField,
946
+ on_error: onError,
947
+ }),
685
948
  };
package/src/index.ts CHANGED
@@ -17,7 +17,8 @@ export {
17
17
  DistanceMetric,
18
18
  } from "./schema";
19
19
  export { JoinBuilder } from "./join";
20
- export { Stage, ChatMessage } from "./functions";
20
+ export { Stage, ChatMessage, parameterRef } from "./functions";
21
+ export type { ParameterRef } from "./functions";
21
22
  export {
22
23
  getValue,
23
24
  getValues,
@@ -44,7 +45,7 @@ export type {
44
45
  } from "./schema";
45
46
  export type { JoinConfig } from "./join";
46
47
  export type {
47
- Script,
48
+ UserFunction,
48
49
  ParameterDefinition,
49
50
  FunctionStageConfig,
50
51
  GroupFunctionConfig,
@@ -85,7 +86,6 @@ export type {
85
86
  EmbedResponse,
86
87
  RawCompletionRequest,
87
88
  RawCompletionResponse,
88
- UserFunction,
89
89
  ToolChoice,
90
90
  ToolConfig,
91
91
  } from "./client";