{"_id":"ts-edge","_rev":"57-6aae9e25dcd7538c5edb6f1db8b121aa","name":"ts-edge","dist-tags":{"latest":"1.0.4"},"versions":{"1.0.0":{"name":"ts-edge","version":"1.0.0","keywords":["graph","workflow","pipeline","typescript","edge","dataflow","state-machine","node-processing","directed-graph","langgraph","functional"],"author":{"name":"cging"},"license":"MIT","_id":"ts-edge@1.0.0","maintainers":[{"name":"cgoing","email":"neo.cgoing@gmail.com"}],"homepage":"https://github.com/cgoinglove/ts-edge#readme","bugs":{"url":"https://github.com/cgoinglove/ts-edge/issues"},"dist":{"shasum":"d8af1828a589dfc19fce3312e09f249e60355156","tarball":"https://registry.npmjs.org/ts-edge/-/ts-edge-1.0.0.tgz","fileCount":7,"integrity":"sha512-Ks5j7vLzObmlfSwCpjG3ePjRzqBCj3weaMUyoZa9UIqVJed4qJKnTEwzcNsEZMQ1WXecAD1dW90KQJmPX5V0Qg==","signatures":[{"sig":"MEUCIAfEOVdlEWLZdwVfUUtt2KynjyEcoLlJYRSKIcJ85ruFAiEAi6xUAWuUL1R+9J0GLlIqFpKyk9OT3S9iAbUgT469Ljk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":96833},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=14.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"24632633a1b4526a8438906f9a840037dc3f2455","scripts":{"lint":"eslint .","test":"vitest","build":"tsup","format":"prettier --write ./**/*.ts","prepare":"husky install","release":"npm run prepare:release && npm run build && npm run prepare:version && npm publish","type-check":"tsc --noEmit","lint-staged":"lint-staged","prepare:release":"npm run format && npm run type-check && npm run test run","prepare:version":"npm --no-git-tag-version version patch"},"_npmUser":{"name":"cgoing","email":"neo.cgoing@gmail.com"},"prettier":{"semi":true,"useTabs":false,"tabWidth":2,"endOfLine":"lf","printWidth":120,"quoteProps":"as-needed","arrowParens":"always","singleQuote":true,"trailingComma":"es5","bracketSpacing":true,"jsxSingleQuote":false,"bracketSameLine":false,"embeddedLanguageFormatting":"auto"},"repository":{"url":"git+https://github.com/cgoinglove/ts-edge.git","type":"git"},"_npmVersion":"10.2.1","description":"A strongly-typed graph-based workflow engine for building flexible, composable data processing pipelines with TypeScript","directories":{},"lint-staged":{"*.{ts,tsx}":["eslint --fix","prettier --write"]},"sideEffects":false,"_nodeVersion":"20.9.0","dependencies":{"ts-safe":"^0.0.4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.6","husky":"^8.0.3","eslint":"^9.19.0","vitest":"^3.0.6","globals":"^15.14.0","prettier":"^3.2.5","@eslint/js":"^9.19.0","typescript":"~5.7.2","@types/node":"^22.13.5","lint-staged":"^14.0.1","typescript-eslint":"^8.22.0"},"_npmOperationalInternal":{"tmp":"tmp/ts-edge_1.0.0_1742466408613_0.2706068396528689","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"ts-edge","version":"1.0.1","keywords":["graph","workflow","pipeline","typescript","edge","dataflow","state-machine","node-processing","directed-graph","langgraph","functional"],"author":{"name":"cging"},"license":"MIT","_id":"ts-edge@1.0.1","maintainers":[{"name":"cgoing","email":"neo.cgoing@gmail.com"}],"homepage":"https://github.com/cgoinglove/ts-edge#readme","bugs":{"url":"https://github.com/cgoinglove/ts-edge/issues"},"dist":{"shasum":"a4d9c709173df3d2ba9e605d850b61428bb74a3a","tarball":"https://registry.npmjs.org/ts-edge/-/ts-edge-1.0.1.tgz","fileCount":7,"integrity":"sha512-VvNqJPqlfuuSr0B30EO/1aceMnprBLO+0JeJkhJ18JW8GcbIDjWhbLvpC0K6SSiWDslt10nIEz3Qi2JI1t7N8A==","signatures":[{"sig":"MEUCIGHT16VZSsALrVwpx18UtbNvI9bSdL87c46eIU37WNwhAiEA8hhl1NBEimZPcqfXbQk3oa/at2wVQSQl77tRJdS+lJE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":102854},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=14.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"e9cffe8fda1a06a6aae31bbd9c128e53c57bac0c","scripts":{"lint":"eslint .","test":"vitest","build":"tsup","format":"prettier --write ./**/*.ts","prepare":"husky install","release":"npm run prepare:release && npm run build && npm run prepare:version && npm publish","type-check":"tsc --noEmit","lint-staged":"lint-staged","prepare:release":"npm run format && npm run type-check && npm run test run","prepare:version":"npm --no-git-tag-version version patch"},"_npmUser":{"name":"cgoing","email":"neo.cgoing@gmail.com"},"prettier":{"semi":true,"useTabs":false,"tabWidth":2,"endOfLine":"lf","printWidth":120,"quoteProps":"as-needed","arrowParens":"always","singleQuote":true,"trailingComma":"es5","bracketSpacing":true,"jsxSingleQuote":false,"bracketSameLine":false,"embeddedLanguageFormatting":"auto"},"repository":{"url":"git+https://github.com/cgoinglove/ts-edge.git","type":"git"},"_npmVersion":"10.2.1","description":"A strongly-typed graph-based workflow engine for building flexible, composable data processing pipelines with TypeScript","directories":{},"lint-staged":{"*.{ts,tsx}":["eslint --fix","prettier --write"]},"sideEffects":false,"_nodeVersion":"20.9.0","dependencies":{"ts-safe":"^0.0.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.6","husky":"^8.0.3","eslint":"^9.19.0","vitest":"^3.0.6","globals":"^15.14.0","prettier":"^3.2.5","@eslint/js":"^9.19.0","typescript":"~5.7.2","@types/node":"^22.13.5","lint-staged":"^14.0.1","typescript-eslint":"^8.22.0"},"_npmOperationalInternal":{"tmp":"tmp/ts-edge_1.0.1_1743951549215_0.5008174568389201","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"ts-edge","version":"1.0.2","keywords":["graph","workflow","pipeline","typescript","edge","dataflow","state-machine","node-processing","directed-graph","langgraph","functional"],"author":{"name":"cging"},"license":"MIT","_id":"ts-edge@1.0.2","maintainers":[{"name":"cgoing","email":"neo.cgoing@gmail.com"}],"homepage":"https://github.com/cgoinglove/ts-edge#readme","bugs":{"url":"https://github.com/cgoinglove/ts-edge/issues"},"dist":{"shasum":"a496ae6f6508bdcef3f3f1a72a7ddac46a6e4bf5","tarball":"https://registry.npmjs.org/ts-edge/-/ts-edge-1.0.2.tgz","fileCount":7,"integrity":"sha512-hYObWyeM8cUIPmf6XwTmxBxhG82vCKuEAgn/srCbgPKIAHs84MHq1FV3CX7uYzikaGFvcm0EqQ0E6sgIVXLssg==","signatures":[{"sig":"MEYCIQDpq9IuALugvoEfanLwu9crKx++X4hq8Kc7nkL1e7LXHwIhALglJmOcfqCd05tEjxkh5cErmpToCkEwoQU27L6zTVIv","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":102972},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=14.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"3389e463b98b2fa2da7bb66d0bff72eec48727d7","scripts":{"lint":"eslint .","test":"vitest","build":"tsup","format":"prettier --write ./**/*.ts","prepare":"husky install","release":"npm run prepare:release && npm run build && npm run prepare:version && npm publish","type-check":"tsc --noEmit","lint-staged":"lint-staged","prepare:release":"npm run format && npm run type-check && npm run test run","prepare:version":"npm --no-git-tag-version version patch"},"_npmUser":{"name":"cgoing","email":"neo.cgoing@gmail.com"},"prettier":{"semi":true,"useTabs":false,"tabWidth":2,"endOfLine":"lf","printWidth":120,"quoteProps":"as-needed","arrowParens":"always","singleQuote":true,"trailingComma":"es5","bracketSpacing":true,"jsxSingleQuote":false,"bracketSameLine":false,"embeddedLanguageFormatting":"auto"},"repository":{"url":"git+https://github.com/cgoinglove/ts-edge.git","type":"git"},"_npmVersion":"10.2.1","description":"A strongly-typed graph-based workflow engine for building flexible, composable data processing pipelines with TypeScript","directories":{},"lint-staged":{"*.{ts,tsx}":["eslint --fix","prettier --write"]},"sideEffects":false,"_nodeVersion":"20.9.0","dependencies":{"ts-safe":"^0.0.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.6","husky":"^8.0.3","eslint":"^9.19.0","vitest":"^3.0.6","globals":"^15.14.0","prettier":"^3.2.5","@eslint/js":"^9.19.0","typescript":"~5.7.2","@types/node":"^22.13.5","lint-staged":"^14.0.1","typescript-eslint":"^8.22.0"},"_npmOperationalInternal":{"tmp":"tmp/ts-edge_1.0.2_1745797320403_0.676841502358908","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"ts-edge","version":"1.0.3","keywords":["graph","workflow","pipeline","typescript","edge","dataflow","state-machine","node-processing","directed-graph","langgraph","functional"],"author":{"name":"cging"},"license":"MIT","_id":"ts-edge@1.0.3","maintainers":[{"name":"cgoing","email":"neo.cgoing@gmail.com"}],"homepage":"https://github.com/cgoinglove/ts-edge#readme","bugs":{"url":"https://github.com/cgoinglove/ts-edge/issues"},"dist":{"shasum":"cd2dff087694ceb11523d4b6944d9a2d3fd5f0e5","tarball":"https://registry.npmjs.org/ts-edge/-/ts-edge-1.0.3.tgz","fileCount":7,"integrity":"sha512-IpxShFFI0VWSApkbdm+46jWFqCXo3hTte3DeAc2iqHabcfELDs0+Q2bItH1KOV/J2XpWuGgYHEm1LRzgZhC4xQ==","signatures":[{"sig":"MEQCICdnqC14Ti+UwSJ4RvR5IVXY9b/b2QP6eT4cZ/8f10VUAiAo0XV/XrbSWvN1bxcJptBW+HluhTj+U4S29a/ZhV3XMw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":103176},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=14.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"81a1993835b5bc2ad96acd620bd1fea5998c3b5e","scripts":{"lint":"eslint .","test":"vitest","build":"tsup","format":"prettier --write ./**/*.ts","prepare":"husky install","release":"npm run prepare:release && npm run build && npm run prepare:version && npm publish","type-check":"tsc --noEmit","lint-staged":"lint-staged","prepare:release":"npm run format && npm run type-check && npm run test run","prepare:version":"npm --no-git-tag-version version patch"},"_npmUser":{"name":"cgoing","email":"neo.cgoing@gmail.com"},"prettier":{"semi":true,"useTabs":false,"tabWidth":2,"endOfLine":"lf","printWidth":120,"quoteProps":"as-needed","arrowParens":"always","singleQuote":true,"trailingComma":"es5","bracketSpacing":true,"jsxSingleQuote":false,"bracketSameLine":false,"embeddedLanguageFormatting":"auto"},"repository":{"url":"git+https://github.com/cgoinglove/ts-edge.git","type":"git"},"_npmVersion":"10.9.2","description":"A strongly-typed graph-based workflow engine for building flexible, composable data processing pipelines with TypeScript","directories":{},"lint-staged":{"*.{ts,tsx}":["eslint --fix","prettier --write"]},"sideEffects":false,"_nodeVersion":"22.15.0","dependencies":{"ts-safe":"^0.0.5"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.6","husky":"^8.0.3","eslint":"^9.19.0","vitest":"^3.0.6","globals":"^15.14.0","prettier":"^3.2.5","@eslint/js":"^9.19.0","typescript":"~5.7.2","@types/node":"^22.13.5","lint-staged":"^14.0.1","typescript-eslint":"^8.22.0"},"_npmOperationalInternal":{"tmp":"tmp/ts-edge_1.0.3_1749649101632_0.9917414677842877","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"ts-edge","publishConfig":{"access":"public"},"description":"A strongly-typed graph-based workflow engine for building flexible, composable data processing pipelines with TypeScript","keywords":["graph","workflow","pipeline","typescript","edge","dataflow","state-machine","node-processing","directed-graph","langgraph","functional"],"author":{"name":"cging"},"license":"MIT","version":"1.0.4","type":"module","sideEffects":false,"main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs","default":"./dist/index.js"}},"scripts":{"prepare":"husky install","test":"vitest","lint-staged":"lint-staged","build":"tsup","format":"prettier --write ./**/*.ts","type-check":"tsc --noEmit","lint":"eslint .","prepare:release":"npm run format && npm run type-check && npm run test run","prepare:version":"npm --no-git-tag-version version patch","release":"npm run prepare:release && npm run build && npm run prepare:version && npm publish"},"devDependencies":{"@eslint/js":"^9.19.0","@types/node":"^22.13.5","eslint":"^9.19.0","globals":"^15.14.0","husky":"^8.0.3","lint-staged":"^14.0.1","prettier":"^3.2.5","tsup":"^8.3.6","typescript":"~5.7.2","typescript-eslint":"^8.22.0","vitest":"^3.0.6"},"lint-staged":{"*.{ts,tsx}":["eslint --fix","prettier --write"]},"engines":{"node":">=14.0.0"},"repository":{"type":"git","url":"git+https://github.com/cgoinglove/ts-edge.git"},"prettier":{"printWidth":120,"tabWidth":2,"useTabs":false,"semi":true,"singleQuote":true,"quoteProps":"as-needed","jsxSingleQuote":false,"trailingComma":"es5","bracketSpacing":true,"bracketSameLine":false,"arrowParens":"always","endOfLine":"lf","embeddedLanguageFormatting":"auto"},"dependencies":{"ts-safe":"^0.0.5"},"_id":"ts-edge@1.0.4","gitHead":"5a6679bed6477f1a8ff9fef7502d014a8e8e759e","bugs":{"url":"https://github.com/cgoinglove/ts-edge/issues"},"homepage":"https://github.com/cgoinglove/ts-edge#readme","_nodeVersion":"22.15.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-pRPqjf8noUPR6VU+PNKMyEskqj/5ABTR6SjRQt3LueqKbO/uSWJc3hvcdJFZMCKXZqUoZL8oPAHR4dVuMqiAFQ==","shasum":"fb4e991c99e555954ee6b500a9ae687482c24526","tarball":"https://registry.npmjs.org/ts-edge/-/ts-edge-1.0.4.tgz","fileCount":7,"unpackedSize":103400,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDWUFwUz8H/ztPsmR8OLYME1FBF5PcKi6jb1I2dT93EXQIhAJaU/Rn5vu36b7iD/R3ElJxq3mMiyKJ+1YmNRW6S4Yso"}]},"_npmUser":{"name":"cgoing","email":"neo.cgoing@gmail.com","actor":{"name":"cgoing","email":"neo.cgoing@gmail.com","type":"user"}},"directories":{},"maintainers":[{"name":"cgoing","email":"neo.cgoing@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ts-edge_1.0.4_1750752093290_0.9550319287358755"},"_hasShrinkwrap":false}},"time":{"created":"2025-03-04T14:05:18.778Z","modified":"2025-06-24T08:01:33.667Z","0.0.0":"2025-03-04T14:05:19.111Z","0.0.1":"2025-03-05T02:43:07.670Z","0.0.2":"2025-03-05T02:50:38.767Z","0.0.3":"2025-03-06T06:47:12.828Z","0.0.4":"2025-03-06T15:16:48.800Z","0.0.5":"2025-03-08T17:10:46.323Z","0.0.6":"2025-03-09T14:53:06.504Z","0.0.8":"2025-03-09T15:06:06.974Z","0.0.9":"2025-03-10T08:21:11.585Z","0.0.10":"2025-03-10T08:22:12.935Z","0.0.11":"2025-03-11T08:10:26.631Z","0.0.12":"2025-03-11T11:30:07.071Z","0.0.13":"2025-03-11T16:00:59.329Z","0.0.14":"2025-03-12T08:40:49.770Z","0.0.15":"2025-03-12T15:32:23.613Z","0.0.16":"2025-03-12T15:34:22.182Z","0.0.17":"2025-03-13T00:49:06.558Z","0.0.18":"2025-03-13T01:09:52.940Z","0.0.19":"2025-03-13T13:53:21.855Z","0.1.1":"2025-03-17T02:29:54.721Z","0.1.2":"2025-03-17T02:48:11.799Z","0.1.3":"2025-03-17T07:30:54.598Z","0.1.4":"2025-03-17T13:45:30.317Z","0.1.5":"2025-03-19T15:16:15.187Z","0.1.6":"2025-03-19T15:17:12.299Z","0.1.7":"2025-03-20T08:19:08.313Z","1.0.0":"2025-03-20T10:26:48.777Z","1.0.1":"2025-04-06T14:59:09.414Z","1.0.2":"2025-04-27T23:42:00.582Z","1.0.3":"2025-06-11T13:38:21.839Z","1.0.4":"2025-06-24T08:01:33.479Z"},"bugs":{"url":"https://github.com/cgoinglove/ts-edge/issues"},"author":{"name":"cging"},"license":"MIT","homepage":"https://github.com/cgoinglove/ts-edge#readme","keywords":["graph","workflow","pipeline","typescript","edge","dataflow","state-machine","node-processing","directed-graph","langgraph","functional"],"repository":{"type":"git","url":"git+https://github.com/cgoinglove/ts-edge.git"},"description":"A strongly-typed graph-based workflow engine for building flexible, composable data processing pipelines with TypeScript","maintainers":[{"name":"cgoing","email":"neo.cgoing@gmail.com"}],"readme":"# 🔗 ts-edge 🔗\n\nEnglish | [한국어](./docs/kr.md)\n\nA lightweight workflow engine for TypeScript that lets you create graph-based execution flows with type safety and minimal complexity.\n\n![parallel](./docs/parallel.png)\n\n## Contents\n\n- [Features](#features)\n- [Installation](#installation)\n- [Type-safe Workflows](#type-safe-workflows) - Ensure type compatibility between nodes\n- [State-based Workflows](#state-based-workflows) - Share state across nodes\n- [Key Features](#key-features)\n- [Helper Functions](#helper-functions)\n\n## Features\n\n- **Lightweight**: Minimal API and options that you can learn and apply quickly\n- **Advanced Type Inference**: Compile-time validation ensures nodes can only connect when their input/output types match\n- **Simple API**: Provides only essential functionality for ease of use\n- **Flexible workflows**: Supports various patterns like conditional branching, parallel processing, and result merging\n- **State Management**: Built-in store for state-based workflows\n\n## Installation\n\n```bash\nnpm install ts-edge\n```\n\n## Type-safe Workflows\n\nType-safe workflows in ts-edge ensure type compatibility between connected nodes:\n\n```typescript\nimport { createGraph } from 'ts-edge';\n\n// Each node receives the output of the previous node as its input\n// TypeScript validates type compatibility between connected nodes at compile time\nconst workflow = createGraph()\n  .addNode({\n    name: 'number to string',\n    execute: (input: number) => {\n      // Convert number to string\n      return `Input received: ${input}`;\n    },\n  })\n  .addNode({\n    name: 'string to boolean',\n    execute: (input: string) => {\n      // Convert string to boolean\n      return input !== '';\n    },\n  })\n  .addNode({\n    name: 'boolean to array',\n    execute: (input: boolean) => {\n      // Convert boolean to array\n      return input ? [] : [1, 2, 3];\n    },\n  })\n  .edge('number to string', 'string to boolean')   // Type check passes\n  // .edge('number to string', 'boolean to array')  // ❌ Type error\n  .edge('string to boolean', 'boolean to array');   // Type check passes\n\n// Compile and run the workflow\nconst app = workflow.compile('number to string');\nconst result = await app.run(100);\nconsole.log(result.output); // [1,2,3]\n```\n\n## State-based Workflows\n\nState-based workflows in ts-edge allow nodes to share and modify a common state:\n\n```typescript\nimport { createStateGraph, graphStore } from 'ts-edge';\n\n// Define counter state type\ntype CounterState = {\n  count: number;\n  increment: () => void;\n  decrement: () => void;\n  updateCount: (count: number) => void;\n};\n\n// Create a state store using graphStore\nconst store = graphStore<CounterState>((set, get) => {\n  return {\n    count: 0,\n    increment: () =>\n      set((prev) => {\n        return { count: prev.count + 1 };\n      }),\n    decrement: () => set({ count: get().count - 1 }),\n    updateCount: (count: number) => set({ count }),\n  };\n});\n\n// Create a state-based workflow\n// In state-based workflows, nodes share and modify common state\n// Note: Return values from state nodes are ignored\nconst workflow = createStateGraph(store)\n  .addNode({\n    name: 'increment',\n    execute: (state) => {\n      // Access state\n      console.log(state.count); // 0\n\n      state.increment();\n    },\n  })\n  .addNode({\n    name: 'checkCount',\n    execute: (state) => {\n      console.log(`Current count: ${state.count}`);\n    },\n  })\n  .addNode({\n    name: 'reset',\n    execute: (state) => {\n      // Reset state\n      state.updateCount(0);\n    },\n  })\n  .edge('increment', 'checkCount')\n  .dynamicEdge('checkCount', (state) => {\n    // Determine next node based on state\n    return state.count > 10 ? 'reset' : 'increment';\n  });\n\n// Compile and run the workflow\nconst app = workflow.compile('increment');\nconst result = await app.run(); // Start with initial state\n// Or start with partial state: await app.run({ count: 10 });\n```\n\n## Key Features\n\n### Basic Node and Edge Definition\n\nNodes process input and produce output. Edges define the flow between nodes. Nodes can include optional metadata for documentation or visualization purposes.\n\n```typescript\nconst workflow = createGraph()\n  .addNode({\n    name: 'nodeA',\n    execute: (input: number) => ({ value: input * 2 }),\n    metadata: { description: 'Doubles the input value', category: 'math' }\n  })\n  .addNode({\n    name: 'nodeB',\n    execute: (input: { value: number }) => ({ result: input.value + 10 }),\n    metadata: { description: 'Adds 10 to the value' }\n  })\n  .edge('nodeA', 'nodeB');\n```\n\n### Node Execution Context\n\nEach node's execute function can receive a context object as a second argument:\n\n```typescript\naddNode({\n  name: 'streamingNode',\n  metadata: { version: 1, role: 'processor' },\n  execute: (input, context) => {\n    // Access node metadata\n    console.log(context.metadata); // { version: 1, role: 'processor' }\n    \n    // Emit stream events (useful for reporting progress during execution)\n    context.stream('Processing started...');\n    // Perform work\n    context.stream('50% complete');\n    // Final result\n    return { result: 'Completed' };\n  }\n});\n```\n\n### Dynamic Routing\n\nMake execution decisions based on node outputs:\n\n```typescript\nworkflow.dynamicEdge('processData', (data) => {\n  if (data.value > 100) return ['highValueProcess', 'standardProcess']; // Route to multiple nodes\n  if (data.value < 0) return 'errorHandler';                            // Route to a single node\n  return 'standardProcess';                                             // Default path\n});\n```\n\nFor better visualization and documentation, you can specify possible targets:\n\n```typescript\nworkflow.dynamicEdge('processData', {\n  possibleTargets: ['highValueProcess', 'errorHandler', 'standardProcess'],\n  router: (data) => {\n    if (data.value > 100) return ['highValueProcess', 'standardProcess'];\n    if (data.value < 0) return 'errorHandler';\n    return 'standardProcess';\n  }\n});\n```\n\n### Parallel Processing with Merge Nodes\n\nProcess data in parallel branches and merge the results:\n\n```typescript\nconst workflow = createGraph()\n  .addNode({\n    name: 'fetchData',\n    execute: (query) => ({ query }),\n  })\n  .addNode({\n    name: 'processBranch1',\n    execute: (data) => ({ summary: summarize(data.query) }),\n  })\n  .addNode({\n    name: 'processBranch2',\n    execute: (data) => ({ details: getDetails(data.query) }),\n  })\n  .addMergeNode({\n    name: 'combineResults',\n    branch: ['processBranch1', 'processBranch2'],  // Branches to merge\n    execute: (inputs) => ({\n      // inputs object contains outputs from each branch node\n      result: {\n        summary: inputs.processBranch1.summary,\n        details: inputs.processBranch2.details,\n      },\n    }),\n  })\n  .edge('fetchData', ['processBranch1', 'processBranch2']);  // One node to many nodes\n```\n\n### Execution Options\n\nControl the behavior of your workflows:\n\n```typescript\n// Basic execution\nconst result = await app.run(input);\n\n// Execution with options\nconst resultWithOptions = await app.run(input, {\n  timeout: 5000,       // Maximum execution time in ms\n  maxNodeVisits: 50,   // Prevent infinite loops\n});\n\n// State graph initialization\nconst stateResult = await stateApp.run({ count: 10, name: 'test' }); // Initialize with partial state\n\n// Prevent state reset\nconst noResetResult = await stateApp.run(undefined, {\n  noResetState: true   // Don't reset state before execution\n});\n```\n\n### Start and End Nodes\n\nWhen compiling a workflow, you can specify:\n\n```typescript\n// Only specify start node - runs until a node with no outgoing edges\nconst app = workflow.compile('inputNode');\n\n// Specify both start and end nodes - terminates at end node\nconst appWithEnd = workflow.compile('inputNode', 'outputNode');\n```\n\n- **When an end node is specified**: The workflow terminates when it reaches the end node and returns that node's output.\n- **When no end node is specified**: The workflow runs until it reaches a leaf node (a node with no outgoing edges) and returns the output of the last executed node.\n\n### Event Subscription\n\nMonitor workflow execution with events:\n\n```typescript\napp.subscribe((event) => {\n  // Workflow start event\n  if (event.eventType === 'WORKFLOW_START') {\n    console.log(`Workflow started with input:`, event.input);\n  }\n  \n  // Node start event\n  else if (event.eventType === 'NODE_START') {\n    console.log(`Node started: ${event.node.name}, input:`, event.node.input);\n  }\n  \n  // Node stream event (triggered by context.stream calls)\n  else if (event.eventType === 'NODE_STREAM') {\n    console.log(`Stream from node ${event.node.name}: ${event.node.chunk}`);\n  }\n  \n  // Node end event\n  else if (event.eventType === 'NODE_END') {\n    if (event.isOk) {\n      console.log(`Node completed: ${event.node.name}, output:`, event.node.output);\n    } else {\n      console.error(`Node error: ${event.node.name}, error:`, event.error);\n    }\n  }\n  \n  // Workflow end event\n  else if (event.eventType === 'WORKFLOW_END') {\n    if (event.isOk) {\n      console.log(`Workflow completed with output:`, event.output);\n    } else {\n      console.error(`Workflow error:`, event.error);\n    }\n  }\n});\n```\n\n### Middleware Support\n\nAdd middleware to intercept, modify, or redirect node execution:\n\n```typescript\nconst app = workflow.compile('startNode');\n\n// Add middleware\napp.use((node, next) => {\n  console.log(`About to execute node: ${node.name}, input:`, node.input);\n\n  // Modify input and continue with same node\n  if (node.name === 'validation') {\n    next({ name: node.name, input: { ...node.input, validated: true } });\n  }\n  \n  // Redirect execution flow to a different node\n  else if (node.name === 'router' && node.input.special) {\n    next({ name: 'specialHandler', input: node.input });\n  }\n  \n  // Continue normal execution flow\n  else {\n    next();\n  }\n  \n  // Not calling next() would stop execution\n});\n```\n\n### Error Handling\n\nts-edge provides a robust error handling system:\n\n```typescript\ntry {\n  const result = await app.run(input);\n  \n  if (result.isOk) {\n    console.log('Success:', result.output);\n  } else {\n    console.error('Execution error:', result.error);\n  }\n} catch (error) {\n  console.error('Unexpected error:', error);\n}\n```\n\n## Helper Functions\n\nThese helpers let you define nodes separately for better organization and reusability across files.\n\n### `graphNode` - Create nodes\n\n```typescript\nimport { graphNode } from 'ts-edge';\n\n// Create a node\nconst userNode = graphNode({\n  name: 'getUser',\n  execute: (id: string) => fetchUser(id),\n  metadata: { description: 'Fetches user data' }\n});\n\n// Infer types\ntype UserNodeType = graphNode.infer<typeof userNode>;\n// { name: 'getUser', input: string, output: User }\n\n// Use in graph\ngraph.addNode(userNode);\n```\n\n### `graphStateNode` - Create state nodes\n\n```typescript\nimport { graphStateNode, graphStore } from 'ts-edge';\n\n// Define state and create store\ntype CounterState = {\n  count: number;\n  name: string;\n  updateCount: (count: number) => void;\n  updateName: (name: string) => void;\n};\n\nconst store = graphStore<CounterState>((set) => {\n  return {\n    count: 0,\n    name: '',\n    updateName(name) {\n      set({ name });\n    },\n    updateCount(count) {\n      set({ count });\n    },\n  };\n});\n\n// Define node in separate file/module\nconst countNode = graphStateNode({\n  name: 'processCount',\n  execute: ({ count, updateCount }: CounterState) => {\n    if (count < 10) {\n      updateCount(10);\n    }\n  },\n});\n\n// Use in state graph\nconst stateGraph = createStateGraph(store).addNode(countNode);\n```\n\n### `graphMergeNode` - Create merge nodes\n\n```typescript\nimport { graphMergeNode } from 'ts-edge';\n\n// Create a merge node\nconst mergeNode = graphMergeNode({\n  name: 'combine',\n  branch: ['userData', 'userStats'],\n  execute: (inputs) => ({ ...inputs.userData, stats: inputs.userStats }),\n});\n\n// Use in graph\ngraph.addMergeNode(mergeNode);\n```\n\n### `graphNodeRouter` - Create routers\n\n```typescript\nimport { graphNodeRouter } from 'ts-edge';\n\n// Create a simple router\nconst simpleRouter = graphNodeRouter((data) => (\n  data.isValid ? 'success' : 'error'\n));\n\n// Create a router with explicit targets\nconst complexRouter = graphNodeRouter(\n  ['success', 'warning', 'error'],\n  (data) => {\n    if (data.score > 90) return 'success';\n    if (data.score > 50) return 'warning';\n    return 'error';\n  }\n);\n\n// Use in graph\ngraph.dynamicEdge('validate', simpleRouter);\n```\n\n## License\n\nMIT","readmeFilename":"README.md"}