@teamkeel/functions-runtime 0.452.1 → 0.454.2

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/index.cjs CHANGED
@@ -47,6 +47,7 @@ __export(index_exports, {
47
47
  RetryConstant: () => RetryConstant,
48
48
  STEP_STATUS: () => STEP_STATUS,
49
49
  STEP_TYPE: () => STEP_TYPE,
50
+ Signature: () => Signature,
50
51
  TaskAPI: () => TaskAPI,
51
52
  checkBuiltInPermissions: () => checkBuiltInPermissions,
52
53
  createFlowContext: () => createFlowContext,
@@ -587,9 +588,9 @@ var InstrumentedClient = class extends import_pg.Client {
587
588
  }
588
589
  async query(...args) {
589
590
  const _super = super.query.bind(this);
590
- const sql4 = args[0];
591
+ const sql5 = args[0];
591
592
  let sqlAttribute = false;
592
- let spanName = txStatements[sql4.toLowerCase()];
593
+ let spanName = txStatements[sql5.toLowerCase()];
593
594
  if (!spanName) {
594
595
  spanName = "Database Query";
595
596
  sqlAttribute = true;
@@ -657,9 +658,9 @@ function getDialect(connString) {
657
658
  pool.on("connect", (client) => {
658
659
  const originalQuery = client.query;
659
660
  client.query = function(...args) {
660
- const sql4 = args[0];
661
+ const sql5 = args[0];
661
662
  let sqlAttribute = false;
662
- let spanName = txStatements[sql4.toLowerCase()];
663
+ let spanName = txStatements[sql5.toLowerCase()];
663
664
  if (!spanName) {
664
665
  spanName = "Database Query";
665
666
  sqlAttribute = true;
@@ -1029,7 +1030,7 @@ function transformRichDataTypes(data) {
1029
1030
  } else if (value.key && value.size && value.filename && value.contentType) {
1030
1031
  row[key] = File.fromDbRecord(value);
1031
1032
  } else {
1032
- row[key] = value;
1033
+ row[key] = transformRichDataTypes(value);
1033
1034
  }
1034
1035
  } else {
1035
1036
  row[key] = value;
@@ -1150,23 +1151,23 @@ var TimePeriod = class _TimePeriod {
1150
1151
  return new _TimePeriod(period, value, offset, complete2);
1151
1152
  }
1152
1153
  periodStartSQL() {
1153
- let sql4 = "NOW()";
1154
+ let sql5 = "NOW()";
1154
1155
  if (this.offset !== 0) {
1155
- sql4 = `${sql4} + INTERVAL '${this.offset} ${this.period}'`;
1156
+ sql5 = `${sql5} + INTERVAL '${this.offset} ${this.period}'`;
1156
1157
  }
1157
1158
  if (this.complete) {
1158
- sql4 = `DATE_TRUNC('${this.period}', ${sql4})`;
1159
+ sql5 = `DATE_TRUNC('${this.period}', ${sql5})`;
1159
1160
  } else {
1160
- sql4 = `(${sql4})`;
1161
+ sql5 = `(${sql5})`;
1161
1162
  }
1162
- return sql4;
1163
+ return sql5;
1163
1164
  }
1164
1165
  periodEndSQL() {
1165
- let sql4 = this.periodStartSQL();
1166
+ let sql5 = this.periodStartSQL();
1166
1167
  if (this.value != 0) {
1167
- sql4 = `(${sql4} + INTERVAL '${this.value} ${this.period}')`;
1168
+ sql5 = `(${sql5} + INTERVAL '${this.value} ${this.period}')`;
1168
1169
  }
1169
- return sql4;
1170
+ return sql5;
1170
1171
  }
1171
1172
  };
1172
1173
 
@@ -2570,7 +2571,9 @@ var FlowRun = class _FlowRun {
2570
2571
  const name = spanNameForModelAPI(this._flowName, "refresh");
2571
2572
  return withSpan(name, async () => {
2572
2573
  const apiUrl = getApiUrl2();
2573
- const url = `${apiUrl}/flows/json/${this._flowName}/${this.id}`;
2574
+ const url = `${apiUrl}/flows/json/${encodeURIComponent(
2575
+ this._flowName
2576
+ )}/${encodeURIComponent(this.id)}`;
2574
2577
  const response = await fetch(url, {
2575
2578
  method: "GET",
2576
2579
  headers: buildHeaders2(this._identity, this._authToken)
@@ -2598,7 +2601,9 @@ var FlowRun = class _FlowRun {
2598
2601
  const name = spanNameForModelAPI(this._flowName, "cancel");
2599
2602
  return withSpan(name, async () => {
2600
2603
  const apiUrl = getApiUrl2();
2601
- const url = `${apiUrl}/flows/json/${this._flowName}/${this.id}/cancel`;
2604
+ const url = `${apiUrl}/flows/json/${encodeURIComponent(
2605
+ this._flowName
2606
+ )}/${encodeURIComponent(this.id)}/cancel`;
2602
2607
  const response = await fetch(url, {
2603
2608
  method: "POST",
2604
2609
  headers: buildHeaders2(this._identity, this._authToken)
@@ -2658,7 +2663,7 @@ var FlowsAPI = class _FlowsAPI {
2658
2663
  const name = spanNameForModelAPI(this._flowName, "start");
2659
2664
  return withSpan(name, async () => {
2660
2665
  const apiUrl = getApiUrl2();
2661
- const url = `${apiUrl}/flows/json/${this._flowName}`;
2666
+ const url = `${apiUrl}/flows/json/${encodeURIComponent(this._flowName)}`;
2662
2667
  const response = await fetch(url, {
2663
2668
  method: "POST",
2664
2669
  headers: buildHeaders2(this._identity, this._authToken),
@@ -2688,7 +2693,9 @@ var FlowsAPI = class _FlowsAPI {
2688
2693
  const name = spanNameForModelAPI(this._flowName, "get");
2689
2694
  return withSpan(name, async () => {
2690
2695
  const apiUrl = getApiUrl2();
2691
- const url = `${apiUrl}/flows/json/${this._flowName}/${runId}`;
2696
+ const url = `${apiUrl}/flows/json/${encodeURIComponent(
2697
+ this._flowName
2698
+ )}/${encodeURIComponent(runId)}`;
2692
2699
  const response = await fetch(url, {
2693
2700
  method: "GET",
2694
2701
  headers: buildHeaders2(this._identity, this._authToken)
@@ -2722,7 +2729,7 @@ var FlowsAPI = class _FlowsAPI {
2722
2729
  const params = new URLSearchParams();
2723
2730
  if (options.limit) params.set("limit", options.limit.toString());
2724
2731
  if (options.offset) params.set("offset", options.offset.toString());
2725
- const url = `${apiUrl}/flows/json/${this._flowName}${params.toString() ? "?" + params.toString() : ""}`;
2732
+ const url = `${apiUrl}/flows/json/${encodeURIComponent(this._flowName)}${params.toString() ? "?" + params.toString() : ""}`;
2726
2733
  const response = await fetch(url, {
2727
2734
  method: "GET",
2728
2735
  headers: buildHeaders2(this._identity, this._authToken)
@@ -3313,15 +3320,59 @@ var import_json_rpc_26 = require("json-rpc-2.0");
3313
3320
  var opentelemetry6 = __toESM(require("@opentelemetry/api"), 1);
3314
3321
 
3315
3322
  // src/tryExecuteFlow.js
3323
+ var import_kysely6 = require("kysely");
3316
3324
  function tryExecuteFlow(db, request, cb) {
3317
- return withDatabase(db, false, async () => {
3318
- return withAuditContext(request, async () => {
3319
- return cb();
3320
- });
3325
+ return withDatabase(db, false, async ({ sDb }) => {
3326
+ const runId = request?.meta?.runId;
3327
+ if (runId && sDb) {
3328
+ await import_kysely6.sql`SELECT pg_advisory_lock(hashtextextended(${runId}, 0))`.execute(
3329
+ sDb
3330
+ );
3331
+ }
3332
+ try {
3333
+ return await withAuditContext(request, async () => {
3334
+ return cb();
3335
+ });
3336
+ } finally {
3337
+ if (runId && sDb) {
3338
+ await import_kysely6.sql`SELECT pg_advisory_unlock(hashtextextended(${runId}, 0))`.execute(sDb).catch(() => void 0);
3339
+ }
3340
+ }
3321
3341
  });
3322
3342
  }
3323
3343
  __name(tryExecuteFlow, "tryExecuteFlow");
3324
3344
 
3345
+ // src/flows/ui/elements/input/signature.ts
3346
+ var Signature = class {
3347
+ static {
3348
+ __name(this, "Signature");
3349
+ }
3350
+ constructor(data) {
3351
+ this.svg = data.svg;
3352
+ this.signedAt = data.signedAt;
3353
+ }
3354
+ async toPngDataURL() {
3355
+ const sharp = (await import("sharp")).default;
3356
+ const png = await sharp(Buffer.from(this.svg), { density: 300 }).png().toBuffer();
3357
+ return `data:image/png;base64,${png.toString("base64")}`;
3358
+ }
3359
+ };
3360
+ var signatureInput = /* @__PURE__ */ __name((name, options) => {
3361
+ return {
3362
+ __type: "input",
3363
+ uiConfig: {
3364
+ __type: "ui.input.signature",
3365
+ name,
3366
+ label: options?.label || name,
3367
+ optional: options?.optional || false,
3368
+ disabled: options?.disabled || false,
3369
+ helpText: options?.helpText
3370
+ },
3371
+ validate: options?.validate,
3372
+ getData: /* @__PURE__ */ __name((x) => new Signature(x), "getData")
3373
+ };
3374
+ }, "signatureInput");
3375
+
3325
3376
  // src/flows/ui/elements/input/text.ts
3326
3377
  var textInput = /* @__PURE__ */ __name((name, options) => {
3327
3378
  return {
@@ -3453,6 +3504,28 @@ var selectOne = /* @__PURE__ */ __name((name, options) => {
3453
3504
  }, "selectOne");
3454
3505
 
3455
3506
  // src/flows/ui/page.ts
3507
+ async function applyElementGetData(content, data) {
3508
+ if (!data || typeof data !== "object" || !Array.isArray(content)) return data;
3509
+ for (const c of content) {
3510
+ const resolved = await c;
3511
+ if (!resolved || !("__type" in resolved)) continue;
3512
+ const name = resolved.uiConfig?.name;
3513
+ if (!name || !(name in data)) continue;
3514
+ if (resolved.__type === "input" && typeof resolved.getData === "function") {
3515
+ data[name] = resolved.getData(data[name]);
3516
+ } else if (resolved.__type === "iterator") {
3517
+ const rows = data[name];
3518
+ const inner = resolved.uiConfig?.content;
3519
+ if (Array.isArray(rows) && Array.isArray(inner)) {
3520
+ for (let i = 0; i < rows.length; i++) {
3521
+ rows[i] = await applyElementGetData(inner, rows[i]);
3522
+ }
3523
+ }
3524
+ }
3525
+ }
3526
+ return data;
3527
+ }
3528
+ __name(applyElementGetData, "applyElementGetData");
3456
3529
  async function callbackFn(elements, elementName, callbackName, data) {
3457
3530
  const element = elements.find(
3458
3531
  (el) => el.uiConfig && el.uiConfig.name === elementName
@@ -3909,6 +3982,87 @@ var fileInput = /* @__PURE__ */ __name((name, options) => {
3909
3982
  };
3910
3983
  }, "fileInput");
3911
3984
 
3985
+ // src/flows/ui/elements/input/imageCapture.ts
3986
+ var isMultiOptions = /* @__PURE__ */ __name((opts) => opts && opts.mode === "multi", "isMultiOptions");
3987
+ function validateEntry(entry, requireCaption, context7) {
3988
+ if (!entry?.file?.key) {
3989
+ return `${context7}: an image is required`;
3990
+ }
3991
+ if (requireCaption && !entry.caption?.trim()) {
3992
+ return `${context7}: a caption is required`;
3993
+ }
3994
+ return true;
3995
+ }
3996
+ __name(validateEntry, "validateEntry");
3997
+ var imageCapture = /* @__PURE__ */ __name((name, options) => {
3998
+ const requireCaption = options?.requireCaption ?? false;
3999
+ const mode = options?.mode ?? "single";
4000
+ const multi = isMultiOptions(options);
4001
+ const max = multi ? options?.max : void 0;
4002
+ const min = multi ? options?.min : void 0;
4003
+ return {
4004
+ __type: "input",
4005
+ uiConfig: {
4006
+ __type: "ui.input.imageCapture",
4007
+ name,
4008
+ label: options?.label || name,
4009
+ optional: options?.optional || false,
4010
+ disabled: options?.disabled || false,
4011
+ helpText: options?.helpText,
4012
+ mode,
4013
+ requireCaption,
4014
+ ...multi ? { max, min } : {}
4015
+ },
4016
+ validate: /* @__PURE__ */ __name(async (data, action) => {
4017
+ const optional = options?.optional ?? false;
4018
+ if (mode === "multi") {
4019
+ const list2 = data ?? [];
4020
+ if (list2.length === 0) {
4021
+ if (!optional) return "At least one image is required";
4022
+ } else {
4023
+ if (min !== void 0 && list2.length < min) {
4024
+ return `At least ${min} image${min === 1 ? "" : "s"} required`;
4025
+ }
4026
+ if (max !== void 0 && list2.length > max) {
4027
+ return `At most ${max} image${max === 1 ? "" : "s"} allowed`;
4028
+ }
4029
+ for (let i = 0; i < list2.length; i++) {
4030
+ const result = validateEntry(
4031
+ list2[i],
4032
+ requireCaption,
4033
+ `Image ${i + 1}`
4034
+ );
4035
+ if (result !== true) return result;
4036
+ }
4037
+ }
4038
+ } else {
4039
+ const entry = data;
4040
+ const hasFile = !!entry?.file?.key;
4041
+ if (!optional && !hasFile) {
4042
+ return "An image is required";
4043
+ }
4044
+ if (hasFile) {
4045
+ const result = validateEntry(entry, requireCaption, "Image");
4046
+ if (result !== true) return result;
4047
+ }
4048
+ }
4049
+ return options?.validate?.(
4050
+ data,
4051
+ action
4052
+ ) ?? true;
4053
+ }, "validate"),
4054
+ getData: /* @__PURE__ */ __name((x) => x, "getData"),
4055
+ getPresignedUploadURL: /* @__PURE__ */ __name((async (input) => {
4056
+ const file2 = new File(input);
4057
+ const url = await file2.getPresignedUploadUrl();
4058
+ return {
4059
+ url: url.toString(),
4060
+ key: file2.key
4061
+ };
4062
+ }), "getPresignedUploadURL")
4063
+ };
4064
+ }, "imageCapture");
4065
+
3912
4066
  // src/flows/ui/elements/iterator.ts
3913
4067
  var iterator = /* @__PURE__ */ __name((name, options) => {
3914
4068
  return {
@@ -4251,7 +4405,10 @@ function createFlowContext(runId, data, action, callback, element, spanId, ctx)
4251
4405
  if (step && step.status === "COMPLETED" /* COMPLETED */) {
4252
4406
  span.setAttribute(KEEL_INTERNAL_ATTR, KEEL_INTERNAL_CHILDREN);
4253
4407
  span.setAttribute("step.status", "COMPLETED" /* COMPLETED */);
4254
- const parsedData2 = transformRichDataTypes(step.value);
4408
+ const parsedData2 = await applyElementGetData(
4409
+ options.content,
4410
+ transformRichDataTypes(step.value)
4411
+ );
4255
4412
  if (step.action) {
4256
4413
  return { data: parsedData2, action: step.action };
4257
4414
  }
@@ -4328,7 +4485,10 @@ function createFlowContext(runId, data, action, callback, element, spanId, ctx)
4328
4485
  endTime: /* @__PURE__ */ new Date()
4329
4486
  }).where("id", "=", step.id).returningAll().executeTakeFirst();
4330
4487
  span.setAttribute("step.status", "COMPLETED" /* COMPLETED */);
4331
- const parsedData = transformRichDataTypes(data);
4488
+ const parsedData = await applyElementGetData(
4489
+ options.content,
4490
+ transformRichDataTypes(data)
4491
+ );
4332
4492
  if (action) {
4333
4493
  return { data: parsedData, action };
4334
4494
  }
@@ -4343,7 +4503,9 @@ function createFlowContext(runId, data, action, callback, element, spanId, ctx)
4343
4503
  dataGrid: dataGridInput,
4344
4504
  datePicker: datePickerInput,
4345
4505
  scan,
4346
- file: fileInput
4506
+ file: fileInput,
4507
+ imageCapture,
4508
+ signature: signatureInput
4347
4509
  },
4348
4510
  display: {
4349
4511
  divider,
@@ -4599,6 +4761,7 @@ __name(ksuid, "ksuid");
4599
4761
  RetryConstant,
4600
4762
  STEP_STATUS,
4601
4763
  STEP_TYPE,
4764
+ Signature,
4602
4765
  TaskAPI,
4603
4766
  checkBuiltInPermissions,
4604
4767
  createFlowContext,