{"_id":"@ayatkevich/flow","name":"@ayatkevich/flow","dist-tags":{"latest":"0.7.0"},"versions":{"0.7.0":{"type":"module","private":false,"name":"@ayatkevich/flow","version":"0.7.0","author":{"name":"Alex Yatkevich"},"license":"MIT","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"compile":"tsc","test":"jest","prepublish":"npm run compile"},"devDependencies":{"@swc/core":"1.9.1","@swc/jest":"0.2.37","@types/jest":"29.5.14","jest":"29.7.0","typescript":"5.6.3"},"prettier":{"printWidth":100,"proseWrap":"always","experimentalTernaries":true},"wallaby":{"runMode":"onsave","env":{"params":{"runner":"--experimental-vm-modules"}},"hints":{"allowIgnoringCoverageInTests":true}},"_id":"@ayatkevich/flow@0.7.0","gitHead":"8049cb482c669e350e9641aae1a2ccc16f9cc6ce","description":"An extensible effect handling library for tracing and verifying generator functions in TypeScript. Flow allows you to infer types of effects and their arguments from individual, concrete traces without manually defining them. This approach not only ensure","_nodeVersion":"23.3.0","_npmVersion":"10.9.0","dist":{"integrity":"sha512-9iwcCZ4Kix7Gjb5FCwwhh0JVDA0qr6Vl+lbvG5xSCAotNvFPZCFCYvnd7dZN0jW3Xm+gzcZpiQZsLu18jVH4pw==","shasum":"26d336a0eef106c47293ee2a68b0864b497124e5","tarball":"https://registry.npmjs.org/@ayatkevich/flow/-/flow-0.7.0.tgz","fileCount":6,"unpackedSize":28822,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHFKiqYZVMtA1dGBeLHwsgn2i2+k7sJPn7BuH8Y0z9ZqAiEA8/gI3QYEBEO3gLv98Ck83WjYYAC7HZqHuRpK3jBwovE="}]},"_npmUser":{"name":"ayatkevich","email":"ayatkevich@gmail.com"},"directories":{},"maintainers":[{"name":"ayatkevich","email":"ayatkevich@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/flow_0.7.0_1733554541261_0.9547281857652776"},"_hasShrinkwrap":false}},"time":{"created":"2024-12-07T06:55:41.126Z","0.7.0":"2024-12-07T06:55:41.457Z","modified":"2024-12-07T06:55:41.775Z"},"maintainers":[{"name":"ayatkevich","email":"ayatkevich@gmail.com"}],"description":"An extensible effect handling library for tracing and verifying generator functions in TypeScript. Flow allows you to infer types of effects and their arguments from individual, concrete traces without manually defining them. This approach not only ensure","author":{"name":"Alex Yatkevich"},"license":"MIT","readme":"# @ayatkevich/flow\n\nAn extensible effect handling library for tracing and verifying generator functions in TypeScript.\nFlow allows you to infer types of effects and their arguments from individual, concrete traces\nwithout manually defining them. This approach not only ensures static type safety but also enables\ndynamic verification of implementations, facilitating test-driven development with extensible\neffects.\n\n## Table of Contents\n\n- [Introduction](#introduction)\n- [Key Features](#key-features)\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Defining a Program](#defining-a-program)\n  - [Implementing the Program](#implementing-the-program)\n  - [Verifying the Implementation](#verifying-the-implementation)\n  - [Handling Side Effects](#handling-side-effects)\n- [Error Handling](#error-handling)\n- [Test-First Programming](#test-first-programming)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Introduction\n\nFlow simplifies the management of side effects in asynchronous generator functions by using traces\nto infer types and arguments. This method eliminates the need for manual type definitions for\neffects, enhancing both development speed and code reliability.\n\n## Key Features\n\n- **Type Inference from Traces**: Automatically infer effect types and arguments from traces.\n- **Dynamic Verification**: Verify implementations against defined traces without executing side\n  effects.\n- **Error Handling**: Type-safe error handling by returning errors as values.\n- **Test-First Development**: Facilitate test-driven development by defining expected behaviors\n  upfront.\n\n## Installation\n\n```bash\nnpm install @ayatkevich/flow\n```\n\n## Usage\n\n### Defining a Program\n\nUse the `program` function to define a set of traces, where each trace is a sequence of steps\n(`yields`, `throws`, or `returns`). Flow uses these traces to infer effect types and arguments.\n\n```typescript\nconst AI = program([\n  trace([\n    yields(fn(\"env\").takes(\"OPENAI_API_KEY\").returns(\"sk-1234567890\")),\n    yields(\n      fn(\"openai\")\n        .takes({\n          key: \"sk-1234567890\",\n          model: \"gpt-4\",\n          messages: [{ role: \"user\", content: \"hi\" }],\n        })\n        .returns(\"Hello!\")\n    ),\n    returns(\"Hello!\"),\n  ]),\n]);\n```\n\nThis program `AI` defines a single trace with three sequential steps:\n\n1. **Yields** an effect to get the OpenAI API key from environment variables.\n2. **Yields** an effect to call the OpenAI API with the obtained key, model, and messages.\n3. **Returns** the result of the OpenAI API call.\n\n### Implementing the Program\n\nImplement the program by defining a generator function using the `implementation` function. The\n`this` context is a proxy object that infers its interface from the program, providing type-safe\naccess to effects.\n\n```typescript\nconst ai = implementation(AI, function* () {\n  const apiKey = yield* this.env(\"OPENAI_API_KEY\");\n  const result = yield* this.openai({\n    key: apiKey,\n    model: \"gpt-4\",\n    messages: [{ role: \"user\", content: \"hi\" }],\n  });\n  return result;\n});\n```\n\nHere, `this.env` and `this.openai` are effect functions inferred from the traces, ensuring that the\ncorrect types are used for arguments and return values.\n\n### Verifying the Implementation\n\nUse the `verify` function to dynamically verify that the implementation conforms to the defined\ntraces. This process checks that the sequence of effects and their arguments match the expectations\nwithout executing any side effects.\n\n```typescript\nverify(AI, ai);\n```\n\n### Handling Side Effects\n\nExecute the implementation with actual side effects using the `handle` function, providing concrete\nimplementations for each effect.\n\n```typescript\nconst result = await handle(ai, {\n  env(name) {\n    return process.env[name];\n  },\n  async openai(params) {\n    const response = await openai.chat.completions.create(params);\n    return response.text;\n  },\n});\n```\n\n## Error Handling\n\nEffect handlers can throw errors, which are then returned as values in the implementation for\ntype-safe error handling. This approach allows you to handle errors within your generator function\nnaturally.\n\n```typescript\nconst AIWithErrors = program([\n  trace([\n    yields(fn(\"env\").takes(\"OPENAI_API_KEY\").returns(\"sk-1234567890\")),\n    yields(\n      fn(\"openai\")\n        .takes({\n          key: \"sk-1234567890\",\n          model: \"gpt-4\",\n          messages: [{ role: \"user\", content: \"hi\" }],\n        })\n        .returns(\"Hello!\")\n    ),\n    returns(\"Hello!\"),\n  ]),\n  trace([\n    yields(fn(\"env\").takes(\"OPENAI_API_KEY\").returns(\"sk-1234567890\")),\n    yields(\n      fn(\"openai\")\n        .takes({\n          key: \"sk-1234567890\",\n          model: \"gpt-4\",\n          messages: [{ role: \"user\", content: \"hi\" }],\n        })\n        .returns(new Error(\"Limit exceeded\"))\n    ),\n    throws(new Error(\"Failed to call OpenAI API\")),\n  ]),\n]);\n\nconst aiWithErrors = implementation(AIWithErrors, function* () {\n  const apiKey = yield* this.env(\"OPENAI_API_KEY\");\n  const result = yield* this.openai({\n    key: apiKey,\n    model: \"gpt-4\",\n    messages: [{ role: \"user\", content: \"hi\" }],\n  });\n  if (result instanceof Error) {\n    throw new Error(\"Failed to call OpenAI API\");\n  }\n  return result;\n});\n```\n\nIn the handler, you can choose to return or throw an error:\n\n```typescript\nconst result = await handle(aiWithErrors, {\n  env(name) {\n    return process.env[name];\n  },\n  async openai(params) {\n    try {\n      const response = await openai.chat.completions.create(params);\n      return response.text;\n    } catch {\n      throw new Error(\"Limit exceeded\");\n    }\n  },\n});\n```\n\n## Test-First Programming\n\nBy programming with traces, you effectively practice test-driven development. You define the\nexpected behaviors and effects upfront, allowing for immediate verification of your implementation\nagainst these expectations. This method reduces the need for manual type definitions and enhances\ncode reliability.\n\n## Contributing\n\nContributions are welcome! Please open an issue or submit a pull request on GitHub.\n\n## License\n\nThis project is licensed under the MIT License.\n","readmeFilename":"readme.md"}