{"_id":"@autowright/resilience","_rev":"2-4e7333dfa29c5f6545e00ae8c7c177cf","name":"@autowright/resilience","dist-tags":{"latest":"0.0.2"},"versions":{"0.0.1":{"name":"@autowright/resilience","version":"0.0.1","keywords":["playwright","resilience","retry","automation","scraping"],"license":"Apache-2.0","_id":"@autowright/resilience@0.0.1","maintainers":[{"name":"nawaaz-korvol","email":"nawaaz@korvol.com"}],"homepage":"https://github.com/korvol/autowright/tree/main/packages/resilience","bugs":{"url":"https://github.com/korvol/autowright/issues"},"dist":{"shasum":"35322ae05b80ebaccada6ef2fc4cf8d5a07d9ee6","tarball":"https://registry.npmjs.org/@autowright/resilience/-/resilience-0.0.1.tgz","fileCount":13,"integrity":"sha512-/clSktH4yJtBxIldYOBjkIgCDpPXyIcFzT/3Jj+me/Bva/2in/wfS/aU3tJe2uGerRZ/H1uFKPbpajy3yLkUZw==","signatures":[{"sig":"MEUCIQDJXmwsmgXVNs+9B6KIQb9ZB/Sz1mhcAjIZsmDt9IYKYgIgLBQFyWdzrvPELXZZj5ktCOCvlO2GcDKCZkZrrZ+aEqQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":14378},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"2dbfe0898f029c6fd8895cd4c7be6c33f87b70fc","scripts":{"lint":"biome lint .","test":"vitest","build":"tsc","check":"biome check .","format":"biome format --write .","test:ci":"vitest run","check:types":"tsc --noEmit","prepublishOnly":"rm -rf dist && tsc -p tsconfig.json"},"_npmUser":{"name":"nawaaz-korvol","email":"nawaaz@korvol.com"},"repository":{"url":"git+https://github.com/korvol/autowright.git","type":"git","directory":"packages/resilience"},"_npmVersion":"11.6.2","description":"Resilient execution runner for Playwright scripts — retry, fail, abort with event hooks","directories":{},"_nodeVersion":"24.12.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/resilience_0.0.1_1772812267465_0.4951353124492792","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@autowright/resilience","version":"0.0.2","description":"Resilient execution runner for Playwright scripts — retry, fail, abort with event hooks","license":"Apache-2.0","type":"module","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"repository":{"type":"git","url":"git+https://github.com/korvol/autowright.git","directory":"packages/resilience"},"homepage":"https://github.com/korvol/autowright/tree/main/packages/resilience","bugs":{"url":"https://github.com/korvol/autowright/issues"},"keywords":["playwright","resilience","retry","automation","scraping"],"publishConfig":{"access":"public"},"scripts":{"build":"tsc","prepublishOnly":"rm -rf dist && tsc -p tsconfig.json","test":"vitest","test:ci":"vitest run","lint":"biome lint .","format":"biome format --write .","check":"biome check .","check:types":"tsc --noEmit"},"gitHead":"081fbb84e096eea33469e29a36274569a6613f7c","_id":"@autowright/resilience@0.0.2","_nodeVersion":"24.12.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-PDcSuZ4Nn1sEc1x7cb0Xqk/C8ybkOnuKIvShKvMM8Bhkt0o2PQpBC5+jHDAi4kbcr24zxqNQ7FhxDkE7Yw0nRw==","shasum":"71afc558700fcdd4c0167ebf3eb227f3fedd5719","tarball":"https://registry.npmjs.org/@autowright/resilience/-/resilience-0.0.2.tgz","fileCount":14,"unpackedSize":19314,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICKwDcrqDuvXQl82/FO7n5C/booHuIK+qV4o5diXN5sZAiATW32lsjAxnOcVbBN9sA3gSar5NO1ifvumuPonm5qZ7A=="}]},"_npmUser":{"name":"nawaaz-korvol","email":"nawaaz@korvol.com"},"directories":{},"maintainers":[{"name":"nawaaz-korvol","email":"nawaaz@korvol.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/resilience_0.0.2_1772813462273_0.8747448951521068"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-06T15:51:07.303Z","modified":"2026-03-06T16:11:02.628Z","0.0.1":"2026-03-06T15:51:07.632Z","0.0.2":"2026-03-06T16:11:02.509Z"},"bugs":{"url":"https://github.com/korvol/autowright/issues"},"license":"Apache-2.0","homepage":"https://github.com/korvol/autowright/tree/main/packages/resilience","keywords":["playwright","resilience","retry","automation","scraping"],"repository":{"type":"git","url":"git+https://github.com/korvol/autowright.git","directory":"packages/resilience"},"description":"Resilient execution runner for Playwright scripts — retry, fail, abort with event hooks","maintainers":[{"name":"nawaaz-korvol","email":"nawaaz@korvol.com"}],"readme":"# @autowright/resilience\n\nResilient execution runner for Playwright scripts. Retry on timeouts, fail gracefully on non-critical errors, abort on fatal failures — with event hooks at every level.\n\n## Install\n\n```bash\nnpm install @autowright/resilience\n```\n\n## Quick Start\n\n```typescript\nimport type { Page } from 'playwright'\nimport { createRunner } from '@autowright/resilience'\n\nconst runner = createRunner<Page>({\n  onError: (error) => {\n    if (error.message.includes('credentials invalid'))\n      return { action: 'abort' }\n    if (error.message.includes('timeout'))\n      return { action: 'retry', maxRetries: 3, backoffMs: 1000 }\n    return null // unrecognised → crash\n  },\n  onAbort: (description, error) => {\n    console.error(`aborted at \"${description}\": ${error.message}`)\n  },\n})\n\nrunner\n  .step({\n    description: 'login',\n    fn: async ({ page, data }) => {\n      await page.fill('#username', username)\n      await page.fill('#password', password)\n      await page.click('#submit')\n      data.set('loggedInAt', Date.now())\n    },\n  })\n  .step({\n    description: 'extract data',\n    fn: async ({ page, data }) => {\n      await page.locator('.closing-date').click()\n      data.set('closingDate', await page.locator('.closing-date').textContent())\n    },\n  })\n\nconst result = await runner.run({ page, data: new Map() })\n// result.status: 'completed' | 'aborted' | 'crashed'\n```\n\n## Core Concept\n\nA script is a sequence of steps. Each step is a function. When a step throws, the runner asks one question: **what should be done?**\n\nThe answer comes from `onError` — a single function that takes the raw error and returns an action:\n\n| Action | Behavior |\n|--------|----------|\n| `{ action: 'retry', maxRetries, backoffMs }` | Wait, re-run the step. Linear backoff (`backoffMs * attempt`). |\n| `{ action: 'fail' }` | Mark step as failed, continue to the next step. |\n| `{ action: 'abort' }` | Mark step as failed, skip remaining steps. |\n| `null` | Unrecognised error. Runner crashes with `status: 'crashed'`. |\n\n## Events\n\nThree events fire during execution. Each exists at **runner level** and **step level**.\n\n**Runner-level** handlers receive the step `description` as the first parameter. **Step-level** handlers receive a `defaultHandler` they can call to delegate to the runner-level handler, ignore, or call conditionally.\n\n### onRetry\n\nFires before each retry attempt, before the backoff wait.\n\n```typescript\n// Runner-level\nonRetry: (description, attempt, ctx) => {\n  console.log(`retrying \"${description}\", attempt ${attempt}`)\n}\n\n// Step-level — clear state before retry, then call default\nonRetry: (attempt, ctx, defaultHandler) => {\n  ctx.page.evaluate(() => localStorage.clear())\n  defaultHandler(attempt)\n}\n```\n\n### onStepFail\n\nFires when a step has failed for good — no retry config, or retries exhausted.\n\n```typescript\n// Runner-level\nonStepFail: (description, error, ctx) => {\n  console.error(`\"${description}\" failed: ${error.message}`)\n}\n```\n\n### onAbort\n\nFires when `onError` returns `{ action: 'abort' }`. The script stops. Remaining steps are marked `skipped`.\n\n```typescript\n// Step-level — screenshot on abort, then call default\nonAbort: (error, ctx, defaultHandler) => {\n  ctx.page.screenshot({ path: 'abort.png' })\n  defaultHandler(error)\n}\n```\n\n## onError Resolution\n\n1. **Step-level `onError`** runs first. If it returns an action, that's used.\n2. If step-level returns `null`, **runner-level `onError`** runs.\n3. If both return `null` — unrecognised error. Runner status: `crashed`.\n\n```typescript\nrunner.step({\n  description: 'login',\n  fn: loginFn,\n  onError: (error) => {\n    // handle step-specific errors\n    if (error.message.includes('2FA required'))\n      return { action: 'abort' }\n    return null // fall through to runner-level\n  },\n})\n```\n\n## Context\n\nEvery step receives a `StepContext<TPage>` with:\n\n- **`page`** — your Playwright page (or any generic type)\n- **`data`** — a `Map<string, unknown>` shared across all steps\n\n```typescript\n// Step 1 writes\nfn: async ({ data }) => { data.set('token', 'abc') }\n\n// Step 2 reads\nfn: async ({ data }) => { console.log(data.get('token')) } // 'abc'\n```\n\n## RunResult\n\n`run()` always resolves — never rejects. Even on crash.\n\n```typescript\ntype RunResult = {\n  status: 'completed' | 'aborted' | 'crashed'\n  steps: StepResult[]\n  error?: Error // only when status === 'crashed'\n}\n\ntype StepResult = {\n  description: string\n  status: 'success' | 'failed' | 'skipped'\n  error?: Error\n}\n```\n\n## API\n\n### `createRunner<TPage>(config: RunnerConfig<TPage>): Runner<TPage>`\n\nCreates a runner instance.\n\n### `runner.step(definition: StepDefinition<TPage>): Runner<TPage>`\n\nAdds a step. Returns the runner for chaining.\n\n### `runner.run(ctx: StepContext<TPage>): Promise<RunResult>`\n\nExecutes all steps sequentially. Always resolves.\n\n## License\n\n[Apache-2.0](https://github.com/korvol/autowright/blob/main/LICENSE)\n","readmeFilename":"README.md"}