{"_id":"@ekaone/nano-otp","_rev":"2-645caaa838ef10f0dd76dc8da9c814bd","name":"@ekaone/nano-otp","dist-tags":{"latest":"0.1.2"},"versions":{"0.0.1":{"name":"@ekaone/nano-otp","version":"0.0.1","keywords":["otp","two-factor authentication","2fa","typescript","library","privacy","security","otp-generation","data-protection","typescript","library","privacy","security","data-protection"],"author":{"url":"https://prasetia.me","name":"Eka Prasetia","email":"ekaone3033@gmail.com"},"license":"MIT","_id":"@ekaone/nano-otp@0.0.1","maintainers":[{"name":"ekaone","email":"ekaone3033@gmail.com"}],"homepage":"https://github.com/ekaone/nano-otp#readme","bugs":{"url":"https://github.com/ekaone/nano-otp/issues"},"dist":{"shasum":"d3a9ac6e87e378539002fc547a0c90879e9d71fa","tarball":"https://registry.npmjs.org/@ekaone/nano-otp/-/nano-otp-0.0.1.tgz","fileCount":9,"integrity":"sha512-TnQJgQyuVAxaUvmUgSDxqmNVtIQDgtYNNZR0BK9g/POTO0UR6maikCducNcf0AAtq6OFMya8c/HhB9ZpQW3MiQ==","signatures":[{"sig":"MEYCIQCStkxCOG/RDCZIvcnGVLpA5wGw0G3P+PqcsOpmb3+CBwIhAIj6is/+OSBQPBQyvpiXzYdNdHmeNQjQ+BSyn9nbzsYx","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":31289},"main":"./dist/index.js","_from":"file:ekaone-nano-otp-0.0.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","clean":"rimraf dist","test:ui":"vitest --ui","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"ekaone","email":"ekaone3033@gmail.com"},"_resolved":"C:\\Users\\Acer\\AppData\\Local\\Temp\\c04bab1ee03d188d19190afdeff5c48a\\ekaone-nano-otp-0.0.1.tgz","_integrity":"sha512-TnQJgQyuVAxaUvmUgSDxqmNVtIQDgtYNNZR0BK9g/POTO0UR6maikCducNcf0AAtq6OFMya8c/HhB9ZpQW3MiQ==","repository":{"url":"git+https://github.com/ekaone/nano-otp.git","type":"git"},"_npmVersion":"11.0.0","description":"A lightweight, zero-dependency TypeScript library for generating and validating OTPs","directories":{},"sideEffects":false,"_nodeVersion":"24.12.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","rimraf":"^6.0.0","vitest":"^4.0.18","@vitest/ui":"^4.0.18","typescript":"^5.7.0","@types/node":"^25.1.0","@vitest/coverage-v8":"^4.0.18"},"_npmOperationalInternal":{"tmp":"tmp/nano-otp_0.0.1_1773728431108_0.5891651410654086","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@ekaone/nano-otp","version":"0.1.2","description":"A lightweight, zero-dependency TypeScript library for generating and validating OTPs","keywords":["otp","one-time-password","two-factor-authentication","2fa","totp","authentication","security","crypto","typescript","zero-dependency"],"author":{"name":"Eka Prasetia","email":"ekaone3033@gmail.com","url":"https://prasetia.me"},"license":"MIT","sideEffects":false,"engines":{"node":">=18"},"main":"./dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/ekaone/nano-otp.git"},"bugs":{"url":"https://github.com/ekaone/nano-otp/issues"},"homepage":"https://github.com/ekaone/nano-otp#readme","devDependencies":{"@types/node":"^25.1.0","@vitest/coverage-v8":"^4.0.18","@vitest/ui":"^4.0.18","rimraf":"^6.0.0","tsup":"^8.5.1","typescript":"^5.7.0","vitest":"^4.0.18"},"scripts":{"build":"tsup","dev":"tsup --watch","clean":"rimraf dist","typecheck":"tsc --noEmit","test":"vitest run","test:watch":"vitest","test:ui":"vitest --ui","test:coverage":"vitest run --coverage"},"_id":"@ekaone/nano-otp@0.1.2","_integrity":"sha512-f9/fUCIPDB8cX2sP8pUVRgCZRKk+Uo6tjT3eK85C2ZxBIJMbakVmkNwZbiUeZXdxDQCsBhi9nlqNJNXsPIU0Ng==","_resolved":"/tmp/bc4c6723c1278176d7cb22a6ea46ce24/ekaone-nano-otp-0.1.2.tgz","_from":"file:ekaone-nano-otp-0.1.2.tgz","_nodeVersion":"24.14.0","_npmVersion":"11.11.1","dist":{"integrity":"sha512-f9/fUCIPDB8cX2sP8pUVRgCZRKk+Uo6tjT3eK85C2ZxBIJMbakVmkNwZbiUeZXdxDQCsBhi9nlqNJNXsPIU0Ng==","shasum":"85678a32c889ed1080e45d6a45c1ba69147b1526","tarball":"https://registry.npmjs.org/@ekaone/nano-otp/-/nano-otp-0.1.2.tgz","fileCount":9,"unpackedSize":30468,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@ekaone%2fnano-otp@0.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDQZgEz8f+lekp6WcVB/1RItINElWllxrZxcS7JKdmYqgIgUZaTjJ+a9Nsk6pLTzORQUBlrEWGogeSAgKAg+NPefv4="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:0a9372dc-b267-4c80-b08f-da7de9d76ae1"}},"directories":{},"maintainers":[{"name":"ekaone","email":"ekaone3033@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/nano-otp_0.1.2_1773740178860_0.24610317368628754"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-17T06:20:31.005Z","modified":"2026-03-17T09:36:19.281Z","0.0.1":"2026-03-17T06:20:31.253Z","0.1.2":"2026-03-17T09:36:19.001Z"},"bugs":{"url":"https://github.com/ekaone/nano-otp/issues"},"author":{"name":"Eka Prasetia","email":"ekaone3033@gmail.com","url":"https://prasetia.me"},"license":"MIT","homepage":"https://github.com/ekaone/nano-otp#readme","keywords":["otp","one-time-password","two-factor-authentication","2fa","totp","authentication","security","crypto","typescript","zero-dependency"],"repository":{"type":"git","url":"git+https://github.com/ekaone/nano-otp.git"},"description":"A lightweight, zero-dependency TypeScript library for generating and validating OTPs","maintainers":[{"name":"ekaone","email":"ekaone3033@gmail.com"}],"readme":"# @ekaone/nano-otp\n\nA tiny, zero-dependency One-Time Password (OTP) library for JavaScript and TypeScript.\n\n**@ekaone/nano-otp** is designed to be lightweight, secure, and easy to integrate into modern applications. It uses Node.js built-in `crypto` for cryptographically secure random generation — no external dependencies required.\n\n> ⚠️ **Status: Under Active Development**\n> APIs and features may change before v1.0.0.\n> Track progress at: https://github.com/ekaone/nano-otp\n\n---\n\n## Installation\n\n```bash\n# pnpm\npnpm add @ekaone/nano-otp\n\n# npm\nnpm install @ekaone/nano-otp\n\n# yarn\nyarn add @ekaone/nano-otp\n\n# bun\nbun add @ekaone/nano-otp\n```\n\n---\n\n## Usage\n\n### Generate an OTP\n\n```javascript\nimport { generate } from \"@ekaone/nano-otp\";\n\n// default: 6-digit numeric OTP\nconst otp = generate();\nconsole.log(otp);\n// => \"482910\"\n\n// custom length\nconst otp8 = generate({ length: 8 });\nconsole.log(otp8);\n// => \"30571846\"\n\n// alpha charset (uppercase letters)\nconst otpAlpha = generate({ charset: \"alpha\" });\nconsole.log(otpAlpha);\n// => \"KXRTBM\"\n\n// alphanumeric charset\nconst otpAlphaNum = generate({ charset: \"alphanumeric\", length: 8 });\nconsole.log(otpAlphaNum);\n// => \"4BX9K2RZ\"\n\n// custom charset\nconst otpCustom = generate({ charset: \"ABCDEF0123\", length: 6 });\nconsole.log(otpCustom);\n// => \"C3A0FB\"\n```\n\n### Verify an OTP\n\n```javascript\nimport { verify } from \"@ekaone/nano-otp\";\n\n// basic match\nconst isValid = verify({ input: \"482910\", code: \"482910\" });\nconsole.log(isValid);\n// => true\n\n// mismatch\nconst isInvalid = verify({ input: \"000000\", code: \"482910\" });\nconsole.log(isInvalid);\n// => false\n\n// with expiry (expiresAt is a Unix timestamp in milliseconds)\nconst isValidWithExpiry = verify({\n  input: \"482910\",\n  code: \"482910\",\n  expiresAt: Date.now() + 5 * 60 * 1000, // expires in 5 minutes\n});\nconsole.log(isValidWithExpiry);\n// => true\n\n// expired OTP\nconst isExpired = verify({\n  input: \"482910\",\n  code: \"482910\",\n  expiresAt: Date.now() - 1000, // already expired\n});\nconsole.log(isExpired);\n// => false\n```\n\n### Generate a batch of OTPs\n\n```javascript\nimport { batch } from \"@ekaone/nano-otp\";\n\n// generate 5 OTPs (may contain duplicates)\nconst otps = batch(5);\nconsole.log(otps);\n// => [\"482910\", \"039471\", \"182930\", \"482910\", \"748201\"]\n\n// with options\nconst otpsAlpha = batch(3, { charset: \"alpha\", length: 8 });\nconsole.log(otpsAlpha);\n// => [\"KXRTBMQZ\", \"LNVWACPD\", \"ERJHMKXB\"]\n```\n\n### Generate a batch of unique OTPs\n\n```javascript\nimport { batchUnique } from \"@ekaone/nano-otp\";\n\n// generate 5 guaranteed-unique OTPs\nconst uniqueOtps = batchUnique(5);\nconsole.log(uniqueOtps);\n// => [\"482910\", \"039471\", \"182930\", \"748201\", \"910284\"]\n```\n\n### Full workflow example\n\n```javascript\nimport { generate, verify } from \"@ekaone/nano-otp\";\n\n// 1. generate and store OTP with a 10-minute expiry\nconst code = generate({ length: 6 });\nconst expiresAt = Date.now() + 10 * 60 * 1000;\n\nconsole.log(\"Send this code to the user:\", code);\n// => \"Send this code to the user: 482910\"\n\n// 2. later, verify what the user submitted\nconst userInput = \"482910\"; // submitted by user\n\nconst result = verify({ input: userInput, code, expiresAt });\nconsole.log(\"Verification passed:\", result);\n// => \"Verification passed: true\"\n```\n\n---\n\n## API\n\n### `generate(options?)`\n\nGenerates a single cryptographically secure OTP.\n\n```ts\ngenerate(options?: OTPOptions): string\n```\n\n| Option    | Type                                      | Default     | Description                              |\n| --------- | ----------------------------------------- | ----------- | ---------------------------------------- |\n| `length`  | `number`                                  | `6`         | Length of the generated OTP              |\n| `charset` | `\"numeric\" \\| \"alpha\" \\| \"alphanumeric\" \\| string` | `\"numeric\"` | Character set to use, or a custom string |\n\n---\n\n### `verify(options)`\n\nVerifies an OTP using constant-time comparison to prevent timing attacks.\n\n```ts\nverify(options: VerifyOptions): boolean\n```\n\n| Option      | Type     | Required | Description                                              |\n| ----------- | -------- | -------- | -------------------------------------------------------- |\n| `input`     | `string` | ✅       | The OTP submitted by the user                            |\n| `code`      | `string` | ✅       | The original generated OTP to compare against            |\n| `expiresAt` | `number` | —        | Unix timestamp (ms) after which the OTP is invalid       |\n| `now`       | `number` | —        | Override current time (ms) — useful for testing          |\n\n---\n\n### `batch(count, options?)`\n\nGenerates multiple OTPs. May contain duplicates.\n\n```ts\nbatch(count: number, options?: OTPOptions): string[]\n```\n\n| Parameter | Type        | Description                        |\n| --------- | ----------- | ---------------------------------- |\n| `count`   | `number`    | Number of OTPs to generate         |\n| `options` | `OTPOptions`| Same options as `generate`         |\n\n---\n\n### `batchUnique(count, options?)`\n\nGenerates multiple guaranteed-unique OTPs. Throws if the requested count exceeds the number of possible combinations for the given charset and length.\n\n```ts\nbatchUnique(count: number, options?: OTPOptions): string[]\n```\n\n| Parameter | Type         | Description                        |\n| --------- | ------------ | ---------------------------------- |\n| `count`   | `number`     | Number of unique OTPs to generate  |\n| `options` | `OTPOptions` | Same options as `generate`         |\n\n---\n\n## Types\n\n```ts\ninterface OTPOptions {\n  length?: number;\n  charset?: \"numeric\" | \"alpha\" | \"alphanumeric\" | string;\n}\n\ninterface VerifyOptions {\n  input: string;\n  code: string;\n  expiresAt?: number;\n  now?: number;\n}\n```\n\n---\n\n## Security\n\n- Random generation uses Node.js `crypto.randomInt` — cryptographically secure, not `Math.random()`\n- `verify` uses constant-time comparison (XOR-based) to prevent timing attacks\n- Zero runtime dependencies — no supply chain risk from third-party packages\n\n---\n\n## License\n\nMIT © Eka Prasetia — see [LICENSE](./LICENSE) for details.\n\n---\n\n## Links\n\n- [NPM Package](https://www.npmjs.com/package/@ekaone/nano-otp)\n- [GitHub Repository](https://github.com/ekaone/nano-otp)\n- [Issue Tracker](https://github.com/ekaone/nano-otp/issues)","readmeFilename":"README.md"}