@teamkeel/functions-runtime 0.413.9 → 0.414.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
@@ -2454,9 +2454,16 @@ var selectOne = /* @__PURE__ */ __name((name, options) => {
2454
2454
  }, "selectOne");
2455
2455
 
2456
2456
  // src/flows/ui/page.ts
2457
- async function page(options, data) {
2457
+ async function page(options, data, action) {
2458
2458
  const content = options.content;
2459
2459
  let hasValidationErrors = false;
2460
+ if (options.actions) {
2461
+ const isValidAction = options.actions.some((a) => {
2462
+ if (typeof a === "string") return a === action;
2463
+ return a && typeof a === "object" && "label" in a && a.label === action;
2464
+ });
2465
+ hasValidationErrors = !isValidAction;
2466
+ }
2460
2467
  const contentUiConfig = await Promise.all(
2461
2468
  content.map(async (c) => {
2462
2469
  const isInput = "__type" in c && c.__type == "input";
@@ -2607,15 +2614,33 @@ var header = /* @__PURE__ */ __name((options) => {
2607
2614
 
2608
2615
  // src/flows/index.ts
2609
2616
  var defaultOpts = {
2610
- maxRetries: 5,
2611
- timeoutInMs: 6e4
2617
+ retries: 5,
2618
+ timeout: 6e4
2612
2619
  };
2613
- function createFlowContext(runId, data, spanId) {
2620
+ function createFlowContext(runId, data, action, spanId, ctx) {
2621
+ const usedNames = /* @__PURE__ */ new Set();
2614
2622
  return {
2623
+ env: ctx.env,
2624
+ now: ctx.now,
2625
+ secrets: ctx.secrets,
2615
2626
  step: /* @__PURE__ */ __name(async (name, optionsOrFn, fn) => {
2616
2627
  const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
2617
2628
  const actualFn = typeof optionsOrFn === "function" ? optionsOrFn : fn;
2618
2629
  const db = useDatabase();
2630
+ if (usedNames.has(name)) {
2631
+ await db.insertInto("keel.flow_step").values({
2632
+ run_id: runId,
2633
+ name,
2634
+ stage: options.stage,
2635
+ status: "FAILED" /* FAILED */,
2636
+ type: "FUNCTION" /* FUNCTION */,
2637
+ error: `Duplicate step name: ${name}`,
2638
+ startTime: /* @__PURE__ */ new Date(),
2639
+ endTime: /* @__PURE__ */ new Date()
2640
+ }).returningAll().executeTakeFirst();
2641
+ throw new Error(`Duplicate step name: ${name}`);
2642
+ }
2643
+ usedNames.add(name);
2619
2644
  const past = await db.selectFrom("keel.flow_step").where("run_id", "=", runId).where("name", "=", name).selectAll().execute();
2620
2645
  const newSteps = past.filter((step) => step.status === "NEW" /* NEW */);
2621
2646
  const completedSteps = past.filter(
@@ -2646,7 +2671,7 @@ function createFlowContext(runId, data, spanId) {
2646
2671
  try {
2647
2672
  result = await withTimeout(
2648
2673
  actualFn(),
2649
- options.timeoutInMs ?? defaultOpts.timeoutInMs
2674
+ options.timeout ?? defaultOpts.timeout
2650
2675
  );
2651
2676
  } catch (e) {
2652
2677
  await db.updateTable("keel.flow_step").set({
@@ -2655,7 +2680,7 @@ function createFlowContext(runId, data, spanId) {
2655
2680
  endTime: /* @__PURE__ */ new Date(),
2656
2681
  error: e instanceof Error ? e.message : "An error occurred"
2657
2682
  }).where("id", "=", newSteps[0].id).returningAll().executeTakeFirst();
2658
- if (failedSteps.length + 1 >= (options.maxRetries ?? defaultOpts.maxRetries)) {
2683
+ if (failedSteps.length + 1 >= (options.retries ?? defaultOpts.retries)) {
2659
2684
  throw new ExhuastedRetriesDisrupt();
2660
2685
  }
2661
2686
  await db.insertInto("keel.flow_step").values({
@@ -2687,8 +2712,25 @@ function createFlowContext(runId, data, spanId) {
2687
2712
  ui: {
2688
2713
  page: /* @__PURE__ */ __name(async (name, options) => {
2689
2714
  const db = useDatabase();
2715
+ if (usedNames.has(name)) {
2716
+ await db.insertInto("keel.flow_step").values({
2717
+ run_id: runId,
2718
+ name,
2719
+ stage: options.stage,
2720
+ status: "FAILED" /* FAILED */,
2721
+ type: "UI" /* UI */,
2722
+ error: `Duplicate step name: ${name}`,
2723
+ startTime: /* @__PURE__ */ new Date(),
2724
+ endTime: /* @__PURE__ */ new Date()
2725
+ }).returningAll().executeTakeFirst();
2726
+ throw new Error(`Duplicate step name: ${name}`);
2727
+ }
2728
+ usedNames.add(name);
2690
2729
  let step = await db.selectFrom("keel.flow_step").where("run_id", "=", runId).where("name", "=", name).selectAll().executeTakeFirst();
2691
2730
  if (step && step.status === "COMPLETED" /* COMPLETED */) {
2731
+ if (step.action) {
2732
+ return { data: step.value, action: step.action };
2733
+ }
2692
2734
  return step.value;
2693
2735
  }
2694
2736
  if (!step) {
@@ -2700,22 +2742,29 @@ function createFlowContext(runId, data, spanId) {
2700
2742
  type: "UI" /* UI */,
2701
2743
  startTime: /* @__PURE__ */ new Date()
2702
2744
  }).returningAll().executeTakeFirst();
2703
- throw new UIRenderDisrupt(step?.id, (await page(options, null)).page);
2745
+ throw new UIRenderDisrupt(
2746
+ step?.id,
2747
+ (await page(options, null, null)).page
2748
+ );
2704
2749
  }
2705
2750
  if (!data) {
2706
- throw new UIRenderDisrupt(step?.id, (await page(options, null)).page);
2751
+ throw new UIRenderDisrupt(
2752
+ step?.id,
2753
+ (await page(options, null, null)).page
2754
+ );
2707
2755
  }
2708
- const p = await page(options, data);
2756
+ const p = await page(options, data, action);
2709
2757
  if (p.hasValidationErrors) {
2710
2758
  throw new UIRenderDisrupt(step?.id, p.page);
2711
2759
  }
2712
2760
  await db.updateTable("keel.flow_step").set({
2713
2761
  status: "COMPLETED" /* COMPLETED */,
2714
2762
  value: JSON.stringify(data),
2763
+ action,
2715
2764
  spanId,
2716
2765
  endTime: /* @__PURE__ */ new Date()
2717
2766
  }).where("id", "=", step.id).returningAll().executeTakeFirst();
2718
- return data;
2767
+ return { data, action };
2719
2768
  }, "page"),
2720
2769
  inputs: {
2721
2770
  text: textInput,
@@ -2770,7 +2819,7 @@ async function handleFlow(request, config) {
2770
2819
  if (!runId) {
2771
2820
  throw new Error("no runId provided");
2772
2821
  }
2773
- const { flows } = config;
2822
+ const { flows, createFlowContextAPI } = config;
2774
2823
  if (!(request.method in flows)) {
2775
2824
  const message = `no corresponding flow found for '${request.method}'`;
2776
2825
  span.setStatus({
@@ -2789,7 +2838,11 @@ async function handleFlow(request, config) {
2789
2838
  const ctx = createFlowContext(
2790
2839
  request.meta.runId,
2791
2840
  request.meta.data,
2792
- span.spanContext().spanId
2841
+ request.meta.action,
2842
+ span.spanContext().spanId,
2843
+ createFlowContextAPI({
2844
+ meta: request.meta
2845
+ })
2793
2846
  );
2794
2847
  const flowFunction = flows[request.method].fn;
2795
2848
  const rawFlowConfig = flows[request.method].config;