@teamkeel/functions-runtime 0.452.0 → 0.453.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/dist/index.cjs CHANGED
@@ -587,9 +587,9 @@ var InstrumentedClient = class extends import_pg.Client {
587
587
  }
588
588
  async query(...args) {
589
589
  const _super = super.query.bind(this);
590
- const sql4 = args[0];
590
+ const sql5 = args[0];
591
591
  let sqlAttribute = false;
592
- let spanName = txStatements[sql4.toLowerCase()];
592
+ let spanName = txStatements[sql5.toLowerCase()];
593
593
  if (!spanName) {
594
594
  spanName = "Database Query";
595
595
  sqlAttribute = true;
@@ -657,9 +657,9 @@ function getDialect(connString) {
657
657
  pool.on("connect", (client) => {
658
658
  const originalQuery = client.query;
659
659
  client.query = function(...args) {
660
- const sql4 = args[0];
660
+ const sql5 = args[0];
661
661
  let sqlAttribute = false;
662
- let spanName = txStatements[sql4.toLowerCase()];
662
+ let spanName = txStatements[sql5.toLowerCase()];
663
663
  if (!spanName) {
664
664
  spanName = "Database Query";
665
665
  sqlAttribute = true;
@@ -1029,7 +1029,7 @@ function transformRichDataTypes(data) {
1029
1029
  } else if (value.key && value.size && value.filename && value.contentType) {
1030
1030
  row[key] = File.fromDbRecord(value);
1031
1031
  } else {
1032
- row[key] = value;
1032
+ row[key] = transformRichDataTypes(value);
1033
1033
  }
1034
1034
  } else {
1035
1035
  row[key] = value;
@@ -1150,23 +1150,23 @@ var TimePeriod = class _TimePeriod {
1150
1150
  return new _TimePeriod(period, value, offset, complete2);
1151
1151
  }
1152
1152
  periodStartSQL() {
1153
- let sql4 = "NOW()";
1153
+ let sql5 = "NOW()";
1154
1154
  if (this.offset !== 0) {
1155
- sql4 = `${sql4} + INTERVAL '${this.offset} ${this.period}'`;
1155
+ sql5 = `${sql5} + INTERVAL '${this.offset} ${this.period}'`;
1156
1156
  }
1157
1157
  if (this.complete) {
1158
- sql4 = `DATE_TRUNC('${this.period}', ${sql4})`;
1158
+ sql5 = `DATE_TRUNC('${this.period}', ${sql5})`;
1159
1159
  } else {
1160
- sql4 = `(${sql4})`;
1160
+ sql5 = `(${sql5})`;
1161
1161
  }
1162
- return sql4;
1162
+ return sql5;
1163
1163
  }
1164
1164
  periodEndSQL() {
1165
- let sql4 = this.periodStartSQL();
1165
+ let sql5 = this.periodStartSQL();
1166
1166
  if (this.value != 0) {
1167
- sql4 = `(${sql4} + INTERVAL '${this.value} ${this.period}')`;
1167
+ sql5 = `(${sql5} + INTERVAL '${this.value} ${this.period}')`;
1168
1168
  }
1169
- return sql4;
1169
+ return sql5;
1170
1170
  }
1171
1171
  };
1172
1172
 
@@ -2570,7 +2570,9 @@ var FlowRun = class _FlowRun {
2570
2570
  const name = spanNameForModelAPI(this._flowName, "refresh");
2571
2571
  return withSpan(name, async () => {
2572
2572
  const apiUrl = getApiUrl2();
2573
- const url = `${apiUrl}/flows/json/${this._flowName}/${this.id}`;
2573
+ const url = `${apiUrl}/flows/json/${encodeURIComponent(
2574
+ this._flowName
2575
+ )}/${encodeURIComponent(this.id)}`;
2574
2576
  const response = await fetch(url, {
2575
2577
  method: "GET",
2576
2578
  headers: buildHeaders2(this._identity, this._authToken)
@@ -2598,7 +2600,9 @@ var FlowRun = class _FlowRun {
2598
2600
  const name = spanNameForModelAPI(this._flowName, "cancel");
2599
2601
  return withSpan(name, async () => {
2600
2602
  const apiUrl = getApiUrl2();
2601
- const url = `${apiUrl}/flows/json/${this._flowName}/${this.id}/cancel`;
2603
+ const url = `${apiUrl}/flows/json/${encodeURIComponent(
2604
+ this._flowName
2605
+ )}/${encodeURIComponent(this.id)}/cancel`;
2602
2606
  const response = await fetch(url, {
2603
2607
  method: "POST",
2604
2608
  headers: buildHeaders2(this._identity, this._authToken)
@@ -2658,7 +2662,7 @@ var FlowsAPI = class _FlowsAPI {
2658
2662
  const name = spanNameForModelAPI(this._flowName, "start");
2659
2663
  return withSpan(name, async () => {
2660
2664
  const apiUrl = getApiUrl2();
2661
- const url = `${apiUrl}/flows/json/${this._flowName}`;
2665
+ const url = `${apiUrl}/flows/json/${encodeURIComponent(this._flowName)}`;
2662
2666
  const response = await fetch(url, {
2663
2667
  method: "POST",
2664
2668
  headers: buildHeaders2(this._identity, this._authToken),
@@ -2688,7 +2692,9 @@ var FlowsAPI = class _FlowsAPI {
2688
2692
  const name = spanNameForModelAPI(this._flowName, "get");
2689
2693
  return withSpan(name, async () => {
2690
2694
  const apiUrl = getApiUrl2();
2691
- const url = `${apiUrl}/flows/json/${this._flowName}/${runId}`;
2695
+ const url = `${apiUrl}/flows/json/${encodeURIComponent(
2696
+ this._flowName
2697
+ )}/${encodeURIComponent(runId)}`;
2692
2698
  const response = await fetch(url, {
2693
2699
  method: "GET",
2694
2700
  headers: buildHeaders2(this._identity, this._authToken)
@@ -2722,7 +2728,7 @@ var FlowsAPI = class _FlowsAPI {
2722
2728
  const params = new URLSearchParams();
2723
2729
  if (options.limit) params.set("limit", options.limit.toString());
2724
2730
  if (options.offset) params.set("offset", options.offset.toString());
2725
- const url = `${apiUrl}/flows/json/${this._flowName}${params.toString() ? "?" + params.toString() : ""}`;
2731
+ const url = `${apiUrl}/flows/json/${encodeURIComponent(this._flowName)}${params.toString() ? "?" + params.toString() : ""}`;
2726
2732
  const response = await fetch(url, {
2727
2733
  method: "GET",
2728
2734
  headers: buildHeaders2(this._identity, this._authToken)
@@ -3313,11 +3319,24 @@ var import_json_rpc_26 = require("json-rpc-2.0");
3313
3319
  var opentelemetry6 = __toESM(require("@opentelemetry/api"), 1);
3314
3320
 
3315
3321
  // src/tryExecuteFlow.js
3322
+ var import_kysely6 = require("kysely");
3316
3323
  function tryExecuteFlow(db, request, cb) {
3317
- return withDatabase(db, false, async () => {
3318
- return withAuditContext(request, async () => {
3319
- return cb();
3320
- });
3324
+ return withDatabase(db, false, async ({ sDb }) => {
3325
+ const runId = request?.meta?.runId;
3326
+ if (runId && sDb) {
3327
+ await import_kysely6.sql`SELECT pg_advisory_lock(hashtextextended(${runId}, 0))`.execute(
3328
+ sDb
3329
+ );
3330
+ }
3331
+ try {
3332
+ return await withAuditContext(request, async () => {
3333
+ return cb();
3334
+ });
3335
+ } finally {
3336
+ if (runId && sDb) {
3337
+ await import_kysely6.sql`SELECT pg_advisory_unlock(hashtextextended(${runId}, 0))`.execute(sDb).catch(() => void 0);
3338
+ }
3339
+ }
3321
3340
  });
3322
3341
  }
3323
3342
  __name(tryExecuteFlow, "tryExecuteFlow");
@@ -3909,6 +3928,104 @@ var fileInput = /* @__PURE__ */ __name((name, options) => {
3909
3928
  };
3910
3929
  }, "fileInput");
3911
3930
 
3931
+ // src/flows/ui/elements/input/imageCapture.ts
3932
+ var isMultiOptions = /* @__PURE__ */ __name((opts) => opts && opts.mode === "multi", "isMultiOptions");
3933
+ function validateEntry(entry, requireCaption, context7) {
3934
+ if (!entry?.file?.key) {
3935
+ return `${context7}: an image is required`;
3936
+ }
3937
+ if (requireCaption && !entry.caption?.trim()) {
3938
+ return `${context7}: a caption is required`;
3939
+ }
3940
+ return true;
3941
+ }
3942
+ __name(validateEntry, "validateEntry");
3943
+ var imageCapture = /* @__PURE__ */ __name((name, options) => {
3944
+ const requireCaption = options?.requireCaption ?? false;
3945
+ const mode = options?.mode ?? "single";
3946
+ const multi = isMultiOptions(options);
3947
+ const max = multi ? options?.max : void 0;
3948
+ const min = multi ? options?.min : void 0;
3949
+ return {
3950
+ __type: "input",
3951
+ uiConfig: {
3952
+ __type: "ui.input.imageCapture",
3953
+ name,
3954
+ label: options?.label || name,
3955
+ optional: options?.optional || false,
3956
+ disabled: options?.disabled || false,
3957
+ helpText: options?.helpText,
3958
+ mode,
3959
+ requireCaption,
3960
+ ...multi ? { max, min } : {}
3961
+ },
3962
+ validate: /* @__PURE__ */ __name(async (data, action) => {
3963
+ const optional = options?.optional ?? false;
3964
+ if (mode === "multi") {
3965
+ const list2 = data ?? [];
3966
+ if (list2.length === 0) {
3967
+ if (!optional) return "At least one image is required";
3968
+ } else {
3969
+ if (min !== void 0 && list2.length < min) {
3970
+ return `At least ${min} image${min === 1 ? "" : "s"} required`;
3971
+ }
3972
+ if (max !== void 0 && list2.length > max) {
3973
+ return `At most ${max} image${max === 1 ? "" : "s"} allowed`;
3974
+ }
3975
+ for (let i = 0; i < list2.length; i++) {
3976
+ const result = validateEntry(
3977
+ list2[i],
3978
+ requireCaption,
3979
+ `Image ${i + 1}`
3980
+ );
3981
+ if (result !== true) return result;
3982
+ }
3983
+ }
3984
+ } else {
3985
+ const entry = data;
3986
+ const hasFile = !!entry?.file?.key;
3987
+ if (!optional && !hasFile) {
3988
+ return "An image is required";
3989
+ }
3990
+ if (hasFile) {
3991
+ const result = validateEntry(entry, requireCaption, "Image");
3992
+ if (result !== true) return result;
3993
+ }
3994
+ }
3995
+ return options?.validate?.(
3996
+ data,
3997
+ action
3998
+ ) ?? true;
3999
+ }, "validate"),
4000
+ getData: /* @__PURE__ */ __name((x) => x, "getData"),
4001
+ getPresignedUploadURL: /* @__PURE__ */ __name((async (input) => {
4002
+ const file2 = new File(input);
4003
+ const url = await file2.getPresignedUploadUrl();
4004
+ return {
4005
+ url: url.toString(),
4006
+ key: file2.key
4007
+ };
4008
+ }), "getPresignedUploadURL")
4009
+ };
4010
+ }, "imageCapture");
4011
+
4012
+ // src/flows/ui/elements/input/signature.ts
4013
+ var signatureInput = /* @__PURE__ */ __name((name, options) => {
4014
+ return {
4015
+ __type: "input",
4016
+ uiConfig: {
4017
+ __type: "ui.input.signature",
4018
+ name,
4019
+ label: options?.label || name,
4020
+ optional: options?.optional || false,
4021
+ disabled: options?.disabled || false,
4022
+ helpText: options?.helpText
4023
+ },
4024
+ validate: options?.validate,
4025
+ getData: /* @__PURE__ */ __name((x) => x, "getData")
4026
+ };
4027
+ }, "signatureInput");
4028
+
3912
4029
  // src/flows/ui/elements/iterator.ts
3913
4030
  var iterator = /* @__PURE__ */ __name((name, options) => {
3914
4031
  return {
@@ -4093,6 +4210,23 @@ var defaultOpts = {
4093
4210
  retries: 4,
4094
4211
  timeout: 6e4
4095
4212
  };
4213
+ async function insertNewStep(db, runId, name, stage) {
4214
+ await db.transaction().execute(async (trx) => {
4215
+ await trx.selectFrom("keel.flow_run").select("id").where("id", "=", runId).forUpdate().executeTakeFirst();
4216
+ const existing = await trx.selectFrom("keel.flow_step").select("id").where("run_id", "=", runId).where("name", "=", name).where("status", "=", "NEW" /* NEW */).executeTakeFirst();
4217
+ if (existing) {
4218
+ return;
4219
+ }
4220
+ await trx.insertInto("keel.flow_step").values({
4221
+ run_id: runId,
4222
+ name,
4223
+ stage,
4224
+ status: "NEW" /* NEW */,
4225
+ type: "FUNCTION" /* FUNCTION */
4226
+ }).execute();
4227
+ });
4228
+ }
4229
+ __name(insertNewStep, "insertNewStep");
4096
4230
  function createFlowContext(runId, data, action, callback, element, spanId, ctx) {
4097
4231
  const usedNames = /* @__PURE__ */ new Set();
4098
4232
  return {
@@ -4183,13 +4317,7 @@ function createFlowContext(runId, data, action, callback, element, spanId, ctx)
4183
4317
  }
4184
4318
  throw new ExhuastedRetriesDisrupt();
4185
4319
  }
4186
- await db.insertInto("keel.flow_step").values({
4187
- run_id: runId,
4188
- name,
4189
- stage: options.stage,
4190
- status: "NEW" /* NEW */,
4191
- type: "FUNCTION" /* FUNCTION */
4192
- }).returningAll().executeTakeFirst();
4320
+ await insertNewStep(db, runId, name, options.stage);
4193
4321
  span.setAttribute("step.status", "NEW" /* NEW */);
4194
4322
  throw new StepCreatedDisrupt(
4195
4323
  options.retryPolicy ? new Date(
@@ -4206,13 +4334,7 @@ function createFlowContext(runId, data, action, callback, element, spanId, ctx)
4206
4334
  span.setAttribute("step.status", "COMPLETED" /* COMPLETED */);
4207
4335
  return result;
4208
4336
  }
4209
- await db.insertInto("keel.flow_step").values({
4210
- run_id: runId,
4211
- name,
4212
- stage: options.stage,
4213
- status: "NEW" /* NEW */,
4214
- type: "FUNCTION" /* FUNCTION */
4215
- }).returningAll().executeTakeFirst();
4337
+ await insertNewStep(db, runId, name, options.stage);
4216
4338
  span.setAttribute(KEEL_INTERNAL_ATTR, KEEL_INTERNAL_CHILDREN);
4217
4339
  span.setAttribute("step.status", "NEW" /* NEW */);
4218
4340
  throw new StepCreatedDisrupt();
@@ -4338,7 +4460,9 @@ function createFlowContext(runId, data, action, callback, element, spanId, ctx)
4338
4460
  dataGrid: dataGridInput,
4339
4461
  datePicker: datePickerInput,
4340
4462
  scan,
4341
- file: fileInput
4463
+ file: fileInput,
4464
+ imageCapture,
4465
+ signature: signatureInput
4342
4466
  },
4343
4467
  display: {
4344
4468
  divider,