@ekodb/ekodb-client 0.17.0 → 0.18.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
@@ -14,6 +14,19 @@ export interface UserFunction {
14
14
  tags?: string[];
15
15
  created_at?: string;
16
16
  updated_at?: string;
17
+ /**
18
+ * REST method this function answers — `"GET"`, `"POST"`, etc.
19
+ * Pair with `http_path` to expose the function under the
20
+ * path-routed dispatcher at `/api/route/{path}`.
21
+ * Requires ekoDB >= 0.42.0.
22
+ */
23
+ http_method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
24
+ /**
25
+ * REST path pattern (e.g. `"/users/:id"`). Path segments
26
+ * starting with `:` are extracted into the function's params
27
+ * map at call time. Requires ekoDB >= 0.42.0.
28
+ */
29
+ http_path?: string;
17
30
  }
18
31
 
19
32
  export interface ParameterDefinition {
@@ -258,6 +271,186 @@ export type FunctionStageConfig =
258
271
  encoding?: "hex" | "base64" | "base64url";
259
272
  output_field: string;
260
273
  }
274
+ | {
275
+ /**
276
+ * Sign a JWT and write the resulting token to every working
277
+ * record. Pair with `BcryptVerify` to issue a session token
278
+ * after login. Use `"{{env.JWT_SECRET}}"` for `secret` so the
279
+ * LLM never sees the operator-owned signing key. `iat` and
280
+ * `exp` are auto-stamped when `expires_in_secs` is set.
281
+ * Requires ekoDB >= 0.42.0.
282
+ */
283
+ type: "JwtSign";
284
+ claims: Record<string, unknown>;
285
+ secret: string;
286
+ algorithm?: "HS256" | "HS384" | "HS512";
287
+ expires_in_secs?: number;
288
+ output_field: string;
289
+ }
290
+ | {
291
+ /**
292
+ * Verify a JWT held in `token_field` on the first working
293
+ * record. On success, writes the decoded claims object into
294
+ * `output_field`. On failure, writes `null` so callers can
295
+ * branch with `If { FieldEquals { value: null } }` to reject.
296
+ * Requires ekoDB >= 0.42.0.
297
+ */
298
+ type: "JwtVerify";
299
+ token_field: string;
300
+ secret: string;
301
+ algorithm?: "HS256" | "HS384" | "HS512";
302
+ output_field: string;
303
+ }
304
+ | {
305
+ /**
306
+ * Send a transactional email through a provider's REST API.
307
+ * Today only `provider = "sendgrid"` is supported. Pull the
308
+ * API key from `"{{env.SENDGRID_API_KEY}}"` so the LLM never
309
+ * sees the operator-owned secret. Result envelope
310
+ * `{provider_status, provider_message, provider}` is written
311
+ * to `output_field` (defaults to `"email_send"`).
312
+ * Requires ekoDB >= 0.42.0.
313
+ */
314
+ type: "EmailSend";
315
+ to: string;
316
+ subject: string;
317
+ body: string;
318
+ from: string;
319
+ reply_to?: string;
320
+ api_key: string;
321
+ provider?: "sendgrid";
322
+ html?: boolean;
323
+ output_field?: string;
324
+ }
325
+ | {
326
+ /** HMAC-SHA256/384/512 sign. Requires ekoDB >= 0.42.0. */
327
+ type: "HmacSign";
328
+ input: string;
329
+ secret: string;
330
+ algorithm?: "sha256" | "sha384" | "sha512";
331
+ output_field: string;
332
+ encoding?: "hex" | "base64";
333
+ }
334
+ | {
335
+ /** HMAC verify (constant-time). Writes a boolean. */
336
+ type: "HmacVerify";
337
+ input: string;
338
+ provided_mac: string;
339
+ secret: string;
340
+ algorithm?: "sha256" | "sha384" | "sha512";
341
+ encoding?: "hex" | "base64";
342
+ output_field: string;
343
+ }
344
+ | {
345
+ /** AES-256-GCM authenticated encryption. */
346
+ type: "AesEncrypt";
347
+ plaintext: string;
348
+ key: string;
349
+ key_encoding?: "hex" | "base64" | "base64url";
350
+ output_field: string;
351
+ }
352
+ | {
353
+ /** AES-256-GCM decrypt. Reads `{ciphertext, nonce}` envelope from `ciphertext_field`. */
354
+ type: "AesDecrypt";
355
+ ciphertext_field: string;
356
+ key: string;
357
+ key_encoding?: "hex" | "base64" | "base64url";
358
+ output_field: string;
359
+ }
360
+ | {
361
+ /** Generate a v4 UUID into `output_field`. */
362
+ type: "UuidGenerate";
363
+ output_field: string;
364
+ }
365
+ | {
366
+ /** TOTP code generation (RFC 6238). */
367
+ type: "TotpGenerate";
368
+ secret: string;
369
+ digits?: 6 | 8;
370
+ period?: number;
371
+ algorithm?: "sha1" | "sha256" | "sha512";
372
+ output_field: string;
373
+ }
374
+ | {
375
+ /** TOTP verify; tolerates `skew` time-steps either side. */
376
+ type: "TotpVerify";
377
+ code: string;
378
+ secret: string;
379
+ digits?: 6 | 8;
380
+ period?: number;
381
+ algorithm?: "sha1" | "sha256" | "sha512";
382
+ skew?: number;
383
+ output_field: string;
384
+ }
385
+ | {
386
+ /** Base64 encode (`url_safe = true` for URL-safe / no-pad). */
387
+ type: "Base64Encode";
388
+ input: string;
389
+ url_safe?: boolean;
390
+ output_field: string;
391
+ }
392
+ | {
393
+ /** Base64 decode → UTF-8 string. Fail-closed. */
394
+ type: "Base64Decode";
395
+ input: string;
396
+ url_safe?: boolean;
397
+ output_field: string;
398
+ }
399
+ | {
400
+ /** Hex encode (lowercase). */
401
+ type: "HexEncode";
402
+ input: string;
403
+ output_field: string;
404
+ }
405
+ | {
406
+ /** Hex decode → UTF-8 string. Fail-closed. */
407
+ type: "HexDecode";
408
+ input: string;
409
+ output_field: string;
410
+ }
411
+ | {
412
+ /** URL-friendly slug. */
413
+ type: "Slugify";
414
+ input: string;
415
+ output_field: string;
416
+ }
417
+ | {
418
+ /**
419
+ * Idempotency-key claim (KV SETNX with TTL). Writes
420
+ * `{claimed: true, key}` on first call, `{claimed: false, key,
421
+ * response}` on replay. Requires ekoDB >= 0.42.0.
422
+ */
423
+ type: "IdempotencyClaim";
424
+ key: string;
425
+ ttl_secs: number;
426
+ output_field: string;
427
+ }
428
+ | {
429
+ /**
430
+ * Fixed-window rate-limit gate. `on_exceed` either errors
431
+ * (`"fail"`, default) or writes `allowed: false` (`"skip"`).
432
+ */
433
+ type: "RateLimit";
434
+ key: string;
435
+ limit: number;
436
+ window_secs: number;
437
+ on_exceed?: "fail" | "skip";
438
+ output_field: string;
439
+ }
440
+ | {
441
+ /** Distributed-lock acquire (token-fenced). */
442
+ type: "LockAcquire";
443
+ key: string;
444
+ ttl_secs: number;
445
+ output_field: string;
446
+ }
447
+ | {
448
+ /** Distributed-lock release; token-fenced (no foreign release). */
449
+ type: "LockRelease";
450
+ key: string;
451
+ token: string;
452
+ output_field: string;
453
+ }
261
454
  | {
262
455
  /**
263
456
  * Try/Catch error handling for graceful failure recovery.
@@ -867,6 +1060,93 @@ export const Stage = {
867
1060
  output_field,
868
1061
  }),
869
1062
 
1063
+ /**
1064
+ * Sign a JWT and write the resulting token to every working
1065
+ * record. Pair with `Stage.bcryptVerify` to issue a session
1066
+ * token after login. Use `"{{env.JWT_SECRET}}"` for `secret` so
1067
+ * the LLM never sees the operator-owned signing key. `iat` and
1068
+ * `exp` are auto-stamped when `expires_in_secs` is set.
1069
+ * Requires ekoDB >= 0.42.0.
1070
+ *
1071
+ * @param claims - JWT payload claims.
1072
+ * @param secret - Signing secret (typically `"{{env.JWT_SECRET}}"`).
1073
+ * @param output_field - Field name to write the signed JWT into.
1074
+ * @param expires_in_secs - Lifetime in seconds (auto-stamps `iat` + `exp`).
1075
+ * @param algorithm - `"HS256"` (default) | `"HS384"` | `"HS512"`.
1076
+ */
1077
+ jwtSign: (
1078
+ claims: Record<string, unknown>,
1079
+ secret: string,
1080
+ output_field: string,
1081
+ expires_in_secs?: number,
1082
+ algorithm?: "HS256" | "HS384" | "HS512",
1083
+ ): FunctionStageConfig => ({
1084
+ type: "JwtSign",
1085
+ claims,
1086
+ secret,
1087
+ algorithm,
1088
+ expires_in_secs,
1089
+ output_field,
1090
+ }),
1091
+
1092
+ /**
1093
+ * Verify a JWT held in `token_field` on the first working record.
1094
+ * On success writes the decoded claims object into `output_field`;
1095
+ * on failure writes `null`. Branch with `Stage.if` matching
1096
+ * `output_field == null` to reject. Requires ekoDB >= 0.42.0.
1097
+ *
1098
+ * @param token_field - Field on the working record holding the JWT.
1099
+ * @param secret - Verification secret (must match the signing secret).
1100
+ * @param output_field - Field name to write decoded claims into.
1101
+ * @param algorithm - Expected algorithm (default `"HS256"`).
1102
+ */
1103
+ jwtVerify: (
1104
+ token_field: string,
1105
+ secret: string,
1106
+ output_field: string,
1107
+ algorithm?: "HS256" | "HS384" | "HS512",
1108
+ ): FunctionStageConfig => ({
1109
+ type: "JwtVerify",
1110
+ token_field,
1111
+ secret,
1112
+ algorithm,
1113
+ output_field,
1114
+ }),
1115
+
1116
+ /**
1117
+ * Send a transactional email. Today only the `"sendgrid"`
1118
+ * provider is supported. Use `"{{env.SENDGRID_API_KEY}}"` for
1119
+ * `api_key` so the LLM never sees the operator-owned secret.
1120
+ * Set `html: true` to send `text/html`. The result envelope
1121
+ * (`{provider_status, provider_message, provider}`) is written
1122
+ * to `output_field` (default `"email_send"`).
1123
+ * Requires ekoDB >= 0.42.0.
1124
+ */
1125
+ emailSend: (
1126
+ to: string,
1127
+ subject: string,
1128
+ body: string,
1129
+ from: string,
1130
+ api_key: string,
1131
+ options?: {
1132
+ reply_to?: string;
1133
+ provider?: "sendgrid";
1134
+ html?: boolean;
1135
+ output_field?: string;
1136
+ },
1137
+ ): FunctionStageConfig => ({
1138
+ type: "EmailSend",
1139
+ to,
1140
+ subject,
1141
+ body,
1142
+ from,
1143
+ reply_to: options?.reply_to,
1144
+ api_key,
1145
+ provider: options?.provider,
1146
+ html: options?.html,
1147
+ output_field: options?.output_field,
1148
+ }),
1149
+
870
1150
  /**
871
1151
  * Try/Catch error handling for graceful failure recovery.
872
1152
  * Executes tryFunctions, and if any fail, executes catchFunctions.
@@ -945,4 +1225,225 @@ export const Stage = {
945
1225
  data_field: dataField,
946
1226
  on_error: onError,
947
1227
  }),
1228
+
1229
+ /**
1230
+ * HMAC-SHA256/384/512 sign. Use for outbound webhook signing or
1231
+ * pre-signed URL generation. Requires ekoDB >= 0.42.0.
1232
+ */
1233
+ hmacSign: (
1234
+ input: string,
1235
+ secret: string,
1236
+ output_field: string,
1237
+ options?: {
1238
+ algorithm?: "sha256" | "sha384" | "sha512";
1239
+ encoding?: "hex" | "base64";
1240
+ },
1241
+ ): FunctionStageConfig => ({
1242
+ type: "HmacSign",
1243
+ input,
1244
+ secret,
1245
+ algorithm: options?.algorithm,
1246
+ output_field,
1247
+ encoding: options?.encoding,
1248
+ }),
1249
+
1250
+ /** HMAC verify (constant-time). Writes a boolean. */
1251
+ hmacVerify: (
1252
+ input: string,
1253
+ provided_mac: string,
1254
+ secret: string,
1255
+ output_field: string,
1256
+ options?: {
1257
+ algorithm?: "sha256" | "sha384" | "sha512";
1258
+ encoding?: "hex" | "base64";
1259
+ },
1260
+ ): FunctionStageConfig => ({
1261
+ type: "HmacVerify",
1262
+ input,
1263
+ provided_mac,
1264
+ secret,
1265
+ algorithm: options?.algorithm,
1266
+ encoding: options?.encoding,
1267
+ output_field,
1268
+ }),
1269
+
1270
+ /** AES-256-GCM encrypt; writes `{ciphertext, nonce}` envelope. */
1271
+ aesEncrypt: (
1272
+ plaintext: string,
1273
+ key: string,
1274
+ output_field: string,
1275
+ key_encoding?: "hex" | "base64" | "base64url",
1276
+ ): FunctionStageConfig => ({
1277
+ type: "AesEncrypt",
1278
+ plaintext,
1279
+ key,
1280
+ key_encoding,
1281
+ output_field,
1282
+ }),
1283
+
1284
+ /** AES-256-GCM decrypt; reads envelope from `ciphertext_field`. */
1285
+ aesDecrypt: (
1286
+ ciphertext_field: string,
1287
+ key: string,
1288
+ output_field: string,
1289
+ key_encoding?: "hex" | "base64" | "base64url",
1290
+ ): FunctionStageConfig => ({
1291
+ type: "AesDecrypt",
1292
+ ciphertext_field,
1293
+ key,
1294
+ key_encoding,
1295
+ output_field,
1296
+ }),
1297
+
1298
+ /** Generate a v4 UUID into `output_field`. */
1299
+ uuidGenerate: (output_field: string): FunctionStageConfig => ({
1300
+ type: "UuidGenerate",
1301
+ output_field,
1302
+ }),
1303
+
1304
+ /** TOTP code generation (RFC 6238). */
1305
+ totpGenerate: (
1306
+ secret: string,
1307
+ output_field: string,
1308
+ options?: {
1309
+ digits?: 6 | 8;
1310
+ period?: number;
1311
+ algorithm?: "sha1" | "sha256" | "sha512";
1312
+ },
1313
+ ): FunctionStageConfig => ({
1314
+ type: "TotpGenerate",
1315
+ secret,
1316
+ digits: options?.digits,
1317
+ period: options?.period,
1318
+ algorithm: options?.algorithm,
1319
+ output_field,
1320
+ }),
1321
+
1322
+ /** TOTP verify; tolerates `skew` time-steps either side (default 1). */
1323
+ totpVerify: (
1324
+ code: string,
1325
+ secret: string,
1326
+ output_field: string,
1327
+ options?: {
1328
+ digits?: 6 | 8;
1329
+ period?: number;
1330
+ algorithm?: "sha1" | "sha256" | "sha512";
1331
+ skew?: number;
1332
+ },
1333
+ ): FunctionStageConfig => ({
1334
+ type: "TotpVerify",
1335
+ code,
1336
+ secret,
1337
+ digits: options?.digits,
1338
+ period: options?.period,
1339
+ algorithm: options?.algorithm,
1340
+ skew: options?.skew,
1341
+ output_field,
1342
+ }),
1343
+
1344
+ /** Base64 encode (`url_safe = true` for URL-safe / no-pad). */
1345
+ base64Encode: (
1346
+ input: string,
1347
+ output_field: string,
1348
+ url_safe?: boolean,
1349
+ ): FunctionStageConfig => ({
1350
+ type: "Base64Encode",
1351
+ input,
1352
+ url_safe,
1353
+ output_field,
1354
+ }),
1355
+
1356
+ /** Base64 decode → UTF-8 string. Fail-closed. */
1357
+ base64Decode: (
1358
+ input: string,
1359
+ output_field: string,
1360
+ url_safe?: boolean,
1361
+ ): FunctionStageConfig => ({
1362
+ type: "Base64Decode",
1363
+ input,
1364
+ url_safe,
1365
+ output_field,
1366
+ }),
1367
+
1368
+ /** Hex encode (lowercase). */
1369
+ hexEncode: (input: string, output_field: string): FunctionStageConfig => ({
1370
+ type: "HexEncode",
1371
+ input,
1372
+ output_field,
1373
+ }),
1374
+
1375
+ /** Hex decode → UTF-8 string. Fail-closed. */
1376
+ hexDecode: (input: string, output_field: string): FunctionStageConfig => ({
1377
+ type: "HexDecode",
1378
+ input,
1379
+ output_field,
1380
+ }),
1381
+
1382
+ /** URL-friendly slug. */
1383
+ slugify: (input: string, output_field: string): FunctionStageConfig => ({
1384
+ type: "Slugify",
1385
+ input,
1386
+ output_field,
1387
+ }),
1388
+
1389
+ /**
1390
+ * Idempotency-key claim (KV SETNX with TTL). Pass an idempotency
1391
+ * key (typically `"{{idempotency_key}}"`) and a TTL; first call
1392
+ * writes `{claimed: true, key}`, subsequent calls within the TTL
1393
+ * write `{claimed: false, key, response}` so the caller can
1394
+ * short-circuit. Requires ekoDB >= 0.42.0.
1395
+ */
1396
+ idempotencyClaim: (
1397
+ key: string,
1398
+ ttl_secs: number,
1399
+ output_field: string,
1400
+ ): FunctionStageConfig => ({
1401
+ type: "IdempotencyClaim",
1402
+ key,
1403
+ ttl_secs,
1404
+ output_field,
1405
+ }),
1406
+
1407
+ /**
1408
+ * Fixed-window rate-limit gate. `on_exceed` either errors
1409
+ * (`"fail"`, default) or writes `allowed: false` (`"skip"`).
1410
+ */
1411
+ rateLimit: (
1412
+ key: string,
1413
+ limit: number,
1414
+ window_secs: number,
1415
+ output_field: string,
1416
+ on_exceed?: "fail" | "skip",
1417
+ ): FunctionStageConfig => ({
1418
+ type: "RateLimit",
1419
+ key,
1420
+ limit,
1421
+ window_secs,
1422
+ on_exceed,
1423
+ output_field,
1424
+ }),
1425
+
1426
+ /** Distributed-lock acquire (token-fenced). */
1427
+ lockAcquire: (
1428
+ key: string,
1429
+ ttl_secs: number,
1430
+ output_field: string,
1431
+ ): FunctionStageConfig => ({
1432
+ type: "LockAcquire",
1433
+ key,
1434
+ ttl_secs,
1435
+ output_field,
1436
+ }),
1437
+
1438
+ /** Distributed-lock release; only releases on token match. */
1439
+ lockRelease: (
1440
+ key: string,
1441
+ token: string,
1442
+ output_field: string,
1443
+ ): FunctionStageConfig => ({
1444
+ type: "LockRelease",
1445
+ key,
1446
+ token,
1447
+ output_field,
1448
+ }),
948
1449
  };