{"id":46267370,"url":"https://github.com/adrozdenko/pactwork","last_synced_at":"2026-03-04T02:23:41.627Z","repository":{"id":336556674,"uuid":"1150130367","full_name":"adrozdenko/pactwork","owner":"adrozdenko","description":"Contract-first API simulation framework - Mocks you can trust","archived":false,"fork":false,"pushed_at":"2026-02-05T12:27:09.000Z","size":121,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-05T13:07:18.538Z","etag":null,"topics":["api-testing","contract-testing","mock","msw","openapi","typescript"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adrozdenko.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-04T23:11:02.000Z","updated_at":"2026-02-05T12:27:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/adrozdenko/pactwork","commit_stats":null,"previous_names":["adrozdenko/pactwork"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/adrozdenko/pactwork","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrozdenko%2Fpactwork","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrozdenko%2Fpactwork/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrozdenko%2Fpactwork/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrozdenko%2Fpactwork/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adrozdenko","download_url":"https://codeload.github.com/adrozdenko/pactwork/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrozdenko%2Fpactwork/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30069896,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-04T01:03:42.280Z","status":"online","status_checked_at":"2026-03-04T02:00:07.464Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-testing","contract-testing","mock","msw","openapi","typescript"],"created_at":"2026-03-04T02:23:41.048Z","updated_at":"2026-03-04T02:23:41.620Z","avatar_url":"https://github.com/adrozdenko.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./docs/logo.png\" alt=\"Pactwork\" width=\"200\" /\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003ePactwork\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eGenerate API mocks from your OpenAPI spec. They stay in sync automatically.\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/pactwork\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/pactwork.svg?style=flat-square\" alt=\"npm version\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/adrozdenko/pactwork/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square\" alt=\"MIT License\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/adrozdenko/pactwork/actions\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/adrozdenko/pactwork/ci.yml?style=flat-square\" alt=\"Build Status\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#why-pactwork\"\u003eWhy Pactwork\u003c/a\u003e •\n  \u003ca href=\"#quick-start\"\u003eQuick Start\u003c/a\u003e •\n  \u003ca href=\"#storybook-integration\"\u003eStorybook\u003c/a\u003e •\n  \u003ca href=\"#runtime-utilities\"\u003eRuntime\u003c/a\u003e •\n  \u003ca href=\"#ci-integration\"\u003eCI/CD\u003c/a\u003e •\n  \u003ca href=\"ROADMAP.md\"\u003eRoadmap\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## Why Pactwork\n\n**The problem:** API mocks drift from reality. You write them manually, the API evolves, and suddenly your tests pass but production breaks.\n\n**The fix:** Point Pactwork at your OpenAPI spec. It generates [MSW](https://mswjs.io/) mocks, validates them continuously, and tells you the moment they fall out of sync.\n\n| Without Pactwork | With Pactwork |\n|------------------|---------------|\n| Write mocks by hand | Generate from your spec |\n| Hope they match the API | Validate automatically |\n| Miss error states | Test every response your API can return |\n| Debug in production | Catch drift in development |\n\n```bash\n# One command. Mocks that match your API.\nnpx pactwork generate --spec ./openapi.yaml\n```\n\n---\n\n## Key Features\n\n| Feature | What you get |\n|---------|-------------|\n| **Mock Generation** | Generate MSW mock handlers from any OpenAPI 2.0, 3.0, or 3.1 spec |\n| **Drift Detection** | Know immediately when your mocks fall out of sync with the spec |\n| **Scenario Catalog** | Test every error your API can return (404, 500, timeouts, etc.) |\n| **Runtime Utilities** | Simulate latency, flaky networks, and error sequences in tests |\n| **Storybook Addon** | Switch API states directly from the Storybook toolbar |\n| **Coverage Reporting** | See which API scenarios your Storybook stories cover |\n| **CI Gates** | Block deployments when mocks don't match the spec |\n| **Type Generation** | Get TypeScript types from your spec, always up to date |\n\n---\n\n## Quick Start\n\n### Install\n\n```bash\nnpm install -D pactwork msw\n```\n\n### Generate mocks\n\n```bash\n# Generate MSW mock handlers from your OpenAPI spec\nnpx pactwork generate --spec ./openapi.yaml --output ./src/mocks\n\n# Include error scenarios for testing\nnpx pactwork generate --spec ./openapi.yaml --with-scenarios\n```\n\n### What you get\n\nPactwork generates ready-to-use MSW handlers and a scenario catalog:\n\n```\nsrc/mocks/\n├── handlers.ts      # MSW handlers for every endpoint in your spec\n└── scenarios.ts     # Error/edge case responses (404, 500, etc.)\n```\n\n```typescript\n// handlers.ts (generated)\nimport { http, HttpResponse } from 'msw'\n\nexport const handlers = [\n  http.get('/api/users/:id', () =\u003e {\n    return HttpResponse.json({ id: 1, name: 'Jane Doe', email: 'jane@example.com' })\n  }),\n  http.post('/api/users', () =\u003e {\n    return HttpResponse.json({ id: 2, name: 'New User' }, { status: 201 })\n  }),\n  // ... one handler per operation in your spec\n]\n```\n\n### Use with MSW\n\n**Browser (Storybook, development):**\n```typescript\nimport { setupWorker } from 'msw/browser'\nimport { handlers } from './mocks/handlers'\n\nconst worker = setupWorker(...handlers)\nawait worker.start()\n```\n\n**Node (tests):**\n```typescript\nimport { setupServer } from 'msw/node'\nimport { handlers } from './mocks/handlers'\n\nconst server = setupServer(...handlers)\nbeforeAll(() =\u003e server.listen())\nafterAll(() =\u003e server.close())\n```\n\n---\n\n## Storybook Integration\n\nTest every API state visually with `@pactwork/storybook-addon`.\n\n### Install\n\n```bash\nnpm install -D @pactwork/storybook-addon\n```\n\n### Configure\n\nRegister the addon in your Storybook config:\n\n```typescript\n// .storybook/main.ts\nexport default {\n  addons: ['@pactwork/storybook-addon'],\n}\n```\n\nThen set up toolbar controls in your preview. See the [full Storybook setup guide](packages/storybook-addon/README.md) for the complete configuration.\n\n### What it does\n\n| Control | Purpose |\n|---------|---------|\n| **Scenario dropdown** | Switch between success, error, and edge case responses |\n| **Latency selector** | Simulate slow networks (0ms to 5s) |\n| **Network toggle** | Simulate offline and connection errors |\n| **Addon panel** | See request logs, active state, and available mock handlers |\n\n### Set scenarios per story\n\n```typescript\n// UserCard.stories.tsx\nimport { setScenario, resetState, setLatency } from '../mocks/handlers'\n\nexport const Loading: Story = {\n  beforeEach: () =\u003e {\n    resetState()\n    setLatency(3000)\n  },\n}\n\nexport const NotFound: Story = {\n  beforeEach: () =\u003e {\n    resetState()\n    setScenario('getUser', 'notFound')\n  },\n}\n\nexport const ServerError: Story = {\n  beforeEach: () =\u003e {\n    resetState()\n    setScenario('getUser', 'serverError')\n  },\n}\n```\n\n---\n\n## Runtime Utilities\n\nControl API behavior programmatically in your tests.\n\n```typescript\nimport { handlers, handlerMeta, scenarios } from './mocks'\nimport { applyScenario, withLatency, withSequence, pipe } from 'pactwork/runtime'\n\n// Return a 404 for getUser\nconst errorHandlers = applyScenario(handlers, handlerMeta, 'getUser', scenarios.getUser.notFound)\n\n// Slow down all responses by 500ms\nconst slowHandlers = withLatency(handlers, handlerMeta, 500)\n\n// Simulate a flaky API: fail twice, then succeed\nconst flakyHandlers = withSequence(handlers, handlerMeta, 'getUser', [500, 500, 200])\n\n// Compose multiple behaviors\nconst testHandlers = pipe(\n  handlers,\n  h =\u003e withLatency(h, handlerMeta, 100),\n  h =\u003e applyScenario(h, handlerMeta, 'getUser', scenarios.getUser.notFound)\n)\n```\n\n### Available utilities\n\n| Utility | What it does |\n|---------|-------------|\n| `applyScenario(handlers, meta, operationId, scenario)` | Replace a response with an error or edge case |\n| `withLatency(handlers, meta, ms)` | Add delay to all responses |\n| `withLatency(handlers, meta, operationId, ms)` | Add delay to one operation |\n| `withSequence(handlers, meta, operationId, statuses)` | Return different statuses in sequence |\n| `withRateLimit(handlers, meta, operationId, opts)` | Simulate rate limiting (429) |\n| `withNetworkError(handlers, meta, operationId, opts)` | Simulate timeout, abort, or connection errors |\n| `withSeed(handlers, meta, seed)` | Deterministic response data |\n| `pipe(handlers, ...transformers)` | Compose multiple utilities together |\n\n---\n\n## CLI Commands\n\n| Command | What it does |\n|---------|-------------|\n| `pactwork generate` | Generate MSW mock handlers from your spec |\n| `pactwork validate` | Check if mocks match the spec |\n| `pactwork breaking` | Compare two spec versions for breaking changes |\n| `pactwork types` | Generate TypeScript types from your spec |\n| `pactwork scenarios` | List available error scenarios from your spec |\n| `pactwork coverage` | Report which scenarios your Storybook stories cover |\n| `pactwork can-i-deploy` | CI gate — block deploys when mocks drift |\n| `pactwork record` | Create a contract from your spec |\n| `pactwork verify` | Verify a contract against the current spec |\n\n### Common flags\n\n```bash\n# CI mode — minimal output, strict exit codes\npactwork validate --ci\n\n# Auto-fix — regenerate when drift is found\npactwork validate --fix\n\n# Generate with error scenarios\npactwork generate --with-scenarios\n\n# Compare API versions for breaking changes\npactwork breaking --old v1.yaml --new v2.yaml\n\n# GitHub Actions annotations\npactwork validate --format github\n\n# Coverage with minimum threshold\npactwork coverage --min-coverage 80\n```\n\n---\n\n## CI Integration\n\n### GitHub Action\n\n```yaml\n- uses: adrozdenko/pactwork@v1\n  with:\n    spec: ./openapi.yaml\n```\n\n### CLI in CI\n\n```yaml\nsteps:\n  - name: Validate mocks\n    run: npx pactwork validate --ci --format github\n\n  - name: Check deployment safety\n    run: npx pactwork can-i-deploy\n```\n\n### Exit codes\n\n| Code | Meaning |\n|------|---------|\n| `0` | Mocks match the spec |\n| `1` | Drift or breaking changes detected |\n| `2` | Warnings treated as errors |\n\n---\n\n## Automated Drift Repair\n\nFor CI pipelines and AI agents, use this workflow to auto-fix drift:\n\n```bash\n# 1. Check for drift\npactwork validate --ci\n# Exit 0 = mocks match, done\n# Exit 1 = drift detected, continue\n\n# 2. Regenerate mocks\npactwork generate\n\n# 3. Verify the fix\npactwork validate --ci\n\n# 4. Commit\ngit add src/mocks\ngit commit -m \"fix: regenerate mocks from updated spec\"\n```\n\n---\n\n## Configuration\n\nCreate `pactwork.config.ts` for project-wide settings:\n\n```typescript\nimport { defineConfig } from 'pactwork'\n\nexport default defineConfig({\n  spec: { path: './api/openapi.yaml' },\n  generate: {\n    output: './src/mocks',\n    typescript: true,\n  },\n})\n```\n\n---\n\n## Requirements\n\n- Node.js 20.11+\n- MSW 2.x\n- OpenAPI 2.0, 3.0, or 3.1 spec\n\n---\n\n## Contributing\n\n```bash\ngit clone https://github.com/adrozdenko/pactwork.git\nnpm install\nnpm test\n```\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for details.\n\n---\n\n## License\n\nMIT © [adrozdenko](https://github.com/adrozdenko)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadrozdenko%2Fpactwork","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadrozdenko%2Fpactwork","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadrozdenko%2Fpactwork/lists"}