{"_id":"@ashforge/otp-ninja","name":"@ashforge/otp-ninja","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@ashforge/otp-ninja","version":"1.0.0","description":"The unified OTP toolkit for QA automation engineers. Email, SMS, and TOTP in one package.","keywords":["otp","one-time-password","totp","2fa","mfa","email-otp","sms-otp","imap","twilio","vonage","qa","automation","playwright","cypress","webdriverio","testing","typescript"],"homepage":"https://github.com/qa-ashutosh/otp-ninja#readme","bugs":{"url":"https://github.com/qa-ashutosh/otp-ninja/issues"},"repository":{"type":"git","url":"git+https://github.com/qa-ashutosh/otp-ninja.git"},"license":"MIT","author":{"name":"Ashutosh Parihar"},"type":"module","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","scripts":{"clean":"rm -rf dist/ coverage/ *.tsbuildinfo","prebuild":"npm run clean","build":"tsup","dev":"tsup --watch","test":"jest","test:watch":"jest --watch","test:coverage":"jest --coverage","lint":"eslint src tests --ext .ts","lint:fix":"eslint src tests --ext .ts --fix","typecheck":"tsc --noEmit","prepublishOnly":"npm run build && npm test"},"dependencies":{"imapflow":"^1.0.162"},"peerDependencies":{"@vonage/server-sdk":">=3.0.0","twilio":">=4.0.0"},"peerDependenciesMeta":{"twilio":{"optional":true},"@vonage/server-sdk":{"optional":true}},"devDependencies":{"@types/jest":"^29.5.12","@types/node":"^20.12.7","@typescript-eslint/eslint-plugin":"^7.7.1","@typescript-eslint/parser":"^7.7.1","eslint":"^8.57.0","jest":"^29.7.0","ts-jest":"^29.1.2","tsup":"^8.0.2","typescript":"^5.4.5"},"engines":{"node":">=16"},"gitHead":"360f356013a4013cd7b623816429986c744ee6b5","_id":"@ashforge/otp-ninja@1.0.0","_nodeVersion":"24.12.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-hcNrC/gPW8mBMunOz5+fHrUCPJl5eftoxXx1UdsPZEoxuPP7ann4uDR1ryUcPmbitYZ0dTNcWCqSgn2GBXWtrw==","shasum":"453834b01aa3630cd994eb8dd4b9e4885188c208","tarball":"https://registry.npmjs.org/@ashforge/otp-ninja/-/otp-ninja-1.0.0.tgz","fileCount":9,"unpackedSize":443371,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFKPWMbCuz2TjLHkzuhx3Z7o0F1T+6wBFl6lC9/r233yAiBxPYXdpeyNHvSVjenYwpujzHWIHorVNs4a9kOnK3Id/g=="}]},"_npmUser":{"name":"ashforge","email":"qa.ashutosh3@gmail.com"},"directories":{},"maintainers":[{"name":"ashforge","email":"qa.ashutosh3@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/otp-ninja_1.0.0_1778216786188_0.24071301310584814"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-08T05:06:26.004Z","1.0.0":"2026-05-08T05:06:26.336Z","modified":"2026-05-08T05:06:26.619Z"},"maintainers":[{"name":"ashforge","email":"qa.ashutosh3@gmail.com"}],"description":"The unified OTP toolkit for QA automation engineers. Email, SMS, and TOTP in one package.","homepage":"https://github.com/qa-ashutosh/otp-ninja#readme","keywords":["otp","one-time-password","totp","2fa","mfa","email-otp","sms-otp","imap","twilio","vonage","qa","automation","playwright","cypress","webdriverio","testing","typescript"],"repository":{"type":"git","url":"git+https://github.com/qa-ashutosh/otp-ninja.git"},"author":{"name":"Ashutosh Parihar"},"bugs":{"url":"https://github.com/qa-ashutosh/otp-ninja/issues"},"license":"MIT","readme":"<div align=\"center\">\n\n<h1>🥷 otp-ninja</h1>\n\n<p><strong>The unified OTP toolkit built for QA automation engineers.</strong><br />Email · SMS · TOTP. One package. Zero compromise.</p>\n\n<br />\n\n[![npm version](https://img.shields.io/npm/v/@ashforge%2Fotp-ninja?style=flat-square&color=00d26a&label=npm)](https://www.npmjs.com/package/@ashforge/otp-ninja)\n[![npm downloads](https://img.shields.io/npm/dm/@ashforge%2Fotp-ninja?style=flat-square&color=00d26a)](https://www.npmjs.com/package/@ashforge/otp-ninja)\n[![CI](https://img.shields.io/github/actions/workflow/status/qa-ashutosh/otp-ninja/ci.yml?branch=main&style=flat-square&label=CI)](https://github.com/qa-ashutosh/otp-ninja/actions)\n[![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178c6?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)\n[![License: MIT](https://img.shields.io/badge/license-MIT-yellow?style=flat-square)](./LICENSE)\n[![Node.js >= 16](https://img.shields.io/badge/node-%3E%3D16-brightgreen?style=flat-square&logo=node.js)](https://nodejs.org)\n[![Zero warnings](https://img.shields.io/badge/npm_install-zero_warnings-success?style=flat-square)](https://www.npmjs.com/package/@ashforge/otp-ninja)\n\n<br />\n\n</div>\n\n---\n\n## Why otp-ninja?\n\nTesting OTP flows is painful. You juggle different libraries for email, SMS, and TOTP. You copy-paste polling loops. You guess why an OTP wasn't found. You waste hours debugging cryptic provider errors with zero context.\n\n**otp-ninja fixes all of that.**\n\nOne package. Three providers. Smart retry built in. TypeScript-first. And when something goes wrong, it tells you exactly what to fix, not just that something broke.\n\n```typescript\nimport { fetchOTP } from '@ashforge/otp-ninja';\n\n// Fetch an email OTP with polling and retry handled automatically\nconst { otp } = await fetchOTP({\n  type: 'email',\n  host: 'imap.gmail.com',\n  user: 'qa-bot@yourcompany.com',\n  password: process.env.GMAIL_APP_PASSWORD!,\n  from: 'no-reply@yourapp.com',\n  timeout: 30_000,\n});\n\nawait page.fill('[data-testid=\"otp-input\"]', otp);\n```\n\n---\n\n## Feature Overview\n\n| Capability | Details |\n|---|---|\n| Email OTP | Gmail, Outlook, Yahoo, iCloud, Fastmail, any IMAP server |\n| SMS OTP | Twilio & Vonage support with install-only-what-you-need flexibility |\n| TOTP | RFC 6238, pure Node.js crypto with zero external deps |\n| Smart retry | Configurable timeout and poll interval with no manual loops |\n| Extraction engine | Plain text, HTML, quoted-printable, custom regex |\n| TypeScript | Strict mode, full IntelliSense, zero `any` |\n| Error handling | Typed errors with recovery steps built in |\n| Framework support | Playwright, Cypress, WebdriverIO, Jest, plain Node.js |\n| Security | Credentials never logged, TLS enforced by default |\n| Bundle size | Minimal, only `imapflow` is a hard dependency |\n\n---\n\n## Installation\n\n```bash\nnpm install @ashforge/otp-ninja\n```\n\nFor SMS via Twilio (optional peer dependency):\n\n```bash\nnpm install @ashforge/otp-ninja twilio\n```\n\nFor SMS via Vonage, no extra install needed, Vonage uses a direct REST call.\n\n**Node.js 16 or higher is required.**\n\n---\n\n## Quick Start\n\n### Email OTP\n\n```typescript\nimport { fetchEmailOTP, gmailConfig } from '@ashforge/otp-ninja';\n\nconst { otp } = await fetchEmailOTP(\n  gmailConfig({\n    user: 'qa-bot@gmail.com',\n    password: process.env.GMAIL_APP_PASSWORD!,\n  }, {\n    from: 'no-reply@yourapp.com',\n    timeout: 30_000,\n  })\n);\n\nconsole.log('OTP:', otp); // \"429817\"\n```\n\n### SMS OTP\n\n```typescript\nimport { fetchSMSOTP } from '@ashforge/otp-ninja';\n\nconst { otp } = await fetchSMSOTP({\n  provider: 'twilio',\n  accountSid: process.env.TWILIO_SID!,\n  authToken: process.env.TWILIO_TOKEN!,\n  to: '+14155552671',\n  timeout: 30_000,\n});\n```\n\n### TOTP (Authenticator Apps)\n\n```typescript\nimport { generateTOTP, generateFreshTOTP, verifyTOTP } from '@ashforge/otp-ninja';\n\n// Generate current token\nconst { otp, remainingSeconds } = generateTOTP({\n  secret: process.env.TOTP_SECRET!, // Base32 from your app's QR code\n});\n\n// Generate a token guaranteed to be valid for at least 5 more seconds\n// Prevents flaky tests that submit right at a 30s window boundary\nconst { otp: freshOtp } = await generateFreshTOTP({ secret: process.env.TOTP_SECRET! });\n\n// Verify a token (returns boolean, never throws)\nconst valid = verifyTOTP('429817', { secret: process.env.TOTP_SECRET! });\n```\n\n### Universal Entry Point\n\n```typescript\nimport { fetchOTP } from '@ashforge/otp-ninja';\n\n// Email\nawait fetchOTP({ type: 'email', host: 'imap.gmail.com', user: '...', password: '...' });\n\n// SMS\nawait fetchOTP({ type: 'sms', provider: 'twilio', accountSid: '...', authToken: '...', to: '...' });\n\n// TOTP\nawait fetchOTP({ type: 'totp', secret: '...' });\n```\n\n---\n\n## Email Providers\n\n### Gmail\n\nGmail requires an **App Password**, not your regular Gmail password.\n\n1. Go to [myaccount.google.com/apppasswords](https://myaccount.google.com/apppasswords)\n2. Click \"Select app\" and choose \"Mail\"\n3. Click \"Select device\" and choose \"Other (custom name)\", then name it `otp-ninja`\n4. Copy the 16-character generated password. This is your `password` value\n\n```typescript\nimport { gmailConfig } from '@ashforge/otp-ninja';\n\nconst config = gmailConfig({\n  user: 'qa-bot@gmail.com',\n  password: 'abcd efgh ijkl mnop', // the 16-char App Password (spaces are fine)\n}, {\n  from: 'no-reply@yourapp.com',\n  timeout: 45_000,\n});\n```\n\n### Outlook / Office 365\n\n```typescript\nimport { outlookConfig } from '@ashforge/otp-ninja';\n\nconst config = outlookConfig({\n  user: 'qa-bot@yourcompany.com',\n  password: process.env.OUTLOOK_APP_PASSWORD!,\n});\n```\n\n**Corporate Outlook note:** Many organisations disable IMAP access by default. If you receive a connection error, ask your IT administrator to enable IMAP for your mailbox, or check Outlook Settings → Mail → Sync email → IMAP.\n\n### Yahoo, iCloud, Fastmail, Zoho\n\n```typescript\nimport { fetchEmailOTP, EMAIL_PROVIDERS } from '@ashforge/otp-ninja';\n\n// Yahoo\nawait fetchEmailOTP({\n  ...EMAIL_PROVIDERS.yahoo,\n  user: 'qa-bot@yahoo.com',\n  password: process.env.YAHOO_APP_PASSWORD!,\n});\n\n// iCloud\nawait fetchEmailOTP({\n  ...EMAIL_PROVIDERS.icloud,\n  user: 'qa-bot@icloud.com',\n  password: process.env.ICLOUD_APP_PASSWORD!,\n});\n\n// Fastmail\nawait fetchEmailOTP({\n  ...EMAIL_PROVIDERS.fastmail,\n  user: 'qa-bot@fastmail.com',\n  password: process.env.FASTMAIL_APP_PASSWORD!,\n});\n```\n\n### Custom IMAP Server\n\n```typescript\nawait fetchEmailOTP({\n  host: 'mail.yourcompany.com',\n  port: 993,\n  tls: true,\n  user: 'qa-bot@yourcompany.com',\n  password: process.env.IMAP_PASSWORD!,\n  from: 'otp@yourapp.com',\n  timeout: 30_000,\n});\n```\n\n### Mailinator (Public Inboxes)\n\nMailinator provides a free public email API with no IMAP required, no account needed. Read up to 100 messages per day on the free tier.\n\n```typescript\nimport { fetchEmailOTP, extractOTP } from '@ashforge/otp-ninja';\n\n// Mailinator uses an HTTP API instead of IMAP, so use `extractOTP()` on the response.\nconst response = await fetch(\n  'https://www.mailinator.com/api/v2/domains/mailinator.com/inboxes/test-inbox/messages',\n  { headers: { Authorization: `Bearer ${process.env.MAILINATOR_TOKEN}` } }\n);\nconst data = await response.json();\nconst latestBody = data.msgs?.[0]?.parts?.[0]?.body ?? '';\nconst { otp } = extractOTP(latestBody);\n```\n\n---\n\n## SMS Providers\n\n### Twilio\n\n```typescript\nimport { fetchSMSOTP } from '@ashforge/otp-ninja';\n\n// Install first: npm install twilio\nconst { otp } = await fetchSMSOTP({\n  provider: 'twilio',\n  accountSid: process.env.TWILIO_ACCOUNT_SID!,  // starts with \"AC\"\n  authToken: process.env.TWILIO_AUTH_TOKEN!,\n  to: '+14155552671',\n  timeout: 30_000,\n  pollInterval: 5_000,\n});\n```\n\n**Twilio free trial note:** Trial accounts can only send SMS to verified phone numbers. Verify your test number at [console.twilio.com](https://console.twilio.com).\n\n### Vonage\n\nNo extra install is needed because Vonage uses a direct REST API call.\n\n```typescript\nconst { otp } = await fetchSMSOTP({\n  provider: 'vonage',\n  apiKey: process.env.VONAGE_API_KEY!,\n  apiSecret: process.env.VONAGE_API_SECRET!,\n  to: '+14155552671',\n  timeout: 30_000,\n});\n```\n\n---\n\n## TOTP for Authenticator Apps\n\notp-ninja implements RFC 6238 TOTP from scratch using Node.js built-in `crypto`. No external dependencies. Compatible with Google Authenticator, Authy, Microsoft Authenticator, 1Password, and Bitwarden.\n\n```typescript\nimport { generateTOTP, generateFreshTOTP, verifyTOTP } from '@ashforge/otp-ninja';\n\n// Basic generation\nconst { otp, remainingSeconds, isExpiring } = generateTOTP({\n  secret: 'JBSWY3DPEHPK3PXP', // Base32 secret from your app\n  digits: 6,    // optional, default 6\n  period: 30,   // optional, default 30 seconds\n  algorithm: 'SHA1', // optional, default SHA1\n});\n\n// Use generateFreshTOTP() in tests to avoid race conditions\n// It waits for a new window if the current token expires in < 5 seconds\nconst { otp: safeOtp } = await generateFreshTOTP({ secret: process.env.TOTP_SECRET! });\n\n// Verify with accepts ±1 window drift for clock skew\nconst isValid = verifyTOTP('429817', { secret: process.env.TOTP_SECRET! });\n```\n\n**Finding your TOTP secret:** Scan the QR code displayed during 2FA setup and extract the `secret` query parameter from the `otpauth://` URI. Many apps let you view this via \"Show key\" or \"Can't scan QR code\" options.\n\n---\n\n## OTP Extraction Engine\n\nUse `extractOTP()` standalone when you already have message text and just need the OTP pulled out.\n\n```typescript\nimport { extractOTP } from '@ashforge/otp-ninja';\n\n// Default matcher for 4–8 digit codes preceded by OTP-related keywords\nconst result = extractOTP('Your verification code is 482910');\n// result.otp === \"482910\"\n\n// Custom regex for non-standard OTP formats\nconst result2 = extractOTP('Use token XK-38291 to continue', {\n  otpPattern: /token\\s+([A-Z]{2}-\\d{5})/i,\n});\n// result2.otp === \"XK-38291\"\n\n// Extract from HTML email bodies with automatic quoted-printable decoding\nconst result3 = extractOTP(htmlEmailBody);\n```\n\n---\n\n## Polling and Retry\n\nEvery provider uses the same polling engine. You configure `timeout` and `pollInterval`, and otp-ninja handles the rest.\n\n```typescript\nawait fetchEmailOTP({\n  // ... connection config ...\n  timeout: 60_000,     // keep polling for up to 60 seconds\n  pollInterval: 3_000, // check every 3 seconds\n});\n```\n\nThe poller:\n\n1. Connects and searches for matching messages\n2. If found, extracts the OTP and returns immediately\n3. If not found, waits `pollInterval` milliseconds and tries again\n4. When `timeout` is exceeded, throws `OTPTimeoutError` with the elapsed time and attempt count\n\n**Tip:** Start with `timeout: 30_000` for most providers. Increase to `60_000` if your OTP sender is known to be slow, or if tests run in CI where networks can be less predictable.\n\n---\n\n## Framework Integration\n\n### Playwright\n\n```typescript\n// tests/helpers/otp.ts\nimport { fetchEmailOTP, gmailConfig, generateFreshTOTP } from '@ashforge/otp-ninja';\n\nexport async function getEmailOTP(): Promise<string> {\n  const { otp } = await fetchEmailOTP(\n    gmailConfig({ user: process.env.QA_EMAIL!, password: process.env.GMAIL_APP_PASSWORD! })\n  );\n  return otp;\n}\n\nexport async function getTOTP(): Promise<string> {\n  const { otp } = await generateFreshTOTP({ secret: process.env.TOTP_SECRET! });\n  return otp;\n}\n\n// tests/login.spec.ts\nimport { test, expect } from '@playwright/test';\nimport { getEmailOTP, getTOTP } from './helpers/otp';\n\ntest('completes 2FA login', async ({ page }) => {\n  await page.goto('/login');\n  await page.fill('#email', process.env.QA_EMAIL!);\n  await page.fill('#password', process.env.QA_PASSWORD!);\n  await page.click('#sign-in');\n\n  const otp = await getEmailOTP();\n  await page.fill('[data-testid=\"otp-input\"]', otp);\n  await page.click('[data-testid=\"verify-btn\"]');\n\n  await expect(page).toHaveURL('/dashboard');\n});\n```\n\nSee [`examples/playwright-example.ts`](./examples/playwright-example.ts) for the full working example.\n\n### Cypress\n\n```typescript\n// cypress/support/commands.ts\nimport { fetchEmailOTP, gmailConfig } from '@ashforge/otp-ninja';\n\nCypress.Commands.add('getEmailOTP', async () => {\n  const { otp } = await fetchEmailOTP(\n    gmailConfig({ user: Cypress.env('QA_EMAIL'), password: Cypress.env('GMAIL_APP_PASSWORD') })\n  );\n  return otp;\n});\n\n// cypress/e2e/login.cy.ts\ncy.getEmailOTP().then((otp) => {\n  cy.get('[data-testid=\"otp-input\"]').type(otp);\n  cy.get('[data-testid=\"verify-btn\"]').click();\n});\n```\n\n### WebdriverIO\n\n```typescript\nimport { fetchEmailOTP, gmailConfig } from '@ashforge/otp-ninja';\n\ndescribe('OTP login', () => {\n  it('submits the OTP correctly', async () => {\n    const { otp } = await fetchEmailOTP(\n      gmailConfig({ user: process.env.QA_EMAIL!, password: process.env.GMAIL_APP_PASSWORD! })\n    );\n    await $('[data-testid=\"otp-input\"]').setValue(otp);\n    await $('[data-testid=\"verify-btn\"]').click();\n  });\n});\n```\n\n### Jest / Node.js\n\n```typescript\nimport { fetchEmailOTP, generateTOTP } from '@ashforge/otp-ninja';\n\ntest('email OTP is 6 digits', async () => {\n  const { otp } = await fetchEmailOTP({ /* ... */ });\n  expect(otp).toMatch(/^\\d{6}$/);\n});\n```\n\n---\n\n## Error Handling\n\nEvery error thrown by otp-ninja is a typed `OTPNinjaError` with a machine-readable `code`, a plain-English message, and a built-in recovery guide.\n\n```typescript\nimport { fetchEmailOTP, isOTPError, isOTPErrorCode } from 'otp-ninja';\n\ntry {\n  const { otp } = await fetchEmailOTP({ /* ... */ });\n} catch (err) {\n  if (isOTPError(err)) {\n    // Every error has these fields:\n    console.log(err.code);       // 'TIMEOUT' | 'OTP_NOT_FOUND' | 'CONNECTION_FAILED' | ...\n    console.log(err.provider);   // 'email' | 'sms' | 'totp'\n    console.log(err.severity);   // 'retryable' | 'fatal' | 'user_error' | 'config_error'\n    console.log(err.isRetryable);   // true if the caller can safely retry\n    console.log(err.isUserError);   // true if the fix requires a config change\n\n    // Full diagnostic block — credential-safe, log this anywhere\n    console.log(err.toDiagnosticString());\n\n    // Structured — compatible with Winston, Pino, Datadog, Splunk\n    logger.error(err.toJSON());\n\n    // Every error includes a recovery guide\n    console.log(err.recovery.action);  // one-line instruction\n    err.recovery.steps.forEach(step => console.log(step));\n  }\n\n  // Check for a specific error code\n  if (isOTPErrorCode(err, 'TIMEOUT')) {\n    // increase timeout and retry\n  }\n\n  if (isOTPErrorCode(err, 'MISSING_DEPENDENCY')) {\n    console.log(err.installCommand); // exact npm install command\n  }\n}\n```\n\n### Error Code Reference\n\n| Code | Class | Severity | Common Cause |\n|---|---|---|---|\n| `OTP_NOT_FOUND` | `OTPNotFoundError` | retryable | No matching message arrived yet |\n| `TIMEOUT` | `OTPTimeoutError` | retryable | Polling exceeded timeout |\n| `CONNECTION_FAILED` | `OTPConnectionError` | retryable | Wrong host/port or IMAP blocked |\n| `AUTHENTICATION_FAILED` | `OTPAuthenticationError` | user_error | Wrong password or missing App Password |\n| `INVALID_CONFIG` | `OTPInvalidConfigError` | config_error | Missing or malformed option |\n| `EXTRACTION_FAILED` | `OTPExtractionError` | user_error | OTP pattern did not match message body |\n| `MISSING_DEPENDENCY` | `OTPMissingDependencyError` | user_error | Peer dep (twilio) not installed |\n| `PROVIDER_ERROR` | `OTPProviderError` | retryable | Provider API returned an error |\n| `NETWORK_ERROR` | `OTPNetworkError` | retryable | Transient DNS/TLS/socket failure |\n| `PERMISSION_DENIED` | `OTPPermissionError` | user_error | IMAP access rights issue |\n| `RATE_LIMITED` | `OTPRateLimitError` | retryable | API rate limit exceeded |\n\n### Example Diagnostic Output\n\nWhen you call `err.toDiagnosticString()`, you get a structured, credential-free diagnostic block:\n\n```\n[otp-ninja] OTPTimeoutError: OTP polling timed out after 30s (10 poll(s) made).\n  Code     : TIMEOUT\n  Provider : email\n  Severity : retryable\n  Time     : 2024-11-15T09:42:18.221Z\n  Operation: fetchEmailOTP\n  Endpoint : imap.gmail.com\n  Account  : ***@gmail.com\n  Attempts : 10 / 10\n  Elapsed  : 30012ms\n  Timeout  : 30000ms\n\n  What to do:\n    Increase the timeout or check for delivery delays on the sending side.\n    1. Your current timeout is 30s, try doubling it: { timeout: 60000 }.\n    2. Check whether the OTP sender (email/SMS provider) is experiencing delays.\n    3. Verify the trigger that sends the OTP actually fired (e.g. the login button was clicked).\n    4. If testing locally, add a deliberate delay before calling fetchOTP to let the message arrive.\n    5. Use OTP_NINJA_DEBUG=true to see polling activity in real time.\n```\n\n---\n\n## Configuration Reference\n\n### Email Options\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `host` | `string` | required | IMAP server hostname |\n| `port` | `number` | `993` | IMAP server port |\n| `tls` | `boolean` | `true` | Use TLS (strongly recommended) |\n| `user` | `string` | required | Email address for authentication |\n| `password` | `string` | required | App Password (not your login password) |\n| `from` | `string` | — | Filter by sender address. This must be the OTP sender, not your own address |\n| `subject` | `string` | — | Filter by subject line (partial match) |\n| `mailbox` | `string` | `'INBOX'` | IMAP mailbox/folder name |\n| `timeout` | `number` | `30000` | Max polling duration in milliseconds |\n| `pollInterval` | `number` | `3000` | Delay between polls in milliseconds |\n| `otpPattern` | `RegExp` | built-in | Custom regex to extract the OTP |\n\n### SMS Options (Twilio)\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `provider` | `'twilio'` | required | Select the Twilio provider |\n| `accountSid` | `string` | required | Twilio Account SID (starts with `AC`) |\n| `authToken` | `string` | required | Twilio Auth Token |\n| `to` | `string` | required | Phone number that received the OTP (E.164 format) |\n| `timeout` | `number` | `30000` | Max polling duration in milliseconds |\n| `pollInterval` | `number` | `5000` | Delay between polls in milliseconds |\n| `otpPattern` | `RegExp` | built-in | Custom regex to extract the OTP |\n\n### TOTP Options\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `secret` | `string` | required | Base32-encoded shared secret |\n| `digits` | `number` | `6` | Token length |\n| `period` | `number` | `30` | Token validity in seconds |\n| `algorithm` | `'SHA1' \\| 'SHA256' \\| 'SHA512'` | `'SHA1'` | HMAC algorithm |\n| `issuer` | `string` | — | Label only. Not used in computation |\n\n---\n\n## Environment Variables\n\nStore credentials in a `.env` file and load them with `dotenv` or your framework's built-in `.env` support.\n\n```bash\n# .env\nGMAIL_APP_PASSWORD=abcd efgh ijkl mnop\nOUTLOOK_APP_PASSWORD=your-app-password\n\nTWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nTWILIO_AUTH_TOKEN=your-auth-token\n\nTOTP_SECRET=JBS8IODPEHPK6HG7P\n\n# Enable verbose debug logging (credentials are masked)\nOTP_NINJA_DEBUG=true\n```\n\n```typescript\nimport 'dotenv/config';\nimport { fetchEmailOTP, gmailConfig } from '@ashforge/otp-ninja';\n\nconst { otp } = await fetchEmailOTP(\n  gmailConfig({\n    user: 'qa-bot@gmail.com',\n    password: process.env.GMAIL_APP_PASSWORD!,\n  })\n);\n```\n\nSee [`.env.example`](./.env.example) for the full template.\n\n---\n\n## Debug Mode\n\nSet `OTP_NINJA_DEBUG=true` to enable verbose logging. All output is credential-safe, and passwords, tokens, and API keys are never printed.\n\n```bash\nOTP_NINJA_DEBUG=true node your-test.mjs\n```\n\nDebug output shows each poll attempt with its timestamp, the number of messages found per search, which message UIDs were inspected, the resolved configuration with sensitive fields masked, and the final OTP value when found.\n\n---\n\n## Security\n\notp-ninja treats credential safety as a hard requirement, not an afterthought.\n\n**Credentials never appear in logs or error messages.** Every error context passes through `maskSensitive()` before anything is stored or printed. Email addresses are reduced to `***@domain.com`. Phone numbers are reduced to `***2671`. API keys, passwords, and tokens become `***`.\n\n**TLS is enabled by default.** IMAP connections use TLS (`secure: true`) unless you explicitly set `tls: false`, which is only appropriate for local test mail servers.\n\n**No credential persistence.** Nothing is cached, stored, or transmitted to any third party. Credentials flow directly to the provider (Gmail, Twilio, etc.) and nowhere else.\n\n**Peer dependencies are optional.** Install only the provider SDKs your workflow actually needs. If you only use TOTP, you install zero provider dependencies.\n\nSee [`SECURITY.md`](./SECURITY.md) for the full security policy and responsible disclosure process.\n\n---\n\n## Examples\n\nThe [`examples/`](./examples/) directory contains complete, runnable examples:\n\n| File | Description |\n|---|---|\n| [`playwright-example.ts`](./examples/playwright-example.ts) | Full Playwright test with email OTP and TOTP |\n| [`general-usage.ts`](./examples/general-usage.ts) | All three providers, error handling, extractOTP standalone |\n\nRun any example directly:\n\n```bash\nOTP_NINJA_DEBUG=true npx ts-node examples/general-usage.ts\n```\n\n---\n\n## Contributing\n\nContributions are welcome. Please read [`CONTRIBUTING.md`](./CONTRIBUTING.md) before opening a PR.\n\n```bash\ngit clone https://github.com/qa-ashutosh/otp-ninja.git\ncd otp-ninja\nnpm install\nnpm run build\nnpm test          # runs all 92 tests\nnpm run test:watch\n```\n\n---\n\n## Changelog\n\nSee [`CHANGELOG.md`](./CHANGELOG.md) for the full release history.\n\n---\n\n## License\n\nMIT. see [`LICENSE`](./LICENSE) for details.\n\n---\n\n<div align=\"center\">\n\nBuilt for the QA automation community. If otp-ninja saves you time, consider giving it a ⭐ on GitHub.\n\n</div>\n","readmeFilename":"README.md","_rev":"1-5191aa17c4ea2e46196dfb575f141526"}