@teamkeel/functions-runtime 0.413.8 → 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 +66 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -7
- package/dist/index.d.ts +30 -7
- package/dist/index.js +69 -20
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
package/dist/index.d.cts
CHANGED
|
@@ -545,19 +545,31 @@ type UIApiResponses = {
|
|
|
545
545
|
};
|
|
546
546
|
};
|
|
547
547
|
|
|
548
|
-
interface FlowContext<C extends FlowConfig> {
|
|
548
|
+
interface FlowContext<C extends FlowConfig, E = any, S = any> {
|
|
549
549
|
step: Step<C>;
|
|
550
550
|
ui: UI<C>;
|
|
551
|
+
env: E;
|
|
552
|
+
now: Date;
|
|
553
|
+
secrets: S;
|
|
551
554
|
}
|
|
552
555
|
type JsonSerializable = string | number | boolean | null | JsonSerializable[] | {
|
|
553
556
|
[key: string]: JsonSerializable;
|
|
554
557
|
};
|
|
555
558
|
type Step<C extends FlowConfig> = {
|
|
556
|
-
<R extends JsonSerializable | void>(
|
|
559
|
+
<R extends JsonSerializable | void>(
|
|
560
|
+
/** The unique name of this step. */
|
|
561
|
+
name: string,
|
|
562
|
+
/** Configuration options for the step. */
|
|
563
|
+
options: {
|
|
564
|
+
/** The stage this step belongs to. Used for organising steps in the UI. */
|
|
557
565
|
stage?: ExtractStageKeys<C>;
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
566
|
+
/** Number of times to retry the step if it fails. Defaults to 5. */
|
|
567
|
+
retries?: number;
|
|
568
|
+
/** Maximum time in milliseconds to wait for the step to complete. Defaults to 60000 (1 minute). */
|
|
569
|
+
timeout?: number;
|
|
570
|
+
},
|
|
571
|
+
/** The step function to execute. */
|
|
572
|
+
fn: () => Promise<R> & {
|
|
561
573
|
catch: (errorHandler: (err: Error) => Promise<void> | void) => Promise<any>;
|
|
562
574
|
}): Promise<R>;
|
|
563
575
|
<R extends JsonSerializable | void>(name: string, fn: () => Promise<R> & {
|
|
@@ -565,8 +577,11 @@ type Step<C extends FlowConfig> = {
|
|
|
565
577
|
}): Promise<R>;
|
|
566
578
|
};
|
|
567
579
|
interface FlowConfig {
|
|
580
|
+
/** The stages to organise the steps in the flow. */
|
|
568
581
|
stages?: StageConfig[];
|
|
582
|
+
/** The title of the flow as shown in the Console. */
|
|
569
583
|
title?: string;
|
|
584
|
+
/** The description of the flow as shown in the Console. */
|
|
570
585
|
description?: string;
|
|
571
586
|
}
|
|
572
587
|
interface FlowConfigAPI {
|
|
@@ -574,20 +589,28 @@ interface FlowConfigAPI {
|
|
|
574
589
|
title: string;
|
|
575
590
|
description?: string;
|
|
576
591
|
}
|
|
577
|
-
type FlowFunction<C extends FlowConfig, I extends any = {}> = (ctx: FlowContext<C>, inputs: I) => Promise<void>;
|
|
592
|
+
type FlowFunction<C extends FlowConfig, E extends any = {}, S extends any = {}, I extends any = {}> = (ctx: FlowContext<C, E, S>, inputs: I) => Promise<void>;
|
|
578
593
|
type ExtractStageKeys<T extends FlowConfig> = T extends {
|
|
579
594
|
stages: infer S;
|
|
580
595
|
} ? S extends ReadonlyArray<infer U> ? U extends string ? U : U extends {
|
|
581
596
|
key: infer K extends string;
|
|
582
597
|
} ? K : never : never : never;
|
|
583
598
|
type StageConfigObject = {
|
|
599
|
+
/** The unique key of the stage. */
|
|
584
600
|
key: string;
|
|
601
|
+
/** The name of the stage as shown in the Console. */
|
|
585
602
|
name: string;
|
|
603
|
+
/** The description of the stage as shown in the Console. */
|
|
586
604
|
description?: string;
|
|
605
|
+
/** Whether the stage is initially hidden in the Console. */
|
|
587
606
|
initiallyHidden?: boolean;
|
|
588
607
|
};
|
|
589
608
|
type StageConfig = string | StageConfigObject;
|
|
590
|
-
declare function createFlowContext<C extends FlowConfig>(runId: string, data: any, spanId: string
|
|
609
|
+
declare function createFlowContext<C extends FlowConfig, E = any, S = any>(runId: string, data: any, action: string | null, spanId: string, ctx: {
|
|
610
|
+
env: E;
|
|
611
|
+
now: Date;
|
|
612
|
+
secrets: S;
|
|
613
|
+
}): FlowContext<C, E, S>;
|
|
591
614
|
|
|
592
615
|
declare function ksuid(): string;
|
|
593
616
|
|
package/dist/index.d.ts
CHANGED
|
@@ -545,19 +545,31 @@ type UIApiResponses = {
|
|
|
545
545
|
};
|
|
546
546
|
};
|
|
547
547
|
|
|
548
|
-
interface FlowContext<C extends FlowConfig> {
|
|
548
|
+
interface FlowContext<C extends FlowConfig, E = any, S = any> {
|
|
549
549
|
step: Step<C>;
|
|
550
550
|
ui: UI<C>;
|
|
551
|
+
env: E;
|
|
552
|
+
now: Date;
|
|
553
|
+
secrets: S;
|
|
551
554
|
}
|
|
552
555
|
type JsonSerializable = string | number | boolean | null | JsonSerializable[] | {
|
|
553
556
|
[key: string]: JsonSerializable;
|
|
554
557
|
};
|
|
555
558
|
type Step<C extends FlowConfig> = {
|
|
556
|
-
<R extends JsonSerializable | void>(
|
|
559
|
+
<R extends JsonSerializable | void>(
|
|
560
|
+
/** The unique name of this step. */
|
|
561
|
+
name: string,
|
|
562
|
+
/** Configuration options for the step. */
|
|
563
|
+
options: {
|
|
564
|
+
/** The stage this step belongs to. Used for organising steps in the UI. */
|
|
557
565
|
stage?: ExtractStageKeys<C>;
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
566
|
+
/** Number of times to retry the step if it fails. Defaults to 5. */
|
|
567
|
+
retries?: number;
|
|
568
|
+
/** Maximum time in milliseconds to wait for the step to complete. Defaults to 60000 (1 minute). */
|
|
569
|
+
timeout?: number;
|
|
570
|
+
},
|
|
571
|
+
/** The step function to execute. */
|
|
572
|
+
fn: () => Promise<R> & {
|
|
561
573
|
catch: (errorHandler: (err: Error) => Promise<void> | void) => Promise<any>;
|
|
562
574
|
}): Promise<R>;
|
|
563
575
|
<R extends JsonSerializable | void>(name: string, fn: () => Promise<R> & {
|
|
@@ -565,8 +577,11 @@ type Step<C extends FlowConfig> = {
|
|
|
565
577
|
}): Promise<R>;
|
|
566
578
|
};
|
|
567
579
|
interface FlowConfig {
|
|
580
|
+
/** The stages to organise the steps in the flow. */
|
|
568
581
|
stages?: StageConfig[];
|
|
582
|
+
/** The title of the flow as shown in the Console. */
|
|
569
583
|
title?: string;
|
|
584
|
+
/** The description of the flow as shown in the Console. */
|
|
570
585
|
description?: string;
|
|
571
586
|
}
|
|
572
587
|
interface FlowConfigAPI {
|
|
@@ -574,20 +589,28 @@ interface FlowConfigAPI {
|
|
|
574
589
|
title: string;
|
|
575
590
|
description?: string;
|
|
576
591
|
}
|
|
577
|
-
type FlowFunction<C extends FlowConfig, I extends any = {}> = (ctx: FlowContext<C>, inputs: I) => Promise<void>;
|
|
592
|
+
type FlowFunction<C extends FlowConfig, E extends any = {}, S extends any = {}, I extends any = {}> = (ctx: FlowContext<C, E, S>, inputs: I) => Promise<void>;
|
|
578
593
|
type ExtractStageKeys<T extends FlowConfig> = T extends {
|
|
579
594
|
stages: infer S;
|
|
580
595
|
} ? S extends ReadonlyArray<infer U> ? U extends string ? U : U extends {
|
|
581
596
|
key: infer K extends string;
|
|
582
597
|
} ? K : never : never : never;
|
|
583
598
|
type StageConfigObject = {
|
|
599
|
+
/** The unique key of the stage. */
|
|
584
600
|
key: string;
|
|
601
|
+
/** The name of the stage as shown in the Console. */
|
|
585
602
|
name: string;
|
|
603
|
+
/** The description of the stage as shown in the Console. */
|
|
586
604
|
description?: string;
|
|
605
|
+
/** Whether the stage is initially hidden in the Console. */
|
|
587
606
|
initiallyHidden?: boolean;
|
|
588
607
|
};
|
|
589
608
|
type StageConfig = string | StageConfigObject;
|
|
590
|
-
declare function createFlowContext<C extends FlowConfig>(runId: string, data: any, spanId: string
|
|
609
|
+
declare function createFlowContext<C extends FlowConfig, E = any, S = any>(runId: string, data: any, action: string | null, spanId: string, ctx: {
|
|
610
|
+
env: E;
|
|
611
|
+
now: Date;
|
|
612
|
+
secrets: S;
|
|
613
|
+
}): FlowContext<C, E, S>;
|
|
591
614
|
|
|
592
615
|
declare function ksuid(): string;
|
|
593
616
|
|
package/dist/index.js
CHANGED
|
@@ -11,10 +11,10 @@ import { sql as sql3 } from "kysely";
|
|
|
11
11
|
// src/database.ts
|
|
12
12
|
import { Kysely, PostgresDialect } from "kysely";
|
|
13
13
|
import * as neon from "@neondatabase/serverless";
|
|
14
|
-
import { AsyncLocalStorage as AsyncLocalStorage2 } from "
|
|
14
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
15
15
|
|
|
16
16
|
// src/auditing.js
|
|
17
|
-
import { AsyncLocalStorage } from "
|
|
17
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
18
18
|
import TraceParent from "traceparent";
|
|
19
19
|
import { sql, SelectionNode } from "kysely";
|
|
20
20
|
var auditContextStorage = new AsyncLocalStorage();
|
|
@@ -354,7 +354,7 @@ __name(spanNameForModelAPI, "spanNameForModelAPI");
|
|
|
354
354
|
|
|
355
355
|
// src/database.ts
|
|
356
356
|
import WebSocket from "ws";
|
|
357
|
-
import { readFileSync } from "
|
|
357
|
+
import { readFileSync } from "fs";
|
|
358
358
|
var dbInstance = new AsyncLocalStorage2();
|
|
359
359
|
var vitestDb = null;
|
|
360
360
|
async function withDatabase(db, requiresTransaction, cb) {
|
|
@@ -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" && "label" in a && a.label === 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";
|
|
@@ -2583,15 +2590,33 @@ var header = /* @__PURE__ */ __name((options) => {
|
|
|
2583
2590
|
|
|
2584
2591
|
// src/flows/index.ts
|
|
2585
2592
|
var defaultOpts = {
|
|
2586
|
-
|
|
2587
|
-
|
|
2593
|
+
retries: 5,
|
|
2594
|
+
timeout: 6e4
|
|
2588
2595
|
};
|
|
2589
|
-
function createFlowContext(runId, data, spanId) {
|
|
2596
|
+
function createFlowContext(runId, data, action, spanId, ctx) {
|
|
2597
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
2590
2598
|
return {
|
|
2599
|
+
env: ctx.env,
|
|
2600
|
+
now: ctx.now,
|
|
2601
|
+
secrets: ctx.secrets,
|
|
2591
2602
|
step: /* @__PURE__ */ __name(async (name, optionsOrFn, fn) => {
|
|
2592
2603
|
const options = typeof optionsOrFn === "function" ? {} : optionsOrFn;
|
|
2593
2604
|
const actualFn = typeof optionsOrFn === "function" ? optionsOrFn : fn;
|
|
2594
2605
|
const db = useDatabase();
|
|
2606
|
+
if (usedNames.has(name)) {
|
|
2607
|
+
await db.insertInto("keel.flow_step").values({
|
|
2608
|
+
run_id: runId,
|
|
2609
|
+
name,
|
|
2610
|
+
stage: options.stage,
|
|
2611
|
+
status: "FAILED" /* FAILED */,
|
|
2612
|
+
type: "FUNCTION" /* FUNCTION */,
|
|
2613
|
+
error: `Duplicate step name: ${name}`,
|
|
2614
|
+
startTime: /* @__PURE__ */ new Date(),
|
|
2615
|
+
endTime: /* @__PURE__ */ new Date()
|
|
2616
|
+
}).returningAll().executeTakeFirst();
|
|
2617
|
+
throw new Error(`Duplicate step name: ${name}`);
|
|
2618
|
+
}
|
|
2619
|
+
usedNames.add(name);
|
|
2595
2620
|
const past = await db.selectFrom("keel.flow_step").where("run_id", "=", runId).where("name", "=", name).selectAll().execute();
|
|
2596
2621
|
const newSteps = past.filter((step) => step.status === "NEW" /* NEW */);
|
|
2597
2622
|
const completedSteps = past.filter(
|
|
@@ -2622,7 +2647,7 @@ function createFlowContext(runId, data, spanId) {
|
|
|
2622
2647
|
try {
|
|
2623
2648
|
result = await withTimeout(
|
|
2624
2649
|
actualFn(),
|
|
2625
|
-
options.
|
|
2650
|
+
options.timeout ?? defaultOpts.timeout
|
|
2626
2651
|
);
|
|
2627
2652
|
} catch (e) {
|
|
2628
2653
|
await db.updateTable("keel.flow_step").set({
|
|
@@ -2631,7 +2656,7 @@ function createFlowContext(runId, data, spanId) {
|
|
|
2631
2656
|
endTime: /* @__PURE__ */ new Date(),
|
|
2632
2657
|
error: e instanceof Error ? e.message : "An error occurred"
|
|
2633
2658
|
}).where("id", "=", newSteps[0].id).returningAll().executeTakeFirst();
|
|
2634
|
-
if (failedSteps.length + 1 >= (options.
|
|
2659
|
+
if (failedSteps.length + 1 >= (options.retries ?? defaultOpts.retries)) {
|
|
2635
2660
|
throw new ExhuastedRetriesDisrupt();
|
|
2636
2661
|
}
|
|
2637
2662
|
await db.insertInto("keel.flow_step").values({
|
|
@@ -2663,8 +2688,25 @@ function createFlowContext(runId, data, spanId) {
|
|
|
2663
2688
|
ui: {
|
|
2664
2689
|
page: /* @__PURE__ */ __name(async (name, options) => {
|
|
2665
2690
|
const db = useDatabase();
|
|
2691
|
+
if (usedNames.has(name)) {
|
|
2692
|
+
await db.insertInto("keel.flow_step").values({
|
|
2693
|
+
run_id: runId,
|
|
2694
|
+
name,
|
|
2695
|
+
stage: options.stage,
|
|
2696
|
+
status: "FAILED" /* FAILED */,
|
|
2697
|
+
type: "UI" /* UI */,
|
|
2698
|
+
error: `Duplicate step name: ${name}`,
|
|
2699
|
+
startTime: /* @__PURE__ */ new Date(),
|
|
2700
|
+
endTime: /* @__PURE__ */ new Date()
|
|
2701
|
+
}).returningAll().executeTakeFirst();
|
|
2702
|
+
throw new Error(`Duplicate step name: ${name}`);
|
|
2703
|
+
}
|
|
2704
|
+
usedNames.add(name);
|
|
2666
2705
|
let step = await db.selectFrom("keel.flow_step").where("run_id", "=", runId).where("name", "=", name).selectAll().executeTakeFirst();
|
|
2667
2706
|
if (step && step.status === "COMPLETED" /* COMPLETED */) {
|
|
2707
|
+
if (step.action) {
|
|
2708
|
+
return { data: step.value, action: step.action };
|
|
2709
|
+
}
|
|
2668
2710
|
return step.value;
|
|
2669
2711
|
}
|
|
2670
2712
|
if (!step) {
|
|
@@ -2676,22 +2718,29 @@ function createFlowContext(runId, data, spanId) {
|
|
|
2676
2718
|
type: "UI" /* UI */,
|
|
2677
2719
|
startTime: /* @__PURE__ */ new Date()
|
|
2678
2720
|
}).returningAll().executeTakeFirst();
|
|
2679
|
-
throw new UIRenderDisrupt(
|
|
2721
|
+
throw new UIRenderDisrupt(
|
|
2722
|
+
step?.id,
|
|
2723
|
+
(await page(options, null, null)).page
|
|
2724
|
+
);
|
|
2680
2725
|
}
|
|
2681
2726
|
if (!data) {
|
|
2682
|
-
throw new UIRenderDisrupt(
|
|
2727
|
+
throw new UIRenderDisrupt(
|
|
2728
|
+
step?.id,
|
|
2729
|
+
(await page(options, null, null)).page
|
|
2730
|
+
);
|
|
2683
2731
|
}
|
|
2684
|
-
const p = await page(options, data);
|
|
2732
|
+
const p = await page(options, data, action);
|
|
2685
2733
|
if (p.hasValidationErrors) {
|
|
2686
2734
|
throw new UIRenderDisrupt(step?.id, p.page);
|
|
2687
2735
|
}
|
|
2688
2736
|
await db.updateTable("keel.flow_step").set({
|
|
2689
2737
|
status: "COMPLETED" /* COMPLETED */,
|
|
2690
2738
|
value: JSON.stringify(data),
|
|
2739
|
+
action,
|
|
2691
2740
|
spanId,
|
|
2692
2741
|
endTime: /* @__PURE__ */ new Date()
|
|
2693
2742
|
}).where("id", "=", step.id).returningAll().executeTakeFirst();
|
|
2694
|
-
return data;
|
|
2743
|
+
return { data, action };
|
|
2695
2744
|
}, "page"),
|
|
2696
2745
|
inputs: {
|
|
2697
2746
|
text: textInput,
|
|
@@ -2746,7 +2795,7 @@ async function handleFlow(request, config) {
|
|
|
2746
2795
|
if (!runId) {
|
|
2747
2796
|
throw new Error("no runId provided");
|
|
2748
2797
|
}
|
|
2749
|
-
const { flows } = config;
|
|
2798
|
+
const { flows, createFlowContextAPI } = config;
|
|
2750
2799
|
if (!(request.method in flows)) {
|
|
2751
2800
|
const message = `no corresponding flow found for '${request.method}'`;
|
|
2752
2801
|
span.setStatus({
|
|
@@ -2762,14 +2811,14 @@ async function handleFlow(request, config) {
|
|
|
2762
2811
|
db = createDatabaseClient({
|
|
2763
2812
|
connString: request.meta?.secrets?.KEEL_DB_CONN
|
|
2764
2813
|
});
|
|
2765
|
-
const flowRun = await db.selectFrom("keel.flow_run").where("id", "=", runId).selectAll().executeTakeFirst();
|
|
2766
|
-
if (!flowRun) {
|
|
2767
|
-
throw new Error("no flow run found");
|
|
2768
|
-
}
|
|
2769
2814
|
const ctx = createFlowContext(
|
|
2770
2815
|
request.meta.runId,
|
|
2771
2816
|
request.meta.data,
|
|
2772
|
-
|
|
2817
|
+
request.meta.action,
|
|
2818
|
+
span.spanContext().spanId,
|
|
2819
|
+
createFlowContextAPI({
|
|
2820
|
+
meta: request.meta
|
|
2821
|
+
})
|
|
2773
2822
|
);
|
|
2774
2823
|
const flowFunction = flows[request.method].fn;
|
|
2775
2824
|
const rawFlowConfig = flows[request.method].config;
|
|
@@ -2786,7 +2835,7 @@ async function handleFlow(request, config) {
|
|
|
2786
2835
|
return stage;
|
|
2787
2836
|
})
|
|
2788
2837
|
};
|
|
2789
|
-
const inputs = parseInputs(
|
|
2838
|
+
const inputs = parseInputs(request.meta?.inputs);
|
|
2790
2839
|
try {
|
|
2791
2840
|
await tryExecuteFlow(db, async () => {
|
|
2792
2841
|
return flowFunction(ctx, inputs);
|