@teamkeel/functions-runtime 0.450.0 → 0.451.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.js CHANGED
@@ -195,17 +195,20 @@ var tracing_exports = {};
195
195
  __export(tracing_exports, {
196
196
  KEEL_INTERNAL_ATTR: () => KEEL_INTERNAL_ATTR,
197
197
  KEEL_INTERNAL_CHILDREN: () => KEEL_INTERNAL_CHILDREN,
198
+ createTraceAPI: () => createTraceAPI,
198
199
  forceFlush: () => forceFlush,
199
200
  getTracer: () => getTracer,
200
201
  init: () => init,
201
202
  spanNameForModelAPI: () => spanNameForModelAPI,
202
- withSpan: () => withSpan
203
+ withSpan: () => withSpan,
204
+ withUserSpan: () => withUserSpan
203
205
  });
204
206
  import * as opentelemetry from "@opentelemetry/api";
205
207
  import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
206
208
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
207
209
  import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
208
210
  import { envDetectorSync } from "@opentelemetry/resources";
211
+ var userSpanContextKey = opentelemetry.createContextKey("keel.userSpan");
209
212
  async function withSpan(name, fn) {
210
213
  return getTracer().startActiveSpan(name, async (span) => {
211
214
  try {
@@ -225,6 +228,73 @@ async function withSpan(name, fn) {
225
228
  });
226
229
  }
227
230
  __name(withSpan, "withSpan");
231
+ function withUserSpan(span, fn) {
232
+ const ctx = opentelemetry.context.active().setValue(userSpanContextKey, span);
233
+ return opentelemetry.context.with(ctx, fn);
234
+ }
235
+ __name(withUserSpan, "withUserSpan");
236
+ function getUserSpan() {
237
+ return opentelemetry.context.active().getValue(userSpanContextKey);
238
+ }
239
+ __name(getUserSpan, "getUserSpan");
240
+ function resolveTraceSpan(defaultSpan) {
241
+ const span = getUserSpan() ?? defaultSpan;
242
+ if (!span) {
243
+ throw new Error("no user span available for ctx.trace");
244
+ }
245
+ return span;
246
+ }
247
+ __name(resolveTraceSpan, "resolveTraceSpan");
248
+ function normalizeTraceAttributeValue(value) {
249
+ if (value === void 0) {
250
+ return void 0;
251
+ }
252
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
253
+ return value;
254
+ }
255
+ if (Array.isArray(value)) {
256
+ if (value.length === 0) {
257
+ return value;
258
+ }
259
+ const firstType = typeof value[0];
260
+ const validArrayType = firstType === "string" || firstType === "number" || firstType === "boolean";
261
+ if (validArrayType && value.every((item) => typeof item === firstType)) {
262
+ return value;
263
+ }
264
+ }
265
+ throw new TypeError(
266
+ "trace attribute values must be strings, numbers, booleans, or arrays of a single primitive type"
267
+ );
268
+ }
269
+ __name(normalizeTraceAttributeValue, "normalizeTraceAttributeValue");
270
+ function createTraceAPI(defaultSpan) {
271
+ return {
272
+ get traceId() {
273
+ return resolveTraceSpan(defaultSpan).spanContext().traceId;
274
+ },
275
+ get spanId() {
276
+ return resolveTraceSpan(defaultSpan).spanContext().spanId;
277
+ },
278
+ setAttribute(key, value) {
279
+ const normalized = normalizeTraceAttributeValue(value);
280
+ if (normalized === void 0) {
281
+ return;
282
+ }
283
+ resolveTraceSpan(defaultSpan).setAttribute(key, normalized);
284
+ },
285
+ setAttributes(attributes) {
286
+ const normalized = {};
287
+ for (const [key, value] of Object.entries(attributes)) {
288
+ const normalizedValue = normalizeTraceAttributeValue(value);
289
+ if (normalizedValue !== void 0) {
290
+ normalized[key] = normalizedValue;
291
+ }
292
+ }
293
+ resolveTraceSpan(defaultSpan).setAttributes(normalized);
294
+ }
295
+ };
296
+ }
297
+ __name(createTraceAPI, "createTraceAPI");
228
298
  function patchFetch() {
229
299
  if (!globalThis.fetch.patched) {
230
300
  const originalFetch = globalThis.fetch;
@@ -1004,7 +1074,11 @@ var TimePeriod = class _TimePeriod {
1004
1074
  let value = rawValue ? parseInt(rawValue, 10) : 1;
1005
1075
  let complete2 = Boolean(isComplete);
1006
1076
  let offset = 0;
1007
- switch (direction?.toLowerCase()) {
1077
+ const dir = direction?.toLowerCase();
1078
+ if (!complete2 && !rawValue && (dir === "last" || dir === "next") && ["day", "week", "month", "year"].includes(period)) {
1079
+ complete2 = true;
1080
+ }
1081
+ switch (dir) {
1008
1082
  case "this":
1009
1083
  offset = 0;
1010
1084
  complete2 = true;
@@ -1062,10 +1136,14 @@ var opMapping = {
1062
1136
  },
1063
1137
  notEquals: { op: sql2`is distinct from` },
1064
1138
  equalsRelative: {
1065
- op: sql2`BETWEEN`,
1066
- value: /* @__PURE__ */ __name((v) => sql2`${sql2.raw(
1067
- TimePeriod.fromExpression(v).periodStartSQL()
1068
- )} AND ${sql2.raw(TimePeriod.fromExpression(v).periodEndSQL())}`, "value")
1139
+ custom: /* @__PURE__ */ __name((qb, fieldName, v) => {
1140
+ const tp = TimePeriod.fromExpression(v);
1141
+ return qb.where(
1142
+ sql2`${sql2.raw(fieldName)} >= ${sql2.raw(
1143
+ tp.periodStartSQL()
1144
+ )} AND ${sql2.raw(fieldName)} < ${sql2.raw(tp.periodEndSQL())}`
1145
+ );
1146
+ }, "custom")
1069
1147
  },
1070
1148
  beforeRelative: {
1071
1149
  op: "<",
@@ -1102,18 +1180,18 @@ var opMapping = {
1102
1180
  notEquals: { op: "=", value: /* @__PURE__ */ __name((v) => sql2`NOT ${v}`, "value") }
1103
1181
  }
1104
1182
  };
1105
- function applyWhereConditions(context6, qb, where = {}) {
1106
- const conf = context6.tableConfig();
1183
+ function applyWhereConditions(context7, qb, where = {}) {
1184
+ const conf = context7.tableConfig();
1107
1185
  for (const key of Object.keys(where)) {
1108
1186
  const v = where[key];
1109
1187
  if (conf && conf[snakeCase(key)]) {
1110
1188
  const rel = conf[snakeCase(key)];
1111
- context6.withJoin(rel.referencesTable, () => {
1112
- qb = applyWhereConditions(context6, qb, v);
1189
+ context7.withJoin(rel.referencesTable, () => {
1190
+ qb = applyWhereConditions(context7, qb, v);
1113
1191
  });
1114
1192
  continue;
1115
1193
  }
1116
- const fieldName = `${context6.tableAlias()}.${snakeCase(key)}`;
1194
+ const fieldName = `${context7.tableAlias()}.${snakeCase(key)}`;
1117
1195
  if (Object.prototype.toString.call(v) !== "[object Object]") {
1118
1196
  const operator = v === null || Array.isArray(v) ? sql2`is not distinct from` : "=";
1119
1197
  qb = qb.where(fieldName, operator, sql2`${v}`);
@@ -1124,7 +1202,9 @@ function applyWhereConditions(context6, qb, where = {}) {
1124
1202
  if (!mapping) {
1125
1203
  throw new Error(`invalid where condition: ${op}`);
1126
1204
  }
1127
- if (mapping.isArrayQuery) {
1205
+ if (mapping.custom) {
1206
+ qb = mapping.custom(qb, fieldName, v[op]);
1207
+ } else if (mapping.isArrayQuery) {
1128
1208
  for (const arrayOp of Object.keys(v[op])) {
1129
1209
  qb = qb.where(
1130
1210
  mapping[arrayOp].value ? mapping[arrayOp].value(v[op][arrayOp]) : sql2`${v[op][arrayOp]}`,
@@ -1147,15 +1227,15 @@ function applyWhereConditions(context6, qb, where = {}) {
1147
1227
  __name(applyWhereConditions, "applyWhereConditions");
1148
1228
 
1149
1229
  // src/applyAdditionalQueryConstraints.js
1150
- function applyLimit(context6, qb, limit) {
1230
+ function applyLimit(context7, qb, limit) {
1151
1231
  return qb.limit(limit);
1152
1232
  }
1153
1233
  __name(applyLimit, "applyLimit");
1154
- function applyOffset(context6, qb, offset) {
1234
+ function applyOffset(context7, qb, offset) {
1155
1235
  return qb.offset(offset);
1156
1236
  }
1157
1237
  __name(applyOffset, "applyOffset");
1158
- function applyOrderBy(context6, qb, tableName, orderBy = {}) {
1238
+ function applyOrderBy(context7, qb, tableName, orderBy = {}) {
1159
1239
  Object.entries(orderBy).forEach(([key, sortOrder]) => {
1160
1240
  qb = qb.orderBy(`${tableName}.${snakeCase(key)}`, sortOrder.toLowerCase());
1161
1241
  });
@@ -1164,41 +1244,41 @@ function applyOrderBy(context6, qb, tableName, orderBy = {}) {
1164
1244
  __name(applyOrderBy, "applyOrderBy");
1165
1245
 
1166
1246
  // src/applyJoins.js
1167
- function applyJoins(context6, qb, where) {
1168
- const conf = context6.tableConfig();
1247
+ function applyJoins(context7, qb, where) {
1248
+ const conf = context7.tableConfig();
1169
1249
  if (!conf) {
1170
1250
  return qb;
1171
1251
  }
1172
- const srcTable = context6.tableAlias();
1252
+ const srcTable = context7.tableAlias();
1173
1253
  for (const key of Object.keys(where)) {
1174
1254
  const rel = conf[snakeCase(key)];
1175
1255
  if (!rel) {
1176
1256
  continue;
1177
1257
  }
1178
1258
  const targetTable = rel.referencesTable;
1179
- if (context6.hasJoin(targetTable)) {
1259
+ if (context7.hasJoin(targetTable)) {
1180
1260
  continue;
1181
1261
  }
1182
- context6.withJoin(targetTable, () => {
1262
+ context7.withJoin(targetTable, () => {
1183
1263
  switch (rel.relationshipType) {
1184
1264
  case "hasMany":
1185
1265
  qb = qb.innerJoin(
1186
- `${targetTable} as ${context6.tableAlias()}`,
1266
+ `${targetTable} as ${context7.tableAlias()}`,
1187
1267
  `${srcTable}.id`,
1188
- `${context6.tableAlias()}.${rel.foreignKey}`
1268
+ `${context7.tableAlias()}.${rel.foreignKey}`
1189
1269
  );
1190
1270
  break;
1191
1271
  case "belongsTo":
1192
1272
  qb = qb.innerJoin(
1193
- `${targetTable} as ${context6.tableAlias()}`,
1273
+ `${targetTable} as ${context7.tableAlias()}`,
1194
1274
  `${srcTable}.${rel.foreignKey}`,
1195
- `${context6.tableAlias()}.id`
1275
+ `${context7.tableAlias()}.id`
1196
1276
  );
1197
1277
  break;
1198
1278
  default:
1199
1279
  throw new Error(`unknown relationshipType: ${rel.relationshipType}`);
1200
1280
  }
1201
- qb = applyJoins(context6, qb, where[key]);
1281
+ qb = applyJoins(context7, qb, where[key]);
1202
1282
  });
1203
1283
  }
1204
1284
  return qb;
@@ -1428,17 +1508,17 @@ var QueryBuilder = class _QueryBuilder {
1428
1508
  * @param {import("./QueryContext").QueryContext} context
1429
1509
  * @param {import("kysely").Kysely} db
1430
1510
  */
1431
- constructor(tableName, context6, db) {
1511
+ constructor(tableName, context7, db) {
1432
1512
  this._tableName = tableName;
1433
- this._context = context6;
1513
+ this._context = context7;
1434
1514
  this._db = db;
1435
1515
  this._modelName = upperCamelCase(this._tableName);
1436
1516
  }
1437
1517
  where(where) {
1438
- const context6 = this._context.clone();
1439
- let builder = applyJoins(context6, this._db, where);
1440
- builder = applyWhereConditions(context6, builder, where);
1441
- return new _QueryBuilder(this._tableName, context6, builder);
1518
+ const context7 = this._context.clone();
1519
+ let builder = applyJoins(context7, this._db, where);
1520
+ builder = applyWhereConditions(context7, builder, where);
1521
+ return new _QueryBuilder(this._tableName, context7, builder);
1442
1522
  }
1443
1523
  sql() {
1444
1524
  return this._db.compile().sql;
@@ -1503,19 +1583,19 @@ var QueryBuilder = class _QueryBuilder {
1503
1583
  const name = spanNameForModelAPI(this._modelName, "findMany");
1504
1584
  const db = useDatabase();
1505
1585
  return withSpan(name, async (span) => {
1506
- const context6 = new QueryContext([this._tableName], this._tableConfigMap);
1586
+ const context7 = new QueryContext([this._tableName], this._tableConfigMap);
1507
1587
  let builder = db.selectFrom((qb) => {
1508
1588
  return this._db.as(this._tableName);
1509
1589
  }).selectAll();
1510
1590
  if (params?.limit) {
1511
- builder = applyLimit(context6, builder, params.limit);
1591
+ builder = applyLimit(context7, builder, params.limit);
1512
1592
  }
1513
1593
  if (params?.offset) {
1514
- builder = applyOffset(context6, builder, params.offset);
1594
+ builder = applyOffset(context7, builder, params.offset);
1515
1595
  }
1516
1596
  if (params?.orderBy !== void 0 && Object.keys(params?.orderBy).length > 0) {
1517
1597
  builder = applyOrderBy(
1518
- context6,
1598
+ context7,
1519
1599
  builder,
1520
1600
  this._tableName,
1521
1601
  params.orderBy
@@ -1549,7 +1629,7 @@ var QueryBuilder = class _QueryBuilder {
1549
1629
  );
1550
1630
  const db = useDatabase();
1551
1631
  return withSpan(name, async (span) => {
1552
- const context6 = new QueryContext([this._tableName], this._tableConfigMap);
1632
+ const context7 = new QueryContext([this._tableName], this._tableConfigMap);
1553
1633
  const isOffsetPagination = params.limit != null && params.limit > 0;
1554
1634
  const isBackward = params.last != null && params.last > 0;
1555
1635
  const DEFAULT_PAGE_SIZE = 50;
@@ -1597,7 +1677,7 @@ var QueryBuilder = class _QueryBuilder {
1597
1677
  ])
1598
1678
  ) : normalizedOrderBy;
1599
1679
  builder = applyOrderBy(
1600
- context6,
1680
+ context7,
1601
1681
  builder,
1602
1682
  this._tableName,
1603
1683
  effectiveOrderBy
@@ -1670,10 +1750,10 @@ var QueryBuilder = class _QueryBuilder {
1670
1750
  }
1671
1751
  }
1672
1752
  if (limit != null) {
1673
- builder = applyLimit(context6, builder, limit);
1753
+ builder = applyLimit(context7, builder, limit);
1674
1754
  }
1675
1755
  if (isOffsetPagination && params.offset) {
1676
- builder = applyOffset(context6, builder, params.offset);
1756
+ builder = applyOffset(context7, builder, params.offset);
1677
1757
  }
1678
1758
  span.setAttribute("sql", builder.compile().sql);
1679
1759
  let rows = await builder.execute();
@@ -1778,9 +1858,9 @@ var ModelAPI = class {
1778
1858
  const db = useDatabase();
1779
1859
  return withSpan(name, async (span) => {
1780
1860
  let builder = db.selectFrom(this._tableName).distinctOn(`${this._tableName}.id`).selectAll(this._tableName);
1781
- const context6 = new QueryContext([this._tableName], this._tableConfigMap);
1782
- builder = applyJoins(context6, builder, where);
1783
- builder = applyWhereConditions(context6, builder, where);
1861
+ const context7 = new QueryContext([this._tableName], this._tableConfigMap);
1862
+ builder = applyJoins(context7, builder, where);
1863
+ builder = applyWhereConditions(context7, builder, where);
1784
1864
  span.setAttribute("sql", builder.compile().sql);
1785
1865
  const row = await builder.executeTakeFirst();
1786
1866
  if (!row) {
@@ -1794,23 +1874,23 @@ var ModelAPI = class {
1794
1874
  const db = useDatabase();
1795
1875
  const where = params?.where || {};
1796
1876
  return withSpan(name, async (span) => {
1797
- const context6 = new QueryContext([this._tableName], this._tableConfigMap);
1877
+ const context7 = new QueryContext([this._tableName], this._tableConfigMap);
1798
1878
  let builder = db.selectFrom((qb) => {
1799
1879
  let builder2 = qb.selectFrom(this._tableName).distinctOn(`${this._tableName}.id`).selectAll(this._tableName);
1800
- builder2 = applyJoins(context6, builder2, where);
1801
- builder2 = applyWhereConditions(context6, builder2, where);
1880
+ builder2 = applyJoins(context7, builder2, where);
1881
+ builder2 = applyWhereConditions(context7, builder2, where);
1802
1882
  builder2 = builder2.as(this._tableName);
1803
1883
  return builder2;
1804
1884
  }).selectAll();
1805
1885
  if (params?.limit) {
1806
- builder = applyLimit(context6, builder, params.limit);
1886
+ builder = applyLimit(context7, builder, params.limit);
1807
1887
  }
1808
1888
  if (params?.offset) {
1809
- builder = applyOffset(context6, builder, params.offset);
1889
+ builder = applyOffset(context7, builder, params.offset);
1810
1890
  }
1811
1891
  if (params?.orderBy !== void 0 && Object.keys(params?.orderBy).length > 0) {
1812
1892
  builder = applyOrderBy(
1813
- context6,
1893
+ context7,
1814
1894
  builder,
1815
1895
  this._tableName,
1816
1896
  params.orderBy
@@ -1861,8 +1941,8 @@ var ModelAPI = class {
1861
1941
  }
1862
1942
  }
1863
1943
  builder = builder.set(snakeCaseObject(row));
1864
- const context6 = new QueryContext([this._tableName], this._tableConfigMap);
1865
- builder = applyWhereConditions(context6, builder, where);
1944
+ const context7 = new QueryContext([this._tableName], this._tableConfigMap);
1945
+ builder = applyWhereConditions(context7, builder, where);
1866
1946
  span.setAttribute("sql", builder.compile().sql);
1867
1947
  try {
1868
1948
  const row2 = await builder.executeTakeFirstOrThrow();
@@ -1877,8 +1957,8 @@ var ModelAPI = class {
1877
1957
  const db = useDatabase();
1878
1958
  return withSpan(name, async (span) => {
1879
1959
  let builder = db.deleteFrom(this._tableName).returning(["id"]);
1880
- const context6 = new QueryContext([this._tableName], this._tableConfigMap);
1881
- builder = applyWhereConditions(context6, builder, where);
1960
+ const context7 = new QueryContext([this._tableName], this._tableConfigMap);
1961
+ builder = applyWhereConditions(context7, builder, where);
1882
1962
  span.setAttribute("sql", builder.compile().sql);
1883
1963
  try {
1884
1964
  const row = await builder.executeTakeFirstOrThrow();
@@ -1891,10 +1971,10 @@ var ModelAPI = class {
1891
1971
  where(where) {
1892
1972
  const db = useDatabase();
1893
1973
  let builder = db.selectFrom(this._tableName).distinctOn(`${this._tableName}.id`).selectAll(this._tableName);
1894
- const context6 = new QueryContext([this._tableName], this._tableConfigMap);
1895
- builder = applyJoins(context6, builder, where);
1896
- builder = applyWhereConditions(context6, builder, where);
1897
- return new QueryBuilder(this._tableName, context6, builder);
1974
+ const context7 = new QueryContext([this._tableName], this._tableConfigMap);
1975
+ builder = applyJoins(context7, builder, where);
1976
+ builder = applyWhereConditions(context7, builder, where);
1977
+ return new QueryBuilder(this._tableName, context7, builder);
1898
1978
  }
1899
1979
  };
1900
1980
  async function create(conn, tableName, tableConfigs, values) {
@@ -2841,7 +2921,8 @@ async function handleRequest(request, config) {
2841
2921
  const headers = new Headers();
2842
2922
  const ctx = createContextAPI({
2843
2923
  responseHeaders: headers,
2844
- meta: request.meta
2924
+ meta: request.meta,
2925
+ span
2845
2926
  });
2846
2927
  const permitted = request.meta && request.meta.permissionState.status === "granted" ? true : null;
2847
2928
  db = createDatabaseClient({
@@ -2964,7 +3045,8 @@ async function handleJob(request, config) {
2964
3045
  );
2965
3046
  }
2966
3047
  const ctx = createJobContextAPI({
2967
- meta: request.meta
3048
+ meta: request.meta,
3049
+ span
2968
3050
  });
2969
3051
  const permitted = request.meta && request.meta.permissionState.status === "granted" ? true : null;
2970
3052
  db = createDatabaseClient({
@@ -3056,7 +3138,8 @@ async function handleSubscriber(request, config) {
3056
3138
  );
3057
3139
  }
3058
3140
  const ctx = createSubscriberContextAPI({
3059
- meta: request.meta
3141
+ meta: request.meta,
3142
+ span
3060
3143
  });
3061
3144
  db = createDatabaseClient({
3062
3145
  connString: request.meta?.secrets?.KEEL_DB_CONN
@@ -3136,7 +3219,8 @@ async function handleRoute(request, config) {
3136
3219
  ...ctx
3137
3220
  } = createContextAPI({
3138
3221
  responseHeaders: new Headers(),
3139
- meta: request.meta
3222
+ meta: request.meta,
3223
+ span
3140
3224
  });
3141
3225
  request.params.headers = headers;
3142
3226
  db = createDatabaseClient({
@@ -3983,6 +4067,7 @@ function createFlowContext(runId, data, action, callback, element, spanId, ctx)
3983
4067
  env: ctx.env,
3984
4068
  now: ctx.now,
3985
4069
  secrets: ctx.secrets,
4070
+ trace: ctx.trace,
3986
4071
  complete: /* @__PURE__ */ __name((options) => {
3987
4072
  return {
3988
4073
  __type: "ui.complete",
@@ -3991,116 +4076,19 @@ function createFlowContext(runId, data, action, callback, element, spanId, ctx)
3991
4076
  }, "complete"),
3992
4077
  step: /* @__PURE__ */ __name(async (name, optionsOrFn, fn) => {
3993
4078
  return withSpan(`Step - ${name}`, async (span) => {
3994
- const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
3995
- const actualFn = typeof optionsOrFn === "function" ? optionsOrFn : fn;
3996
- options.retries = options.retries ?? defaultOpts.retries;
3997
- options.timeout = options.timeout ?? defaultOpts.timeout;
3998
- const db = useDatabase();
3999
- if (usedNames.has(name)) {
4000
- await db.insertInto("keel.flow_step").values({
4001
- run_id: runId,
4002
- name,
4003
- stage: options.stage,
4004
- status: "FAILED" /* FAILED */,
4005
- type: "FUNCTION" /* FUNCTION */,
4006
- error: `Duplicate step name: ${name}`,
4007
- startTime: /* @__PURE__ */ new Date(),
4008
- endTime: /* @__PURE__ */ new Date()
4009
- }).returningAll().executeTakeFirst();
4010
- throw new Error(`Duplicate step name: ${name}`);
4011
- }
4012
- usedNames.add(name);
4013
- const past = await db.selectFrom("keel.flow_step").where("run_id", "=", runId).where("name", "=", name).selectAll().execute();
4014
- const newSteps = past.filter((step) => step.status === "NEW" /* NEW */);
4015
- const completedSteps = past.filter(
4016
- (step) => step.status === "COMPLETED" /* COMPLETED */
4017
- );
4018
- const failedSteps = past.filter(
4019
- (step) => step.status === "FAILED" /* FAILED */
4020
- );
4021
- if (newSteps.length > 1) {
4022
- throw new Error("Multiple NEW steps found for the same step");
4023
- }
4024
- if (completedSteps.length > 1) {
4025
- throw new Error("Multiple completed steps found for the same step");
4026
- }
4027
- if (completedSteps.length > 1 && newSteps.length > 1) {
4028
- throw new Error(
4029
- "Multiple completed and new steps found for the same step"
4030
- );
4031
- }
4032
- if (completedSteps.length === 1) {
4033
- span.setAttribute(KEEL_INTERNAL_ATTR, KEEL_INTERNAL_CHILDREN);
4034
- return completedSteps[0].value;
4035
- }
4036
- if (newSteps.length === 1) {
4037
- let result = null;
4038
- await db.updateTable("keel.flow_step").set({
4039
- startTime: /* @__PURE__ */ new Date()
4040
- }).where("id", "=", newSteps[0].id).returningAll().executeTakeFirst();
4041
- try {
4042
- const stepArgs = {
4043
- attempt: failedSteps.length + 1,
4044
- stepOptions: options
4045
- };
4046
- result = await withTimeout(actualFn(stepArgs), options.timeout);
4047
- } catch (e) {
4048
- await db.updateTable("keel.flow_step").set({
4049
- status: "FAILED" /* FAILED */,
4050
- spanId,
4051
- endTime: /* @__PURE__ */ new Date(),
4052
- error: e instanceof Error ? e.message : "An error occurred"
4053
- }).where("id", "=", newSteps[0].id).returningAll().executeTakeFirst();
4054
- if (failedSteps.length >= options.retries || e instanceof NonRetriableError) {
4055
- if (options.onFailure) {
4056
- await options.onFailure();
4057
- }
4058
- throw new ExhuastedRetriesDisrupt();
4059
- }
4060
- await db.insertInto("keel.flow_step").values({
4061
- run_id: runId,
4062
- name,
4063
- stage: options.stage,
4064
- status: "NEW" /* NEW */,
4065
- type: "FUNCTION" /* FUNCTION */
4066
- }).returningAll().executeTakeFirst();
4067
- throw new StepCreatedDisrupt(
4068
- options.retryPolicy ? new Date(
4069
- Date.now() + options.retryPolicy(failedSteps.length + 1)
4070
- ) : void 0
4071
- );
4072
- }
4073
- await db.updateTable("keel.flow_step").set({
4074
- status: "COMPLETED" /* COMPLETED */,
4075
- value: JSON.stringify(result),
4076
- spanId,
4077
- endTime: /* @__PURE__ */ new Date()
4078
- }).where("id", "=", newSteps[0].id).returningAll().executeTakeFirst();
4079
- return result;
4080
- }
4081
- await db.insertInto("keel.flow_step").values({
4082
- run_id: runId,
4083
- name,
4084
- stage: options.stage,
4085
- status: "NEW" /* NEW */,
4086
- type: "FUNCTION" /* FUNCTION */
4087
- }).returningAll().executeTakeFirst();
4088
- span.setAttribute(KEEL_INTERNAL_ATTR, KEEL_INTERNAL_CHILDREN);
4089
- throw new StepCreatedDisrupt();
4090
- });
4091
- }, "step"),
4092
- ui: {
4093
- page: /* @__PURE__ */ __name((async (name, options) => {
4094
- return withSpan(`Page - ${name}`, async (span) => {
4079
+ return withUserSpan(span, async () => {
4080
+ const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
4081
+ const actualFn = typeof optionsOrFn === "function" ? optionsOrFn : fn;
4082
+ options.retries = options.retries ?? defaultOpts.retries;
4083
+ options.timeout = options.timeout ?? defaultOpts.timeout;
4095
4084
  const db = useDatabase();
4096
- const isCallback = element && callback;
4097
4085
  if (usedNames.has(name)) {
4098
4086
  await db.insertInto("keel.flow_step").values({
4099
4087
  run_id: runId,
4100
4088
  name,
4101
4089
  stage: options.stage,
4102
4090
  status: "FAILED" /* FAILED */,
4103
- type: "UI" /* UI */,
4091
+ type: "FUNCTION" /* FUNCTION */,
4104
4092
  error: `Duplicate step name: ${name}`,
4105
4093
  startTime: /* @__PURE__ */ new Date(),
4106
4094
  endTime: /* @__PURE__ */ new Date()
@@ -4108,85 +4096,188 @@ function createFlowContext(runId, data, action, callback, element, spanId, ctx)
4108
4096
  throw new Error(`Duplicate step name: ${name}`);
4109
4097
  }
4110
4098
  usedNames.add(name);
4111
- let step = await db.selectFrom("keel.flow_step").where("run_id", "=", runId).where("name", "=", name).selectAll().executeTakeFirst();
4112
- if (step && step.status === "COMPLETED" /* COMPLETED */) {
4113
- span.setAttribute(KEEL_INTERNAL_ATTR, KEEL_INTERNAL_CHILDREN);
4114
- const parsedData2 = transformRichDataTypes(step.value);
4115
- if (step.action) {
4116
- return { data: parsedData2, action: step.action };
4117
- }
4118
- return parsedData2;
4099
+ const past = await db.selectFrom("keel.flow_step").where("run_id", "=", runId).where("name", "=", name).selectAll().execute();
4100
+ const newSteps = past.filter(
4101
+ (step) => step.status === "NEW" /* NEW */
4102
+ );
4103
+ const completedSteps = past.filter(
4104
+ (step) => step.status === "COMPLETED" /* COMPLETED */
4105
+ );
4106
+ const failedSteps = past.filter(
4107
+ (step) => step.status === "FAILED" /* FAILED */
4108
+ );
4109
+ if (newSteps.length > 1) {
4110
+ throw new Error("Multiple NEW steps found for the same step");
4119
4111
  }
4120
- if (!step) {
4121
- step = await db.insertInto("keel.flow_step").values({
4122
- run_id: runId,
4123
- name,
4124
- stage: options.stage,
4125
- status: "PENDING" /* PENDING */,
4126
- type: "UI" /* UI */,
4127
- startTime: /* @__PURE__ */ new Date()
4128
- }).returningAll().executeTakeFirst();
4129
- span.setAttribute("rendered", true);
4130
- throw new UIRenderDisrupt(
4131
- step?.id,
4132
- (await page(options, null, null)).page
4112
+ if (completedSteps.length > 1) {
4113
+ throw new Error("Multiple completed steps found for the same step");
4114
+ }
4115
+ if (completedSteps.length > 1 && newSteps.length > 1) {
4116
+ throw new Error(
4117
+ "Multiple completed and new steps found for the same step"
4133
4118
  );
4134
4119
  }
4135
- if (isCallback) {
4136
- span.setAttribute("callback", callback);
4120
+ if (completedSteps.length === 1) {
4121
+ span.setAttribute(KEEL_INTERNAL_ATTR, KEEL_INTERNAL_CHILDREN);
4122
+ return completedSteps[0].value;
4123
+ }
4124
+ if (newSteps.length === 1) {
4125
+ let result = null;
4126
+ await db.updateTable("keel.flow_step").set({
4127
+ startTime: /* @__PURE__ */ new Date()
4128
+ }).where("id", "=", newSteps[0].id).returningAll().executeTakeFirst();
4137
4129
  try {
4138
- const response = await callbackFn(
4139
- options.content,
4140
- element,
4141
- callback,
4142
- data
4143
- );
4144
- throw new CallbackDisrupt(response, false);
4130
+ const stepArgs = {
4131
+ attempt: failedSteps.length + 1,
4132
+ stepOptions: options
4133
+ };
4134
+ result = await withTimeout(actualFn(stepArgs), options.timeout);
4145
4135
  } catch (e) {
4146
- if (e instanceof CallbackDisrupt) {
4147
- throw e;
4136
+ await db.updateTable("keel.flow_step").set({
4137
+ status: "FAILED" /* FAILED */,
4138
+ spanId,
4139
+ endTime: /* @__PURE__ */ new Date(),
4140
+ error: e instanceof Error ? e.message : "An error occurred"
4141
+ }).where("id", "=", newSteps[0].id).returningAll().executeTakeFirst();
4142
+ if (failedSteps.length >= options.retries || e instanceof NonRetriableError) {
4143
+ if (options.onFailure) {
4144
+ await options.onFailure();
4145
+ }
4146
+ throw new ExhuastedRetriesDisrupt();
4148
4147
  }
4149
- throw new CallbackDisrupt(
4150
- e instanceof Error ? e.message : `An error occurred`,
4151
- true
4148
+ await db.insertInto("keel.flow_step").values({
4149
+ run_id: runId,
4150
+ name,
4151
+ stage: options.stage,
4152
+ status: "NEW" /* NEW */,
4153
+ type: "FUNCTION" /* FUNCTION */
4154
+ }).returningAll().executeTakeFirst();
4155
+ throw new StepCreatedDisrupt(
4156
+ options.retryPolicy ? new Date(
4157
+ Date.now() + options.retryPolicy(failedSteps.length + 1)
4158
+ ) : void 0
4152
4159
  );
4153
4160
  }
4161
+ await db.updateTable("keel.flow_step").set({
4162
+ status: "COMPLETED" /* COMPLETED */,
4163
+ value: JSON.stringify(result),
4164
+ spanId,
4165
+ endTime: /* @__PURE__ */ new Date()
4166
+ }).where("id", "=", newSteps[0].id).returningAll().executeTakeFirst();
4167
+ return result;
4154
4168
  }
4155
- if (!data) {
4156
- throw new UIRenderDisrupt(
4157
- step?.id,
4158
- (await page(options, null, null)).page
4159
- );
4160
- }
4161
- try {
4162
- const p = await page(options, data, action);
4163
- if (p.hasValidationErrors) {
4164
- throw new UIRenderDisrupt(step?.id, p.page);
4169
+ await db.insertInto("keel.flow_step").values({
4170
+ run_id: runId,
4171
+ name,
4172
+ stage: options.stage,
4173
+ status: "NEW" /* NEW */,
4174
+ type: "FUNCTION" /* FUNCTION */
4175
+ }).returningAll().executeTakeFirst();
4176
+ span.setAttribute(KEEL_INTERNAL_ATTR, KEEL_INTERNAL_CHILDREN);
4177
+ throw new StepCreatedDisrupt();
4178
+ });
4179
+ });
4180
+ }, "step"),
4181
+ ui: {
4182
+ page: /* @__PURE__ */ __name((async (name, options) => {
4183
+ return withSpan(`Page - ${name}`, async (span) => {
4184
+ return withUserSpan(span, async () => {
4185
+ const db = useDatabase();
4186
+ const isCallback = element && callback;
4187
+ if (usedNames.has(name)) {
4188
+ await db.insertInto("keel.flow_step").values({
4189
+ run_id: runId,
4190
+ name,
4191
+ stage: options.stage,
4192
+ status: "FAILED" /* FAILED */,
4193
+ type: "UI" /* UI */,
4194
+ error: `Duplicate step name: ${name}`,
4195
+ startTime: /* @__PURE__ */ new Date(),
4196
+ endTime: /* @__PURE__ */ new Date()
4197
+ }).returningAll().executeTakeFirst();
4198
+ throw new Error(`Duplicate step name: ${name}`);
4199
+ }
4200
+ usedNames.add(name);
4201
+ let step = await db.selectFrom("keel.flow_step").where("run_id", "=", runId).where("name", "=", name).selectAll().executeTakeFirst();
4202
+ if (step && step.status === "COMPLETED" /* COMPLETED */) {
4203
+ span.setAttribute(KEEL_INTERNAL_ATTR, KEEL_INTERNAL_CHILDREN);
4204
+ const parsedData2 = transformRichDataTypes(step.value);
4205
+ if (step.action) {
4206
+ return { data: parsedData2, action: step.action };
4207
+ }
4208
+ return parsedData2;
4209
+ }
4210
+ if (!step) {
4211
+ step = await db.insertInto("keel.flow_step").values({
4212
+ run_id: runId,
4213
+ name,
4214
+ stage: options.stage,
4215
+ status: "PENDING" /* PENDING */,
4216
+ type: "UI" /* UI */,
4217
+ startTime: /* @__PURE__ */ new Date()
4218
+ }).returningAll().executeTakeFirst();
4219
+ span.setAttribute("rendered", true);
4220
+ throw new UIRenderDisrupt(
4221
+ step?.id,
4222
+ (await page(options, null, null)).page
4223
+ );
4224
+ }
4225
+ if (isCallback) {
4226
+ span.setAttribute("callback", callback);
4227
+ try {
4228
+ const response = await callbackFn(
4229
+ options.content,
4230
+ element,
4231
+ callback,
4232
+ data
4233
+ );
4234
+ throw new CallbackDisrupt(response, false);
4235
+ } catch (e) {
4236
+ if (e instanceof CallbackDisrupt) {
4237
+ throw e;
4238
+ }
4239
+ throw new CallbackDisrupt(
4240
+ e instanceof Error ? e.message : `An error occurred`,
4241
+ true
4242
+ );
4243
+ }
4165
4244
  }
4166
- } catch (e) {
4167
- if (e instanceof UIRenderDisrupt) {
4245
+ if (!data) {
4246
+ throw new UIRenderDisrupt(
4247
+ step?.id,
4248
+ (await page(options, null, null)).page
4249
+ );
4250
+ }
4251
+ try {
4252
+ const p = await page(options, data, action);
4253
+ if (p.hasValidationErrors) {
4254
+ throw new UIRenderDisrupt(step?.id, p.page);
4255
+ }
4256
+ } catch (e) {
4257
+ if (e instanceof UIRenderDisrupt) {
4258
+ throw e;
4259
+ }
4260
+ await db.updateTable("keel.flow_step").set({
4261
+ status: "FAILED" /* FAILED */,
4262
+ spanId,
4263
+ endTime: /* @__PURE__ */ new Date(),
4264
+ error: e instanceof Error ? e.message : "An error occurred"
4265
+ }).where("id", "=", step?.id).returningAll().executeTakeFirst();
4168
4266
  throw e;
4169
4267
  }
4170
4268
  await db.updateTable("keel.flow_step").set({
4171
- status: "FAILED" /* FAILED */,
4269
+ status: "COMPLETED" /* COMPLETED */,
4270
+ value: JSON.stringify(data),
4271
+ action,
4172
4272
  spanId,
4173
- endTime: /* @__PURE__ */ new Date(),
4174
- error: e instanceof Error ? e.message : "An error occurred"
4175
- }).where("id", "=", step?.id).returningAll().executeTakeFirst();
4176
- throw e;
4177
- }
4178
- await db.updateTable("keel.flow_step").set({
4179
- status: "COMPLETED" /* COMPLETED */,
4180
- value: JSON.stringify(data),
4181
- action,
4182
- spanId,
4183
- endTime: /* @__PURE__ */ new Date()
4184
- }).where("id", "=", step.id).returningAll().executeTakeFirst();
4185
- const parsedData = transformRichDataTypes(data);
4186
- if (action) {
4187
- return { data: parsedData, action };
4188
- }
4189
- return parsedData;
4273
+ endTime: /* @__PURE__ */ new Date()
4274
+ }).where("id", "=", step.id).returningAll().executeTakeFirst();
4275
+ const parsedData = transformRichDataTypes(data);
4276
+ if (action) {
4277
+ return { data: parsedData, action };
4278
+ }
4279
+ return parsedData;
4280
+ });
4190
4281
  });
4191
4282
  }), "page"),
4192
4283
  inputs: {
@@ -4300,7 +4391,8 @@ async function handleFlow(request, config) {
4300
4391
  request.meta.element,
4301
4392
  span.spanContext().spanId,
4302
4393
  createFlowContextAPI({
4303
- meta: request.meta
4394
+ meta: request.meta,
4395
+ span
4304
4396
  })
4305
4397
  );
4306
4398
  const flowFunction = flows[request.method].fn;
@@ -4428,6 +4520,7 @@ __name(handleFlow, "handleFlow");
4428
4520
 
4429
4521
  // src/index.ts
4430
4522
  import KSUID2 from "ksuid";
4523
+ var createTraceAPI2 = createTraceAPI;
4431
4524
  function ksuid() {
4432
4525
  return KSUID2.randomSync().string;
4433
4526
  }
@@ -4452,6 +4545,7 @@ export {
4452
4545
  TaskAPI,
4453
4546
  checkBuiltInPermissions,
4454
4547
  createFlowContext,
4548
+ createTraceAPI2 as createTraceAPI,
4455
4549
  handleFlow,
4456
4550
  handleJob,
4457
4551
  handleRequest,