@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.cjs CHANGED
@@ -50,6 +50,7 @@ __export(index_exports, {
50
50
  TaskAPI: () => TaskAPI,
51
51
  checkBuiltInPermissions: () => checkBuiltInPermissions,
52
52
  createFlowContext: () => createFlowContext,
53
+ createTraceAPI: () => createTraceAPI2,
53
54
  handleFlow: () => handleFlow,
54
55
  handleJob: () => handleJob,
55
56
  handleRequest: () => handleRequest,
@@ -251,17 +252,20 @@ var tracing_exports = {};
251
252
  __export(tracing_exports, {
252
253
  KEEL_INTERNAL_ATTR: () => KEEL_INTERNAL_ATTR,
253
254
  KEEL_INTERNAL_CHILDREN: () => KEEL_INTERNAL_CHILDREN,
255
+ createTraceAPI: () => createTraceAPI,
254
256
  forceFlush: () => forceFlush,
255
257
  getTracer: () => getTracer,
256
258
  init: () => init,
257
259
  spanNameForModelAPI: () => spanNameForModelAPI,
258
- withSpan: () => withSpan
260
+ withSpan: () => withSpan,
261
+ withUserSpan: () => withUserSpan
259
262
  });
260
263
  var opentelemetry = __toESM(require("@opentelemetry/api"), 1);
261
264
  var import_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
262
265
  var import_exporter_trace_otlp_proto = require("@opentelemetry/exporter-trace-otlp-proto");
263
266
  var import_sdk_trace_node = require("@opentelemetry/sdk-trace-node");
264
267
  var import_resources = require("@opentelemetry/resources");
268
+ var userSpanContextKey = opentelemetry.createContextKey("keel.userSpan");
265
269
  async function withSpan(name, fn) {
266
270
  return getTracer().startActiveSpan(name, async (span) => {
267
271
  try {
@@ -281,6 +285,73 @@ async function withSpan(name, fn) {
281
285
  });
282
286
  }
283
287
  __name(withSpan, "withSpan");
288
+ function withUserSpan(span, fn) {
289
+ const ctx = opentelemetry.context.active().setValue(userSpanContextKey, span);
290
+ return opentelemetry.context.with(ctx, fn);
291
+ }
292
+ __name(withUserSpan, "withUserSpan");
293
+ function getUserSpan() {
294
+ return opentelemetry.context.active().getValue(userSpanContextKey);
295
+ }
296
+ __name(getUserSpan, "getUserSpan");
297
+ function resolveTraceSpan(defaultSpan) {
298
+ const span = getUserSpan() ?? defaultSpan;
299
+ if (!span) {
300
+ throw new Error("no user span available for ctx.trace");
301
+ }
302
+ return span;
303
+ }
304
+ __name(resolveTraceSpan, "resolveTraceSpan");
305
+ function normalizeTraceAttributeValue(value) {
306
+ if (value === void 0) {
307
+ return void 0;
308
+ }
309
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
310
+ return value;
311
+ }
312
+ if (Array.isArray(value)) {
313
+ if (value.length === 0) {
314
+ return value;
315
+ }
316
+ const firstType = typeof value[0];
317
+ const validArrayType = firstType === "string" || firstType === "number" || firstType === "boolean";
318
+ if (validArrayType && value.every((item) => typeof item === firstType)) {
319
+ return value;
320
+ }
321
+ }
322
+ throw new TypeError(
323
+ "trace attribute values must be strings, numbers, booleans, or arrays of a single primitive type"
324
+ );
325
+ }
326
+ __name(normalizeTraceAttributeValue, "normalizeTraceAttributeValue");
327
+ function createTraceAPI(defaultSpan) {
328
+ return {
329
+ get traceId() {
330
+ return resolveTraceSpan(defaultSpan).spanContext().traceId;
331
+ },
332
+ get spanId() {
333
+ return resolveTraceSpan(defaultSpan).spanContext().spanId;
334
+ },
335
+ setAttribute(key, value) {
336
+ const normalized = normalizeTraceAttributeValue(value);
337
+ if (normalized === void 0) {
338
+ return;
339
+ }
340
+ resolveTraceSpan(defaultSpan).setAttribute(key, normalized);
341
+ },
342
+ setAttributes(attributes) {
343
+ const normalized = {};
344
+ for (const [key, value] of Object.entries(attributes)) {
345
+ const normalizedValue = normalizeTraceAttributeValue(value);
346
+ if (normalizedValue !== void 0) {
347
+ normalized[key] = normalizedValue;
348
+ }
349
+ }
350
+ resolveTraceSpan(defaultSpan).setAttributes(normalized);
351
+ }
352
+ };
353
+ }
354
+ __name(createTraceAPI, "createTraceAPI");
284
355
  function patchFetch() {
285
356
  if (!globalThis.fetch.patched) {
286
357
  const originalFetch = globalThis.fetch;
@@ -1056,7 +1127,11 @@ var TimePeriod = class _TimePeriod {
1056
1127
  let value = rawValue ? parseInt(rawValue, 10) : 1;
1057
1128
  let complete2 = Boolean(isComplete);
1058
1129
  let offset = 0;
1059
- switch (direction?.toLowerCase()) {
1130
+ const dir = direction?.toLowerCase();
1131
+ if (!complete2 && !rawValue && (dir === "last" || dir === "next") && ["day", "week", "month", "year"].includes(period)) {
1132
+ complete2 = true;
1133
+ }
1134
+ switch (dir) {
1060
1135
  case "this":
1061
1136
  offset = 0;
1062
1137
  complete2 = true;
@@ -1114,10 +1189,14 @@ var opMapping = {
1114
1189
  },
1115
1190
  notEquals: { op: import_kysely4.sql`is distinct from` },
1116
1191
  equalsRelative: {
1117
- op: import_kysely4.sql`BETWEEN`,
1118
- value: /* @__PURE__ */ __name((v) => import_kysely4.sql`${import_kysely4.sql.raw(
1119
- TimePeriod.fromExpression(v).periodStartSQL()
1120
- )} AND ${import_kysely4.sql.raw(TimePeriod.fromExpression(v).periodEndSQL())}`, "value")
1192
+ custom: /* @__PURE__ */ __name((qb, fieldName, v) => {
1193
+ const tp = TimePeriod.fromExpression(v);
1194
+ return qb.where(
1195
+ import_kysely4.sql`${import_kysely4.sql.raw(fieldName)} >= ${import_kysely4.sql.raw(
1196
+ tp.periodStartSQL()
1197
+ )} AND ${import_kysely4.sql.raw(fieldName)} < ${import_kysely4.sql.raw(tp.periodEndSQL())}`
1198
+ );
1199
+ }, "custom")
1121
1200
  },
1122
1201
  beforeRelative: {
1123
1202
  op: "<",
@@ -1154,18 +1233,18 @@ var opMapping = {
1154
1233
  notEquals: { op: "=", value: /* @__PURE__ */ __name((v) => import_kysely4.sql`NOT ${v}`, "value") }
1155
1234
  }
1156
1235
  };
1157
- function applyWhereConditions(context6, qb, where = {}) {
1158
- const conf = context6.tableConfig();
1236
+ function applyWhereConditions(context7, qb, where = {}) {
1237
+ const conf = context7.tableConfig();
1159
1238
  for (const key of Object.keys(where)) {
1160
1239
  const v = where[key];
1161
1240
  if (conf && conf[(0, import_change_case.snakeCase)(key)]) {
1162
1241
  const rel = conf[(0, import_change_case.snakeCase)(key)];
1163
- context6.withJoin(rel.referencesTable, () => {
1164
- qb = applyWhereConditions(context6, qb, v);
1242
+ context7.withJoin(rel.referencesTable, () => {
1243
+ qb = applyWhereConditions(context7, qb, v);
1165
1244
  });
1166
1245
  continue;
1167
1246
  }
1168
- const fieldName = `${context6.tableAlias()}.${(0, import_change_case.snakeCase)(key)}`;
1247
+ const fieldName = `${context7.tableAlias()}.${(0, import_change_case.snakeCase)(key)}`;
1169
1248
  if (Object.prototype.toString.call(v) !== "[object Object]") {
1170
1249
  const operator = v === null || Array.isArray(v) ? import_kysely4.sql`is not distinct from` : "=";
1171
1250
  qb = qb.where(fieldName, operator, import_kysely4.sql`${v}`);
@@ -1176,7 +1255,9 @@ function applyWhereConditions(context6, qb, where = {}) {
1176
1255
  if (!mapping) {
1177
1256
  throw new Error(`invalid where condition: ${op}`);
1178
1257
  }
1179
- if (mapping.isArrayQuery) {
1258
+ if (mapping.custom) {
1259
+ qb = mapping.custom(qb, fieldName, v[op]);
1260
+ } else if (mapping.isArrayQuery) {
1180
1261
  for (const arrayOp of Object.keys(v[op])) {
1181
1262
  qb = qb.where(
1182
1263
  mapping[arrayOp].value ? mapping[arrayOp].value(v[op][arrayOp]) : import_kysely4.sql`${v[op][arrayOp]}`,
@@ -1199,15 +1280,15 @@ function applyWhereConditions(context6, qb, where = {}) {
1199
1280
  __name(applyWhereConditions, "applyWhereConditions");
1200
1281
 
1201
1282
  // src/applyAdditionalQueryConstraints.js
1202
- function applyLimit(context6, qb, limit) {
1283
+ function applyLimit(context7, qb, limit) {
1203
1284
  return qb.limit(limit);
1204
1285
  }
1205
1286
  __name(applyLimit, "applyLimit");
1206
- function applyOffset(context6, qb, offset) {
1287
+ function applyOffset(context7, qb, offset) {
1207
1288
  return qb.offset(offset);
1208
1289
  }
1209
1290
  __name(applyOffset, "applyOffset");
1210
- function applyOrderBy(context6, qb, tableName, orderBy = {}) {
1291
+ function applyOrderBy(context7, qb, tableName, orderBy = {}) {
1211
1292
  Object.entries(orderBy).forEach(([key, sortOrder]) => {
1212
1293
  qb = qb.orderBy(`${tableName}.${(0, import_change_case.snakeCase)(key)}`, sortOrder.toLowerCase());
1213
1294
  });
@@ -1216,41 +1297,41 @@ function applyOrderBy(context6, qb, tableName, orderBy = {}) {
1216
1297
  __name(applyOrderBy, "applyOrderBy");
1217
1298
 
1218
1299
  // src/applyJoins.js
1219
- function applyJoins(context6, qb, where) {
1220
- const conf = context6.tableConfig();
1300
+ function applyJoins(context7, qb, where) {
1301
+ const conf = context7.tableConfig();
1221
1302
  if (!conf) {
1222
1303
  return qb;
1223
1304
  }
1224
- const srcTable = context6.tableAlias();
1305
+ const srcTable = context7.tableAlias();
1225
1306
  for (const key of Object.keys(where)) {
1226
1307
  const rel = conf[(0, import_change_case.snakeCase)(key)];
1227
1308
  if (!rel) {
1228
1309
  continue;
1229
1310
  }
1230
1311
  const targetTable = rel.referencesTable;
1231
- if (context6.hasJoin(targetTable)) {
1312
+ if (context7.hasJoin(targetTable)) {
1232
1313
  continue;
1233
1314
  }
1234
- context6.withJoin(targetTable, () => {
1315
+ context7.withJoin(targetTable, () => {
1235
1316
  switch (rel.relationshipType) {
1236
1317
  case "hasMany":
1237
1318
  qb = qb.innerJoin(
1238
- `${targetTable} as ${context6.tableAlias()}`,
1319
+ `${targetTable} as ${context7.tableAlias()}`,
1239
1320
  `${srcTable}.id`,
1240
- `${context6.tableAlias()}.${rel.foreignKey}`
1321
+ `${context7.tableAlias()}.${rel.foreignKey}`
1241
1322
  );
1242
1323
  break;
1243
1324
  case "belongsTo":
1244
1325
  qb = qb.innerJoin(
1245
- `${targetTable} as ${context6.tableAlias()}`,
1326
+ `${targetTable} as ${context7.tableAlias()}`,
1246
1327
  `${srcTable}.${rel.foreignKey}`,
1247
- `${context6.tableAlias()}.id`
1328
+ `${context7.tableAlias()}.id`
1248
1329
  );
1249
1330
  break;
1250
1331
  default:
1251
1332
  throw new Error(`unknown relationshipType: ${rel.relationshipType}`);
1252
1333
  }
1253
- qb = applyJoins(context6, qb, where[key]);
1334
+ qb = applyJoins(context7, qb, where[key]);
1254
1335
  });
1255
1336
  }
1256
1337
  return qb;
@@ -1480,17 +1561,17 @@ var QueryBuilder = class _QueryBuilder {
1480
1561
  * @param {import("./QueryContext").QueryContext} context
1481
1562
  * @param {import("kysely").Kysely} db
1482
1563
  */
1483
- constructor(tableName, context6, db) {
1564
+ constructor(tableName, context7, db) {
1484
1565
  this._tableName = tableName;
1485
- this._context = context6;
1566
+ this._context = context7;
1486
1567
  this._db = db;
1487
1568
  this._modelName = upperCamelCase(this._tableName);
1488
1569
  }
1489
1570
  where(where) {
1490
- const context6 = this._context.clone();
1491
- let builder = applyJoins(context6, this._db, where);
1492
- builder = applyWhereConditions(context6, builder, where);
1493
- return new _QueryBuilder(this._tableName, context6, builder);
1571
+ const context7 = this._context.clone();
1572
+ let builder = applyJoins(context7, this._db, where);
1573
+ builder = applyWhereConditions(context7, builder, where);
1574
+ return new _QueryBuilder(this._tableName, context7, builder);
1494
1575
  }
1495
1576
  sql() {
1496
1577
  return this._db.compile().sql;
@@ -1555,19 +1636,19 @@ var QueryBuilder = class _QueryBuilder {
1555
1636
  const name = spanNameForModelAPI(this._modelName, "findMany");
1556
1637
  const db = useDatabase();
1557
1638
  return withSpan(name, async (span) => {
1558
- const context6 = new QueryContext([this._tableName], this._tableConfigMap);
1639
+ const context7 = new QueryContext([this._tableName], this._tableConfigMap);
1559
1640
  let builder = db.selectFrom((qb) => {
1560
1641
  return this._db.as(this._tableName);
1561
1642
  }).selectAll();
1562
1643
  if (params?.limit) {
1563
- builder = applyLimit(context6, builder, params.limit);
1644
+ builder = applyLimit(context7, builder, params.limit);
1564
1645
  }
1565
1646
  if (params?.offset) {
1566
- builder = applyOffset(context6, builder, params.offset);
1647
+ builder = applyOffset(context7, builder, params.offset);
1567
1648
  }
1568
1649
  if (params?.orderBy !== void 0 && Object.keys(params?.orderBy).length > 0) {
1569
1650
  builder = applyOrderBy(
1570
- context6,
1651
+ context7,
1571
1652
  builder,
1572
1653
  this._tableName,
1573
1654
  params.orderBy
@@ -1601,7 +1682,7 @@ var QueryBuilder = class _QueryBuilder {
1601
1682
  );
1602
1683
  const db = useDatabase();
1603
1684
  return withSpan(name, async (span) => {
1604
- const context6 = new QueryContext([this._tableName], this._tableConfigMap);
1685
+ const context7 = new QueryContext([this._tableName], this._tableConfigMap);
1605
1686
  const isOffsetPagination = params.limit != null && params.limit > 0;
1606
1687
  const isBackward = params.last != null && params.last > 0;
1607
1688
  const DEFAULT_PAGE_SIZE = 50;
@@ -1649,7 +1730,7 @@ var QueryBuilder = class _QueryBuilder {
1649
1730
  ])
1650
1731
  ) : normalizedOrderBy;
1651
1732
  builder = applyOrderBy(
1652
- context6,
1733
+ context7,
1653
1734
  builder,
1654
1735
  this._tableName,
1655
1736
  effectiveOrderBy
@@ -1722,10 +1803,10 @@ var QueryBuilder = class _QueryBuilder {
1722
1803
  }
1723
1804
  }
1724
1805
  if (limit != null) {
1725
- builder = applyLimit(context6, builder, limit);
1806
+ builder = applyLimit(context7, builder, limit);
1726
1807
  }
1727
1808
  if (isOffsetPagination && params.offset) {
1728
- builder = applyOffset(context6, builder, params.offset);
1809
+ builder = applyOffset(context7, builder, params.offset);
1729
1810
  }
1730
1811
  span.setAttribute("sql", builder.compile().sql);
1731
1812
  let rows = await builder.execute();
@@ -1830,9 +1911,9 @@ var ModelAPI = class {
1830
1911
  const db = useDatabase();
1831
1912
  return withSpan(name, async (span) => {
1832
1913
  let builder = db.selectFrom(this._tableName).distinctOn(`${this._tableName}.id`).selectAll(this._tableName);
1833
- const context6 = new QueryContext([this._tableName], this._tableConfigMap);
1834
- builder = applyJoins(context6, builder, where);
1835
- builder = applyWhereConditions(context6, builder, where);
1914
+ const context7 = new QueryContext([this._tableName], this._tableConfigMap);
1915
+ builder = applyJoins(context7, builder, where);
1916
+ builder = applyWhereConditions(context7, builder, where);
1836
1917
  span.setAttribute("sql", builder.compile().sql);
1837
1918
  const row = await builder.executeTakeFirst();
1838
1919
  if (!row) {
@@ -1846,23 +1927,23 @@ var ModelAPI = class {
1846
1927
  const db = useDatabase();
1847
1928
  const where = params?.where || {};
1848
1929
  return withSpan(name, async (span) => {
1849
- const context6 = new QueryContext([this._tableName], this._tableConfigMap);
1930
+ const context7 = new QueryContext([this._tableName], this._tableConfigMap);
1850
1931
  let builder = db.selectFrom((qb) => {
1851
1932
  let builder2 = qb.selectFrom(this._tableName).distinctOn(`${this._tableName}.id`).selectAll(this._tableName);
1852
- builder2 = applyJoins(context6, builder2, where);
1853
- builder2 = applyWhereConditions(context6, builder2, where);
1933
+ builder2 = applyJoins(context7, builder2, where);
1934
+ builder2 = applyWhereConditions(context7, builder2, where);
1854
1935
  builder2 = builder2.as(this._tableName);
1855
1936
  return builder2;
1856
1937
  }).selectAll();
1857
1938
  if (params?.limit) {
1858
- builder = applyLimit(context6, builder, params.limit);
1939
+ builder = applyLimit(context7, builder, params.limit);
1859
1940
  }
1860
1941
  if (params?.offset) {
1861
- builder = applyOffset(context6, builder, params.offset);
1942
+ builder = applyOffset(context7, builder, params.offset);
1862
1943
  }
1863
1944
  if (params?.orderBy !== void 0 && Object.keys(params?.orderBy).length > 0) {
1864
1945
  builder = applyOrderBy(
1865
- context6,
1946
+ context7,
1866
1947
  builder,
1867
1948
  this._tableName,
1868
1949
  params.orderBy
@@ -1913,8 +1994,8 @@ var ModelAPI = class {
1913
1994
  }
1914
1995
  }
1915
1996
  builder = builder.set(snakeCaseObject(row));
1916
- const context6 = new QueryContext([this._tableName], this._tableConfigMap);
1917
- builder = applyWhereConditions(context6, builder, where);
1997
+ const context7 = new QueryContext([this._tableName], this._tableConfigMap);
1998
+ builder = applyWhereConditions(context7, builder, where);
1918
1999
  span.setAttribute("sql", builder.compile().sql);
1919
2000
  try {
1920
2001
  const row2 = await builder.executeTakeFirstOrThrow();
@@ -1929,8 +2010,8 @@ var ModelAPI = class {
1929
2010
  const db = useDatabase();
1930
2011
  return withSpan(name, async (span) => {
1931
2012
  let builder = db.deleteFrom(this._tableName).returning(["id"]);
1932
- const context6 = new QueryContext([this._tableName], this._tableConfigMap);
1933
- builder = applyWhereConditions(context6, builder, where);
2013
+ const context7 = new QueryContext([this._tableName], this._tableConfigMap);
2014
+ builder = applyWhereConditions(context7, builder, where);
1934
2015
  span.setAttribute("sql", builder.compile().sql);
1935
2016
  try {
1936
2017
  const row = await builder.executeTakeFirstOrThrow();
@@ -1943,10 +2024,10 @@ var ModelAPI = class {
1943
2024
  where(where) {
1944
2025
  const db = useDatabase();
1945
2026
  let builder = db.selectFrom(this._tableName).distinctOn(`${this._tableName}.id`).selectAll(this._tableName);
1946
- const context6 = new QueryContext([this._tableName], this._tableConfigMap);
1947
- builder = applyJoins(context6, builder, where);
1948
- builder = applyWhereConditions(context6, builder, where);
1949
- return new QueryBuilder(this._tableName, context6, builder);
2027
+ const context7 = new QueryContext([this._tableName], this._tableConfigMap);
2028
+ builder = applyJoins(context7, builder, where);
2029
+ builder = applyWhereConditions(context7, builder, where);
2030
+ return new QueryBuilder(this._tableName, context7, builder);
1950
2031
  }
1951
2032
  };
1952
2033
  async function create(conn, tableName, tableConfigs, values) {
@@ -2889,7 +2970,8 @@ async function handleRequest(request, config) {
2889
2970
  const headers = new Headers();
2890
2971
  const ctx = createContextAPI({
2891
2972
  responseHeaders: headers,
2892
- meta: request.meta
2973
+ meta: request.meta,
2974
+ span
2893
2975
  });
2894
2976
  const permitted = request.meta && request.meta.permissionState.status === "granted" ? true : null;
2895
2977
  db = createDatabaseClient({
@@ -3008,7 +3090,8 @@ async function handleJob(request, config) {
3008
3090
  );
3009
3091
  }
3010
3092
  const ctx = createJobContextAPI({
3011
- meta: request.meta
3093
+ meta: request.meta,
3094
+ span
3012
3095
  });
3013
3096
  const permitted = request.meta && request.meta.permissionState.status === "granted" ? true : null;
3014
3097
  db = createDatabaseClient({
@@ -3096,7 +3179,8 @@ async function handleSubscriber(request, config) {
3096
3179
  );
3097
3180
  }
3098
3181
  const ctx = createSubscriberContextAPI({
3099
- meta: request.meta
3182
+ meta: request.meta,
3183
+ span
3100
3184
  });
3101
3185
  db = createDatabaseClient({
3102
3186
  connString: request.meta?.secrets?.KEEL_DB_CONN
@@ -3172,7 +3256,8 @@ async function handleRoute(request, config) {
3172
3256
  ...ctx
3173
3257
  } = createContextAPI({
3174
3258
  responseHeaders: new Headers(),
3175
- meta: request.meta
3259
+ meta: request.meta,
3260
+ span
3176
3261
  });
3177
3262
  request.params.headers = headers;
3178
3263
  db = createDatabaseClient({
@@ -4015,6 +4100,7 @@ function createFlowContext(runId, data, action, callback, element, spanId, ctx)
4015
4100
  env: ctx.env,
4016
4101
  now: ctx.now,
4017
4102
  secrets: ctx.secrets,
4103
+ trace: ctx.trace,
4018
4104
  complete: /* @__PURE__ */ __name((options) => {
4019
4105
  return {
4020
4106
  __type: "ui.complete",
@@ -4023,116 +4109,19 @@ function createFlowContext(runId, data, action, callback, element, spanId, ctx)
4023
4109
  }, "complete"),
4024
4110
  step: /* @__PURE__ */ __name(async (name, optionsOrFn, fn) => {
4025
4111
  return withSpan(`Step - ${name}`, async (span) => {
4026
- const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
4027
- const actualFn = typeof optionsOrFn === "function" ? optionsOrFn : fn;
4028
- options.retries = options.retries ?? defaultOpts.retries;
4029
- options.timeout = options.timeout ?? defaultOpts.timeout;
4030
- const db = useDatabase();
4031
- if (usedNames.has(name)) {
4032
- await db.insertInto("keel.flow_step").values({
4033
- run_id: runId,
4034
- name,
4035
- stage: options.stage,
4036
- status: "FAILED" /* FAILED */,
4037
- type: "FUNCTION" /* FUNCTION */,
4038
- error: `Duplicate step name: ${name}`,
4039
- startTime: /* @__PURE__ */ new Date(),
4040
- endTime: /* @__PURE__ */ new Date()
4041
- }).returningAll().executeTakeFirst();
4042
- throw new Error(`Duplicate step name: ${name}`);
4043
- }
4044
- usedNames.add(name);
4045
- const past = await db.selectFrom("keel.flow_step").where("run_id", "=", runId).where("name", "=", name).selectAll().execute();
4046
- const newSteps = past.filter((step) => step.status === "NEW" /* NEW */);
4047
- const completedSteps = past.filter(
4048
- (step) => step.status === "COMPLETED" /* COMPLETED */
4049
- );
4050
- const failedSteps = past.filter(
4051
- (step) => step.status === "FAILED" /* FAILED */
4052
- );
4053
- if (newSteps.length > 1) {
4054
- throw new Error("Multiple NEW steps found for the same step");
4055
- }
4056
- if (completedSteps.length > 1) {
4057
- throw new Error("Multiple completed steps found for the same step");
4058
- }
4059
- if (completedSteps.length > 1 && newSteps.length > 1) {
4060
- throw new Error(
4061
- "Multiple completed and new steps found for the same step"
4062
- );
4063
- }
4064
- if (completedSteps.length === 1) {
4065
- span.setAttribute(KEEL_INTERNAL_ATTR, KEEL_INTERNAL_CHILDREN);
4066
- return completedSteps[0].value;
4067
- }
4068
- if (newSteps.length === 1) {
4069
- let result = null;
4070
- await db.updateTable("keel.flow_step").set({
4071
- startTime: /* @__PURE__ */ new Date()
4072
- }).where("id", "=", newSteps[0].id).returningAll().executeTakeFirst();
4073
- try {
4074
- const stepArgs = {
4075
- attempt: failedSteps.length + 1,
4076
- stepOptions: options
4077
- };
4078
- result = await withTimeout(actualFn(stepArgs), options.timeout);
4079
- } catch (e) {
4080
- await db.updateTable("keel.flow_step").set({
4081
- status: "FAILED" /* FAILED */,
4082
- spanId,
4083
- endTime: /* @__PURE__ */ new Date(),
4084
- error: e instanceof Error ? e.message : "An error occurred"
4085
- }).where("id", "=", newSteps[0].id).returningAll().executeTakeFirst();
4086
- if (failedSteps.length >= options.retries || e instanceof NonRetriableError) {
4087
- if (options.onFailure) {
4088
- await options.onFailure();
4089
- }
4090
- throw new ExhuastedRetriesDisrupt();
4091
- }
4092
- await db.insertInto("keel.flow_step").values({
4093
- run_id: runId,
4094
- name,
4095
- stage: options.stage,
4096
- status: "NEW" /* NEW */,
4097
- type: "FUNCTION" /* FUNCTION */
4098
- }).returningAll().executeTakeFirst();
4099
- throw new StepCreatedDisrupt(
4100
- options.retryPolicy ? new Date(
4101
- Date.now() + options.retryPolicy(failedSteps.length + 1)
4102
- ) : void 0
4103
- );
4104
- }
4105
- await db.updateTable("keel.flow_step").set({
4106
- status: "COMPLETED" /* COMPLETED */,
4107
- value: JSON.stringify(result),
4108
- spanId,
4109
- endTime: /* @__PURE__ */ new Date()
4110
- }).where("id", "=", newSteps[0].id).returningAll().executeTakeFirst();
4111
- return result;
4112
- }
4113
- await db.insertInto("keel.flow_step").values({
4114
- run_id: runId,
4115
- name,
4116
- stage: options.stage,
4117
- status: "NEW" /* NEW */,
4118
- type: "FUNCTION" /* FUNCTION */
4119
- }).returningAll().executeTakeFirst();
4120
- span.setAttribute(KEEL_INTERNAL_ATTR, KEEL_INTERNAL_CHILDREN);
4121
- throw new StepCreatedDisrupt();
4122
- });
4123
- }, "step"),
4124
- ui: {
4125
- page: /* @__PURE__ */ __name((async (name, options) => {
4126
- return withSpan(`Page - ${name}`, async (span) => {
4112
+ return withUserSpan(span, async () => {
4113
+ const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
4114
+ const actualFn = typeof optionsOrFn === "function" ? optionsOrFn : fn;
4115
+ options.retries = options.retries ?? defaultOpts.retries;
4116
+ options.timeout = options.timeout ?? defaultOpts.timeout;
4127
4117
  const db = useDatabase();
4128
- const isCallback = element && callback;
4129
4118
  if (usedNames.has(name)) {
4130
4119
  await db.insertInto("keel.flow_step").values({
4131
4120
  run_id: runId,
4132
4121
  name,
4133
4122
  stage: options.stage,
4134
4123
  status: "FAILED" /* FAILED */,
4135
- type: "UI" /* UI */,
4124
+ type: "FUNCTION" /* FUNCTION */,
4136
4125
  error: `Duplicate step name: ${name}`,
4137
4126
  startTime: /* @__PURE__ */ new Date(),
4138
4127
  endTime: /* @__PURE__ */ new Date()
@@ -4140,85 +4129,188 @@ function createFlowContext(runId, data, action, callback, element, spanId, ctx)
4140
4129
  throw new Error(`Duplicate step name: ${name}`);
4141
4130
  }
4142
4131
  usedNames.add(name);
4143
- let step = await db.selectFrom("keel.flow_step").where("run_id", "=", runId).where("name", "=", name).selectAll().executeTakeFirst();
4144
- if (step && step.status === "COMPLETED" /* COMPLETED */) {
4145
- span.setAttribute(KEEL_INTERNAL_ATTR, KEEL_INTERNAL_CHILDREN);
4146
- const parsedData2 = transformRichDataTypes(step.value);
4147
- if (step.action) {
4148
- return { data: parsedData2, action: step.action };
4149
- }
4150
- return parsedData2;
4132
+ const past = await db.selectFrom("keel.flow_step").where("run_id", "=", runId).where("name", "=", name).selectAll().execute();
4133
+ const newSteps = past.filter(
4134
+ (step) => step.status === "NEW" /* NEW */
4135
+ );
4136
+ const completedSteps = past.filter(
4137
+ (step) => step.status === "COMPLETED" /* COMPLETED */
4138
+ );
4139
+ const failedSteps = past.filter(
4140
+ (step) => step.status === "FAILED" /* FAILED */
4141
+ );
4142
+ if (newSteps.length > 1) {
4143
+ throw new Error("Multiple NEW steps found for the same step");
4151
4144
  }
4152
- if (!step) {
4153
- step = await db.insertInto("keel.flow_step").values({
4154
- run_id: runId,
4155
- name,
4156
- stage: options.stage,
4157
- status: "PENDING" /* PENDING */,
4158
- type: "UI" /* UI */,
4159
- startTime: /* @__PURE__ */ new Date()
4160
- }).returningAll().executeTakeFirst();
4161
- span.setAttribute("rendered", true);
4162
- throw new UIRenderDisrupt(
4163
- step?.id,
4164
- (await page(options, null, null)).page
4145
+ if (completedSteps.length > 1) {
4146
+ throw new Error("Multiple completed steps found for the same step");
4147
+ }
4148
+ if (completedSteps.length > 1 && newSteps.length > 1) {
4149
+ throw new Error(
4150
+ "Multiple completed and new steps found for the same step"
4165
4151
  );
4166
4152
  }
4167
- if (isCallback) {
4168
- span.setAttribute("callback", callback);
4153
+ if (completedSteps.length === 1) {
4154
+ span.setAttribute(KEEL_INTERNAL_ATTR, KEEL_INTERNAL_CHILDREN);
4155
+ return completedSteps[0].value;
4156
+ }
4157
+ if (newSteps.length === 1) {
4158
+ let result = null;
4159
+ await db.updateTable("keel.flow_step").set({
4160
+ startTime: /* @__PURE__ */ new Date()
4161
+ }).where("id", "=", newSteps[0].id).returningAll().executeTakeFirst();
4169
4162
  try {
4170
- const response = await callbackFn(
4171
- options.content,
4172
- element,
4173
- callback,
4174
- data
4175
- );
4176
- throw new CallbackDisrupt(response, false);
4163
+ const stepArgs = {
4164
+ attempt: failedSteps.length + 1,
4165
+ stepOptions: options
4166
+ };
4167
+ result = await withTimeout(actualFn(stepArgs), options.timeout);
4177
4168
  } catch (e) {
4178
- if (e instanceof CallbackDisrupt) {
4179
- throw e;
4169
+ await db.updateTable("keel.flow_step").set({
4170
+ status: "FAILED" /* FAILED */,
4171
+ spanId,
4172
+ endTime: /* @__PURE__ */ new Date(),
4173
+ error: e instanceof Error ? e.message : "An error occurred"
4174
+ }).where("id", "=", newSteps[0].id).returningAll().executeTakeFirst();
4175
+ if (failedSteps.length >= options.retries || e instanceof NonRetriableError) {
4176
+ if (options.onFailure) {
4177
+ await options.onFailure();
4178
+ }
4179
+ throw new ExhuastedRetriesDisrupt();
4180
4180
  }
4181
- throw new CallbackDisrupt(
4182
- e instanceof Error ? e.message : `An error occurred`,
4183
- true
4181
+ await db.insertInto("keel.flow_step").values({
4182
+ run_id: runId,
4183
+ name,
4184
+ stage: options.stage,
4185
+ status: "NEW" /* NEW */,
4186
+ type: "FUNCTION" /* FUNCTION */
4187
+ }).returningAll().executeTakeFirst();
4188
+ throw new StepCreatedDisrupt(
4189
+ options.retryPolicy ? new Date(
4190
+ Date.now() + options.retryPolicy(failedSteps.length + 1)
4191
+ ) : void 0
4184
4192
  );
4185
4193
  }
4194
+ await db.updateTable("keel.flow_step").set({
4195
+ status: "COMPLETED" /* COMPLETED */,
4196
+ value: JSON.stringify(result),
4197
+ spanId,
4198
+ endTime: /* @__PURE__ */ new Date()
4199
+ }).where("id", "=", newSteps[0].id).returningAll().executeTakeFirst();
4200
+ return result;
4186
4201
  }
4187
- if (!data) {
4188
- throw new UIRenderDisrupt(
4189
- step?.id,
4190
- (await page(options, null, null)).page
4191
- );
4192
- }
4193
- try {
4194
- const p = await page(options, data, action);
4195
- if (p.hasValidationErrors) {
4196
- throw new UIRenderDisrupt(step?.id, p.page);
4202
+ await db.insertInto("keel.flow_step").values({
4203
+ run_id: runId,
4204
+ name,
4205
+ stage: options.stage,
4206
+ status: "NEW" /* NEW */,
4207
+ type: "FUNCTION" /* FUNCTION */
4208
+ }).returningAll().executeTakeFirst();
4209
+ span.setAttribute(KEEL_INTERNAL_ATTR, KEEL_INTERNAL_CHILDREN);
4210
+ throw new StepCreatedDisrupt();
4211
+ });
4212
+ });
4213
+ }, "step"),
4214
+ ui: {
4215
+ page: /* @__PURE__ */ __name((async (name, options) => {
4216
+ return withSpan(`Page - ${name}`, async (span) => {
4217
+ return withUserSpan(span, async () => {
4218
+ const db = useDatabase();
4219
+ const isCallback = element && callback;
4220
+ if (usedNames.has(name)) {
4221
+ await db.insertInto("keel.flow_step").values({
4222
+ run_id: runId,
4223
+ name,
4224
+ stage: options.stage,
4225
+ status: "FAILED" /* FAILED */,
4226
+ type: "UI" /* UI */,
4227
+ error: `Duplicate step name: ${name}`,
4228
+ startTime: /* @__PURE__ */ new Date(),
4229
+ endTime: /* @__PURE__ */ new Date()
4230
+ }).returningAll().executeTakeFirst();
4231
+ throw new Error(`Duplicate step name: ${name}`);
4232
+ }
4233
+ usedNames.add(name);
4234
+ let step = await db.selectFrom("keel.flow_step").where("run_id", "=", runId).where("name", "=", name).selectAll().executeTakeFirst();
4235
+ if (step && step.status === "COMPLETED" /* COMPLETED */) {
4236
+ span.setAttribute(KEEL_INTERNAL_ATTR, KEEL_INTERNAL_CHILDREN);
4237
+ const parsedData2 = transformRichDataTypes(step.value);
4238
+ if (step.action) {
4239
+ return { data: parsedData2, action: step.action };
4240
+ }
4241
+ return parsedData2;
4242
+ }
4243
+ if (!step) {
4244
+ step = await db.insertInto("keel.flow_step").values({
4245
+ run_id: runId,
4246
+ name,
4247
+ stage: options.stage,
4248
+ status: "PENDING" /* PENDING */,
4249
+ type: "UI" /* UI */,
4250
+ startTime: /* @__PURE__ */ new Date()
4251
+ }).returningAll().executeTakeFirst();
4252
+ span.setAttribute("rendered", true);
4253
+ throw new UIRenderDisrupt(
4254
+ step?.id,
4255
+ (await page(options, null, null)).page
4256
+ );
4257
+ }
4258
+ if (isCallback) {
4259
+ span.setAttribute("callback", callback);
4260
+ try {
4261
+ const response = await callbackFn(
4262
+ options.content,
4263
+ element,
4264
+ callback,
4265
+ data
4266
+ );
4267
+ throw new CallbackDisrupt(response, false);
4268
+ } catch (e) {
4269
+ if (e instanceof CallbackDisrupt) {
4270
+ throw e;
4271
+ }
4272
+ throw new CallbackDisrupt(
4273
+ e instanceof Error ? e.message : `An error occurred`,
4274
+ true
4275
+ );
4276
+ }
4197
4277
  }
4198
- } catch (e) {
4199
- if (e instanceof UIRenderDisrupt) {
4278
+ if (!data) {
4279
+ throw new UIRenderDisrupt(
4280
+ step?.id,
4281
+ (await page(options, null, null)).page
4282
+ );
4283
+ }
4284
+ try {
4285
+ const p = await page(options, data, action);
4286
+ if (p.hasValidationErrors) {
4287
+ throw new UIRenderDisrupt(step?.id, p.page);
4288
+ }
4289
+ } catch (e) {
4290
+ if (e instanceof UIRenderDisrupt) {
4291
+ throw e;
4292
+ }
4293
+ await db.updateTable("keel.flow_step").set({
4294
+ status: "FAILED" /* FAILED */,
4295
+ spanId,
4296
+ endTime: /* @__PURE__ */ new Date(),
4297
+ error: e instanceof Error ? e.message : "An error occurred"
4298
+ }).where("id", "=", step?.id).returningAll().executeTakeFirst();
4200
4299
  throw e;
4201
4300
  }
4202
4301
  await db.updateTable("keel.flow_step").set({
4203
- status: "FAILED" /* FAILED */,
4302
+ status: "COMPLETED" /* COMPLETED */,
4303
+ value: JSON.stringify(data),
4304
+ action,
4204
4305
  spanId,
4205
- endTime: /* @__PURE__ */ new Date(),
4206
- error: e instanceof Error ? e.message : "An error occurred"
4207
- }).where("id", "=", step?.id).returningAll().executeTakeFirst();
4208
- throw e;
4209
- }
4210
- await db.updateTable("keel.flow_step").set({
4211
- status: "COMPLETED" /* COMPLETED */,
4212
- value: JSON.stringify(data),
4213
- action,
4214
- spanId,
4215
- endTime: /* @__PURE__ */ new Date()
4216
- }).where("id", "=", step.id).returningAll().executeTakeFirst();
4217
- const parsedData = transformRichDataTypes(data);
4218
- if (action) {
4219
- return { data: parsedData, action };
4220
- }
4221
- return parsedData;
4306
+ endTime: /* @__PURE__ */ new Date()
4307
+ }).where("id", "=", step.id).returningAll().executeTakeFirst();
4308
+ const parsedData = transformRichDataTypes(data);
4309
+ if (action) {
4310
+ return { data: parsedData, action };
4311
+ }
4312
+ return parsedData;
4313
+ });
4222
4314
  });
4223
4315
  }), "page"),
4224
4316
  inputs: {
@@ -4332,7 +4424,8 @@ async function handleFlow(request, config) {
4332
4424
  request.meta.element,
4333
4425
  span.spanContext().spanId,
4334
4426
  createFlowContextAPI({
4335
- meta: request.meta
4427
+ meta: request.meta,
4428
+ span
4336
4429
  })
4337
4430
  );
4338
4431
  const flowFunction = flows[request.method].fn;
@@ -4460,6 +4553,7 @@ __name(handleFlow, "handleFlow");
4460
4553
 
4461
4554
  // src/index.ts
4462
4555
  var import_ksuid2 = __toESM(require("ksuid"), 1);
4556
+ var createTraceAPI2 = createTraceAPI;
4463
4557
  function ksuid() {
4464
4558
  return import_ksuid2.default.randomSync().string;
4465
4559
  }
@@ -4485,6 +4579,7 @@ __name(ksuid, "ksuid");
4485
4579
  TaskAPI,
4486
4580
  checkBuiltInPermissions,
4487
4581
  createFlowContext,
4582
+ createTraceAPI,
4488
4583
  handleFlow,
4489
4584
  handleJob,
4490
4585
  handleRequest,