@teamkeel/functions-runtime 0.413.9 → 0.414.1

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.d.cts CHANGED
@@ -352,11 +352,12 @@ type PageOptions<C extends FlowConfig, A extends PageActions[], T extends UIElem
352
352
  validate?: (data: ExtractFormData<T>) => Promise<true | string>;
353
353
  actions?: A;
354
354
  };
355
- type UiPage<C extends FlowConfig> = <T extends UIElements, const A extends PageActions[] = []>(name: string, options: PageOptions<C, A, T>) => A["length"] extends 0 ? ExtractFormData<T> : {
355
+ type UiPage<C extends FlowConfig> = <T extends UIElements, const A extends PageActions[] = []>(name: string, options: PageOptions<C, A, T>) => Promise<A["length"] extends 0 ? ExtractFormData<T> : {
356
356
  data: ExtractFormData<T>;
357
357
  action: ActionValue<A[number]>;
358
- };
359
- type PageActions = string | {
358
+ }>;
359
+ type PageActions = string | PageActionConfig;
360
+ type PageActionConfig = {
360
361
  label: string;
361
362
  value: string;
362
363
  mode?: "primary" | "secondary" | "destructive";
@@ -545,19 +546,31 @@ type UIApiResponses = {
545
546
  };
546
547
  };
547
548
 
548
- interface FlowContext<C extends FlowConfig> {
549
+ interface FlowContext<C extends FlowConfig, E = any, S = any> {
549
550
  step: Step<C>;
550
551
  ui: UI<C>;
552
+ env: E;
553
+ now: Date;
554
+ secrets: S;
551
555
  }
552
556
  type JsonSerializable = string | number | boolean | null | JsonSerializable[] | {
553
557
  [key: string]: JsonSerializable;
554
558
  };
555
559
  type Step<C extends FlowConfig> = {
556
- <R extends JsonSerializable | void>(name: string, options: {
560
+ <R extends JsonSerializable | void>(
561
+ /** The unique name of this step. */
562
+ name: string,
563
+ /** Configuration options for the step. */
564
+ options: {
565
+ /** The stage this step belongs to. Used for organising steps in the UI. */
557
566
  stage?: ExtractStageKeys<C>;
558
- maxRetries?: number;
559
- timeoutInMs?: number;
560
- }, fn: () => Promise<R> & {
567
+ /** Number of times to retry the step if it fails. Defaults to 5. */
568
+ retries?: number;
569
+ /** Maximum time in milliseconds to wait for the step to complete. Defaults to 60000 (1 minute). */
570
+ timeout?: number;
571
+ },
572
+ /** The step function to execute. */
573
+ fn: () => Promise<R> & {
561
574
  catch: (errorHandler: (err: Error) => Promise<void> | void) => Promise<any>;
562
575
  }): Promise<R>;
563
576
  <R extends JsonSerializable | void>(name: string, fn: () => Promise<R> & {
@@ -565,8 +578,11 @@ type Step<C extends FlowConfig> = {
565
578
  }): Promise<R>;
566
579
  };
567
580
  interface FlowConfig {
581
+ /** The stages to organise the steps in the flow. */
568
582
  stages?: StageConfig[];
583
+ /** The title of the flow as shown in the Console. */
569
584
  title?: string;
585
+ /** The description of the flow as shown in the Console. */
570
586
  description?: string;
571
587
  }
572
588
  interface FlowConfigAPI {
@@ -574,20 +590,28 @@ interface FlowConfigAPI {
574
590
  title: string;
575
591
  description?: string;
576
592
  }
577
- type FlowFunction<C extends FlowConfig, I extends any = {}> = (ctx: FlowContext<C>, inputs: I) => Promise<void>;
593
+ type FlowFunction<C extends FlowConfig, E extends any = {}, S extends any = {}, I extends any = {}> = (ctx: FlowContext<C, E, S>, inputs: I) => Promise<void>;
578
594
  type ExtractStageKeys<T extends FlowConfig> = T extends {
579
595
  stages: infer S;
580
596
  } ? S extends ReadonlyArray<infer U> ? U extends string ? U : U extends {
581
597
  key: infer K extends string;
582
598
  } ? K : never : never : never;
583
599
  type StageConfigObject = {
600
+ /** The unique key of the stage. */
584
601
  key: string;
602
+ /** The name of the stage as shown in the Console. */
585
603
  name: string;
604
+ /** The description of the stage as shown in the Console. */
586
605
  description?: string;
606
+ /** Whether the stage is initially hidden in the Console. */
587
607
  initiallyHidden?: boolean;
588
608
  };
589
609
  type StageConfig = string | StageConfigObject;
590
- declare function createFlowContext<C extends FlowConfig>(runId: string, data: any, spanId: string): FlowContext<C>;
610
+ declare function createFlowContext<C extends FlowConfig, E = any, S = any>(runId: string, data: any, action: string | null, spanId: string, ctx: {
611
+ env: E;
612
+ now: Date;
613
+ secrets: S;
614
+ }): FlowContext<C, E, S>;
591
615
 
592
616
  declare function ksuid(): string;
593
617
 
package/dist/index.d.ts CHANGED
@@ -352,11 +352,12 @@ type PageOptions<C extends FlowConfig, A extends PageActions[], T extends UIElem
352
352
  validate?: (data: ExtractFormData<T>) => Promise<true | string>;
353
353
  actions?: A;
354
354
  };
355
- type UiPage<C extends FlowConfig> = <T extends UIElements, const A extends PageActions[] = []>(name: string, options: PageOptions<C, A, T>) => A["length"] extends 0 ? ExtractFormData<T> : {
355
+ type UiPage<C extends FlowConfig> = <T extends UIElements, const A extends PageActions[] = []>(name: string, options: PageOptions<C, A, T>) => Promise<A["length"] extends 0 ? ExtractFormData<T> : {
356
356
  data: ExtractFormData<T>;
357
357
  action: ActionValue<A[number]>;
358
- };
359
- type PageActions = string | {
358
+ }>;
359
+ type PageActions = string | PageActionConfig;
360
+ type PageActionConfig = {
360
361
  label: string;
361
362
  value: string;
362
363
  mode?: "primary" | "secondary" | "destructive";
@@ -545,19 +546,31 @@ type UIApiResponses = {
545
546
  };
546
547
  };
547
548
 
548
- interface FlowContext<C extends FlowConfig> {
549
+ interface FlowContext<C extends FlowConfig, E = any, S = any> {
549
550
  step: Step<C>;
550
551
  ui: UI<C>;
552
+ env: E;
553
+ now: Date;
554
+ secrets: S;
551
555
  }
552
556
  type JsonSerializable = string | number | boolean | null | JsonSerializable[] | {
553
557
  [key: string]: JsonSerializable;
554
558
  };
555
559
  type Step<C extends FlowConfig> = {
556
- <R extends JsonSerializable | void>(name: string, options: {
560
+ <R extends JsonSerializable | void>(
561
+ /** The unique name of this step. */
562
+ name: string,
563
+ /** Configuration options for the step. */
564
+ options: {
565
+ /** The stage this step belongs to. Used for organising steps in the UI. */
557
566
  stage?: ExtractStageKeys<C>;
558
- maxRetries?: number;
559
- timeoutInMs?: number;
560
- }, fn: () => Promise<R> & {
567
+ /** Number of times to retry the step if it fails. Defaults to 5. */
568
+ retries?: number;
569
+ /** Maximum time in milliseconds to wait for the step to complete. Defaults to 60000 (1 minute). */
570
+ timeout?: number;
571
+ },
572
+ /** The step function to execute. */
573
+ fn: () => Promise<R> & {
561
574
  catch: (errorHandler: (err: Error) => Promise<void> | void) => Promise<any>;
562
575
  }): Promise<R>;
563
576
  <R extends JsonSerializable | void>(name: string, fn: () => Promise<R> & {
@@ -565,8 +578,11 @@ type Step<C extends FlowConfig> = {
565
578
  }): Promise<R>;
566
579
  };
567
580
  interface FlowConfig {
581
+ /** The stages to organise the steps in the flow. */
568
582
  stages?: StageConfig[];
583
+ /** The title of the flow as shown in the Console. */
569
584
  title?: string;
585
+ /** The description of the flow as shown in the Console. */
570
586
  description?: string;
571
587
  }
572
588
  interface FlowConfigAPI {
@@ -574,20 +590,28 @@ interface FlowConfigAPI {
574
590
  title: string;
575
591
  description?: string;
576
592
  }
577
- type FlowFunction<C extends FlowConfig, I extends any = {}> = (ctx: FlowContext<C>, inputs: I) => Promise<void>;
593
+ type FlowFunction<C extends FlowConfig, E extends any = {}, S extends any = {}, I extends any = {}> = (ctx: FlowContext<C, E, S>, inputs: I) => Promise<void>;
578
594
  type ExtractStageKeys<T extends FlowConfig> = T extends {
579
595
  stages: infer S;
580
596
  } ? S extends ReadonlyArray<infer U> ? U extends string ? U : U extends {
581
597
  key: infer K extends string;
582
598
  } ? K : never : never : never;
583
599
  type StageConfigObject = {
600
+ /** The unique key of the stage. */
584
601
  key: string;
602
+ /** The name of the stage as shown in the Console. */
585
603
  name: string;
604
+ /** The description of the stage as shown in the Console. */
586
605
  description?: string;
606
+ /** Whether the stage is initially hidden in the Console. */
587
607
  initiallyHidden?: boolean;
588
608
  };
589
609
  type StageConfig = string | StageConfigObject;
590
- declare function createFlowContext<C extends FlowConfig>(runId: string, data: any, spanId: string): FlowContext<C>;
610
+ declare function createFlowContext<C extends FlowConfig, E = any, S = any>(runId: string, data: any, action: string | null, spanId: string, ctx: {
611
+ env: E;
612
+ now: Date;
613
+ secrets: S;
614
+ }): FlowContext<C, E, S>;
591
615
 
592
616
  declare function ksuid(): string;
593
617
 
package/dist/index.js CHANGED
@@ -2430,9 +2430,16 @@ var selectOne = /* @__PURE__ */ __name((name, options) => {
2430
2430
  }, "selectOne");
2431
2431
 
2432
2432
  // src/flows/ui/page.ts
2433
- async function page(options, data) {
2433
+ async function page(options, data, action) {
2434
2434
  const content = options.content;
2435
2435
  let hasValidationErrors = false;
2436
+ if (options.actions) {
2437
+ const isValidAction = options.actions.some((a) => {
2438
+ if (typeof a === "string") return a === action;
2439
+ return a && typeof a === "object" && "value" in a && a.value === action;
2440
+ });
2441
+ hasValidationErrors = !isValidAction;
2442
+ }
2436
2443
  const contentUiConfig = await Promise.all(
2437
2444
  content.map(async (c) => {
2438
2445
  const isInput = "__type" in c && c.__type == "input";
@@ -2457,7 +2464,14 @@ async function page(options, data) {
2457
2464
  title: options.title,
2458
2465
  description: options.description,
2459
2466
  content: contentUiConfig,
2460
- actions: options.actions
2467
+ actions: options.actions?.map((a) => {
2468
+ if (typeof a === "string") {
2469
+ return { label: a, value: a, mode: "primary" };
2470
+ } else if (typeof a === "object") {
2471
+ a.mode = a.mode || "primary";
2472
+ }
2473
+ return a;
2474
+ })
2461
2475
  },
2462
2476
  hasValidationErrors
2463
2477
  };
@@ -2583,15 +2597,33 @@ var header = /* @__PURE__ */ __name((options) => {
2583
2597
 
2584
2598
  // src/flows/index.ts
2585
2599
  var defaultOpts = {
2586
- maxRetries: 5,
2587
- timeoutInMs: 6e4
2600
+ retries: 5,
2601
+ timeout: 6e4
2588
2602
  };
2589
- function createFlowContext(runId, data, spanId) {
2603
+ function createFlowContext(runId, data, action, spanId, ctx) {
2604
+ const usedNames = /* @__PURE__ */ new Set();
2590
2605
  return {
2606
+ env: ctx.env,
2607
+ now: ctx.now,
2608
+ secrets: ctx.secrets,
2591
2609
  step: /* @__PURE__ */ __name(async (name, optionsOrFn, fn) => {
2592
2610
  const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
2593
2611
  const actualFn = typeof optionsOrFn === "function" ? optionsOrFn : fn;
2594
2612
  const db = useDatabase();
2613
+ if (usedNames.has(name)) {
2614
+ await db.insertInto("keel.flow_step").values({
2615
+ run_id: runId,
2616
+ name,
2617
+ stage: options.stage,
2618
+ status: "FAILED" /* FAILED */,
2619
+ type: "FUNCTION" /* FUNCTION */,
2620
+ error: `Duplicate step name: ${name}`,
2621
+ startTime: /* @__PURE__ */ new Date(),
2622
+ endTime: /* @__PURE__ */ new Date()
2623
+ }).returningAll().executeTakeFirst();
2624
+ throw new Error(`Duplicate step name: ${name}`);
2625
+ }
2626
+ usedNames.add(name);
2595
2627
  const past = await db.selectFrom("keel.flow_step").where("run_id", "=", runId).where("name", "=", name).selectAll().execute();
2596
2628
  const newSteps = past.filter((step) => step.status === "NEW" /* NEW */);
2597
2629
  const completedSteps = past.filter(
@@ -2622,7 +2654,7 @@ function createFlowContext(runId, data, spanId) {
2622
2654
  try {
2623
2655
  result = await withTimeout(
2624
2656
  actualFn(),
2625
- options.timeoutInMs ?? defaultOpts.timeoutInMs
2657
+ options.timeout ?? defaultOpts.timeout
2626
2658
  );
2627
2659
  } catch (e) {
2628
2660
  await db.updateTable("keel.flow_step").set({
@@ -2631,7 +2663,7 @@ function createFlowContext(runId, data, spanId) {
2631
2663
  endTime: /* @__PURE__ */ new Date(),
2632
2664
  error: e instanceof Error ? e.message : "An error occurred"
2633
2665
  }).where("id", "=", newSteps[0].id).returningAll().executeTakeFirst();
2634
- if (failedSteps.length + 1 >= (options.maxRetries ?? defaultOpts.maxRetries)) {
2666
+ if (failedSteps.length + 1 >= (options.retries ?? defaultOpts.retries)) {
2635
2667
  throw new ExhuastedRetriesDisrupt();
2636
2668
  }
2637
2669
  await db.insertInto("keel.flow_step").values({
@@ -2663,8 +2695,25 @@ function createFlowContext(runId, data, spanId) {
2663
2695
  ui: {
2664
2696
  page: /* @__PURE__ */ __name(async (name, options) => {
2665
2697
  const db = useDatabase();
2698
+ if (usedNames.has(name)) {
2699
+ await db.insertInto("keel.flow_step").values({
2700
+ run_id: runId,
2701
+ name,
2702
+ stage: options.stage,
2703
+ status: "FAILED" /* FAILED */,
2704
+ type: "UI" /* UI */,
2705
+ error: `Duplicate step name: ${name}`,
2706
+ startTime: /* @__PURE__ */ new Date(),
2707
+ endTime: /* @__PURE__ */ new Date()
2708
+ }).returningAll().executeTakeFirst();
2709
+ throw new Error(`Duplicate step name: ${name}`);
2710
+ }
2711
+ usedNames.add(name);
2666
2712
  let step = await db.selectFrom("keel.flow_step").where("run_id", "=", runId).where("name", "=", name).selectAll().executeTakeFirst();
2667
2713
  if (step && step.status === "COMPLETED" /* COMPLETED */) {
2714
+ if (step.action) {
2715
+ return { data: step.value, action: step.action };
2716
+ }
2668
2717
  return step.value;
2669
2718
  }
2670
2719
  if (!step) {
@@ -2676,22 +2725,29 @@ function createFlowContext(runId, data, spanId) {
2676
2725
  type: "UI" /* UI */,
2677
2726
  startTime: /* @__PURE__ */ new Date()
2678
2727
  }).returningAll().executeTakeFirst();
2679
- throw new UIRenderDisrupt(step?.id, (await page(options, null)).page);
2728
+ throw new UIRenderDisrupt(
2729
+ step?.id,
2730
+ (await page(options, null, null)).page
2731
+ );
2680
2732
  }
2681
2733
  if (!data) {
2682
- throw new UIRenderDisrupt(step?.id, (await page(options, null)).page);
2734
+ throw new UIRenderDisrupt(
2735
+ step?.id,
2736
+ (await page(options, null, null)).page
2737
+ );
2683
2738
  }
2684
- const p = await page(options, data);
2739
+ const p = await page(options, data, action);
2685
2740
  if (p.hasValidationErrors) {
2686
2741
  throw new UIRenderDisrupt(step?.id, p.page);
2687
2742
  }
2688
2743
  await db.updateTable("keel.flow_step").set({
2689
2744
  status: "COMPLETED" /* COMPLETED */,
2690
2745
  value: JSON.stringify(data),
2746
+ action,
2691
2747
  spanId,
2692
2748
  endTime: /* @__PURE__ */ new Date()
2693
2749
  }).where("id", "=", step.id).returningAll().executeTakeFirst();
2694
- return data;
2750
+ return { data, action };
2695
2751
  }, "page"),
2696
2752
  inputs: {
2697
2753
  text: textInput,
@@ -2746,7 +2802,7 @@ async function handleFlow(request, config) {
2746
2802
  if (!runId) {
2747
2803
  throw new Error("no runId provided");
2748
2804
  }
2749
- const { flows } = config;
2805
+ const { flows, createFlowContextAPI } = config;
2750
2806
  if (!(request.method in flows)) {
2751
2807
  const message = `no corresponding flow found for '${request.method}'`;
2752
2808
  span.setStatus({
@@ -2765,7 +2821,11 @@ async function handleFlow(request, config) {
2765
2821
  const ctx = createFlowContext(
2766
2822
  request.meta.runId,
2767
2823
  request.meta.data,
2768
- span.spanContext().spanId
2824
+ request.meta.action,
2825
+ span.spanContext().spanId,
2826
+ createFlowContextAPI({
2827
+ meta: request.meta
2828
+ })
2769
2829
  );
2770
2830
  const flowFunction = flows[request.method].fn;
2771
2831
  const rawFlowConfig = flows[request.method].config;