4 | 5 |  6 | 7 |
8 | 9 |10 | Documentation 11 | · 12 | Blog 13 | · 14 | Community 15 |
16 |21 | {children} 22 |
23 | ), 24 | ul: ({ children, ...props }: any) => ( 25 |2 | 3 |  4 | 5 |
6 | 7 |8 | Documentation 9 | · 10 | Blog 11 | · 12 | Community 13 |
14 | 15 | # Workflow kit 16 | 17 | **Workflow Kit** enables you to build user-defined workflows with Inngest by providing a set of workflow actions to the **[Workflow Engine](https://www.inngest.com/docs/reference/workflow-kit/engine?ref=github-workflow-kit-readme)** while using the **[pre-built React components](https://www.inngest.com/docs/reference/workflow-kit/components-api?ref=github-workflow-kit-readme)** to build your Workflow Editor UI. 18 | 19 |  20 | 21 | ## Installation 22 | 23 | Workflow kit requires the [Inngest TypeScript SDK](https://github.com/inngest/inngest-js) as a dependency. You can install both via `npm` or similar: 24 | 25 | ```shell {{ title: "npm" }} 26 | npm install @inngest/workflow-kit inngest 27 | ``` 28 | 29 | ## Documentation 30 | 31 | The full Workflow kit documentation is available [here](https://www.inngest.com/docs/reference/workflow-kit). You can also jump to specific guides and references: 32 | 33 | - [Creating workflow actions](https://www.inngest.com/docs/reference/workflow-kit/actions?ref=github-workflow-kit-readme) 34 | - [Using the workflow engine](https://www.inngest.com/docs/reference/workflow-kit/engine?ref=github-workflow-kit-readme) 35 | - [Workflow instance format](https://www.inngest.com/docs/reference/workflow-kit/workflow-instance?ref=github-workflow-kit-readme) 36 | - [Components API (React)](https://www.inngest.com/docs/reference/workflow-kit/components-api?ref=github-workflow-kit-readme) 37 | 38 | ## Examples 39 | 40 | See Workflow kit in action in fully functioning example projects: 41 | 42 | - [Next.js Blog CMS](/examples/nextjs-blog-cms#readme) - A ready-to-deploy Next.js demo using the Workflow Kit, Supabase, and OpenAI to power some AI content workflows. 43 | 44 | ## License 45 | 46 | [Apache 2.0](/packages/workflow/LICENSE.md) 47 | -------------------------------------------------------------------------------- /packages/workflow/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2 | module.exports = { 3 | preset: 'ts-jest', 4 | testEnvironment: 'node', 5 | }; -------------------------------------------------------------------------------- /packages/workflow/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@inngest/workflow-kit", 3 | "version": "0.1.3", 4 | "description": "Durable visual workflows in your app, instantly", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "changeset": "changeset", 8 | "build": "rm -rf dist && tsc && cp src/ui/ui.css dist/ui/ui.css", 9 | "test": "jest --logHeapUsage --maxWorkers=8 --coverage --ci --silent=false", 10 | "storybook": "storybook dev -p 6006", 11 | "build-storybook": "storybook build" 12 | }, 13 | "homepage": "https://github.com/inngest/workflow-kit/tree/main/packages/workflow#readme", 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/inngest/workflow-kit.git", 17 | "directory": "packages/workflow" 18 | }, 19 | "keywords": [ 20 | "inngest", 21 | "workflow" 22 | ], 23 | "author": "Inngest Inc", 24 | "license": "Apache-2.0", 25 | "typings": "index.d.ts", 26 | "publishConfig": { 27 | "access": "public" 28 | }, 29 | "files": [ 30 | "dist" 31 | ], 32 | "exports": { 33 | ".": { 34 | "require": { 35 | "types": "./dist/index.d.ts", 36 | "default": "./dist/index.js" 37 | }, 38 | "import": { 39 | "types": "./dist/index.d.ts", 40 | "default": "./dist/index.js" 41 | }, 42 | "default": { 43 | "types": "./dist/index.d.ts", 44 | "default": "./dist/index.js" 45 | } 46 | }, 47 | "./ui": { 48 | "require": { 49 | "types": "./dist/ui/index.d.ts", 50 | "default": "./dist/ui/index.js" 51 | }, 52 | "import": { 53 | "types": "./dist/ui/index.d.ts", 54 | "default": "./dist/ui/index.js" 55 | }, 56 | "default": { 57 | "types": "./dist/ui/index.d.ts", 58 | "default": "./dist/ui/index.js" 59 | } 60 | }, 61 | "./ui/ui.css": "./dist/ui/ui.css", 62 | "./package.json": "./package.json" 63 | }, 64 | "dependencies": { 65 | "@dagrejs/dagre": "^1.1.4", 66 | "@radix-ui/react-popover": "^1.1.1", 67 | "@sinclair/typebox": "^0.33.7", 68 | "@xyflow/react": "^12.1.1", 69 | "graphology": "^0.25.4", 70 | "graphology-dag": "^0.4.1", 71 | "json-logic-js": "^2.0.5", 72 | "@astronautlabs/jsonpath": "^1.1.1", 73 | "reactflow": "^11.11.3" 74 | }, 75 | "devDependencies": { 76 | "@chromatic-com/storybook": "^1.7.0", 77 | "@storybook/addon-essentials": "^8.2.9", 78 | "@storybook/addon-interactions": "^8.2.9", 79 | "@storybook/addon-links": "^8.2.9", 80 | "@storybook/addon-onboarding": "^8.2.9", 81 | "@storybook/blocks": "^8.2.9", 82 | "@storybook/react": "^8.2.9", 83 | "@storybook/react-vite": "^8.2.9", 84 | "@storybook/test": "^8.2.9", 85 | "@types/jest": "^29.5.12", 86 | "@types/json-logic-js": "^2.0.7", 87 | "@types/jsonpath": "^0.2.4", 88 | "@types/node": "^22.5.5", 89 | "@types/react": "^18.3.5", 90 | "inngest": "^3.19.14", 91 | "jest": "^29.7.0", 92 | "preset": "link:@storybook/react-vite/preset", 93 | "prop-types": "^15.8.1", 94 | "storybook": "^8.2.9", 95 | "ts-jest": "^29.1.5", 96 | "typescript": "^5.7.2" 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /packages/workflow/src/builtin.test.ts: -------------------------------------------------------------------------------- 1 | import { builtinActions } from "./builtin"; 2 | import { resolveInputs } from "./interpolation"; 3 | import { 4 | ActionHandler, 5 | ActionHandlerArgs, 6 | Workflow, 7 | WorkflowAction, 8 | } from "./types"; 9 | 10 | describe("builtin:if", () => { 11 | // This tests the handler logic of the builtin:if action. 12 | const action = builtinActions["builtin:if"]; 13 | if (!action) { 14 | throw new Error("builtin:if action not found"); 15 | } 16 | 17 | it("evaluates simple conditions without refs", async () => { 18 | const workflowAction: WorkflowAction = { 19 | id: "1", 20 | kind: "builtin:if", 21 | inputs: { 22 | condition: { "==": [1, 1] }, 23 | }, 24 | }; 25 | 26 | let result = await action.handler(handlerInput(workflowAction)); 27 | expect(result).toEqual({ result: true }); 28 | 29 | workflowAction.inputs = { condition: { "==": [2, 1] } }; 30 | result = await action.handler(handlerInput(workflowAction)); 31 | expect(result).toEqual({ result: false }); 32 | }); 33 | 34 | it("evaluates complex conditions with refs", async () => { 35 | const state = new Map(Object.entries({ action_a: 1.123 })); 36 | const event = { data: { name: "jimothy" } }; 37 | 38 | const workflowAction: WorkflowAction = { 39 | id: "1", 40 | kind: "builtin:if", 41 | inputs: resolveInputs( 42 | { 43 | condition: { 44 | and: [ 45 | { "==": ["!ref($.state.action_a)", 1.123] }, 46 | { "==": ["!ref($.event.data.name)", "jimothy"] }, 47 | ], 48 | }, 49 | }, 50 | { state: Object.fromEntries(state), event } 51 | ), 52 | }; 53 | 54 | let result = await action.handler({ 55 | workflowAction, 56 | event, 57 | state, 58 | step: {} as any, 59 | workflow: { 60 | actions: [workflowAction], 61 | edges: [], 62 | }, 63 | }); 64 | 65 | expect(result).toEqual({ result: true }); 66 | }); 67 | 68 | const handlerInput = (workflowAction: WorkflowAction): ActionHandlerArgs => { 69 | return { 70 | workflowAction, 71 | event: { 72 | data: { 73 | age: 82.1, 74 | likes: ["a"], 75 | }, 76 | }, 77 | step: {}, 78 | workflow: { 79 | actions: [workflowAction], 80 | edges: [], 81 | }, 82 | state: new Map(), 83 | }; 84 | }; 85 | }); 86 | -------------------------------------------------------------------------------- /packages/workflow/src/builtin.ts: -------------------------------------------------------------------------------- 1 | // TODO: Define builtin nodes, like if statements. 2 | 3 | import { Type } from "@sinclair/typebox"; 4 | import { ActionHandlerArgs, EngineAction } from "./types"; 5 | import { apply } from "json-logic-js"; 6 | 7 | export const builtinActions: Record{action.name || engineAction?.name || action.kind}
35 |{ action.description || engineAction?.description || "Performs an action" }
38 | 39 | {/* TODO: Add handle with menu options */} 40 |{edge.name}
89 |{ trigger === undefined ? "Select a trigger" : trigger?.event?.name }
31 |{ workflow?.description || "Starts the workflow" }
34 | 35 | { trigger &&{engineAction.name}
25 |{engineAction.description}
26 |