@otplib/core 13.1.0 → 13.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/metafile-cjs.json +1 -1
- package/dist/metafile-esm.json +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/utils.ts","../src/crypto-context.ts","../src/base32-context.ts","../src/utility-types.ts"],"sourcesContent":["export type {\n Base32EncodeOptions,\n Base32Plugin,\n CryptoPlugin,\n Digits,\n HashAlgorithm,\n SecretOptions,\n OTPResult,\n OTPResultOk,\n OTPResultError,\n} from \"./types.js\";\n\nexport type { OTPGuardrailsConfig, OTPGuardrails } from \"./utils.js\";\n\nexport {\n OTPError,\n SecretError,\n SecretTooShortError,\n SecretTooLongError,\n CounterError,\n CounterNegativeError,\n CounterOverflowError,\n TimeError,\n TimeNegativeError,\n PeriodError,\n PeriodTooSmallError,\n PeriodTooLargeError,\n DigitsError,\n AlgorithmError,\n TokenError,\n TokenLengthError,\n TokenFormatError,\n CryptoError,\n HMACError,\n RandomBytesError,\n Base32Error,\n Base32EncodeError,\n Base32DecodeError,\n CounterToleranceError,\n CounterToleranceTooLargeError,\n EpochToleranceError,\n EpochToleranceNegativeError,\n EpochToleranceTooLargeError,\n PluginError,\n CryptoPluginMissingError,\n Base32PluginMissingError,\n ConfigurationError,\n SecretMissingError,\n LabelMissingError,\n IssuerMissingError,\n SecretTypeError,\n type OTPErrorOptions,\n} from \"./errors.js\";\n\nexport {\n createGuardrails,\n hasGuardrailOverrides,\n validateSecret,\n validateCounter,\n validateTime,\n validatePeriod,\n validateToken,\n validateCounterTolerance,\n validateEpochTolerance,\n counterToBytes,\n dynamicTruncate,\n truncateDigits,\n validateByteLengthEqual,\n constantTimeEqual,\n getDigestSize,\n stringToBytes,\n hexToBytes,\n normalizeSecret,\n normalizeCounterTolerance,\n normalizeEpochTolerance,\n generateSecret,\n requireCryptoPlugin,\n requireBase32Plugin,\n requireSecret,\n requireLabel,\n requireIssuer,\n requireBase32String,\n wrapResult,\n wrapResultAsync,\n MIN_SECRET_BYTES,\n MAX_SECRET_BYTES,\n RECOMMENDED_SECRET_BYTES,\n MIN_PERIOD,\n MAX_PERIOD,\n DEFAULT_PERIOD,\n MAX_COUNTER,\n MAX_WINDOW,\n} from \"./utils.js\";\n\nexport { CryptoContext, createCryptoContext } from \"./crypto-context.js\";\n\nexport { Base32Context, createBase32Context } from \"./base32-context.js\";\n\n// Utility types for enhanced developer experience\nexport type {\n Brand,\n Base32Secret,\n OTPToken,\n RequireKeys,\n OptionalKeys,\n PluginConfig,\n WithRequiredPlugins,\n GenerationReady,\n NarrowBy,\n} from \"./utility-types.js\";\n\nexport { hasPlugins, hasCrypto, hasBase32 } from \"./utility-types.js\";\n","/**\n * Options for OTPError construction\n */\nexport type OTPErrorOptions = {\n /**\n * The underlying error that caused this error.\n * Useful for error chaining and debugging.\n */\n cause?: unknown;\n};\n\n/**\n * Base error class for all otplib errors\n *\n * Supports ES2022 error chaining via the `cause` property.\n *\n * @example\n * ```typescript\n * try {\n * // ... operation that throws\n * } catch (error) {\n * throw new OTPError('Operation failed', { cause: error });\n * }\n * ```\n */\nexport class OTPError extends Error {\n constructor(message: string, options?: OTPErrorOptions) {\n super(message, options);\n this.name = \"OTPError\";\n }\n}\n\n/**\n * Error thrown when secret validation fails\n */\nexport class SecretError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"SecretError\";\n }\n}\n\n/**\n * Error thrown when secret is too short (< 128 bits)\n */\nexport class SecretTooShortError extends SecretError {\n constructor(minBytes: number, actualBytes: number) {\n super(\n `Secret must be at least ${minBytes} bytes (${minBytes * 8} bits), got ${actualBytes} bytes`,\n );\n this.name = \"SecretTooShortError\";\n }\n}\n\n/**\n * Error thrown when secret is unreasonably large (> 64 bytes)\n */\nexport class SecretTooLongError extends SecretError {\n constructor(maxBytes: number, actualBytes: number) {\n super(`Secret must not exceed ${maxBytes} bytes, got ${actualBytes} bytes`);\n this.name = \"SecretTooLongError\";\n }\n}\n\n/**\n * Error thrown when counter is invalid\n */\nexport class CounterError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"CounterError\";\n }\n}\n\n/**\n * Error thrown when counter is negative\n */\nexport class CounterNegativeError extends CounterError {\n constructor() {\n super(\"Counter must be non-negative\");\n this.name = \"CounterNegativeError\";\n }\n}\n\n/**\n * Error thrown when counter exceeds maximum value (2^53 - 1 for safe integer)\n */\nexport class CounterOverflowError extends CounterError {\n constructor() {\n super(\"Counter exceeds maximum safe integer value\");\n this.name = \"CounterOverflowError\";\n }\n}\n\n/**\n * Error thrown when time is invalid\n */\nexport class TimeError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"TimeError\";\n }\n}\n\n/**\n * Error thrown when time is negative\n */\nexport class TimeNegativeError extends TimeError {\n constructor() {\n super(\"Time must be non-negative\");\n this.name = \"TimeNegativeError\";\n }\n}\n\n/**\n * Error thrown when period is invalid\n */\nexport class PeriodError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"PeriodError\";\n }\n}\n\n/**\n * Error thrown when period is too small\n */\nexport class PeriodTooSmallError extends PeriodError {\n constructor(minPeriod: number) {\n super(`Period must be at least ${minPeriod} second(s)`);\n this.name = \"PeriodTooSmallError\";\n }\n}\n\n/**\n * Error thrown when period is too large\n */\nexport class PeriodTooLargeError extends PeriodError {\n constructor(maxPeriod: number) {\n super(`Period must not exceed ${maxPeriod} seconds`);\n this.name = \"PeriodTooLargeError\";\n }\n}\n\n/**\n * Error thrown when digits value is invalid\n */\nexport class DigitsError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"DigitsError\";\n }\n}\n\n/**\n * Error thrown when hash algorithm is invalid\n */\nexport class AlgorithmError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"AlgorithmError\";\n }\n}\n\n/**\n * Error thrown when token is invalid\n */\nexport class TokenError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"TokenError\";\n }\n}\n\n/**\n * Error thrown when token has incorrect length\n */\nexport class TokenLengthError extends TokenError {\n constructor(expected: number, actual: number) {\n super(`Token must be ${expected} digits, got ${actual}`);\n this.name = \"TokenLengthError\";\n }\n}\n\n/**\n * Error thrown when token contains non-digit characters\n */\nexport class TokenFormatError extends TokenError {\n constructor() {\n super(\"Token must contain only digits\");\n this.name = \"TokenFormatError\";\n }\n}\n\n/**\n * Error thrown when crypto operation fails\n */\nexport class CryptoError extends OTPError {\n constructor(message: string, options?: OTPErrorOptions) {\n super(message, options);\n this.name = \"CryptoError\";\n }\n}\n\n/**\n * Error thrown when HMAC computation fails\n *\n * The original error from the crypto plugin is available via `cause`.\n *\n * @example\n * ```typescript\n * try {\n * await cryptoContext.hmac('sha1', key, data);\n * } catch (error) {\n * if (error instanceof HMACError) {\n * console.log('HMAC failed:', error.message);\n * console.log('Original error:', error.cause);\n * }\n * }\n * ```\n */\nexport class HMACError extends CryptoError {\n constructor(message: string, options?: OTPErrorOptions) {\n super(`HMAC computation failed: ${message}`, options);\n this.name = \"HMACError\";\n }\n}\n\n/**\n * Error thrown when random byte generation fails\n *\n * The original error from the crypto plugin is available via `cause`.\n */\nexport class RandomBytesError extends CryptoError {\n constructor(message: string, options?: OTPErrorOptions) {\n super(`Random byte generation failed: ${message}`, options);\n this.name = \"RandomBytesError\";\n }\n}\n\n/**\n * Error thrown when Base32 operation fails\n */\nexport class Base32Error extends OTPError {\n constructor(message: string, options?: OTPErrorOptions) {\n super(message, options);\n this.name = \"Base32Error\";\n }\n}\n\n/**\n * Error thrown when Base32 encoding fails\n *\n * The original error from the Base32 plugin is available via `cause`.\n *\n * @example\n * ```typescript\n * try {\n * base32Context.encode(data);\n * } catch (error) {\n * if (error instanceof Base32EncodeError) {\n * console.log('Encoding failed:', error.message);\n * console.log('Original error:', error.cause);\n * }\n * }\n * ```\n */\nexport class Base32EncodeError extends Base32Error {\n constructor(message: string, options?: OTPErrorOptions) {\n super(`Base32 encoding failed: ${message}`, options);\n this.name = \"Base32EncodeError\";\n }\n}\n\n/**\n * Error thrown when Base32 decoding fails\n *\n * The original error from the Base32 plugin is available via `cause`.\n *\n * @example\n * ```typescript\n * try {\n * base32Context.decode(invalidString);\n * } catch (error) {\n * if (error instanceof Base32DecodeError) {\n * console.log('Decoding failed:', error.message);\n * console.log('Original error:', error.cause);\n * }\n * }\n * ```\n */\nexport class Base32DecodeError extends Base32Error {\n constructor(message: string, options?: OTPErrorOptions) {\n super(`Base32 decoding failed: ${message}`, options);\n this.name = \"Base32DecodeError\";\n }\n}\n\n/**\n * Error thrown when counter tolerance is invalid\n */\nexport class CounterToleranceError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"CounterToleranceError\";\n }\n}\n\n/**\n * Error thrown when counter tolerance is too large\n */\nexport class CounterToleranceTooLargeError extends CounterToleranceError {\n constructor(maxTolerance: number, actualSize: number) {\n super(\n `Counter tolerance size must not exceed ${maxTolerance}, got ${actualSize}. ` +\n `Large tolerances can cause performance issues.`,\n );\n this.name = \"CounterToleranceTooLargeError\";\n }\n}\n\n/**\n * Error thrown when epoch tolerance is invalid\n */\nexport class EpochToleranceError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"EpochToleranceError\";\n }\n}\n\n/**\n * Error thrown when epoch tolerance contains negative values\n */\nexport class EpochToleranceNegativeError extends EpochToleranceError {\n constructor() {\n super(\"Epoch tolerance cannot contain negative values\");\n this.name = \"EpochToleranceNegativeError\";\n }\n}\n\n/**\n * Error thrown when epoch tolerance is too large\n */\nexport class EpochToleranceTooLargeError extends EpochToleranceError {\n constructor(maxTolerance: number, actualValue: number) {\n super(\n `Epoch tolerance must not exceed ${maxTolerance} seconds, got ${actualValue}. ` +\n `Large tolerances can cause performance issues.`,\n );\n this.name = \"EpochToleranceTooLargeError\";\n }\n}\n\n/**\n * Error thrown when a required plugin is missing\n */\nexport class PluginError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"PluginError\";\n }\n}\n\n/**\n * Error thrown when crypto plugin is not configured\n */\nexport class CryptoPluginMissingError extends PluginError {\n constructor() {\n super(\"Crypto plugin is required.\");\n this.name = \"CryptoPluginMissingError\";\n }\n}\n\n/**\n * Error thrown when Base32 plugin is not configured\n */\nexport class Base32PluginMissingError extends PluginError {\n constructor() {\n super(\"Base32 plugin is required.\");\n this.name = \"Base32PluginMissingError\";\n }\n}\n\n/**\n * Error thrown when required configuration is missing\n */\nexport class ConfigurationError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigurationError\";\n }\n}\n\n/**\n * Error thrown when secret is not configured\n */\nexport class SecretMissingError extends ConfigurationError {\n constructor() {\n super(\n \"Secret is required. \" +\n \"Use generateSecret() to create one, or provide via { secret: 'YOUR_BASE32_SECRET' }\",\n );\n this.name = \"SecretMissingError\";\n }\n}\n\n/**\n * Error thrown when label is not configured (required for URI generation)\n */\nexport class LabelMissingError extends ConfigurationError {\n constructor() {\n super(\"Label is required for URI generation. Example: { label: 'user@example.com' }\");\n this.name = \"LabelMissingError\";\n }\n}\n\n/**\n * Error thrown when issuer is not configured (required for URI generation)\n */\nexport class IssuerMissingError extends ConfigurationError {\n constructor() {\n super(\"Issuer is required for URI generation. Example: { issuer: 'MyApp' }\");\n this.name = \"IssuerMissingError\";\n }\n}\n\n/**\n * Error thrown when secret must be a Base32 string but is provided as bytes\n */\nexport class SecretTypeError extends ConfigurationError {\n constructor() {\n super(\n \"Class API requires secret to be a Base32 string, not Uint8Array. \" +\n \"Use generateSecret() or provide a Base32-encoded string.\",\n );\n this.name = \"SecretTypeError\";\n }\n}\n","import {\n OTPError,\n SecretTooShortError,\n SecretTooLongError,\n CounterNegativeError,\n CounterOverflowError,\n TimeNegativeError,\n PeriodTooSmallError,\n PeriodTooLargeError,\n TokenLengthError,\n TokenFormatError,\n CounterToleranceTooLargeError,\n EpochToleranceNegativeError,\n EpochToleranceTooLargeError,\n CryptoPluginMissingError,\n Base32PluginMissingError,\n SecretMissingError,\n LabelMissingError,\n IssuerMissingError,\n SecretTypeError,\n} from \"./errors.js\";\n\nimport type {\n HashAlgorithm,\n SecretOptions,\n OTPResultOk,\n OTPResultError,\n OTPResult,\n} from \"./types.js\";\n\n/**\n * Singleton TextEncoder instance to avoid repeated allocations\n */\nconst textEncoder = new TextEncoder();\n\n/**\n * Minimum secret length in bytes (128 bits as per RFC 4226)\n */\nexport const MIN_SECRET_BYTES = 16;\n\n/**\n * Maximum secret length in bytes (512 bits)\n *\n * The 64-byte maximum is not part of the RFCs.\n * This is to prevent excessive memory usage in HMAC operations.\n */\nexport const MAX_SECRET_BYTES = 64;\n\n/**\n * Recommended secret length in bytes (160 bits as per RFC 4226)\n */\nexport const RECOMMENDED_SECRET_BYTES = 20;\n\n/**\n * Minimum period in seconds\n */\nexport const MIN_PERIOD = 1;\n\n/**\n * Maximum period in seconds (1 hour)\n */\nexport const MAX_PERIOD = 3600;\n\n/**\n * Default period in seconds (30 seconds as per RFC 6238)\n */\nexport const DEFAULT_PERIOD = 30;\n\n/**\n * Maximum safe integer for counter (2^53 - 1)\n */\nexport const MAX_COUNTER = Number.MAX_SAFE_INTEGER;\n\n/**\n * Maximum verification window size\n *\n * Limits the number of HMAC computations during verification to prevent DoS attacks.\n * A window of 100 means up to 201 HMAC computations ([-100, +100] range).\n *\n * For TOTP: window=1 is typically sufficient (allows +-30 seconds clock drift)\n * For HOTP: window=10-50 handles reasonable counter desynchronization\n */\nexport const MAX_WINDOW = 100;\n\n/**\n * Configurable guardrails for OTP validation\n *\n * Allows overriding default safety limits for non-standard production requirements.\n * Use with caution - custom guardrails can weaken security.\n */\nexport type OTPGuardrailsConfig = {\n MIN_SECRET_BYTES: number;\n MAX_SECRET_BYTES: number;\n MIN_PERIOD: number;\n MAX_PERIOD: number;\n MAX_COUNTER: number;\n MAX_WINDOW: number;\n};\n\n/**\n * Module-private symbol to track guardrail override status\n *\n * This symbol is used as a property key to store whether guardrails contain custom values.\n * Being module-private and a symbol ensures:\n * - Cannot be accessed outside this module (not exported)\n * - Cannot be recreated (each Symbol() call is unique)\n * - Hidden from normal enumeration (Object.keys, JSON.stringify, for-in)\n * - Minimal memory overhead (~1 byte per object)\n * - No garbage collection concerns\n *\n * @internal\n */\nconst OVERRIDE_SYMBOL = Symbol(\"otplib.guardrails.override\");\n\n/**\n * Complete guardrails configuration\n *\n * This represents the final, immutable configuration used by validation functions.\n * Internally tracks whether any values were overridden from RFC recommendations,\n * enabling security auditing and compliance monitoring without exposing implementation\n * details in the public API.\n *\n * The override status is stored using a module-private Symbol that cannot be accessed\n * or recreated outside this module, providing true encapsulation.\n *\n * @see {@link OTPGuardrailsConfig} for the base configuration structure\n * @see {@link createGuardrails} for creating guardrails instances\n * @see {@link hasGuardrailOverrides} to check if guardrails were customized\n */\nexport type OTPGuardrails = Readonly<OTPGuardrailsConfig> & {\n [OVERRIDE_SYMBOL]?: boolean;\n};\n\n/**\n * Default guardrails matching RFC recommendations\n *\n * Frozen to ensure immutability. Used as default parameter for validation functions.\n * For custom guardrails, use the createGuardrails() factory function.\n */\nconst DEFAULT_GUARDRAILS: OTPGuardrails = Object.freeze({\n MIN_SECRET_BYTES,\n MAX_SECRET_BYTES,\n MIN_PERIOD,\n MAX_PERIOD,\n MAX_COUNTER,\n MAX_WINDOW,\n [OVERRIDE_SYMBOL]: false,\n});\n\n/**\n * Create guardrails configuration object\n *\n * Factory function that merges custom guardrails with defaults and returns\n * an immutable (frozen) object. Validates custom guardrails to ensure they\n * maintain basic safety invariants.\n *\n * When called without arguments or with `undefined`, returns the default guardrails\n * singleton (optimized to avoid unnecessary allocations). When called with custom\n * values, creates a new frozen object and internally marks it as overridden.\n *\n * @param custom - Optional partial guardrails to override defaults\n * @returns Frozen guardrails object\n * @throws {Error} If custom guardrails violate safety invariants\n *\n * @example Basic usage\n * ```ts\n * import { createGuardrails, hasGuardrailOverrides } from '@otplib/core'\n *\n * // Returns default singleton (no overrides)\n * const defaults = createGuardrails();\n * hasGuardrailOverrides(defaults); // false\n *\n * // Creates new object with overrides\n * const custom = createGuardrails({\n * MIN_SECRET_BYTES: 8,\n * MAX_WINDOW: 200\n * });\n * hasGuardrailOverrides(custom); // true\n * ```\n *\n * @example Monitoring custom guardrails\n * ```ts\n * import { createGuardrails, hasGuardrailOverrides } from '@otplib/core';\n *\n * const guardrails = createGuardrails({ MAX_WINDOW: 20 });\n *\n * if (hasGuardrailOverrides(guardrails)) {\n * logger.warn('Non-default guardrails in use', { guardrails });\n * }\n * ```\n *\n * @see {@link hasGuardrailOverrides} to check if guardrails were customized\n */\nexport function createGuardrails(custom?: Partial<OTPGuardrailsConfig>): OTPGuardrails {\n if (!custom) {\n return DEFAULT_GUARDRAILS;\n }\n\n return Object.freeze({\n ...DEFAULT_GUARDRAILS,\n ...custom,\n [OVERRIDE_SYMBOL]: true,\n });\n}\n\n/**\n * Check if guardrails contain custom overrides\n *\n * Returns `true` if the guardrails object was created with custom values,\n * `false` if using RFC-recommended defaults. Useful for security auditing,\n * compliance monitoring, and development warnings.\n *\n * This function accesses a module-private Symbol property that cannot be\n * accessed or modified outside this module, ensuring reliable detection.\n *\n * @param guardrails - The guardrails object to check\n * @returns `true` if guardrails were customized, `false` if using defaults\n *\n * @example Security monitoring\n * ```ts\n * import { createGuardrails, hasGuardrailOverrides } from '@otplib/core';\n *\n * const guardrails = createGuardrails({ MAX_WINDOW: 20 });\n *\n * if (hasGuardrailOverrides(guardrails)) {\n * console.warn('Custom guardrails detected:', guardrails);\n * // Log to security audit system\n * }\n * ```\n *\n * @example Compliance check\n * ```ts\n * function validateGuardrails(guardrails: OTPGuardrails) {\n * if (hasGuardrailOverrides(guardrails)) {\n * throw new Error('Custom guardrails not allowed in production');\n * }\n * }\n * ```\n */\nexport function hasGuardrailOverrides(guardrails: OTPGuardrails): boolean {\n return guardrails[OVERRIDE_SYMBOL] ?? false;\n}\n\n/**\n * Validate secret key\n *\n * @param secret - The secret to validate\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {SecretTooShortError} If secret is too short\n * @throws {SecretTooLongError} If secret is too long\n */\nexport function validateSecret(\n secret: Uint8Array,\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n if (secret.length < guardrails.MIN_SECRET_BYTES) {\n throw new SecretTooShortError(guardrails.MIN_SECRET_BYTES, secret.length);\n }\n\n if (secret.length > guardrails.MAX_SECRET_BYTES) {\n throw new SecretTooLongError(guardrails.MAX_SECRET_BYTES, secret.length);\n }\n}\n\n/**\n * Validate counter value\n *\n * @param counter - The counter to validate\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {CounterNegativeError} If counter is negative\n * @throws {CounterOverflowError} If counter exceeds safe integer\n */\nexport function validateCounter(\n counter: number | bigint,\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n const value = typeof counter === \"bigint\" ? counter : BigInt(counter);\n\n if (value < 0n) {\n throw new CounterNegativeError();\n }\n\n if (value > BigInt(guardrails.MAX_COUNTER)) {\n throw new CounterOverflowError();\n }\n}\n\n/**\n * Validate time value\n *\n * @param time - The time in seconds to validate\n * @throws {TimeNegativeError} If time is negative\n */\nexport function validateTime(time: number): void {\n if (time < 0) {\n throw new TimeNegativeError();\n }\n}\n\n/**\n * Validate period value\n *\n * @param period - The period in seconds to validate\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {PeriodTooSmallError} If period is too small\n * @throws {PeriodTooLargeError} If period is too large\n */\nexport function validatePeriod(\n period: number,\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n if (!Number.isInteger(period) || period < guardrails.MIN_PERIOD) {\n throw new PeriodTooSmallError(guardrails.MIN_PERIOD);\n }\n\n if (period > guardrails.MAX_PERIOD) {\n throw new PeriodTooLargeError(guardrails.MAX_PERIOD);\n }\n}\n\n/**\n * Validate token\n *\n * @param token - The token string to validate\n * @param digits - Expected number of digits\n * @throws {TokenLengthError} If token has incorrect length\n * @throws {TokenFormatError} If token contains non-digit characters\n */\nexport function validateToken(token: string, digits: number): void {\n if (token.length !== digits) {\n throw new TokenLengthError(digits, token.length);\n }\n\n if (!/^\\d+$/.test(token)) {\n throw new TokenFormatError();\n }\n}\n\n/**\n * Validate counter tolerance for HOTP verification\n *\n * Prevents DoS attacks by limiting the number of counter values checked.\n *\n * @param counterTolerance - Counter tolerance specification (number or array of offsets)\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {CounterToleranceTooLargeError} If tolerance size exceeds MAX_WINDOW\n *\n * @example\n * ```ts\n * validateCounterTolerance(1); // OK: 3 offsets [-1, 0, 1]\n * validateCounterTolerance(100); // OK: 201 offsets [-100, ..., 100]\n * validateCounterTolerance(101); // Throws: exceeds MAX_WINDOW\n * validateCounterTolerance([0, 1]); // OK: 2 offsets\n * ```\n */\nexport function validateCounterTolerance(\n counterTolerance: number | number[],\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n const size = Array.isArray(counterTolerance) ? counterTolerance.length : counterTolerance * 2 + 1;\n\n if (size > guardrails.MAX_WINDOW * 2 + 1) {\n throw new CounterToleranceTooLargeError(\n guardrails.MAX_WINDOW,\n Array.isArray(counterTolerance) ? counterTolerance.length : counterTolerance,\n );\n }\n}\n\n/**\n * Validate epoch tolerance for TOTP verification\n *\n * Prevents DoS attacks by limiting the time range checked.\n * Also validates that tolerance values are non-negative.\n *\n * @param epochTolerance - Epoch tolerance specification (number or tuple [past, future])\n * @param period - The TOTP period in seconds (default: 30). Used to calculate max tolerance.\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {EpochToleranceNegativeError} If tolerance contains negative values\n * @throws {EpochToleranceTooLargeError} If tolerance exceeds MAX_WINDOW periods\n *\n * @example\n * ```ts\n * validateEpochTolerance(30); // OK: 30 seconds (default period 30s)\n * validateEpochTolerance([5, 0]); // OK: 5 seconds past only\n * validateEpochTolerance([-5, 0]); // Throws: negative values not allowed\n * validateEpochTolerance(3600); // Throws: exceeds MAX_WINDOW * period\n * validateEpochTolerance(6000, 60); // OK with 60s period (MAX_WINDOW * 60 = 6000)\n * ```\n */\nexport function validateEpochTolerance(\n epochTolerance: number | [number, number],\n period: number = DEFAULT_PERIOD,\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n const [pastTolerance, futureTolerance] = Array.isArray(epochTolerance)\n ? epochTolerance\n : [epochTolerance, epochTolerance];\n\n // Check for negative values\n if (pastTolerance < 0 || futureTolerance < 0) {\n throw new EpochToleranceNegativeError();\n }\n\n // Check total tolerance doesn't exceed reasonable limits\n // Convert to periods and check against MAX_WINDOW\n const maxToleranceSeconds = guardrails.MAX_WINDOW * period;\n const maxAllowed = Math.max(pastTolerance, futureTolerance);\n\n if (maxAllowed > maxToleranceSeconds) {\n throw new EpochToleranceTooLargeError(maxToleranceSeconds, maxAllowed);\n }\n}\n\n/**\n * Convert counter to 8-byte big-endian array\n *\n * Per RFC 4226 Section 5.1, the counter value is represented as an 8-byte\n * big-endian (network byte order) unsigned integer.\n *\n * @see {@link https://tools.ietf.org/html/rfc4226#section-5.1 | RFC 4226 Section 5.1 - Symbol Descriptions}\n *\n * @param value - The counter value to convert\n * @returns 8-byte big-endian array\n */\nexport function counterToBytes(value: number | bigint): Uint8Array {\n const bigintValue = typeof value === \"bigint\" ? value : BigInt(value);\n const buffer = new ArrayBuffer(8);\n const view = new DataView(buffer);\n\n view.setBigUint64(0, bigintValue, false);\n\n return new Uint8Array(buffer);\n}\n\n/**\n * Perform Dynamic Truncation as per RFC 4226 Section 5.3\n *\n * The algorithm:\n * 1. Take the low-order 4 bits of the last byte as offset\n * 2. Extract 4 bytes starting at offset\n * 3. Mask the most significant bit to get a 31-bit unsigned integer\n *\n * This ensures consistent extraction across different HMAC output sizes\n * while producing a value that fits in a signed 32-bit integer.\n *\n * @see {@link https://tools.ietf.org/html/rfc4226#section-5.3 | RFC 4226 Section 5.3 - Generating an HOTP Value}\n *\n * @param hmacResult - HMAC result (at least 20 bytes for SHA-1)\n * @returns Truncated 31-bit unsigned integer\n */\nexport function dynamicTruncate(hmacResult: Uint8Array): number {\n const offset = hmacResult[hmacResult.length - 1] & 0x0f;\n\n const binary =\n ((hmacResult[offset] & 0x7f) << 24) |\n (hmacResult[offset + 1] << 16) |\n (hmacResult[offset + 2] << 8) |\n hmacResult[offset + 3];\n\n return binary;\n}\n\n/**\n * Convert truncated integer to OTP string with specified digits\n *\n * Computes: Snum mod 10^Digit (RFC 4226 Section 5.3)\n *\n * The result is zero-padded to ensure consistent length,\n * as required for proper token comparison.\n *\n * @see {@link https://tools.ietf.org/html/rfc4226#section-5.3 | RFC 4226 Section 5.3 - Generating an HOTP Value}\n *\n * @param value - The truncated integer value (Snum)\n * @param digits - Number of digits for the OTP (Digit, typically 6-8)\n * @returns OTP string with leading zeros if necessary\n */\nexport function truncateDigits(value: number, digits: number): string {\n const maxOtp = 10 ** digits;\n const otp = value % maxOtp;\n return otp.toString().padStart(digits, \"0\");\n}\n\n/**\n * Validate that two byte arrays have equal length\n *\n * Useful as a preliminary check before performing byte-by-byte comparisons.\n *\n * @param a - First byte array\n * @param b - Second byte array\n * @returns true if arrays have equal length, false otherwise\n */\nexport function validateByteLengthEqual(a: Uint8Array, b: Uint8Array): boolean {\n return a.length === b.length;\n}\n\n/**\n * Constant-time comparison to prevent timing attacks\n *\n * This implements a timing-safe equality check as recommended in\n * RFC 4226 Section 7.2 for token validation to prevent\n * timing side-channel attacks.\n *\n * @see {@link https://tools.ietf.org/html/rfc4226#section-7.2 | RFC 4226 Section 7.2 - Validation and Verification}\n *\n * @param a - First value to compare\n * @param b - Second value to compare\n * @returns true if values are equal, false otherwise\n */\nexport function constantTimeEqual(a: string | Uint8Array, b: string | Uint8Array): boolean {\n const bufA = stringToBytes(a);\n const bufB = stringToBytes(b);\n\n if (!validateByteLengthEqual(bufA, bufB)) {\n return false;\n }\n\n let result = 0;\n for (let i = 0; i < bufA.length; i++) {\n result |= bufA[i] ^ bufB[i];\n }\n\n return result === 0;\n}\n\n/**\n * Get HMAC digest size in bytes for a given algorithm\n *\n * @param algorithm - The hash algorithm\n * @returns Digest size in bytes\n */\nexport function getDigestSize(algorithm: HashAlgorithm): number {\n switch (algorithm) {\n case \"sha1\":\n return 20;\n case \"sha256\":\n return 32;\n case \"sha512\":\n return 64;\n }\n}\n\n/**\n * Convert a string or Uint8Array to Uint8Array\n *\n * This utility function normalizes input to Uint8Array, converting strings\n * using UTF-8 encoding. Uint8Array inputs are returned as-is.\n *\n * @param value - The value to convert (string or Uint8Array)\n * @returns The value as a Uint8Array (UTF-8 encoded for strings)\n *\n * @example\n * ```ts\n * import { stringToBytes } from '@otplib/core'\n *\n * const bytes1 = stringToBytes('1234567890123456')\n * // Returns: Uint8Array([49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54])\n *\n * const bytes2 = stringToBytes(new Uint8Array([1, 2, 3]))\n * // Returns: Uint8Array([1, 2, 3]) - returned as-is\n * ```\n */\nexport function stringToBytes(value: string | Uint8Array): Uint8Array {\n return typeof value === \"string\" ? textEncoder.encode(value) : value;\n}\n\n/**\n * Convert a hex string to a Uint8Array\n *\n * This is useful for working with RFC test vectors and debugging HMAC outputs,\n * which are commonly represented as hexadecimal strings.\n *\n * If your environment supports it, consider using `Uint8Array.fromHex()` instead.\n *\n * @param hex - The hex string to convert (lowercase or uppercase, no 0x prefix)\n * @returns The bytes as a Uint8Array\n *\n * @example\n * ```ts\n * import { hexToBytes } from '@otplib/core'\n *\n * // Convert RFC 4226 HMAC test vector\n * const hmac = hexToBytes('cc93cf18508d94934c64b65d8ba7667fb7cde4b0')\n * // Returns: Uint8Array([0xcc, 0x93, 0xcf, ...])\n * ```\n */\nexport function hexToBytes(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);\n }\n return bytes;\n}\n\n/**\n * Normalize secret input to Uint8Array\n *\n * Accepts either a Base32-encoded string or Uint8Array and returns Uint8Array.\n * If a Base32Plugin is provided, string secrets will be automatically decoded.\n *\n * @param secret - The secret to normalize (string or Uint8Array)\n * @param base32 - Optional Base32Plugin to decode string secrets\n * @returns The secret as Uint8Array\n * @throws {Error} If secret is a string but no Base32Plugin is provided\n *\n * @example\n * ```ts\n * import { normalizeSecret } from '@otplib/core'\n * import { ScureBase32Plugin } from '@otplib/plugin-base32-scure'\n *\n * const base32 = new ScureBase32Plugin()\n *\n * // Uint8Array - returned as-is\n * const secret1 = normalizeSecret(new Uint8Array([1, 2, 3]))\n *\n * // Base32 string - automatically decoded\n * const secret2 = normalizeSecret('JBSWY3DPEHPK3PXP', base32)\n * ```\n */\nexport function normalizeSecret(\n secret: string | Uint8Array,\n base32?: { decode: (str: string) => Uint8Array },\n): Uint8Array {\n if (typeof secret === \"string\") {\n requireBase32Plugin(base32);\n return base32.decode(secret);\n }\n return secret;\n}\n\n/**\n * Generate a random Base32-encoded secret\n *\n * Creates a cryptographically secure random secret suitable for OTP generation.\n * The default length of 20 bytes (160 bits) matches RFC 4226 recommendations\n * and provides good security margin.\n *\n * @param options - Secret generation options\n * @returns Base32-encoded secret string (without padding for Google Authenticator compatibility)\n *\n * @example\n * ```ts\n * import { generateSecret } from '@otplib/core';\n * import { NodeCryptoPlugin } from '@otplib/plugin-crypto-node';\n * import { ScureBase32Plugin } from '@otplib/plugin-base32-scure';\n *\n * const secret = generateSecret({\n * crypto: new NodeCryptoPlugin(),\n * base32: new ScureBase32Plugin(),\n * });\n * // Returns: 'JBSWY3DPEHPK3PXP...' (32 characters)\n * ```\n *\n * @example Custom length\n * ```ts\n * const secret = generateSecret({\n * crypto: new NodeCryptoPlugin(),\n * base32: new ScureBase32Plugin(),\n * length: 32, // 256 bits for SHA-256\n * });\n * ```\n */\nexport function generateSecret(options: SecretOptions): string {\n const { crypto, base32, length = RECOMMENDED_SECRET_BYTES } = options;\n\n requireCryptoPlugin(crypto);\n requireBase32Plugin(base32);\n\n const randomBytes = crypto.randomBytes(length);\n return base32.encode(randomBytes, { padding: false });\n}\n\n/**\n * Normalize counter tolerance to an array of offsets\n *\n * Converts a number or array counter tolerance specification into an array of offsets\n * - Number: creates symmetric range [-tolerance, +tolerance]\n * - Array: uses the array as-is (already contains specific offsets)\n *\n * @param counterTolerance - Counter tolerance specification (number or array of offsets)\n * @returns Array of offsets to check\n *\n * @example\n * ```ts\n * normalizeCounterTolerance(0) // [0]\n * normalizeCounterTolerance(1) // [-1, 0, 1]\n * normalizeCounterTolerance(2) // [-2, -1, 0, 1, 2]\n * normalizeCounterTolerance([0, 1]) // [0, 1]\n * normalizeCounterTolerance([-1, 0, 1]) // [-1, 0, 1]\n * ```\n */\nexport function normalizeCounterTolerance(counterTolerance: number | number[] = 0): number[] {\n if (Array.isArray(counterTolerance)) {\n return counterTolerance;\n }\n\n const result: number[] = [];\n for (let i = -counterTolerance; i <= counterTolerance; i++) {\n // Bitwise OR with 0 converts -0 to 0 and preserves other integers\n result.push(i | 0);\n }\n return result;\n}\n\n/**\n * Normalize epoch tolerance to [past, future] tuple\n *\n * Converts a number or tuple epoch tolerance specification into a [past, future] tuple\n * - Number: creates symmetric tolerance [tolerance, tolerance]\n * - Tuple: uses the tuple as-is\n *\n * @param epochTolerance - Epoch tolerance specification (number or tuple [past, future])\n * @returns Tuple [pastTolerance, futureTolerance] in seconds\n *\n * @example\n * ```ts\n * normalizeEpochTolerance(0) // [0, 0]\n * normalizeEpochTolerance(30) // [30, 30]\n * normalizeEpochTolerance([5, 0]) // [5, 0]\n * normalizeEpochTolerance([10, 5]) // [10, 5]\n * ```\n */\nexport function normalizeEpochTolerance(\n epochTolerance: number | [number, number] = 0,\n): [number, number] {\n return Array.isArray(epochTolerance) ? epochTolerance : [epochTolerance, epochTolerance];\n}\n\n/**\n * Require crypto plugin to be configured\n *\n * @param crypto - The crypto plugin\n * @throws {CryptoPluginMissingError} If crypto plugin is not set\n */\nexport function requireCryptoPlugin<T>(crypto: T | undefined): asserts crypto is T {\n if (!crypto) {\n throw new CryptoPluginMissingError();\n }\n}\n\n/**\n * Require Base32 plugin to be configured\n *\n * @param base32 - The Base32 plugin\n * @throws {Base32PluginMissingError} If Base32 plugin is not set\n */\nexport function requireBase32Plugin<T>(base32: T | undefined): asserts base32 is T {\n if (!base32) {\n throw new Base32PluginMissingError();\n }\n}\n\n/**\n * Require secret to be configured\n *\n * @param secret - The secret value\n * @throws {SecretMissingError} If secret is not set\n */\nexport function requireSecret<T>(secret: T | undefined): asserts secret is T {\n if (!secret) {\n throw new SecretMissingError();\n }\n}\n\n/**\n * Require label to be configured (for URI generation)\n *\n * @param label - The label value\n * @throws {LabelMissingError} If label is not set\n */\nexport function requireLabel(label: string | undefined): asserts label is string {\n if (!label) {\n throw new LabelMissingError();\n }\n}\n\n/**\n * Require issuer to be configured (for URI generation)\n *\n * @param issuer - The issuer value\n * @throws {IssuerMissingError} If issuer is not set\n */\nexport function requireIssuer(issuer: string | undefined): asserts issuer is string {\n if (!issuer) {\n throw new IssuerMissingError();\n }\n}\n\n/**\n * Require secret to be a Base32 string (for URI generation)\n *\n * @param secret - The secret value\n * @throws {SecretTypeError} If secret is not a string\n */\nexport function requireBase32String(secret: string | Uint8Array): asserts secret is string {\n if (typeof secret !== \"string\") {\n throw new SecretTypeError();\n }\n}\n\n/**\n * Create a success result\n * @internal\n */\nfunction ok<T>(value: T): OTPResultOk<T> {\n return { ok: true, value };\n}\n\n/**\n * Create a failure result\n * @internal\n */\nfunction err<E>(error: E): OTPResultError<E> {\n return { ok: false, error };\n}\n\n/**\n * Wrap a synchronous function to return OTPResult instead of throwing\n *\n * Preserves the original OTPError subclass so users can access\n * specific error information via instanceof checks.\n *\n * @internal\n */\nexport function wrapResult<T, Args extends unknown[]>(\n fn: (...args: Args) => T,\n): (...args: Args) => OTPResult<T, OTPError> {\n return (...args: Args): OTPResult<T, OTPError> => {\n try {\n return ok(fn(...args));\n } catch (error) {\n return err(error as OTPError);\n }\n };\n}\n\n/**\n * Wrap an async function to return OTPResult instead of throwing\n *\n * Preserves the original OTPError subclass so users can access\n * specific error information via instanceof checks.\n *\n * @internal\n */\nexport function wrapResultAsync<T, Args extends unknown[]>(\n fn: (...args: Args) => Promise<T>,\n): (...args: Args) => Promise<OTPResult<T, OTPError>> {\n return async (...args: Args): Promise<OTPResult<T, OTPError>> => {\n try {\n return ok(await fn(...args));\n } catch (error) {\n return err(error as OTPError);\n }\n };\n}\n","import { HMACError, RandomBytesError } from \"./errors.js\";\n\nimport type { CryptoPlugin, HashAlgorithm } from \"./types.js\";\n\n/**\n * CryptoContext provides a unified interface for crypto operations\n * using a pluggable crypto backend\n */\nexport class CryptoContext {\n /**\n * Create a new CryptoContext with the given crypto plugin\n *\n * @param crypto - The crypto plugin to use\n */\n constructor(private readonly crypto: CryptoPlugin) {}\n\n /**\n * Get the underlying crypto plugin\n */\n get plugin(): CryptoPlugin {\n return this.crypto;\n }\n\n /**\n * Compute HMAC using the configured crypto plugin\n *\n * @param algorithm - The hash algorithm to use\n * @param key - The secret key as a byte array\n * @param data - The data to authenticate as a byte array\n * @returns HMAC digest as a byte array\n * @throws {HMACError} If HMAC computation fails\n */\n async hmac(algorithm: HashAlgorithm, key: Uint8Array, data: Uint8Array): Promise<Uint8Array> {\n try {\n const result = this.crypto.hmac(algorithm, key, data);\n return result instanceof Promise ? await result : result;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new HMACError(message, { cause: error });\n }\n }\n\n /**\n * Synchronous HMAC computation\n *\n * @param algorithm - The hash algorithm to use\n * @param key - The secret key as a byte array\n * @param data - The data to authenticate as a byte array\n * @returns HMAC digest as a byte array\n * @throws {HMACError} If HMAC computation fails or if crypto plugin doesn't support sync operations\n */\n hmacSync(algorithm: HashAlgorithm, key: Uint8Array, data: Uint8Array): Uint8Array {\n try {\n const result = this.crypto.hmac(algorithm, key, data);\n if (result instanceof Promise) {\n throw new HMACError(\"Crypto plugin does not support synchronous HMAC operations\");\n }\n return result;\n } catch (error) {\n if (error instanceof HMACError) {\n throw error;\n }\n const message = error instanceof Error ? error.message : String(error);\n throw new HMACError(message, { cause: error });\n }\n }\n\n /**\n * Generate cryptographically secure random bytes\n *\n * @param length - Number of random bytes to generate\n * @returns Random bytes\n * @throws {RandomBytesError} If random byte generation fails\n */\n randomBytes(length: number): Uint8Array {\n try {\n return this.crypto.randomBytes(length);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new RandomBytesError(message, { cause: error });\n }\n }\n}\n\n/**\n * Create a CryptoContext from a crypto plugin\n *\n * @param crypto - The crypto plugin to use\n * @returns A new CryptoContext instance\n */\nexport function createCryptoContext(crypto: CryptoPlugin): CryptoContext {\n return new CryptoContext(crypto);\n}\n","import { Base32EncodeError, Base32DecodeError } from \"./errors.js\";\n\nimport type { Base32Plugin, Base32EncodeOptions } from \"./types.js\";\n\n/**\n * Base32Context provides a unified interface for Base32 operations\n * using a pluggable Base32 backend.\n *\n * All errors from the underlying plugin are wrapped in otplib error types\n * with the original error preserved via the `cause` property.\n */\nexport class Base32Context {\n /**\n * Create a new Base32Context with the given Base32 plugin\n *\n * @param base32 - The Base32 plugin to use\n */\n constructor(private readonly base32: Base32Plugin) {}\n\n /**\n * Get the underlying Base32 plugin\n */\n get plugin(): Base32Plugin {\n return this.base32;\n }\n\n /**\n * Encode binary data to Base32 string using the configured plugin\n *\n * @param data - Uint8Array to encode\n * @param options - Encoding options\n * @returns Base32 encoded string\n * @throws {Base32EncodeError} If encoding fails\n */\n encode(data: Uint8Array, options?: Base32EncodeOptions): string {\n try {\n return this.base32.encode(data, options);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Base32EncodeError(message, { cause: error });\n }\n }\n\n /**\n * Decode Base32 string to binary data using the configured plugin\n *\n * @param str - Base32 string to decode\n * @returns Decoded Uint8Array\n * @throws {Base32DecodeError} If string contains invalid characters or decoding fails\n */\n decode(str: string): Uint8Array {\n try {\n return this.base32.decode(str);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Base32DecodeError(message, { cause: error });\n }\n }\n}\n\n/**\n * Create a Base32Context from a Base32 plugin\n *\n * @param base32 - The Base32 plugin to use\n * @returns A new Base32Context instance\n */\nexport function createBase32Context(base32: Base32Plugin): Base32Context {\n return new Base32Context(base32);\n}\n","/**\n * TypeScript Utility Types for otplib\n *\n * These types enhance developer experience by providing:\n * - Branded types for type-safe string handling\n * - Type guards for discriminated unions\n * - Helper types for option extraction\n */\n\nimport type { CryptoPlugin, Base32Plugin } from \"./types\";\n\n/**\n * Brand type for creating nominal types from primitives\n *\n * @example\n * ```ts\n * type UserId = Brand<string, 'UserId'>;\n * const id: UserId = 'abc' as UserId;\n * ```\n */\nexport type Brand<T, B extends string> = T & { readonly __brand: B };\n\n/**\n * Branded string type for Base32-encoded secrets\n *\n * Use this type to distinguish Base32-encoded secrets from regular strings\n * at compile time, preventing accidental misuse.\n *\n * @example\n * ```ts\n * import type { Base32Secret } from '@otplib/core';\n *\n * function processSecret(secret: Base32Secret): void {\n * // TypeScript ensures only Base32Secret values are passed\n * }\n *\n * const secret = generateSecret() as Base32Secret;\n * processSecret(secret); // OK\n * processSecret('random-string'); // Type error\n * ```\n */\nexport type Base32Secret = Brand<string, \"Base32Secret\">;\n\n/**\n * Branded string type for OTP tokens\n *\n * Use this type to distinguish OTP tokens from regular strings\n * at compile time, preventing accidental misuse.\n *\n * @example\n * ```ts\n * import type { OTPToken } from '@otplib/core';\n *\n * function validateToken(token: OTPToken): boolean {\n * // TypeScript ensures only OTPToken values are passed\n * }\n *\n * const token = await generate() as OTPToken;\n * validateToken(token); // OK\n * validateToken('123456'); // Type error\n * ```\n */\nexport type OTPToken = Brand<string, \"OTPToken\">;\n\n/**\n * Helper type to make all properties of T required except those in K\n *\n * @example\n * ```ts\n * type Options = { a?: string; b?: number; c?: boolean };\n * type RequiredAB = RequireKeys<Options, 'a' | 'b'>;\n * // { a: string; b: number; c?: boolean }\n * ```\n */\nexport type RequireKeys<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;\n\n/**\n * Helper type to make all properties of T optional except those in K\n *\n * @example\n * ```ts\n * type Options = { a: string; b: number; c: boolean };\n * type OptionalBC = OptionalKeys<Options, 'b' | 'c'>;\n * // { a: string; b?: number; c?: boolean }\n * ```\n */\nexport type OptionalKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;\n\n/**\n * Extract the plugin configuration from an options type\n *\n * @example\n * ```ts\n * type Plugins = PluginConfig<TOTPOptions>;\n * // { crypto: CryptoPlugin; base32?: Base32Plugin }\n * ```\n */\nexport type PluginConfig<T> = T extends { crypto?: CryptoPlugin; base32?: Base32Plugin }\n ? Pick<T, \"crypto\" | \"base32\">\n : never;\n\n/**\n * Ensure an options type has plugins defined\n *\n * @example\n * ```ts\n * type ConfiguredOptions = WithRequiredPlugins<TOTPOptions>;\n * // TOTPOptions with crypto and base32 required\n * ```\n */\nexport type WithRequiredPlugins<T extends { crypto?: CryptoPlugin; base32?: Base32Plugin }> = T & {\n crypto: CryptoPlugin;\n base32: Base32Plugin;\n};\n\n/**\n * Options type for OTP generation (crypto required)\n *\n * @example\n * ```ts\n * type MyGenerateOptions = GenerationReady<HOTPOptions>;\n * // HOTPOptions with crypto required\n * ```\n */\nexport type GenerationReady<T extends { crypto?: CryptoPlugin }> = T & {\n crypto: CryptoPlugin;\n};\n\n/**\n * Type predicate to check if an object has the required plugins\n *\n * @example\n * ```ts\n * const options = getOptions();\n * if (hasPlugins(options)) {\n * // TypeScript knows plugins are defined\n * options.crypto.hmac(...);\n * }\n * ```\n */\nexport function hasPlugins<T extends { crypto?: CryptoPlugin; base32?: Base32Plugin }>(\n options: T,\n): options is T & { crypto: CryptoPlugin; base32: Base32Plugin } {\n return options.crypto !== undefined && options.base32 !== undefined;\n}\n\n/**\n * Type predicate to check if an object has a crypto plugin\n *\n * @example\n * ```ts\n * if (hasCrypto(options)) {\n * await options.crypto.hmac('sha1', key, data);\n * }\n * ```\n */\nexport function hasCrypto<T extends { crypto?: CryptoPlugin }>(\n options: T,\n): options is T & { crypto: CryptoPlugin } {\n return options.crypto !== undefined;\n}\n\n/**\n * Type predicate to check if an object has a base32 plugin\n *\n * @example\n * ```ts\n * if (hasBase32(options)) {\n * const decoded = options.base32.decode(secret);\n * }\n * ```\n */\nexport function hasBase32<T extends { base32?: Base32Plugin }>(\n options: T,\n): options is T & { base32: Base32Plugin } {\n return options.base32 !== undefined;\n}\n\n/**\n * Narrow union type by a specific property value\n *\n * @example\n * ```ts\n * type Result = VerifyResultValid | VerifyResultInvalid;\n * type ValidOnly = NarrowBy<Result, 'valid', true>;\n * // VerifyResultValid\n * ```\n */\nexport type NarrowBy<T, K extends keyof T, V extends T[K]> = T extends { [key in K]: V }\n ? T\n : never;\n"],"mappings":"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","names":["src_exports","__export","AlgorithmError","Base32Context","Base32DecodeError","Base32EncodeError","Base32Error","Base32PluginMissingError","ConfigurationError","CounterError","CounterNegativeError","CounterOverflowError","CounterToleranceError","CounterToleranceTooLargeError","CryptoContext","CryptoError","CryptoPluginMissingError","DEFAULT_PERIOD","DigitsError","EpochToleranceError","EpochToleranceNegativeError","EpochToleranceTooLargeError","HMACError","IssuerMissingError","LabelMissingError","MAX_COUNTER","MAX_PERIOD","MAX_SECRET_BYTES","MAX_WINDOW","MIN_PERIOD","MIN_SECRET_BYTES","OTPError","PeriodError","PeriodTooLargeError","PeriodTooSmallError","PluginError","RECOMMENDED_SECRET_BYTES","RandomBytesError","SecretError","SecretMissingError","SecretTooLongError","SecretTooShortError","SecretTypeError","TimeError","TimeNegativeError","TokenError","TokenFormatError","TokenLengthError","constantTimeEqual","counterToBytes","createBase32Context","createCryptoContext","createGuardrails","dynamicTruncate","generateSecret","getDigestSize","hasBase32","hasCrypto","hasGuardrailOverrides","hasPlugins","hexToBytes","normalizeCounterTolerance","normalizeEpochTolerance","normalizeSecret","requireBase32Plugin","requireBase32String","requireCryptoPlugin","requireIssuer","requireLabel","requireSecret","stringToBytes","truncateDigits","validateByteLengthEqual","validateCounter","validateCounterTolerance","validateEpochTolerance","validatePeriod","validateSecret","validateTime","validateToken","wrapResult","wrapResultAsync","__toCommonJS","OTPError","message","options","SecretError","SecretTooShortError","minBytes","actualBytes","SecretTooLongError","maxBytes","CounterError","CounterNegativeError","CounterOverflowError","TimeError","TimeNegativeError","PeriodError","PeriodTooSmallError","minPeriod","PeriodTooLargeError","maxPeriod","DigitsError","AlgorithmError","TokenError","TokenLengthError","expected","actual","TokenFormatError","CryptoError","HMACError","RandomBytesError","Base32Error","Base32EncodeError","Base32DecodeError","CounterToleranceError","CounterToleranceTooLargeError","maxTolerance","actualSize","EpochToleranceError","EpochToleranceNegativeError","EpochToleranceTooLargeError","actualValue","PluginError","CryptoPluginMissingError","Base32PluginMissingError","ConfigurationError","SecretMissingError","LabelMissingError","IssuerMissingError","SecretTypeError","textEncoder","MIN_SECRET_BYTES","MAX_SECRET_BYTES","RECOMMENDED_SECRET_BYTES","MIN_PERIOD","MAX_PERIOD","DEFAULT_PERIOD","MAX_COUNTER","MAX_WINDOW","OVERRIDE_SYMBOL","DEFAULT_GUARDRAILS","createGuardrails","custom","hasGuardrailOverrides","guardrails","validateSecret","secret","SecretTooShortError","SecretTooLongError","validateCounter","counter","value","CounterNegativeError","CounterOverflowError","validateTime","time","TimeNegativeError","validatePeriod","period","PeriodTooSmallError","PeriodTooLargeError","validateToken","token","digits","TokenLengthError","TokenFormatError","validateCounterTolerance","counterTolerance","CounterToleranceTooLargeError","validateEpochTolerance","epochTolerance","pastTolerance","futureTolerance","EpochToleranceNegativeError","maxToleranceSeconds","maxAllowed","EpochToleranceTooLargeError","counterToBytes","bigintValue","buffer","dynamicTruncate","hmacResult","offset","truncateDigits","maxOtp","validateByteLengthEqual","a","b","constantTimeEqual","bufA","stringToBytes","bufB","result","getDigestSize","algorithm","hexToBytes","hex","bytes","i","normalizeSecret","base32","requireBase32Plugin","generateSecret","options","crypto","length","requireCryptoPlugin","randomBytes","normalizeCounterTolerance","normalizeEpochTolerance","CryptoPluginMissingError","Base32PluginMissingError","requireSecret","SecretMissingError","requireLabel","label","LabelMissingError","requireIssuer","issuer","IssuerMissingError","requireBase32String","SecretTypeError","ok","err","error","wrapResult","fn","args","wrapResultAsync","CryptoContext","crypto","algorithm","key","data","result","error","message","HMACError","length","RandomBytesError","createCryptoContext","Base32Context","base32","data","options","error","message","Base32EncodeError","str","Base32DecodeError","createBase32Context","hasPlugins","options","hasCrypto","hasBase32"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/utils.ts","../src/crypto-context.ts","../src/base32-context.ts","../src/utility-types.ts"],"sourcesContent":["export type {\n Base32EncodeOptions,\n Base32Plugin,\n CryptoPlugin,\n Digits,\n HashAlgorithm,\n SecretOptions,\n OTPResult,\n OTPResultOk,\n OTPResultError,\n} from \"./types.js\";\n\nexport type { OTPGuardrailsConfig, OTPGuardrails } from \"./utils.js\";\n\nexport {\n OTPError,\n SecretError,\n SecretTooShortError,\n SecretTooLongError,\n CounterError,\n CounterNegativeError,\n CounterOverflowError,\n TimeError,\n TimeNegativeError,\n PeriodError,\n PeriodTooSmallError,\n PeriodTooLargeError,\n DigitsError,\n AlgorithmError,\n TokenError,\n TokenLengthError,\n TokenFormatError,\n CryptoError,\n HMACError,\n RandomBytesError,\n Base32Error,\n Base32EncodeError,\n Base32DecodeError,\n CounterToleranceError,\n CounterToleranceTooLargeError,\n EpochToleranceError,\n EpochToleranceNegativeError,\n EpochToleranceTooLargeError,\n PluginError,\n CryptoPluginMissingError,\n Base32PluginMissingError,\n ConfigurationError,\n SecretMissingError,\n LabelMissingError,\n IssuerMissingError,\n SecretTypeError,\n type OTPErrorOptions,\n} from \"./errors.js\";\n\nexport {\n createGuardrails,\n hasGuardrailOverrides,\n validateSecret,\n validateCounter,\n validateTime,\n validatePeriod,\n validateToken,\n validateCounterTolerance,\n validateEpochTolerance,\n counterToBytes,\n dynamicTruncate,\n truncateDigits,\n validateByteLengthEqual,\n constantTimeEqual,\n getDigestSize,\n stringToBytes,\n hexToBytes,\n normalizeSecret,\n normalizeCounterTolerance,\n normalizeEpochTolerance,\n generateSecret,\n requireCryptoPlugin,\n requireBase32Plugin,\n requireSecret,\n requireLabel,\n requireIssuer,\n requireBase32String,\n wrapResult,\n wrapResultAsync,\n MIN_SECRET_BYTES,\n MAX_SECRET_BYTES,\n RECOMMENDED_SECRET_BYTES,\n MIN_PERIOD,\n MAX_PERIOD,\n DEFAULT_PERIOD,\n MAX_COUNTER,\n MAX_WINDOW,\n} from \"./utils.js\";\n\nexport { CryptoContext, createCryptoContext } from \"./crypto-context.js\";\n\nexport { Base32Context, createBase32Context } from \"./base32-context.js\";\n\n// Utility types for enhanced developer experience\nexport type {\n Brand,\n Base32Secret,\n OTPToken,\n RequireKeys,\n OptionalKeys,\n PluginConfig,\n WithRequiredPlugins,\n GenerationReady,\n NarrowBy,\n} from \"./utility-types.js\";\n\nexport { hasPlugins, hasCrypto, hasBase32 } from \"./utility-types.js\";\n","/**\n * Options for OTPError construction\n */\nexport type OTPErrorOptions = {\n /**\n * The underlying error that caused this error.\n * Useful for error chaining and debugging.\n */\n cause?: unknown;\n};\n\n/**\n * Base error class for all otplib errors\n *\n * Supports ES2022 error chaining via the `cause` property.\n *\n * @example\n * ```typescript\n * try {\n * // ... operation that throws\n * } catch (error) {\n * throw new OTPError('Operation failed', { cause: error });\n * }\n * ```\n */\nexport class OTPError extends Error {\n constructor(message: string, options?: OTPErrorOptions) {\n super(message, options);\n this.name = \"OTPError\";\n }\n}\n\n/**\n * Error thrown when secret validation fails\n */\nexport class SecretError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"SecretError\";\n }\n}\n\n/**\n * Error thrown when secret is too short (< 128 bits)\n */\nexport class SecretTooShortError extends SecretError {\n constructor(minBytes: number, actualBytes: number) {\n super(\n `Secret must be at least ${minBytes} bytes (${minBytes * 8} bits), got ${actualBytes} bytes`,\n );\n this.name = \"SecretTooShortError\";\n }\n}\n\n/**\n * Error thrown when secret is unreasonably large (> 64 bytes)\n */\nexport class SecretTooLongError extends SecretError {\n constructor(maxBytes: number, actualBytes: number) {\n super(`Secret must not exceed ${maxBytes} bytes, got ${actualBytes} bytes`);\n this.name = \"SecretTooLongError\";\n }\n}\n\n/**\n * Error thrown when counter is invalid\n */\nexport class CounterError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"CounterError\";\n }\n}\n\n/**\n * Error thrown when counter is negative\n */\nexport class CounterNegativeError extends CounterError {\n constructor() {\n super(\"Counter must be non-negative\");\n this.name = \"CounterNegativeError\";\n }\n}\n\n/**\n * Error thrown when counter exceeds maximum value (2^53 - 1 for safe integer)\n */\nexport class CounterOverflowError extends CounterError {\n constructor() {\n super(\"Counter exceeds maximum safe integer value\");\n this.name = \"CounterOverflowError\";\n }\n}\n\n/**\n * Error thrown when time is invalid\n */\nexport class TimeError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"TimeError\";\n }\n}\n\n/**\n * Error thrown when time is negative\n */\nexport class TimeNegativeError extends TimeError {\n constructor() {\n super(\"Time must be non-negative\");\n this.name = \"TimeNegativeError\";\n }\n}\n\n/**\n * Error thrown when period is invalid\n */\nexport class PeriodError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"PeriodError\";\n }\n}\n\n/**\n * Error thrown when period is too small\n */\nexport class PeriodTooSmallError extends PeriodError {\n constructor(minPeriod: number) {\n super(`Period must be at least ${minPeriod} second(s)`);\n this.name = \"PeriodTooSmallError\";\n }\n}\n\n/**\n * Error thrown when period is too large\n */\nexport class PeriodTooLargeError extends PeriodError {\n constructor(maxPeriod: number) {\n super(`Period must not exceed ${maxPeriod} seconds`);\n this.name = \"PeriodTooLargeError\";\n }\n}\n\n/**\n * Error thrown when digits value is invalid\n */\nexport class DigitsError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"DigitsError\";\n }\n}\n\n/**\n * Error thrown when hash algorithm is invalid\n */\nexport class AlgorithmError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"AlgorithmError\";\n }\n}\n\n/**\n * Error thrown when token is invalid\n */\nexport class TokenError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"TokenError\";\n }\n}\n\n/**\n * Error thrown when token has incorrect length\n */\nexport class TokenLengthError extends TokenError {\n constructor(expected: number, actual: number) {\n super(`Token must be ${expected} digits, got ${actual}`);\n this.name = \"TokenLengthError\";\n }\n}\n\n/**\n * Error thrown when token contains non-digit characters\n */\nexport class TokenFormatError extends TokenError {\n constructor() {\n super(\"Token must contain only digits\");\n this.name = \"TokenFormatError\";\n }\n}\n\n/**\n * Error thrown when crypto operation fails\n */\nexport class CryptoError extends OTPError {\n constructor(message: string, options?: OTPErrorOptions) {\n super(message, options);\n this.name = \"CryptoError\";\n }\n}\n\n/**\n * Error thrown when HMAC computation fails\n *\n * The original error from the crypto plugin is available via `cause`.\n *\n * @example\n * ```typescript\n * try {\n * await cryptoContext.hmac('sha1', key, data);\n * } catch (error) {\n * if (error instanceof HMACError) {\n * console.log('HMAC failed:', error.message);\n * console.log('Original error:', error.cause);\n * }\n * }\n * ```\n */\nexport class HMACError extends CryptoError {\n constructor(message: string, options?: OTPErrorOptions) {\n super(`HMAC computation failed: ${message}`, options);\n this.name = \"HMACError\";\n }\n}\n\n/**\n * Error thrown when random byte generation fails\n *\n * The original error from the crypto plugin is available via `cause`.\n */\nexport class RandomBytesError extends CryptoError {\n constructor(message: string, options?: OTPErrorOptions) {\n super(`Random byte generation failed: ${message}`, options);\n this.name = \"RandomBytesError\";\n }\n}\n\n/**\n * Error thrown when Base32 operation fails\n */\nexport class Base32Error extends OTPError {\n constructor(message: string, options?: OTPErrorOptions) {\n super(message, options);\n this.name = \"Base32Error\";\n }\n}\n\n/**\n * Error thrown when Base32 encoding fails\n *\n * The original error from the Base32 plugin is available via `cause`.\n *\n * @example\n * ```typescript\n * try {\n * base32Context.encode(data);\n * } catch (error) {\n * if (error instanceof Base32EncodeError) {\n * console.log('Encoding failed:', error.message);\n * console.log('Original error:', error.cause);\n * }\n * }\n * ```\n */\nexport class Base32EncodeError extends Base32Error {\n constructor(message: string, options?: OTPErrorOptions) {\n super(`Base32 encoding failed: ${message}`, options);\n this.name = \"Base32EncodeError\";\n }\n}\n\n/**\n * Error thrown when Base32 decoding fails\n *\n * The original error from the Base32 plugin is available via `cause`.\n *\n * @example\n * ```typescript\n * try {\n * base32Context.decode(invalidString);\n * } catch (error) {\n * if (error instanceof Base32DecodeError) {\n * console.log('Decoding failed:', error.message);\n * console.log('Original error:', error.cause);\n * }\n * }\n * ```\n */\nexport class Base32DecodeError extends Base32Error {\n constructor(message: string, options?: OTPErrorOptions) {\n super(`Base32 decoding failed: ${message}`, options);\n this.name = \"Base32DecodeError\";\n }\n}\n\n/**\n * Error thrown when counter tolerance is invalid\n */\nexport class CounterToleranceError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"CounterToleranceError\";\n }\n}\n\n/**\n * Error thrown when counter tolerance is too large\n */\nexport class CounterToleranceTooLargeError extends CounterToleranceError {\n constructor(maxTolerance: number, actualSize: number) {\n super(\n `Counter tolerance size must not exceed ${maxTolerance}, got ${actualSize}. ` +\n `Large tolerances can cause performance issues.`,\n );\n this.name = \"CounterToleranceTooLargeError\";\n }\n}\n\n/**\n * Error thrown when epoch tolerance is invalid\n */\nexport class EpochToleranceError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"EpochToleranceError\";\n }\n}\n\n/**\n * Error thrown when epoch tolerance contains negative values\n */\nexport class EpochToleranceNegativeError extends EpochToleranceError {\n constructor() {\n super(\"Epoch tolerance cannot contain negative values\");\n this.name = \"EpochToleranceNegativeError\";\n }\n}\n\n/**\n * Error thrown when epoch tolerance is too large\n */\nexport class EpochToleranceTooLargeError extends EpochToleranceError {\n constructor(maxTolerance: number, actualValue: number) {\n super(\n `Epoch tolerance must not exceed ${maxTolerance} seconds, got ${actualValue}. ` +\n `Large tolerances can cause performance issues.`,\n );\n this.name = \"EpochToleranceTooLargeError\";\n }\n}\n\n/**\n * Error thrown when a required plugin is missing\n */\nexport class PluginError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"PluginError\";\n }\n}\n\n/**\n * Error thrown when crypto plugin is not configured\n */\nexport class CryptoPluginMissingError extends PluginError {\n constructor() {\n super(\"Crypto plugin is required.\");\n this.name = \"CryptoPluginMissingError\";\n }\n}\n\n/**\n * Error thrown when Base32 plugin is not configured\n */\nexport class Base32PluginMissingError extends PluginError {\n constructor() {\n super(\"Base32 plugin is required.\");\n this.name = \"Base32PluginMissingError\";\n }\n}\n\n/**\n * Error thrown when required configuration is missing\n */\nexport class ConfigurationError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigurationError\";\n }\n}\n\n/**\n * Error thrown when secret is not configured\n */\nexport class SecretMissingError extends ConfigurationError {\n constructor() {\n super(\n \"Secret is required. \" +\n \"Use generateSecret() to create one, or provide via { secret: 'YOUR_BASE32_SECRET' }\",\n );\n this.name = \"SecretMissingError\";\n }\n}\n\n/**\n * Error thrown when label is not configured (required for URI generation)\n */\nexport class LabelMissingError extends ConfigurationError {\n constructor() {\n super(\"Label is required for URI generation. Example: { label: 'user@example.com' }\");\n this.name = \"LabelMissingError\";\n }\n}\n\n/**\n * Error thrown when issuer is not configured (required for URI generation)\n */\nexport class IssuerMissingError extends ConfigurationError {\n constructor() {\n super(\"Issuer is required for URI generation. Example: { issuer: 'MyApp' }\");\n this.name = \"IssuerMissingError\";\n }\n}\n\n/**\n * Error thrown when secret must be a Base32 string but is provided as bytes\n */\nexport class SecretTypeError extends ConfigurationError {\n constructor() {\n super(\n \"Class API requires secret to be a Base32 string, not Uint8Array. \" +\n \"Use generateSecret() or provide a Base32-encoded string.\",\n );\n this.name = \"SecretTypeError\";\n }\n}\n","import {\n OTPError,\n SecretTooShortError,\n SecretTooLongError,\n CounterNegativeError,\n CounterOverflowError,\n TimeNegativeError,\n PeriodTooSmallError,\n PeriodTooLargeError,\n TokenLengthError,\n TokenFormatError,\n CounterToleranceTooLargeError,\n EpochToleranceNegativeError,\n EpochToleranceTooLargeError,\n CryptoPluginMissingError,\n Base32PluginMissingError,\n SecretMissingError,\n LabelMissingError,\n IssuerMissingError,\n SecretTypeError,\n} from \"./errors.js\";\n\nimport type {\n HashAlgorithm,\n SecretOptions,\n OTPResultOk,\n OTPResultError,\n OTPResult,\n} from \"./types.js\";\n\n/**\n * Singleton TextEncoder instance to avoid repeated allocations\n */\nconst textEncoder = new TextEncoder();\n\n/**\n * Minimum secret length in bytes (128 bits as per RFC 4226)\n */\nexport const MIN_SECRET_BYTES = 16;\n\n/**\n * Maximum secret length in bytes (512 bits)\n *\n * The 64-byte maximum is not part of the RFCs.\n * This is to prevent excessive memory usage in HMAC operations.\n */\nexport const MAX_SECRET_BYTES = 64;\n\n/**\n * Recommended secret length in bytes (160 bits as per RFC 4226)\n */\nexport const RECOMMENDED_SECRET_BYTES = 20;\n\n/**\n * Minimum period in seconds\n */\nexport const MIN_PERIOD = 1;\n\n/**\n * Maximum period in seconds (1 hour)\n */\nexport const MAX_PERIOD = 3600;\n\n/**\n * Default period in seconds (30 seconds as per RFC 6238)\n */\nexport const DEFAULT_PERIOD = 30;\n\n/**\n * Maximum safe integer for counter (2^53 - 1)\n */\nexport const MAX_COUNTER = Number.MAX_SAFE_INTEGER;\n\n/**\n * Maximum verification window size\n *\n * Limits the number of HMAC computations during verification to prevent DoS attacks.\n * A window of 100 means up to 201 HMAC computations ([-100, +100] range).\n *\n * For TOTP: window=1 is typically sufficient (allows +-30 seconds clock drift)\n * For HOTP: window=10-50 handles reasonable counter desynchronization\n */\nexport const MAX_WINDOW = 100;\n\n/**\n * Configurable guardrails for OTP validation\n *\n * Allows overriding default safety limits for non-standard production requirements.\n * Use with caution - custom guardrails can weaken security.\n */\nexport type OTPGuardrailsConfig = {\n MIN_SECRET_BYTES: number;\n MAX_SECRET_BYTES: number;\n MIN_PERIOD: number;\n MAX_PERIOD: number;\n MAX_COUNTER: number;\n MAX_WINDOW: number;\n};\n\n/**\n * Module-private symbol to track guardrail override status\n *\n * This symbol is used as a property key to store whether guardrails contain custom values.\n * Being module-private and a symbol ensures:\n * - Cannot be accessed outside this module (not exported)\n * - Cannot be recreated (each Symbol() call is unique)\n * - Hidden from normal enumeration (Object.keys, JSON.stringify, for-in)\n * - Minimal memory overhead (~1 byte per object)\n * - No garbage collection concerns\n *\n * @internal\n */\nconst OVERRIDE_SYMBOL = Symbol(\"otplib.guardrails.override\");\n\n/**\n * Complete guardrails configuration\n *\n * This represents the final, immutable configuration used by validation functions.\n * Internally tracks whether any values were overridden from RFC recommendations,\n * enabling security auditing and compliance monitoring without exposing implementation\n * details in the public API.\n *\n * The override status is stored using a module-private Symbol that cannot be accessed\n * or recreated outside this module, providing true encapsulation.\n *\n * @see {@link OTPGuardrailsConfig} for the base configuration structure\n * @see {@link createGuardrails} for creating guardrails instances\n * @see {@link hasGuardrailOverrides} to check if guardrails were customized\n */\nexport type OTPGuardrails = Readonly<OTPGuardrailsConfig> & {\n [OVERRIDE_SYMBOL]?: boolean;\n};\n\n/**\n * Default guardrails matching RFC recommendations\n *\n * Frozen to ensure immutability. Used as default parameter for validation functions.\n * For custom guardrails, use the createGuardrails() factory function.\n */\nconst DEFAULT_GUARDRAILS: OTPGuardrails = Object.freeze({\n MIN_SECRET_BYTES,\n MAX_SECRET_BYTES,\n MIN_PERIOD,\n MAX_PERIOD,\n MAX_COUNTER,\n MAX_WINDOW,\n [OVERRIDE_SYMBOL]: false,\n});\n\n/**\n * Create guardrails configuration object\n *\n * Factory function that merges custom guardrails with defaults and returns\n * an immutable (frozen) object. Validates custom guardrails to ensure they\n * maintain basic safety invariants.\n *\n * When called without arguments or with `undefined`, returns the default guardrails\n * singleton (optimized to avoid unnecessary allocations). When called with custom\n * values, creates a new frozen object and internally marks it as overridden.\n *\n * @param custom - Optional partial guardrails to override defaults\n * @returns Frozen guardrails object\n * @throws {Error} If custom guardrails violate safety invariants\n *\n * @example Basic usage\n * ```ts\n * import { createGuardrails, hasGuardrailOverrides } from '@otplib/core'\n *\n * // Returns default singleton (no overrides)\n * const defaults = createGuardrails();\n * hasGuardrailOverrides(defaults); // false\n *\n * // Creates new object with overrides\n * const custom = createGuardrails({\n * MIN_SECRET_BYTES: 8,\n * MAX_WINDOW: 200\n * });\n * hasGuardrailOverrides(custom); // true\n * ```\n *\n * @example Monitoring custom guardrails\n * ```ts\n * import { createGuardrails, hasGuardrailOverrides } from '@otplib/core';\n *\n * const guardrails = createGuardrails({ MAX_WINDOW: 20 });\n *\n * if (hasGuardrailOverrides(guardrails)) {\n * logger.warn('Non-default guardrails in use', { guardrails });\n * }\n * ```\n *\n * @see {@link hasGuardrailOverrides} to check if guardrails were customized\n */\nexport function createGuardrails(custom?: Partial<OTPGuardrailsConfig>): OTPGuardrails {\n if (!custom) {\n return DEFAULT_GUARDRAILS;\n }\n\n return Object.freeze({\n ...DEFAULT_GUARDRAILS,\n ...custom,\n [OVERRIDE_SYMBOL]: true,\n });\n}\n\n/**\n * Check if guardrails contain custom overrides\n *\n * Returns `true` if the guardrails object was created with custom values,\n * `false` if using RFC-recommended defaults. Useful for security auditing,\n * compliance monitoring, and development warnings.\n *\n * This function accesses a module-private Symbol property that cannot be\n * accessed or modified outside this module, ensuring reliable detection.\n *\n * @param guardrails - The guardrails object to check\n * @returns `true` if guardrails were customized, `false` if using defaults\n *\n * @example Security monitoring\n * ```ts\n * import { createGuardrails, hasGuardrailOverrides } from '@otplib/core';\n *\n * const guardrails = createGuardrails({ MAX_WINDOW: 20 });\n *\n * if (hasGuardrailOverrides(guardrails)) {\n * console.warn('Custom guardrails detected:', guardrails);\n * // Log to security audit system\n * }\n * ```\n *\n * @example Compliance check\n * ```ts\n * function validateGuardrails(guardrails: OTPGuardrails) {\n * if (hasGuardrailOverrides(guardrails)) {\n * throw new Error('Custom guardrails not allowed in production');\n * }\n * }\n * ```\n */\nexport function hasGuardrailOverrides(guardrails: OTPGuardrails): boolean {\n return guardrails[OVERRIDE_SYMBOL] ?? false;\n}\n\n/**\n * Validate secret key\n *\n * @param secret - The secret to validate\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {SecretTooShortError} If secret is too short\n * @throws {SecretTooLongError} If secret is too long\n */\nexport function validateSecret(\n secret: Uint8Array,\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n if (secret.length < guardrails.MIN_SECRET_BYTES) {\n throw new SecretTooShortError(guardrails.MIN_SECRET_BYTES, secret.length);\n }\n\n if (secret.length > guardrails.MAX_SECRET_BYTES) {\n throw new SecretTooLongError(guardrails.MAX_SECRET_BYTES, secret.length);\n }\n}\n\n/**\n * Validate counter value\n *\n * @param counter - The counter to validate\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {CounterNegativeError} If counter is negative\n * @throws {CounterOverflowError} If counter exceeds safe integer\n */\nexport function validateCounter(\n counter: number | bigint,\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n const value = typeof counter === \"bigint\" ? counter : BigInt(counter);\n\n if (value < 0n) {\n throw new CounterNegativeError();\n }\n\n if (value > BigInt(guardrails.MAX_COUNTER)) {\n throw new CounterOverflowError();\n }\n}\n\n/**\n * Validate time value\n *\n * @param time - The time in seconds to validate\n * @throws {TimeNegativeError} If time is negative\n */\nexport function validateTime(time: number): void {\n if (time < 0) {\n throw new TimeNegativeError();\n }\n}\n\n/**\n * Validate period value\n *\n * @param period - The period in seconds to validate\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {PeriodTooSmallError} If period is too small\n * @throws {PeriodTooLargeError} If period is too large\n */\nexport function validatePeriod(\n period: number,\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n if (!Number.isInteger(period) || period < guardrails.MIN_PERIOD) {\n throw new PeriodTooSmallError(guardrails.MIN_PERIOD);\n }\n\n if (period > guardrails.MAX_PERIOD) {\n throw new PeriodTooLargeError(guardrails.MAX_PERIOD);\n }\n}\n\n/**\n * Validate token\n *\n * @param token - The token string to validate\n * @param digits - Expected number of digits\n * @throws {TokenLengthError} If token has incorrect length\n * @throws {TokenFormatError} If token contains non-digit characters\n */\nexport function validateToken(token: string, digits: number): void {\n if (token.length !== digits) {\n throw new TokenLengthError(digits, token.length);\n }\n\n if (!/^\\d+$/.test(token)) {\n throw new TokenFormatError();\n }\n}\n\n/**\n * Validate counter tolerance for HOTP verification\n *\n * Prevents DoS attacks by limiting the number of counter values checked.\n *\n * @param counterTolerance - Counter tolerance specification (number or array of offsets)\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {CounterToleranceTooLargeError} If tolerance size exceeds MAX_WINDOW\n *\n * @example\n * ```ts\n * validateCounterTolerance(1); // OK: 3 offsets [-1, 0, 1]\n * validateCounterTolerance(100); // OK: 201 offsets [-100, ..., 100]\n * validateCounterTolerance(101); // Throws: exceeds MAX_WINDOW\n * validateCounterTolerance([0, 1]); // OK: 2 offsets\n * ```\n */\nexport function validateCounterTolerance(\n counterTolerance: number | number[],\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n const size = Array.isArray(counterTolerance) ? counterTolerance.length : counterTolerance * 2 + 1;\n\n if (size > guardrails.MAX_WINDOW * 2 + 1) {\n throw new CounterToleranceTooLargeError(\n guardrails.MAX_WINDOW,\n Array.isArray(counterTolerance) ? counterTolerance.length : counterTolerance,\n );\n }\n}\n\n/**\n * Validate epoch tolerance for TOTP verification\n *\n * Prevents DoS attacks by limiting the time range checked.\n * Also validates that tolerance values are non-negative.\n *\n * @param epochTolerance - Epoch tolerance specification (number or tuple [past, future])\n * @param period - The TOTP period in seconds (default: 30). Used to calculate max tolerance.\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {EpochToleranceNegativeError} If tolerance contains negative values\n * @throws {EpochToleranceTooLargeError} If tolerance exceeds MAX_WINDOW periods\n *\n * @example\n * ```ts\n * validateEpochTolerance(30); // OK: 30 seconds (default period 30s)\n * validateEpochTolerance([5, 0]); // OK: 5 seconds past only\n * validateEpochTolerance([-5, 0]); // Throws: negative values not allowed\n * validateEpochTolerance(3600); // Throws: exceeds MAX_WINDOW * period\n * validateEpochTolerance(6000, 60); // OK with 60s period (MAX_WINDOW * 60 = 6000)\n * ```\n */\nexport function validateEpochTolerance(\n epochTolerance: number | [number, number],\n period: number = DEFAULT_PERIOD,\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n const [pastTolerance, futureTolerance] = Array.isArray(epochTolerance)\n ? epochTolerance\n : [epochTolerance, epochTolerance];\n\n // Check for negative values\n if (pastTolerance < 0 || futureTolerance < 0) {\n throw new EpochToleranceNegativeError();\n }\n\n // Check total tolerance doesn't exceed reasonable limits\n // Convert to periods and check against MAX_WINDOW\n const maxToleranceSeconds = guardrails.MAX_WINDOW * period;\n const maxAllowed = Math.max(pastTolerance, futureTolerance);\n\n if (maxAllowed > maxToleranceSeconds) {\n throw new EpochToleranceTooLargeError(maxToleranceSeconds, maxAllowed);\n }\n}\n\n/**\n * Convert counter to 8-byte big-endian array\n *\n * Per RFC 4226 Section 5.1, the counter value is represented as an 8-byte\n * big-endian (network byte order) unsigned integer.\n *\n * @see {@link https://tools.ietf.org/html/rfc4226#section-5.1 | RFC 4226 Section 5.1 - Symbol Descriptions}\n *\n * @param value - The counter value to convert\n * @returns 8-byte big-endian array\n */\nexport function counterToBytes(value: number | bigint): Uint8Array {\n const bigintValue = typeof value === \"bigint\" ? value : BigInt(value);\n const buffer = new ArrayBuffer(8);\n const view = new DataView(buffer);\n\n view.setBigUint64(0, bigintValue, false);\n\n return new Uint8Array(buffer);\n}\n\n/**\n * Perform Dynamic Truncation as per RFC 4226 Section 5.3\n *\n * The algorithm:\n * 1. Take the low-order 4 bits of the last byte as offset\n * 2. Extract 4 bytes starting at offset\n * 3. Mask the most significant bit to get a 31-bit unsigned integer\n *\n * This ensures consistent extraction across different HMAC output sizes\n * while producing a value that fits in a signed 32-bit integer.\n *\n * @see {@link https://tools.ietf.org/html/rfc4226#section-5.3 | RFC 4226 Section 5.3 - Generating an HOTP Value}\n *\n * @param hmacResult - HMAC result (at least 20 bytes for SHA-1)\n * @returns Truncated 31-bit unsigned integer\n */\nexport function dynamicTruncate(hmacResult: Uint8Array): number {\n const offset = hmacResult[hmacResult.length - 1] & 0x0f;\n\n const binary =\n ((hmacResult[offset] & 0x7f) << 24) |\n (hmacResult[offset + 1] << 16) |\n (hmacResult[offset + 2] << 8) |\n hmacResult[offset + 3];\n\n return binary;\n}\n\n/**\n * Convert truncated integer to OTP string with specified digits\n *\n * Computes: Snum mod 10^Digit (RFC 4226 Section 5.3)\n *\n * The result is zero-padded to ensure consistent length,\n * as required for proper token comparison.\n *\n * @see {@link https://tools.ietf.org/html/rfc4226#section-5.3 | RFC 4226 Section 5.3 - Generating an HOTP Value}\n *\n * @param value - The truncated integer value (Snum)\n * @param digits - Number of digits for the OTP (Digit, typically 6-8)\n * @returns OTP string with leading zeros if necessary\n */\nexport function truncateDigits(value: number, digits: number): string {\n const maxOtp = 10 ** digits;\n const otp = value % maxOtp;\n return otp.toString().padStart(digits, \"0\");\n}\n\n/**\n * Validate that two byte arrays have equal length\n *\n * Useful as a preliminary check before performing byte-by-byte comparisons.\n *\n * @param a - First byte array\n * @param b - Second byte array\n * @returns true if arrays have equal length, false otherwise\n */\nexport function validateByteLengthEqual(a: Uint8Array, b: Uint8Array): boolean {\n return a.length === b.length;\n}\n\n/**\n * Constant-time comparison to prevent timing attacks\n *\n * This implements a timing-safe equality check as recommended in\n * RFC 4226 Section 7.2 for token validation to prevent\n * timing side-channel attacks.\n *\n * @see {@link https://tools.ietf.org/html/rfc4226#section-7.2 | RFC 4226 Section 7.2 - Validation and Verification}\n *\n * @param a - First value to compare\n * @param b - Second value to compare\n * @returns true if values are equal, false otherwise\n */\nexport function constantTimeEqual(a: string | Uint8Array, b: string | Uint8Array): boolean {\n const bufA = stringToBytes(a);\n const bufB = stringToBytes(b);\n\n if (!validateByteLengthEqual(bufA, bufB)) {\n return false;\n }\n\n let result = 0;\n for (let i = 0; i < bufA.length; i++) {\n result |= bufA[i] ^ bufB[i];\n }\n\n return result === 0;\n}\n\n/**\n * Get HMAC digest size in bytes for a given algorithm\n *\n * @param algorithm - The hash algorithm\n * @returns Digest size in bytes\n */\nexport function getDigestSize(algorithm: HashAlgorithm): number {\n switch (algorithm) {\n case \"sha1\":\n return 20;\n case \"sha256\":\n return 32;\n case \"sha512\":\n return 64;\n }\n}\n\n/**\n * Convert a string or Uint8Array to Uint8Array\n *\n * This utility function normalizes input to Uint8Array, converting strings\n * using UTF-8 encoding. Uint8Array inputs are returned as-is.\n *\n * @param value - The value to convert (string or Uint8Array)\n * @returns The value as a Uint8Array (UTF-8 encoded for strings)\n *\n * @example\n * ```ts\n * import { stringToBytes } from '@otplib/core'\n *\n * const bytes1 = stringToBytes('1234567890123456')\n * // Returns: Uint8Array([49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54])\n *\n * const bytes2 = stringToBytes(new Uint8Array([1, 2, 3]))\n * // Returns: Uint8Array([1, 2, 3]) - returned as-is\n * ```\n */\nexport function stringToBytes(value: string | Uint8Array): Uint8Array {\n return typeof value === \"string\" ? textEncoder.encode(value) : value;\n}\n\n/**\n * Convert a hex string to a Uint8Array\n *\n * This is useful for working with RFC test vectors and debugging HMAC outputs,\n * which are commonly represented as hexadecimal strings.\n *\n * If your environment supports it, consider using `Uint8Array.fromHex()` instead.\n *\n * @param hex - The hex string to convert (lowercase or uppercase, no 0x prefix)\n * @returns The bytes as a Uint8Array\n *\n * @example\n * ```ts\n * import { hexToBytes } from '@otplib/core'\n *\n * // Convert RFC 4226 HMAC test vector\n * const hmac = hexToBytes('cc93cf18508d94934c64b65d8ba7667fb7cde4b0')\n * // Returns: Uint8Array([0xcc, 0x93, 0xcf, ...])\n * ```\n */\nexport function hexToBytes(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);\n }\n return bytes;\n}\n\n/**\n * Normalize secret input to Uint8Array\n *\n * Accepts either a Base32-encoded string or Uint8Array and returns Uint8Array.\n * If a Base32Plugin is provided, string secrets will be automatically decoded.\n *\n * @param secret - The secret to normalize (string or Uint8Array)\n * @param base32 - Optional Base32Plugin to decode string secrets\n * @returns The secret as Uint8Array\n * @throws {Error} If secret is a string but no Base32Plugin is provided\n *\n * @example\n * ```ts\n * import { normalizeSecret } from '@otplib/core'\n * import { ScureBase32Plugin } from '@otplib/plugin-base32-scure'\n *\n * const base32 = new ScureBase32Plugin()\n *\n * // Uint8Array - returned as-is\n * const secret1 = normalizeSecret(new Uint8Array([1, 2, 3]))\n *\n * // Base32 string - automatically decoded\n * const secret2 = normalizeSecret('JBSWY3DPEHPK3PXP', base32)\n * ```\n */\nexport function normalizeSecret(\n secret: string | Uint8Array,\n base32?: { decode: (str: string) => Uint8Array },\n): Uint8Array {\n if (typeof secret === \"string\") {\n requireBase32Plugin(base32);\n return base32.decode(secret);\n }\n return secret;\n}\n\n/**\n * Generate a random Base32-encoded secret\n *\n * Creates a cryptographically secure random secret suitable for OTP generation.\n * The default length of 20 bytes (160 bits) matches RFC 4226 recommendations\n * and provides good security margin.\n *\n * @param options - Secret generation options\n * @returns Base32-encoded secret string (without padding for Google Authenticator compatibility)\n *\n * @example\n * ```ts\n * import { generateSecret } from '@otplib/core';\n * import { NodeCryptoPlugin } from '@otplib/plugin-crypto-node';\n * import { ScureBase32Plugin } from '@otplib/plugin-base32-scure';\n *\n * const secret = generateSecret({\n * crypto: new NodeCryptoPlugin(),\n * base32: new ScureBase32Plugin(),\n * });\n * // Returns: 'JBSWY3DPEHPK3PXP...' (32 characters)\n * ```\n *\n * @example Custom length\n * ```ts\n * const secret = generateSecret({\n * crypto: new NodeCryptoPlugin(),\n * base32: new ScureBase32Plugin(),\n * length: 32, // 256 bits for SHA-256\n * });\n * ```\n */\nexport function generateSecret(options: SecretOptions): string {\n const { crypto, base32, length = RECOMMENDED_SECRET_BYTES } = options;\n\n requireCryptoPlugin(crypto);\n requireBase32Plugin(base32);\n\n const randomBytes = crypto.randomBytes(length);\n return base32.encode(randomBytes, { padding: false });\n}\n\n/**\n * Normalize counter tolerance to an array of offsets\n *\n * Converts a number or array counter tolerance specification into an array of offsets\n * - Number: creates symmetric range [-tolerance, +tolerance]\n * - Array: uses the array as-is (already contains specific offsets)\n *\n * @param counterTolerance - Counter tolerance specification (number or array of offsets)\n * @returns Array of offsets to check\n *\n * @example\n * ```ts\n * normalizeCounterTolerance(0) // [0]\n * normalizeCounterTolerance(1) // [-1, 0, 1]\n * normalizeCounterTolerance(2) // [-2, -1, 0, 1, 2]\n * normalizeCounterTolerance([0, 1]) // [0, 1]\n * normalizeCounterTolerance([-1, 0, 1]) // [-1, 0, 1]\n * ```\n */\nexport function normalizeCounterTolerance(counterTolerance: number | number[] = 0): number[] {\n if (Array.isArray(counterTolerance)) {\n return counterTolerance;\n }\n\n const result: number[] = [];\n for (let i = -counterTolerance; i <= counterTolerance; i++) {\n // Bitwise OR with 0 converts -0 to 0 and preserves other integers\n result.push(i | 0);\n }\n return result;\n}\n\n/**\n * Normalize epoch tolerance to [past, future] tuple\n *\n * Converts a number or tuple epoch tolerance specification into a [past, future] tuple\n * - Number: creates symmetric tolerance [tolerance, tolerance]\n * - Tuple: uses the tuple as-is\n *\n * @param epochTolerance - Epoch tolerance specification (number or tuple [past, future])\n * @returns Tuple [pastTolerance, futureTolerance] in seconds\n *\n * @example\n * ```ts\n * normalizeEpochTolerance(0) // [0, 0]\n * normalizeEpochTolerance(30) // [30, 30]\n * normalizeEpochTolerance([5, 0]) // [5, 0]\n * normalizeEpochTolerance([10, 5]) // [10, 5]\n * ```\n */\nexport function normalizeEpochTolerance(\n epochTolerance: number | [number, number] = 0,\n): [number, number] {\n return Array.isArray(epochTolerance) ? epochTolerance : [epochTolerance, epochTolerance];\n}\n\n/**\n * Require crypto plugin to be configured\n *\n * @param crypto - The crypto plugin\n * @throws {CryptoPluginMissingError} If crypto plugin is not set\n */\nexport function requireCryptoPlugin<T>(crypto: T | undefined): asserts crypto is T {\n if (!crypto) {\n throw new CryptoPluginMissingError();\n }\n}\n\n/**\n * Require Base32 plugin to be configured\n *\n * @param base32 - The Base32 plugin\n * @throws {Base32PluginMissingError} If Base32 plugin is not set\n */\nexport function requireBase32Plugin<T>(base32: T | undefined): asserts base32 is T {\n if (!base32) {\n throw new Base32PluginMissingError();\n }\n}\n\n/**\n * Require secret to be configured\n *\n * @param secret - The secret value\n * @throws {SecretMissingError} If secret is not set\n */\nexport function requireSecret<T>(secret: T | undefined): asserts secret is T {\n if (!secret) {\n throw new SecretMissingError();\n }\n}\n\n/**\n * Require label to be configured (for URI generation)\n *\n * @param label - The label value\n * @throws {LabelMissingError} If label is not set\n */\nexport function requireLabel(label: string | undefined): asserts label is string {\n if (!label) {\n throw new LabelMissingError();\n }\n}\n\n/**\n * Require issuer to be configured (for URI generation)\n *\n * @param issuer - The issuer value\n * @throws {IssuerMissingError} If issuer is not set\n */\nexport function requireIssuer(issuer: string | undefined): asserts issuer is string {\n if (!issuer) {\n throw new IssuerMissingError();\n }\n}\n\n/**\n * Require secret to be a Base32 string (for URI generation)\n *\n * @param secret - The secret value\n * @throws {SecretTypeError} If secret is not a string\n */\nexport function requireBase32String(secret: string | Uint8Array): asserts secret is string {\n if (typeof secret !== \"string\") {\n throw new SecretTypeError();\n }\n}\n\n/**\n * Create a success result\n * @internal\n */\nfunction ok<T>(value: T): OTPResultOk<T> {\n return { ok: true, value };\n}\n\n/**\n * Create a failure result\n * @internal\n */\nfunction err<E>(error: E): OTPResultError<E> {\n return { ok: false, error };\n}\n\n/**\n * Wrap a synchronous function to return OTPResult instead of throwing\n *\n * Preserves the original OTPError subclass so users can access\n * specific error information via instanceof checks.\n *\n * @internal\n */\nexport function wrapResult<T, Args extends unknown[]>(\n fn: (...args: Args) => T,\n): (...args: Args) => OTPResult<T, OTPError> {\n return (...args: Args): OTPResult<T, OTPError> => {\n try {\n return ok(fn(...args));\n } catch (error) {\n return err(error as OTPError);\n }\n };\n}\n\n/**\n * Wrap an async function to return OTPResult instead of throwing\n *\n * Preserves the original OTPError subclass so users can access\n * specific error information via instanceof checks.\n *\n * @internal\n */\nexport function wrapResultAsync<T, Args extends unknown[]>(\n fn: (...args: Args) => Promise<T>,\n): (...args: Args) => Promise<OTPResult<T, OTPError>> {\n return async (...args: Args): Promise<OTPResult<T, OTPError>> => {\n try {\n return ok(await fn(...args));\n } catch (error) {\n return err(error as OTPError);\n }\n };\n}\n","import { HMACError, RandomBytesError } from \"./errors.js\";\n\nimport type { CryptoPlugin, HashAlgorithm } from \"./types.js\";\n\n/**\n * CryptoContext provides a unified interface for crypto operations\n * using a pluggable crypto backend\n */\nexport class CryptoContext {\n /**\n * Create a new CryptoContext with the given crypto plugin\n *\n * @param crypto - The crypto plugin to use\n */\n constructor(private readonly crypto: CryptoPlugin) {}\n\n /**\n * Get the underlying crypto plugin\n */\n get plugin(): CryptoPlugin {\n return this.crypto;\n }\n\n /**\n * Compute HMAC using the configured crypto plugin\n *\n * @param algorithm - The hash algorithm to use\n * @param key - The secret key as a byte array\n * @param data - The data to authenticate as a byte array\n * @returns HMAC digest as a byte array\n * @throws {HMACError} If HMAC computation fails\n */\n async hmac(algorithm: HashAlgorithm, key: Uint8Array, data: Uint8Array): Promise<Uint8Array> {\n try {\n const result = this.crypto.hmac(algorithm, key, data);\n return result instanceof Promise ? await result : result;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new HMACError(message, { cause: error });\n }\n }\n\n /**\n * Synchronous HMAC computation\n *\n * @param algorithm - The hash algorithm to use\n * @param key - The secret key as a byte array\n * @param data - The data to authenticate as a byte array\n * @returns HMAC digest as a byte array\n * @throws {HMACError} If HMAC computation fails or if crypto plugin doesn't support sync operations\n */\n hmacSync(algorithm: HashAlgorithm, key: Uint8Array, data: Uint8Array): Uint8Array {\n try {\n const result = this.crypto.hmac(algorithm, key, data);\n if (result instanceof Promise) {\n throw new HMACError(\"Crypto plugin does not support synchronous HMAC operations\");\n }\n return result;\n } catch (error) {\n if (error instanceof HMACError) {\n throw error;\n }\n const message = error instanceof Error ? error.message : String(error);\n throw new HMACError(message, { cause: error });\n }\n }\n\n /**\n * Generate cryptographically secure random bytes\n *\n * @param length - Number of random bytes to generate\n * @returns Random bytes\n * @throws {RandomBytesError} If random byte generation fails\n */\n randomBytes(length: number): Uint8Array {\n try {\n return this.crypto.randomBytes(length);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new RandomBytesError(message, { cause: error });\n }\n }\n}\n\n/**\n * Create a CryptoContext from a crypto plugin\n *\n * @param crypto - The crypto plugin to use\n * @returns A new CryptoContext instance\n */\nexport function createCryptoContext(crypto: CryptoPlugin): CryptoContext {\n return new CryptoContext(crypto);\n}\n","import { Base32EncodeError, Base32DecodeError } from \"./errors.js\";\n\nimport type { Base32Plugin, Base32EncodeOptions } from \"./types.js\";\n\n/**\n * Base32Context provides a unified interface for Base32 operations\n * using a pluggable Base32 backend.\n *\n * All errors from the underlying plugin are wrapped in otplib error types\n * with the original error preserved via the `cause` property.\n */\nexport class Base32Context {\n /**\n * Create a new Base32Context with the given Base32 plugin\n *\n * @param base32 - The Base32 plugin to use\n */\n constructor(private readonly base32: Base32Plugin) {}\n\n /**\n * Get the underlying Base32 plugin\n */\n get plugin(): Base32Plugin {\n return this.base32;\n }\n\n /**\n * Encode binary data to Base32 string using the configured plugin\n *\n * @param data - Uint8Array to encode\n * @param options - Encoding options\n * @returns Base32 encoded string\n * @throws {Base32EncodeError} If encoding fails\n */\n encode(data: Uint8Array, options?: Base32EncodeOptions): string {\n try {\n return this.base32.encode(data, options);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Base32EncodeError(message, { cause: error });\n }\n }\n\n /**\n * Decode Base32 string to binary data using the configured plugin\n *\n * @param str - Base32 string to decode\n * @returns Decoded Uint8Array\n * @throws {Base32DecodeError} If string contains invalid characters or decoding fails\n */\n decode(str: string): Uint8Array {\n try {\n return this.base32.decode(str);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Base32DecodeError(message, { cause: error });\n }\n }\n}\n\n/**\n * Create a Base32Context from a Base32 plugin\n *\n * @param base32 - The Base32 plugin to use\n * @returns A new Base32Context instance\n */\nexport function createBase32Context(base32: Base32Plugin): Base32Context {\n return new Base32Context(base32);\n}\n","/**\n * TypeScript Utility Types for otplib\n *\n * These types enhance developer experience by providing:\n * - Branded types for type-safe string handling\n * - Type guards for discriminated unions\n * - Helper types for option extraction\n */\n\nimport type { CryptoPlugin, Base32Plugin } from \"./types.js\";\n\n/**\n * Brand type for creating nominal types from primitives\n *\n * @example\n * ```ts\n * type UserId = Brand<string, 'UserId'>;\n * const id: UserId = 'abc' as UserId;\n * ```\n */\nexport type Brand<T, B extends string> = T & { readonly __brand: B };\n\n/**\n * Branded string type for Base32-encoded secrets\n *\n * Use this type to distinguish Base32-encoded secrets from regular strings\n * at compile time, preventing accidental misuse.\n *\n * @example\n * ```ts\n * import type { Base32Secret } from '@otplib/core';\n *\n * function processSecret(secret: Base32Secret): void {\n * // TypeScript ensures only Base32Secret values are passed\n * }\n *\n * const secret = generateSecret() as Base32Secret;\n * processSecret(secret); // OK\n * processSecret('random-string'); // Type error\n * ```\n */\nexport type Base32Secret = Brand<string, \"Base32Secret\">;\n\n/**\n * Branded string type for OTP tokens\n *\n * Use this type to distinguish OTP tokens from regular strings\n * at compile time, preventing accidental misuse.\n *\n * @example\n * ```ts\n * import type { OTPToken } from '@otplib/core';\n *\n * function validateToken(token: OTPToken): boolean {\n * // TypeScript ensures only OTPToken values are passed\n * }\n *\n * const token = await generate() as OTPToken;\n * validateToken(token); // OK\n * validateToken('123456'); // Type error\n * ```\n */\nexport type OTPToken = Brand<string, \"OTPToken\">;\n\n/**\n * Helper type to make all properties of T required except those in K\n *\n * @example\n * ```ts\n * type Options = { a?: string; b?: number; c?: boolean };\n * type RequiredAB = RequireKeys<Options, 'a' | 'b'>;\n * // { a: string; b: number; c?: boolean }\n * ```\n */\nexport type RequireKeys<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;\n\n/**\n * Helper type to make all properties of T optional except those in K\n *\n * @example\n * ```ts\n * type Options = { a: string; b: number; c: boolean };\n * type OptionalBC = OptionalKeys<Options, 'b' | 'c'>;\n * // { a: string; b?: number; c?: boolean }\n * ```\n */\nexport type OptionalKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;\n\n/**\n * Extract the plugin configuration from an options type\n *\n * @example\n * ```ts\n * type Plugins = PluginConfig<TOTPOptions>;\n * // { crypto: CryptoPlugin; base32?: Base32Plugin }\n * ```\n */\nexport type PluginConfig<T> = T extends { crypto?: CryptoPlugin; base32?: Base32Plugin }\n ? Pick<T, \"crypto\" | \"base32\">\n : never;\n\n/**\n * Ensure an options type has plugins defined\n *\n * @example\n * ```ts\n * type ConfiguredOptions = WithRequiredPlugins<TOTPOptions>;\n * // TOTPOptions with crypto and base32 required\n * ```\n */\nexport type WithRequiredPlugins<T extends { crypto?: CryptoPlugin; base32?: Base32Plugin }> = T & {\n crypto: CryptoPlugin;\n base32: Base32Plugin;\n};\n\n/**\n * Options type for OTP generation (crypto required)\n *\n * @example\n * ```ts\n * type MyGenerateOptions = GenerationReady<HOTPOptions>;\n * // HOTPOptions with crypto required\n * ```\n */\nexport type GenerationReady<T extends { crypto?: CryptoPlugin }> = T & {\n crypto: CryptoPlugin;\n};\n\n/**\n * Type predicate to check if an object has the required plugins\n *\n * @example\n * ```ts\n * const options = getOptions();\n * if (hasPlugins(options)) {\n * // TypeScript knows plugins are defined\n * options.crypto.hmac(...);\n * }\n * ```\n */\nexport function hasPlugins<T extends { crypto?: CryptoPlugin; base32?: Base32Plugin }>(\n options: T,\n): options is T & { crypto: CryptoPlugin; base32: Base32Plugin } {\n return options.crypto !== undefined && options.base32 !== undefined;\n}\n\n/**\n * Type predicate to check if an object has a crypto plugin\n *\n * @example\n * ```ts\n * if (hasCrypto(options)) {\n * await options.crypto.hmac('sha1', key, data);\n * }\n * ```\n */\nexport function hasCrypto<T extends { crypto?: CryptoPlugin }>(\n options: T,\n): options is T & { crypto: CryptoPlugin } {\n return options.crypto !== undefined;\n}\n\n/**\n * Type predicate to check if an object has a base32 plugin\n *\n * @example\n * ```ts\n * if (hasBase32(options)) {\n * const decoded = options.base32.decode(secret);\n * }\n * ```\n */\nexport function hasBase32<T extends { base32?: Base32Plugin }>(\n options: T,\n): options is T & { base32: Base32Plugin } {\n return options.base32 !== undefined;\n}\n\n/**\n * Narrow union type by a specific property value\n *\n * @example\n * ```ts\n * type Result = VerifyResultValid | VerifyResultInvalid;\n * type ValidOnly = NarrowBy<Result, 'valid', true>;\n * // VerifyResultValid\n * ```\n */\nexport type NarrowBy<T, K extends keyof T, V extends T[K]> = T extends { [key in K]: V }\n ? T\n : never;\n"],"mappings":"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","names":["src_exports","__export","AlgorithmError","Base32Context","Base32DecodeError","Base32EncodeError","Base32Error","Base32PluginMissingError","ConfigurationError","CounterError","CounterNegativeError","CounterOverflowError","CounterToleranceError","CounterToleranceTooLargeError","CryptoContext","CryptoError","CryptoPluginMissingError","DEFAULT_PERIOD","DigitsError","EpochToleranceError","EpochToleranceNegativeError","EpochToleranceTooLargeError","HMACError","IssuerMissingError","LabelMissingError","MAX_COUNTER","MAX_PERIOD","MAX_SECRET_BYTES","MAX_WINDOW","MIN_PERIOD","MIN_SECRET_BYTES","OTPError","PeriodError","PeriodTooLargeError","PeriodTooSmallError","PluginError","RECOMMENDED_SECRET_BYTES","RandomBytesError","SecretError","SecretMissingError","SecretTooLongError","SecretTooShortError","SecretTypeError","TimeError","TimeNegativeError","TokenError","TokenFormatError","TokenLengthError","constantTimeEqual","counterToBytes","createBase32Context","createCryptoContext","createGuardrails","dynamicTruncate","generateSecret","getDigestSize","hasBase32","hasCrypto","hasGuardrailOverrides","hasPlugins","hexToBytes","normalizeCounterTolerance","normalizeEpochTolerance","normalizeSecret","requireBase32Plugin","requireBase32String","requireCryptoPlugin","requireIssuer","requireLabel","requireSecret","stringToBytes","truncateDigits","validateByteLengthEqual","validateCounter","validateCounterTolerance","validateEpochTolerance","validatePeriod","validateSecret","validateTime","validateToken","wrapResult","wrapResultAsync","__toCommonJS","OTPError","message","options","SecretError","SecretTooShortError","minBytes","actualBytes","SecretTooLongError","maxBytes","CounterError","CounterNegativeError","CounterOverflowError","TimeError","TimeNegativeError","PeriodError","PeriodTooSmallError","minPeriod","PeriodTooLargeError","maxPeriod","DigitsError","AlgorithmError","TokenError","TokenLengthError","expected","actual","TokenFormatError","CryptoError","HMACError","RandomBytesError","Base32Error","Base32EncodeError","Base32DecodeError","CounterToleranceError","CounterToleranceTooLargeError","maxTolerance","actualSize","EpochToleranceError","EpochToleranceNegativeError","EpochToleranceTooLargeError","actualValue","PluginError","CryptoPluginMissingError","Base32PluginMissingError","ConfigurationError","SecretMissingError","LabelMissingError","IssuerMissingError","SecretTypeError","textEncoder","MIN_SECRET_BYTES","MAX_SECRET_BYTES","RECOMMENDED_SECRET_BYTES","MIN_PERIOD","MAX_PERIOD","DEFAULT_PERIOD","MAX_COUNTER","MAX_WINDOW","OVERRIDE_SYMBOL","DEFAULT_GUARDRAILS","createGuardrails","custom","hasGuardrailOverrides","guardrails","validateSecret","secret","SecretTooShortError","SecretTooLongError","validateCounter","counter","value","CounterNegativeError","CounterOverflowError","validateTime","time","TimeNegativeError","validatePeriod","period","PeriodTooSmallError","PeriodTooLargeError","validateToken","token","digits","TokenLengthError","TokenFormatError","validateCounterTolerance","counterTolerance","CounterToleranceTooLargeError","validateEpochTolerance","epochTolerance","pastTolerance","futureTolerance","EpochToleranceNegativeError","maxToleranceSeconds","maxAllowed","EpochToleranceTooLargeError","counterToBytes","bigintValue","buffer","dynamicTruncate","hmacResult","offset","truncateDigits","maxOtp","validateByteLengthEqual","a","b","constantTimeEqual","bufA","stringToBytes","bufB","result","getDigestSize","algorithm","hexToBytes","hex","bytes","i","normalizeSecret","base32","requireBase32Plugin","generateSecret","options","crypto","length","requireCryptoPlugin","randomBytes","normalizeCounterTolerance","normalizeEpochTolerance","CryptoPluginMissingError","Base32PluginMissingError","requireSecret","SecretMissingError","requireLabel","label","LabelMissingError","requireIssuer","issuer","IssuerMissingError","requireBase32String","SecretTypeError","ok","err","error","wrapResult","fn","args","wrapResultAsync","CryptoContext","crypto","algorithm","key","data","result","error","message","HMACError","length","RandomBytesError","createCryptoContext","Base32Context","base32","data","options","error","message","Base32EncodeError","str","Base32DecodeError","createBase32Context","hasPlugins","options","hasCrypto","hasBase32"]}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/utils.ts","../src/crypto-context.ts","../src/base32-context.ts","../src/utility-types.ts"],"sourcesContent":["/**\n * Options for OTPError construction\n */\nexport type OTPErrorOptions = {\n /**\n * The underlying error that caused this error.\n * Useful for error chaining and debugging.\n */\n cause?: unknown;\n};\n\n/**\n * Base error class for all otplib errors\n *\n * Supports ES2022 error chaining via the `cause` property.\n *\n * @example\n * ```typescript\n * try {\n * // ... operation that throws\n * } catch (error) {\n * throw new OTPError('Operation failed', { cause: error });\n * }\n * ```\n */\nexport class OTPError extends Error {\n constructor(message: string, options?: OTPErrorOptions) {\n super(message, options);\n this.name = \"OTPError\";\n }\n}\n\n/**\n * Error thrown when secret validation fails\n */\nexport class SecretError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"SecretError\";\n }\n}\n\n/**\n * Error thrown when secret is too short (< 128 bits)\n */\nexport class SecretTooShortError extends SecretError {\n constructor(minBytes: number, actualBytes: number) {\n super(\n `Secret must be at least ${minBytes} bytes (${minBytes * 8} bits), got ${actualBytes} bytes`,\n );\n this.name = \"SecretTooShortError\";\n }\n}\n\n/**\n * Error thrown when secret is unreasonably large (> 64 bytes)\n */\nexport class SecretTooLongError extends SecretError {\n constructor(maxBytes: number, actualBytes: number) {\n super(`Secret must not exceed ${maxBytes} bytes, got ${actualBytes} bytes`);\n this.name = \"SecretTooLongError\";\n }\n}\n\n/**\n * Error thrown when counter is invalid\n */\nexport class CounterError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"CounterError\";\n }\n}\n\n/**\n * Error thrown when counter is negative\n */\nexport class CounterNegativeError extends CounterError {\n constructor() {\n super(\"Counter must be non-negative\");\n this.name = \"CounterNegativeError\";\n }\n}\n\n/**\n * Error thrown when counter exceeds maximum value (2^53 - 1 for safe integer)\n */\nexport class CounterOverflowError extends CounterError {\n constructor() {\n super(\"Counter exceeds maximum safe integer value\");\n this.name = \"CounterOverflowError\";\n }\n}\n\n/**\n * Error thrown when time is invalid\n */\nexport class TimeError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"TimeError\";\n }\n}\n\n/**\n * Error thrown when time is negative\n */\nexport class TimeNegativeError extends TimeError {\n constructor() {\n super(\"Time must be non-negative\");\n this.name = \"TimeNegativeError\";\n }\n}\n\n/**\n * Error thrown when period is invalid\n */\nexport class PeriodError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"PeriodError\";\n }\n}\n\n/**\n * Error thrown when period is too small\n */\nexport class PeriodTooSmallError extends PeriodError {\n constructor(minPeriod: number) {\n super(`Period must be at least ${minPeriod} second(s)`);\n this.name = \"PeriodTooSmallError\";\n }\n}\n\n/**\n * Error thrown when period is too large\n */\nexport class PeriodTooLargeError extends PeriodError {\n constructor(maxPeriod: number) {\n super(`Period must not exceed ${maxPeriod} seconds`);\n this.name = \"PeriodTooLargeError\";\n }\n}\n\n/**\n * Error thrown when digits value is invalid\n */\nexport class DigitsError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"DigitsError\";\n }\n}\n\n/**\n * Error thrown when hash algorithm is invalid\n */\nexport class AlgorithmError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"AlgorithmError\";\n }\n}\n\n/**\n * Error thrown when token is invalid\n */\nexport class TokenError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"TokenError\";\n }\n}\n\n/**\n * Error thrown when token has incorrect length\n */\nexport class TokenLengthError extends TokenError {\n constructor(expected: number, actual: number) {\n super(`Token must be ${expected} digits, got ${actual}`);\n this.name = \"TokenLengthError\";\n }\n}\n\n/**\n * Error thrown when token contains non-digit characters\n */\nexport class TokenFormatError extends TokenError {\n constructor() {\n super(\"Token must contain only digits\");\n this.name = \"TokenFormatError\";\n }\n}\n\n/**\n * Error thrown when crypto operation fails\n */\nexport class CryptoError extends OTPError {\n constructor(message: string, options?: OTPErrorOptions) {\n super(message, options);\n this.name = \"CryptoError\";\n }\n}\n\n/**\n * Error thrown when HMAC computation fails\n *\n * The original error from the crypto plugin is available via `cause`.\n *\n * @example\n * ```typescript\n * try {\n * await cryptoContext.hmac('sha1', key, data);\n * } catch (error) {\n * if (error instanceof HMACError) {\n * console.log('HMAC failed:', error.message);\n * console.log('Original error:', error.cause);\n * }\n * }\n * ```\n */\nexport class HMACError extends CryptoError {\n constructor(message: string, options?: OTPErrorOptions) {\n super(`HMAC computation failed: ${message}`, options);\n this.name = \"HMACError\";\n }\n}\n\n/**\n * Error thrown when random byte generation fails\n *\n * The original error from the crypto plugin is available via `cause`.\n */\nexport class RandomBytesError extends CryptoError {\n constructor(message: string, options?: OTPErrorOptions) {\n super(`Random byte generation failed: ${message}`, options);\n this.name = \"RandomBytesError\";\n }\n}\n\n/**\n * Error thrown when Base32 operation fails\n */\nexport class Base32Error extends OTPError {\n constructor(message: string, options?: OTPErrorOptions) {\n super(message, options);\n this.name = \"Base32Error\";\n }\n}\n\n/**\n * Error thrown when Base32 encoding fails\n *\n * The original error from the Base32 plugin is available via `cause`.\n *\n * @example\n * ```typescript\n * try {\n * base32Context.encode(data);\n * } catch (error) {\n * if (error instanceof Base32EncodeError) {\n * console.log('Encoding failed:', error.message);\n * console.log('Original error:', error.cause);\n * }\n * }\n * ```\n */\nexport class Base32EncodeError extends Base32Error {\n constructor(message: string, options?: OTPErrorOptions) {\n super(`Base32 encoding failed: ${message}`, options);\n this.name = \"Base32EncodeError\";\n }\n}\n\n/**\n * Error thrown when Base32 decoding fails\n *\n * The original error from the Base32 plugin is available via `cause`.\n *\n * @example\n * ```typescript\n * try {\n * base32Context.decode(invalidString);\n * } catch (error) {\n * if (error instanceof Base32DecodeError) {\n * console.log('Decoding failed:', error.message);\n * console.log('Original error:', error.cause);\n * }\n * }\n * ```\n */\nexport class Base32DecodeError extends Base32Error {\n constructor(message: string, options?: OTPErrorOptions) {\n super(`Base32 decoding failed: ${message}`, options);\n this.name = \"Base32DecodeError\";\n }\n}\n\n/**\n * Error thrown when counter tolerance is invalid\n */\nexport class CounterToleranceError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"CounterToleranceError\";\n }\n}\n\n/**\n * Error thrown when counter tolerance is too large\n */\nexport class CounterToleranceTooLargeError extends CounterToleranceError {\n constructor(maxTolerance: number, actualSize: number) {\n super(\n `Counter tolerance size must not exceed ${maxTolerance}, got ${actualSize}. ` +\n `Large tolerances can cause performance issues.`,\n );\n this.name = \"CounterToleranceTooLargeError\";\n }\n}\n\n/**\n * Error thrown when epoch tolerance is invalid\n */\nexport class EpochToleranceError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"EpochToleranceError\";\n }\n}\n\n/**\n * Error thrown when epoch tolerance contains negative values\n */\nexport class EpochToleranceNegativeError extends EpochToleranceError {\n constructor() {\n super(\"Epoch tolerance cannot contain negative values\");\n this.name = \"EpochToleranceNegativeError\";\n }\n}\n\n/**\n * Error thrown when epoch tolerance is too large\n */\nexport class EpochToleranceTooLargeError extends EpochToleranceError {\n constructor(maxTolerance: number, actualValue: number) {\n super(\n `Epoch tolerance must not exceed ${maxTolerance} seconds, got ${actualValue}. ` +\n `Large tolerances can cause performance issues.`,\n );\n this.name = \"EpochToleranceTooLargeError\";\n }\n}\n\n/**\n * Error thrown when a required plugin is missing\n */\nexport class PluginError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"PluginError\";\n }\n}\n\n/**\n * Error thrown when crypto plugin is not configured\n */\nexport class CryptoPluginMissingError extends PluginError {\n constructor() {\n super(\"Crypto plugin is required.\");\n this.name = \"CryptoPluginMissingError\";\n }\n}\n\n/**\n * Error thrown when Base32 plugin is not configured\n */\nexport class Base32PluginMissingError extends PluginError {\n constructor() {\n super(\"Base32 plugin is required.\");\n this.name = \"Base32PluginMissingError\";\n }\n}\n\n/**\n * Error thrown when required configuration is missing\n */\nexport class ConfigurationError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigurationError\";\n }\n}\n\n/**\n * Error thrown when secret is not configured\n */\nexport class SecretMissingError extends ConfigurationError {\n constructor() {\n super(\n \"Secret is required. \" +\n \"Use generateSecret() to create one, or provide via { secret: 'YOUR_BASE32_SECRET' }\",\n );\n this.name = \"SecretMissingError\";\n }\n}\n\n/**\n * Error thrown when label is not configured (required for URI generation)\n */\nexport class LabelMissingError extends ConfigurationError {\n constructor() {\n super(\"Label is required for URI generation. Example: { label: 'user@example.com' }\");\n this.name = \"LabelMissingError\";\n }\n}\n\n/**\n * Error thrown when issuer is not configured (required for URI generation)\n */\nexport class IssuerMissingError extends ConfigurationError {\n constructor() {\n super(\"Issuer is required for URI generation. Example: { issuer: 'MyApp' }\");\n this.name = \"IssuerMissingError\";\n }\n}\n\n/**\n * Error thrown when secret must be a Base32 string but is provided as bytes\n */\nexport class SecretTypeError extends ConfigurationError {\n constructor() {\n super(\n \"Class API requires secret to be a Base32 string, not Uint8Array. \" +\n \"Use generateSecret() or provide a Base32-encoded string.\",\n );\n this.name = \"SecretTypeError\";\n }\n}\n","import {\n OTPError,\n SecretTooShortError,\n SecretTooLongError,\n CounterNegativeError,\n CounterOverflowError,\n TimeNegativeError,\n PeriodTooSmallError,\n PeriodTooLargeError,\n TokenLengthError,\n TokenFormatError,\n CounterToleranceTooLargeError,\n EpochToleranceNegativeError,\n EpochToleranceTooLargeError,\n CryptoPluginMissingError,\n Base32PluginMissingError,\n SecretMissingError,\n LabelMissingError,\n IssuerMissingError,\n SecretTypeError,\n} from \"./errors.js\";\n\nimport type {\n HashAlgorithm,\n SecretOptions,\n OTPResultOk,\n OTPResultError,\n OTPResult,\n} from \"./types.js\";\n\n/**\n * Singleton TextEncoder instance to avoid repeated allocations\n */\nconst textEncoder = new TextEncoder();\n\n/**\n * Minimum secret length in bytes (128 bits as per RFC 4226)\n */\nexport const MIN_SECRET_BYTES = 16;\n\n/**\n * Maximum secret length in bytes (512 bits)\n *\n * The 64-byte maximum is not part of the RFCs.\n * This is to prevent excessive memory usage in HMAC operations.\n */\nexport const MAX_SECRET_BYTES = 64;\n\n/**\n * Recommended secret length in bytes (160 bits as per RFC 4226)\n */\nexport const RECOMMENDED_SECRET_BYTES = 20;\n\n/**\n * Minimum period in seconds\n */\nexport const MIN_PERIOD = 1;\n\n/**\n * Maximum period in seconds (1 hour)\n */\nexport const MAX_PERIOD = 3600;\n\n/**\n * Default period in seconds (30 seconds as per RFC 6238)\n */\nexport const DEFAULT_PERIOD = 30;\n\n/**\n * Maximum safe integer for counter (2^53 - 1)\n */\nexport const MAX_COUNTER = Number.MAX_SAFE_INTEGER;\n\n/**\n * Maximum verification window size\n *\n * Limits the number of HMAC computations during verification to prevent DoS attacks.\n * A window of 100 means up to 201 HMAC computations ([-100, +100] range).\n *\n * For TOTP: window=1 is typically sufficient (allows +-30 seconds clock drift)\n * For HOTP: window=10-50 handles reasonable counter desynchronization\n */\nexport const MAX_WINDOW = 100;\n\n/**\n * Configurable guardrails for OTP validation\n *\n * Allows overriding default safety limits for non-standard production requirements.\n * Use with caution - custom guardrails can weaken security.\n */\nexport type OTPGuardrailsConfig = {\n MIN_SECRET_BYTES: number;\n MAX_SECRET_BYTES: number;\n MIN_PERIOD: number;\n MAX_PERIOD: number;\n MAX_COUNTER: number;\n MAX_WINDOW: number;\n};\n\n/**\n * Module-private symbol to track guardrail override status\n *\n * This symbol is used as a property key to store whether guardrails contain custom values.\n * Being module-private and a symbol ensures:\n * - Cannot be accessed outside this module (not exported)\n * - Cannot be recreated (each Symbol() call is unique)\n * - Hidden from normal enumeration (Object.keys, JSON.stringify, for-in)\n * - Minimal memory overhead (~1 byte per object)\n * - No garbage collection concerns\n *\n * @internal\n */\nconst OVERRIDE_SYMBOL = Symbol(\"otplib.guardrails.override\");\n\n/**\n * Complete guardrails configuration\n *\n * This represents the final, immutable configuration used by validation functions.\n * Internally tracks whether any values were overridden from RFC recommendations,\n * enabling security auditing and compliance monitoring without exposing implementation\n * details in the public API.\n *\n * The override status is stored using a module-private Symbol that cannot be accessed\n * or recreated outside this module, providing true encapsulation.\n *\n * @see {@link OTPGuardrailsConfig} for the base configuration structure\n * @see {@link createGuardrails} for creating guardrails instances\n * @see {@link hasGuardrailOverrides} to check if guardrails were customized\n */\nexport type OTPGuardrails = Readonly<OTPGuardrailsConfig> & {\n [OVERRIDE_SYMBOL]?: boolean;\n};\n\n/**\n * Default guardrails matching RFC recommendations\n *\n * Frozen to ensure immutability. Used as default parameter for validation functions.\n * For custom guardrails, use the createGuardrails() factory function.\n */\nconst DEFAULT_GUARDRAILS: OTPGuardrails = Object.freeze({\n MIN_SECRET_BYTES,\n MAX_SECRET_BYTES,\n MIN_PERIOD,\n MAX_PERIOD,\n MAX_COUNTER,\n MAX_WINDOW,\n [OVERRIDE_SYMBOL]: false,\n});\n\n/**\n * Create guardrails configuration object\n *\n * Factory function that merges custom guardrails with defaults and returns\n * an immutable (frozen) object. Validates custom guardrails to ensure they\n * maintain basic safety invariants.\n *\n * When called without arguments or with `undefined`, returns the default guardrails\n * singleton (optimized to avoid unnecessary allocations). When called with custom\n * values, creates a new frozen object and internally marks it as overridden.\n *\n * @param custom - Optional partial guardrails to override defaults\n * @returns Frozen guardrails object\n * @throws {Error} If custom guardrails violate safety invariants\n *\n * @example Basic usage\n * ```ts\n * import { createGuardrails, hasGuardrailOverrides } from '@otplib/core'\n *\n * // Returns default singleton (no overrides)\n * const defaults = createGuardrails();\n * hasGuardrailOverrides(defaults); // false\n *\n * // Creates new object with overrides\n * const custom = createGuardrails({\n * MIN_SECRET_BYTES: 8,\n * MAX_WINDOW: 200\n * });\n * hasGuardrailOverrides(custom); // true\n * ```\n *\n * @example Monitoring custom guardrails\n * ```ts\n * import { createGuardrails, hasGuardrailOverrides } from '@otplib/core';\n *\n * const guardrails = createGuardrails({ MAX_WINDOW: 20 });\n *\n * if (hasGuardrailOverrides(guardrails)) {\n * logger.warn('Non-default guardrails in use', { guardrails });\n * }\n * ```\n *\n * @see {@link hasGuardrailOverrides} to check if guardrails were customized\n */\nexport function createGuardrails(custom?: Partial<OTPGuardrailsConfig>): OTPGuardrails {\n if (!custom) {\n return DEFAULT_GUARDRAILS;\n }\n\n return Object.freeze({\n ...DEFAULT_GUARDRAILS,\n ...custom,\n [OVERRIDE_SYMBOL]: true,\n });\n}\n\n/**\n * Check if guardrails contain custom overrides\n *\n * Returns `true` if the guardrails object was created with custom values,\n * `false` if using RFC-recommended defaults. Useful for security auditing,\n * compliance monitoring, and development warnings.\n *\n * This function accesses a module-private Symbol property that cannot be\n * accessed or modified outside this module, ensuring reliable detection.\n *\n * @param guardrails - The guardrails object to check\n * @returns `true` if guardrails were customized, `false` if using defaults\n *\n * @example Security monitoring\n * ```ts\n * import { createGuardrails, hasGuardrailOverrides } from '@otplib/core';\n *\n * const guardrails = createGuardrails({ MAX_WINDOW: 20 });\n *\n * if (hasGuardrailOverrides(guardrails)) {\n * console.warn('Custom guardrails detected:', guardrails);\n * // Log to security audit system\n * }\n * ```\n *\n * @example Compliance check\n * ```ts\n * function validateGuardrails(guardrails: OTPGuardrails) {\n * if (hasGuardrailOverrides(guardrails)) {\n * throw new Error('Custom guardrails not allowed in production');\n * }\n * }\n * ```\n */\nexport function hasGuardrailOverrides(guardrails: OTPGuardrails): boolean {\n return guardrails[OVERRIDE_SYMBOL] ?? false;\n}\n\n/**\n * Validate secret key\n *\n * @param secret - The secret to validate\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {SecretTooShortError} If secret is too short\n * @throws {SecretTooLongError} If secret is too long\n */\nexport function validateSecret(\n secret: Uint8Array,\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n if (secret.length < guardrails.MIN_SECRET_BYTES) {\n throw new SecretTooShortError(guardrails.MIN_SECRET_BYTES, secret.length);\n }\n\n if (secret.length > guardrails.MAX_SECRET_BYTES) {\n throw new SecretTooLongError(guardrails.MAX_SECRET_BYTES, secret.length);\n }\n}\n\n/**\n * Validate counter value\n *\n * @param counter - The counter to validate\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {CounterNegativeError} If counter is negative\n * @throws {CounterOverflowError} If counter exceeds safe integer\n */\nexport function validateCounter(\n counter: number | bigint,\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n const value = typeof counter === \"bigint\" ? counter : BigInt(counter);\n\n if (value < 0n) {\n throw new CounterNegativeError();\n }\n\n if (value > BigInt(guardrails.MAX_COUNTER)) {\n throw new CounterOverflowError();\n }\n}\n\n/**\n * Validate time value\n *\n * @param time - The time in seconds to validate\n * @throws {TimeNegativeError} If time is negative\n */\nexport function validateTime(time: number): void {\n if (time < 0) {\n throw new TimeNegativeError();\n }\n}\n\n/**\n * Validate period value\n *\n * @param period - The period in seconds to validate\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {PeriodTooSmallError} If period is too small\n * @throws {PeriodTooLargeError} If period is too large\n */\nexport function validatePeriod(\n period: number,\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n if (!Number.isInteger(period) || period < guardrails.MIN_PERIOD) {\n throw new PeriodTooSmallError(guardrails.MIN_PERIOD);\n }\n\n if (period > guardrails.MAX_PERIOD) {\n throw new PeriodTooLargeError(guardrails.MAX_PERIOD);\n }\n}\n\n/**\n * Validate token\n *\n * @param token - The token string to validate\n * @param digits - Expected number of digits\n * @throws {TokenLengthError} If token has incorrect length\n * @throws {TokenFormatError} If token contains non-digit characters\n */\nexport function validateToken(token: string, digits: number): void {\n if (token.length !== digits) {\n throw new TokenLengthError(digits, token.length);\n }\n\n if (!/^\\d+$/.test(token)) {\n throw new TokenFormatError();\n }\n}\n\n/**\n * Validate counter tolerance for HOTP verification\n *\n * Prevents DoS attacks by limiting the number of counter values checked.\n *\n * @param counterTolerance - Counter tolerance specification (number or array of offsets)\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {CounterToleranceTooLargeError} If tolerance size exceeds MAX_WINDOW\n *\n * @example\n * ```ts\n * validateCounterTolerance(1); // OK: 3 offsets [-1, 0, 1]\n * validateCounterTolerance(100); // OK: 201 offsets [-100, ..., 100]\n * validateCounterTolerance(101); // Throws: exceeds MAX_WINDOW\n * validateCounterTolerance([0, 1]); // OK: 2 offsets\n * ```\n */\nexport function validateCounterTolerance(\n counterTolerance: number | number[],\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n const size = Array.isArray(counterTolerance) ? counterTolerance.length : counterTolerance * 2 + 1;\n\n if (size > guardrails.MAX_WINDOW * 2 + 1) {\n throw new CounterToleranceTooLargeError(\n guardrails.MAX_WINDOW,\n Array.isArray(counterTolerance) ? counterTolerance.length : counterTolerance,\n );\n }\n}\n\n/**\n * Validate epoch tolerance for TOTP verification\n *\n * Prevents DoS attacks by limiting the time range checked.\n * Also validates that tolerance values are non-negative.\n *\n * @param epochTolerance - Epoch tolerance specification (number or tuple [past, future])\n * @param period - The TOTP period in seconds (default: 30). Used to calculate max tolerance.\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {EpochToleranceNegativeError} If tolerance contains negative values\n * @throws {EpochToleranceTooLargeError} If tolerance exceeds MAX_WINDOW periods\n *\n * @example\n * ```ts\n * validateEpochTolerance(30); // OK: 30 seconds (default period 30s)\n * validateEpochTolerance([5, 0]); // OK: 5 seconds past only\n * validateEpochTolerance([-5, 0]); // Throws: negative values not allowed\n * validateEpochTolerance(3600); // Throws: exceeds MAX_WINDOW * period\n * validateEpochTolerance(6000, 60); // OK with 60s period (MAX_WINDOW * 60 = 6000)\n * ```\n */\nexport function validateEpochTolerance(\n epochTolerance: number | [number, number],\n period: number = DEFAULT_PERIOD,\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n const [pastTolerance, futureTolerance] = Array.isArray(epochTolerance)\n ? epochTolerance\n : [epochTolerance, epochTolerance];\n\n // Check for negative values\n if (pastTolerance < 0 || futureTolerance < 0) {\n throw new EpochToleranceNegativeError();\n }\n\n // Check total tolerance doesn't exceed reasonable limits\n // Convert to periods and check against MAX_WINDOW\n const maxToleranceSeconds = guardrails.MAX_WINDOW * period;\n const maxAllowed = Math.max(pastTolerance, futureTolerance);\n\n if (maxAllowed > maxToleranceSeconds) {\n throw new EpochToleranceTooLargeError(maxToleranceSeconds, maxAllowed);\n }\n}\n\n/**\n * Convert counter to 8-byte big-endian array\n *\n * Per RFC 4226 Section 5.1, the counter value is represented as an 8-byte\n * big-endian (network byte order) unsigned integer.\n *\n * @see {@link https://tools.ietf.org/html/rfc4226#section-5.1 | RFC 4226 Section 5.1 - Symbol Descriptions}\n *\n * @param value - The counter value to convert\n * @returns 8-byte big-endian array\n */\nexport function counterToBytes(value: number | bigint): Uint8Array {\n const bigintValue = typeof value === \"bigint\" ? value : BigInt(value);\n const buffer = new ArrayBuffer(8);\n const view = new DataView(buffer);\n\n view.setBigUint64(0, bigintValue, false);\n\n return new Uint8Array(buffer);\n}\n\n/**\n * Perform Dynamic Truncation as per RFC 4226 Section 5.3\n *\n * The algorithm:\n * 1. Take the low-order 4 bits of the last byte as offset\n * 2. Extract 4 bytes starting at offset\n * 3. Mask the most significant bit to get a 31-bit unsigned integer\n *\n * This ensures consistent extraction across different HMAC output sizes\n * while producing a value that fits in a signed 32-bit integer.\n *\n * @see {@link https://tools.ietf.org/html/rfc4226#section-5.3 | RFC 4226 Section 5.3 - Generating an HOTP Value}\n *\n * @param hmacResult - HMAC result (at least 20 bytes for SHA-1)\n * @returns Truncated 31-bit unsigned integer\n */\nexport function dynamicTruncate(hmacResult: Uint8Array): number {\n const offset = hmacResult[hmacResult.length - 1] & 0x0f;\n\n const binary =\n ((hmacResult[offset] & 0x7f) << 24) |\n (hmacResult[offset + 1] << 16) |\n (hmacResult[offset + 2] << 8) |\n hmacResult[offset + 3];\n\n return binary;\n}\n\n/**\n * Convert truncated integer to OTP string with specified digits\n *\n * Computes: Snum mod 10^Digit (RFC 4226 Section 5.3)\n *\n * The result is zero-padded to ensure consistent length,\n * as required for proper token comparison.\n *\n * @see {@link https://tools.ietf.org/html/rfc4226#section-5.3 | RFC 4226 Section 5.3 - Generating an HOTP Value}\n *\n * @param value - The truncated integer value (Snum)\n * @param digits - Number of digits for the OTP (Digit, typically 6-8)\n * @returns OTP string with leading zeros if necessary\n */\nexport function truncateDigits(value: number, digits: number): string {\n const maxOtp = 10 ** digits;\n const otp = value % maxOtp;\n return otp.toString().padStart(digits, \"0\");\n}\n\n/**\n * Validate that two byte arrays have equal length\n *\n * Useful as a preliminary check before performing byte-by-byte comparisons.\n *\n * @param a - First byte array\n * @param b - Second byte array\n * @returns true if arrays have equal length, false otherwise\n */\nexport function validateByteLengthEqual(a: Uint8Array, b: Uint8Array): boolean {\n return a.length === b.length;\n}\n\n/**\n * Constant-time comparison to prevent timing attacks\n *\n * This implements a timing-safe equality check as recommended in\n * RFC 4226 Section 7.2 for token validation to prevent\n * timing side-channel attacks.\n *\n * @see {@link https://tools.ietf.org/html/rfc4226#section-7.2 | RFC 4226 Section 7.2 - Validation and Verification}\n *\n * @param a - First value to compare\n * @param b - Second value to compare\n * @returns true if values are equal, false otherwise\n */\nexport function constantTimeEqual(a: string | Uint8Array, b: string | Uint8Array): boolean {\n const bufA = stringToBytes(a);\n const bufB = stringToBytes(b);\n\n if (!validateByteLengthEqual(bufA, bufB)) {\n return false;\n }\n\n let result = 0;\n for (let i = 0; i < bufA.length; i++) {\n result |= bufA[i] ^ bufB[i];\n }\n\n return result === 0;\n}\n\n/**\n * Get HMAC digest size in bytes for a given algorithm\n *\n * @param algorithm - The hash algorithm\n * @returns Digest size in bytes\n */\nexport function getDigestSize(algorithm: HashAlgorithm): number {\n switch (algorithm) {\n case \"sha1\":\n return 20;\n case \"sha256\":\n return 32;\n case \"sha512\":\n return 64;\n }\n}\n\n/**\n * Convert a string or Uint8Array to Uint8Array\n *\n * This utility function normalizes input to Uint8Array, converting strings\n * using UTF-8 encoding. Uint8Array inputs are returned as-is.\n *\n * @param value - The value to convert (string or Uint8Array)\n * @returns The value as a Uint8Array (UTF-8 encoded for strings)\n *\n * @example\n * ```ts\n * import { stringToBytes } from '@otplib/core'\n *\n * const bytes1 = stringToBytes('1234567890123456')\n * // Returns: Uint8Array([49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54])\n *\n * const bytes2 = stringToBytes(new Uint8Array([1, 2, 3]))\n * // Returns: Uint8Array([1, 2, 3]) - returned as-is\n * ```\n */\nexport function stringToBytes(value: string | Uint8Array): Uint8Array {\n return typeof value === \"string\" ? textEncoder.encode(value) : value;\n}\n\n/**\n * Convert a hex string to a Uint8Array\n *\n * This is useful for working with RFC test vectors and debugging HMAC outputs,\n * which are commonly represented as hexadecimal strings.\n *\n * If your environment supports it, consider using `Uint8Array.fromHex()` instead.\n *\n * @param hex - The hex string to convert (lowercase or uppercase, no 0x prefix)\n * @returns The bytes as a Uint8Array\n *\n * @example\n * ```ts\n * import { hexToBytes } from '@otplib/core'\n *\n * // Convert RFC 4226 HMAC test vector\n * const hmac = hexToBytes('cc93cf18508d94934c64b65d8ba7667fb7cde4b0')\n * // Returns: Uint8Array([0xcc, 0x93, 0xcf, ...])\n * ```\n */\nexport function hexToBytes(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);\n }\n return bytes;\n}\n\n/**\n * Normalize secret input to Uint8Array\n *\n * Accepts either a Base32-encoded string or Uint8Array and returns Uint8Array.\n * If a Base32Plugin is provided, string secrets will be automatically decoded.\n *\n * @param secret - The secret to normalize (string or Uint8Array)\n * @param base32 - Optional Base32Plugin to decode string secrets\n * @returns The secret as Uint8Array\n * @throws {Error} If secret is a string but no Base32Plugin is provided\n *\n * @example\n * ```ts\n * import { normalizeSecret } from '@otplib/core'\n * import { ScureBase32Plugin } from '@otplib/plugin-base32-scure'\n *\n * const base32 = new ScureBase32Plugin()\n *\n * // Uint8Array - returned as-is\n * const secret1 = normalizeSecret(new Uint8Array([1, 2, 3]))\n *\n * // Base32 string - automatically decoded\n * const secret2 = normalizeSecret('JBSWY3DPEHPK3PXP', base32)\n * ```\n */\nexport function normalizeSecret(\n secret: string | Uint8Array,\n base32?: { decode: (str: string) => Uint8Array },\n): Uint8Array {\n if (typeof secret === \"string\") {\n requireBase32Plugin(base32);\n return base32.decode(secret);\n }\n return secret;\n}\n\n/**\n * Generate a random Base32-encoded secret\n *\n * Creates a cryptographically secure random secret suitable for OTP generation.\n * The default length of 20 bytes (160 bits) matches RFC 4226 recommendations\n * and provides good security margin.\n *\n * @param options - Secret generation options\n * @returns Base32-encoded secret string (without padding for Google Authenticator compatibility)\n *\n * @example\n * ```ts\n * import { generateSecret } from '@otplib/core';\n * import { NodeCryptoPlugin } from '@otplib/plugin-crypto-node';\n * import { ScureBase32Plugin } from '@otplib/plugin-base32-scure';\n *\n * const secret = generateSecret({\n * crypto: new NodeCryptoPlugin(),\n * base32: new ScureBase32Plugin(),\n * });\n * // Returns: 'JBSWY3DPEHPK3PXP...' (32 characters)\n * ```\n *\n * @example Custom length\n * ```ts\n * const secret = generateSecret({\n * crypto: new NodeCryptoPlugin(),\n * base32: new ScureBase32Plugin(),\n * length: 32, // 256 bits for SHA-256\n * });\n * ```\n */\nexport function generateSecret(options: SecretOptions): string {\n const { crypto, base32, length = RECOMMENDED_SECRET_BYTES } = options;\n\n requireCryptoPlugin(crypto);\n requireBase32Plugin(base32);\n\n const randomBytes = crypto.randomBytes(length);\n return base32.encode(randomBytes, { padding: false });\n}\n\n/**\n * Normalize counter tolerance to an array of offsets\n *\n * Converts a number or array counter tolerance specification into an array of offsets\n * - Number: creates symmetric range [-tolerance, +tolerance]\n * - Array: uses the array as-is (already contains specific offsets)\n *\n * @param counterTolerance - Counter tolerance specification (number or array of offsets)\n * @returns Array of offsets to check\n *\n * @example\n * ```ts\n * normalizeCounterTolerance(0) // [0]\n * normalizeCounterTolerance(1) // [-1, 0, 1]\n * normalizeCounterTolerance(2) // [-2, -1, 0, 1, 2]\n * normalizeCounterTolerance([0, 1]) // [0, 1]\n * normalizeCounterTolerance([-1, 0, 1]) // [-1, 0, 1]\n * ```\n */\nexport function normalizeCounterTolerance(counterTolerance: number | number[] = 0): number[] {\n if (Array.isArray(counterTolerance)) {\n return counterTolerance;\n }\n\n const result: number[] = [];\n for (let i = -counterTolerance; i <= counterTolerance; i++) {\n // Bitwise OR with 0 converts -0 to 0 and preserves other integers\n result.push(i | 0);\n }\n return result;\n}\n\n/**\n * Normalize epoch tolerance to [past, future] tuple\n *\n * Converts a number or tuple epoch tolerance specification into a [past, future] tuple\n * - Number: creates symmetric tolerance [tolerance, tolerance]\n * - Tuple: uses the tuple as-is\n *\n * @param epochTolerance - Epoch tolerance specification (number or tuple [past, future])\n * @returns Tuple [pastTolerance, futureTolerance] in seconds\n *\n * @example\n * ```ts\n * normalizeEpochTolerance(0) // [0, 0]\n * normalizeEpochTolerance(30) // [30, 30]\n * normalizeEpochTolerance([5, 0]) // [5, 0]\n * normalizeEpochTolerance([10, 5]) // [10, 5]\n * ```\n */\nexport function normalizeEpochTolerance(\n epochTolerance: number | [number, number] = 0,\n): [number, number] {\n return Array.isArray(epochTolerance) ? epochTolerance : [epochTolerance, epochTolerance];\n}\n\n/**\n * Require crypto plugin to be configured\n *\n * @param crypto - The crypto plugin\n * @throws {CryptoPluginMissingError} If crypto plugin is not set\n */\nexport function requireCryptoPlugin<T>(crypto: T | undefined): asserts crypto is T {\n if (!crypto) {\n throw new CryptoPluginMissingError();\n }\n}\n\n/**\n * Require Base32 plugin to be configured\n *\n * @param base32 - The Base32 plugin\n * @throws {Base32PluginMissingError} If Base32 plugin is not set\n */\nexport function requireBase32Plugin<T>(base32: T | undefined): asserts base32 is T {\n if (!base32) {\n throw new Base32PluginMissingError();\n }\n}\n\n/**\n * Require secret to be configured\n *\n * @param secret - The secret value\n * @throws {SecretMissingError} If secret is not set\n */\nexport function requireSecret<T>(secret: T | undefined): asserts secret is T {\n if (!secret) {\n throw new SecretMissingError();\n }\n}\n\n/**\n * Require label to be configured (for URI generation)\n *\n * @param label - The label value\n * @throws {LabelMissingError} If label is not set\n */\nexport function requireLabel(label: string | undefined): asserts label is string {\n if (!label) {\n throw new LabelMissingError();\n }\n}\n\n/**\n * Require issuer to be configured (for URI generation)\n *\n * @param issuer - The issuer value\n * @throws {IssuerMissingError} If issuer is not set\n */\nexport function requireIssuer(issuer: string | undefined): asserts issuer is string {\n if (!issuer) {\n throw new IssuerMissingError();\n }\n}\n\n/**\n * Require secret to be a Base32 string (for URI generation)\n *\n * @param secret - The secret value\n * @throws {SecretTypeError} If secret is not a string\n */\nexport function requireBase32String(secret: string | Uint8Array): asserts secret is string {\n if (typeof secret !== \"string\") {\n throw new SecretTypeError();\n }\n}\n\n/**\n * Create a success result\n * @internal\n */\nfunction ok<T>(value: T): OTPResultOk<T> {\n return { ok: true, value };\n}\n\n/**\n * Create a failure result\n * @internal\n */\nfunction err<E>(error: E): OTPResultError<E> {\n return { ok: false, error };\n}\n\n/**\n * Wrap a synchronous function to return OTPResult instead of throwing\n *\n * Preserves the original OTPError subclass so users can access\n * specific error information via instanceof checks.\n *\n * @internal\n */\nexport function wrapResult<T, Args extends unknown[]>(\n fn: (...args: Args) => T,\n): (...args: Args) => OTPResult<T, OTPError> {\n return (...args: Args): OTPResult<T, OTPError> => {\n try {\n return ok(fn(...args));\n } catch (error) {\n return err(error as OTPError);\n }\n };\n}\n\n/**\n * Wrap an async function to return OTPResult instead of throwing\n *\n * Preserves the original OTPError subclass so users can access\n * specific error information via instanceof checks.\n *\n * @internal\n */\nexport function wrapResultAsync<T, Args extends unknown[]>(\n fn: (...args: Args) => Promise<T>,\n): (...args: Args) => Promise<OTPResult<T, OTPError>> {\n return async (...args: Args): Promise<OTPResult<T, OTPError>> => {\n try {\n return ok(await fn(...args));\n } catch (error) {\n return err(error as OTPError);\n }\n };\n}\n","import { HMACError, RandomBytesError } from \"./errors.js\";\n\nimport type { CryptoPlugin, HashAlgorithm } from \"./types.js\";\n\n/**\n * CryptoContext provides a unified interface for crypto operations\n * using a pluggable crypto backend\n */\nexport class CryptoContext {\n /**\n * Create a new CryptoContext with the given crypto plugin\n *\n * @param crypto - The crypto plugin to use\n */\n constructor(private readonly crypto: CryptoPlugin) {}\n\n /**\n * Get the underlying crypto plugin\n */\n get plugin(): CryptoPlugin {\n return this.crypto;\n }\n\n /**\n * Compute HMAC using the configured crypto plugin\n *\n * @param algorithm - The hash algorithm to use\n * @param key - The secret key as a byte array\n * @param data - The data to authenticate as a byte array\n * @returns HMAC digest as a byte array\n * @throws {HMACError} If HMAC computation fails\n */\n async hmac(algorithm: HashAlgorithm, key: Uint8Array, data: Uint8Array): Promise<Uint8Array> {\n try {\n const result = this.crypto.hmac(algorithm, key, data);\n return result instanceof Promise ? await result : result;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new HMACError(message, { cause: error });\n }\n }\n\n /**\n * Synchronous HMAC computation\n *\n * @param algorithm - The hash algorithm to use\n * @param key - The secret key as a byte array\n * @param data - The data to authenticate as a byte array\n * @returns HMAC digest as a byte array\n * @throws {HMACError} If HMAC computation fails or if crypto plugin doesn't support sync operations\n */\n hmacSync(algorithm: HashAlgorithm, key: Uint8Array, data: Uint8Array): Uint8Array {\n try {\n const result = this.crypto.hmac(algorithm, key, data);\n if (result instanceof Promise) {\n throw new HMACError(\"Crypto plugin does not support synchronous HMAC operations\");\n }\n return result;\n } catch (error) {\n if (error instanceof HMACError) {\n throw error;\n }\n const message = error instanceof Error ? error.message : String(error);\n throw new HMACError(message, { cause: error });\n }\n }\n\n /**\n * Generate cryptographically secure random bytes\n *\n * @param length - Number of random bytes to generate\n * @returns Random bytes\n * @throws {RandomBytesError} If random byte generation fails\n */\n randomBytes(length: number): Uint8Array {\n try {\n return this.crypto.randomBytes(length);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new RandomBytesError(message, { cause: error });\n }\n }\n}\n\n/**\n * Create a CryptoContext from a crypto plugin\n *\n * @param crypto - The crypto plugin to use\n * @returns A new CryptoContext instance\n */\nexport function createCryptoContext(crypto: CryptoPlugin): CryptoContext {\n return new CryptoContext(crypto);\n}\n","import { Base32EncodeError, Base32DecodeError } from \"./errors.js\";\n\nimport type { Base32Plugin, Base32EncodeOptions } from \"./types.js\";\n\n/**\n * Base32Context provides a unified interface for Base32 operations\n * using a pluggable Base32 backend.\n *\n * All errors from the underlying plugin are wrapped in otplib error types\n * with the original error preserved via the `cause` property.\n */\nexport class Base32Context {\n /**\n * Create a new Base32Context with the given Base32 plugin\n *\n * @param base32 - The Base32 plugin to use\n */\n constructor(private readonly base32: Base32Plugin) {}\n\n /**\n * Get the underlying Base32 plugin\n */\n get plugin(): Base32Plugin {\n return this.base32;\n }\n\n /**\n * Encode binary data to Base32 string using the configured plugin\n *\n * @param data - Uint8Array to encode\n * @param options - Encoding options\n * @returns Base32 encoded string\n * @throws {Base32EncodeError} If encoding fails\n */\n encode(data: Uint8Array, options?: Base32EncodeOptions): string {\n try {\n return this.base32.encode(data, options);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Base32EncodeError(message, { cause: error });\n }\n }\n\n /**\n * Decode Base32 string to binary data using the configured plugin\n *\n * @param str - Base32 string to decode\n * @returns Decoded Uint8Array\n * @throws {Base32DecodeError} If string contains invalid characters or decoding fails\n */\n decode(str: string): Uint8Array {\n try {\n return this.base32.decode(str);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Base32DecodeError(message, { cause: error });\n }\n }\n}\n\n/**\n * Create a Base32Context from a Base32 plugin\n *\n * @param base32 - The Base32 plugin to use\n * @returns A new Base32Context instance\n */\nexport function createBase32Context(base32: Base32Plugin): Base32Context {\n return new Base32Context(base32);\n}\n","/**\n * TypeScript Utility Types for otplib\n *\n * These types enhance developer experience by providing:\n * - Branded types for type-safe string handling\n * - Type guards for discriminated unions\n * - Helper types for option extraction\n */\n\nimport type { CryptoPlugin, Base32Plugin } from \"./types\";\n\n/**\n * Brand type for creating nominal types from primitives\n *\n * @example\n * ```ts\n * type UserId = Brand<string, 'UserId'>;\n * const id: UserId = 'abc' as UserId;\n * ```\n */\nexport type Brand<T, B extends string> = T & { readonly __brand: B };\n\n/**\n * Branded string type for Base32-encoded secrets\n *\n * Use this type to distinguish Base32-encoded secrets from regular strings\n * at compile time, preventing accidental misuse.\n *\n * @example\n * ```ts\n * import type { Base32Secret } from '@otplib/core';\n *\n * function processSecret(secret: Base32Secret): void {\n * // TypeScript ensures only Base32Secret values are passed\n * }\n *\n * const secret = generateSecret() as Base32Secret;\n * processSecret(secret); // OK\n * processSecret('random-string'); // Type error\n * ```\n */\nexport type Base32Secret = Brand<string, \"Base32Secret\">;\n\n/**\n * Branded string type for OTP tokens\n *\n * Use this type to distinguish OTP tokens from regular strings\n * at compile time, preventing accidental misuse.\n *\n * @example\n * ```ts\n * import type { OTPToken } from '@otplib/core';\n *\n * function validateToken(token: OTPToken): boolean {\n * // TypeScript ensures only OTPToken values are passed\n * }\n *\n * const token = await generate() as OTPToken;\n * validateToken(token); // OK\n * validateToken('123456'); // Type error\n * ```\n */\nexport type OTPToken = Brand<string, \"OTPToken\">;\n\n/**\n * Helper type to make all properties of T required except those in K\n *\n * @example\n * ```ts\n * type Options = { a?: string; b?: number; c?: boolean };\n * type RequiredAB = RequireKeys<Options, 'a' | 'b'>;\n * // { a: string; b: number; c?: boolean }\n * ```\n */\nexport type RequireKeys<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;\n\n/**\n * Helper type to make all properties of T optional except those in K\n *\n * @example\n * ```ts\n * type Options = { a: string; b: number; c: boolean };\n * type OptionalBC = OptionalKeys<Options, 'b' | 'c'>;\n * // { a: string; b?: number; c?: boolean }\n * ```\n */\nexport type OptionalKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;\n\n/**\n * Extract the plugin configuration from an options type\n *\n * @example\n * ```ts\n * type Plugins = PluginConfig<TOTPOptions>;\n * // { crypto: CryptoPlugin; base32?: Base32Plugin }\n * ```\n */\nexport type PluginConfig<T> = T extends { crypto?: CryptoPlugin; base32?: Base32Plugin }\n ? Pick<T, \"crypto\" | \"base32\">\n : never;\n\n/**\n * Ensure an options type has plugins defined\n *\n * @example\n * ```ts\n * type ConfiguredOptions = WithRequiredPlugins<TOTPOptions>;\n * // TOTPOptions with crypto and base32 required\n * ```\n */\nexport type WithRequiredPlugins<T extends { crypto?: CryptoPlugin; base32?: Base32Plugin }> = T & {\n crypto: CryptoPlugin;\n base32: Base32Plugin;\n};\n\n/**\n * Options type for OTP generation (crypto required)\n *\n * @example\n * ```ts\n * type MyGenerateOptions = GenerationReady<HOTPOptions>;\n * // HOTPOptions with crypto required\n * ```\n */\nexport type GenerationReady<T extends { crypto?: CryptoPlugin }> = T & {\n crypto: CryptoPlugin;\n};\n\n/**\n * Type predicate to check if an object has the required plugins\n *\n * @example\n * ```ts\n * const options = getOptions();\n * if (hasPlugins(options)) {\n * // TypeScript knows plugins are defined\n * options.crypto.hmac(...);\n * }\n * ```\n */\nexport function hasPlugins<T extends { crypto?: CryptoPlugin; base32?: Base32Plugin }>(\n options: T,\n): options is T & { crypto: CryptoPlugin; base32: Base32Plugin } {\n return options.crypto !== undefined && options.base32 !== undefined;\n}\n\n/**\n * Type predicate to check if an object has a crypto plugin\n *\n * @example\n * ```ts\n * if (hasCrypto(options)) {\n * await options.crypto.hmac('sha1', key, data);\n * }\n * ```\n */\nexport function hasCrypto<T extends { crypto?: CryptoPlugin }>(\n options: T,\n): options is T & { crypto: CryptoPlugin } {\n return options.crypto !== undefined;\n}\n\n/**\n * Type predicate to check if an object has a base32 plugin\n *\n * @example\n * ```ts\n * if (hasBase32(options)) {\n * const decoded = options.base32.decode(secret);\n * }\n * ```\n */\nexport function hasBase32<T extends { base32?: Base32Plugin }>(\n options: T,\n): options is T & { base32: Base32Plugin } {\n return options.base32 !== undefined;\n}\n\n/**\n * Narrow union type by a specific property value\n *\n * @example\n * ```ts\n * type Result = VerifyResultValid | VerifyResultInvalid;\n * type ValidOnly = NarrowBy<Result, 'valid', true>;\n * // VerifyResultValid\n * ```\n */\nexport type NarrowBy<T, K extends keyof T, V extends T[K]> = T extends { [key in K]: V }\n ? T\n : never;\n"],"mappings":"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","names":["OTPError","message","options","SecretError","SecretTooShortError","minBytes","actualBytes","SecretTooLongError","maxBytes","CounterError","CounterNegativeError","CounterOverflowError","TimeError","TimeNegativeError","PeriodError","PeriodTooSmallError","minPeriod","PeriodTooLargeError","maxPeriod","DigitsError","AlgorithmError","TokenError","TokenLengthError","expected","actual","TokenFormatError","CryptoError","HMACError","RandomBytesError","Base32Error","Base32EncodeError","Base32DecodeError","CounterToleranceError","CounterToleranceTooLargeError","maxTolerance","actualSize","EpochToleranceError","EpochToleranceNegativeError","EpochToleranceTooLargeError","actualValue","PluginError","CryptoPluginMissingError","Base32PluginMissingError","ConfigurationError","SecretMissingError","LabelMissingError","IssuerMissingError","SecretTypeError","textEncoder","MIN_SECRET_BYTES","MAX_SECRET_BYTES","RECOMMENDED_SECRET_BYTES","MIN_PERIOD","MAX_PERIOD","DEFAULT_PERIOD","MAX_COUNTER","MAX_WINDOW","OVERRIDE_SYMBOL","DEFAULT_GUARDRAILS","createGuardrails","custom","hasGuardrailOverrides","guardrails","validateSecret","secret","SecretTooShortError","SecretTooLongError","validateCounter","counter","value","CounterNegativeError","CounterOverflowError","validateTime","time","TimeNegativeError","validatePeriod","period","PeriodTooSmallError","PeriodTooLargeError","validateToken","token","digits","TokenLengthError","TokenFormatError","validateCounterTolerance","counterTolerance","CounterToleranceTooLargeError","validateEpochTolerance","epochTolerance","pastTolerance","futureTolerance","EpochToleranceNegativeError","maxToleranceSeconds","maxAllowed","EpochToleranceTooLargeError","counterToBytes","bigintValue","buffer","dynamicTruncate","hmacResult","offset","truncateDigits","maxOtp","validateByteLengthEqual","a","b","constantTimeEqual","bufA","stringToBytes","bufB","result","getDigestSize","algorithm","hexToBytes","hex","bytes","i","normalizeSecret","base32","requireBase32Plugin","generateSecret","options","crypto","length","requireCryptoPlugin","randomBytes","normalizeCounterTolerance","normalizeEpochTolerance","CryptoPluginMissingError","Base32PluginMissingError","requireSecret","SecretMissingError","requireLabel","label","LabelMissingError","requireIssuer","issuer","IssuerMissingError","requireBase32String","SecretTypeError","ok","err","error","wrapResult","fn","args","wrapResultAsync","CryptoContext","crypto","algorithm","key","data","result","error","message","HMACError","length","RandomBytesError","createCryptoContext","Base32Context","base32","data","options","error","message","Base32EncodeError","str","Base32DecodeError","createBase32Context","hasPlugins","options","hasCrypto","hasBase32"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/utils.ts","../src/crypto-context.ts","../src/base32-context.ts","../src/utility-types.ts"],"sourcesContent":["/**\n * Options for OTPError construction\n */\nexport type OTPErrorOptions = {\n /**\n * The underlying error that caused this error.\n * Useful for error chaining and debugging.\n */\n cause?: unknown;\n};\n\n/**\n * Base error class for all otplib errors\n *\n * Supports ES2022 error chaining via the `cause` property.\n *\n * @example\n * ```typescript\n * try {\n * // ... operation that throws\n * } catch (error) {\n * throw new OTPError('Operation failed', { cause: error });\n * }\n * ```\n */\nexport class OTPError extends Error {\n constructor(message: string, options?: OTPErrorOptions) {\n super(message, options);\n this.name = \"OTPError\";\n }\n}\n\n/**\n * Error thrown when secret validation fails\n */\nexport class SecretError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"SecretError\";\n }\n}\n\n/**\n * Error thrown when secret is too short (< 128 bits)\n */\nexport class SecretTooShortError extends SecretError {\n constructor(minBytes: number, actualBytes: number) {\n super(\n `Secret must be at least ${minBytes} bytes (${minBytes * 8} bits), got ${actualBytes} bytes`,\n );\n this.name = \"SecretTooShortError\";\n }\n}\n\n/**\n * Error thrown when secret is unreasonably large (> 64 bytes)\n */\nexport class SecretTooLongError extends SecretError {\n constructor(maxBytes: number, actualBytes: number) {\n super(`Secret must not exceed ${maxBytes} bytes, got ${actualBytes} bytes`);\n this.name = \"SecretTooLongError\";\n }\n}\n\n/**\n * Error thrown when counter is invalid\n */\nexport class CounterError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"CounterError\";\n }\n}\n\n/**\n * Error thrown when counter is negative\n */\nexport class CounterNegativeError extends CounterError {\n constructor() {\n super(\"Counter must be non-negative\");\n this.name = \"CounterNegativeError\";\n }\n}\n\n/**\n * Error thrown when counter exceeds maximum value (2^53 - 1 for safe integer)\n */\nexport class CounterOverflowError extends CounterError {\n constructor() {\n super(\"Counter exceeds maximum safe integer value\");\n this.name = \"CounterOverflowError\";\n }\n}\n\n/**\n * Error thrown when time is invalid\n */\nexport class TimeError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"TimeError\";\n }\n}\n\n/**\n * Error thrown when time is negative\n */\nexport class TimeNegativeError extends TimeError {\n constructor() {\n super(\"Time must be non-negative\");\n this.name = \"TimeNegativeError\";\n }\n}\n\n/**\n * Error thrown when period is invalid\n */\nexport class PeriodError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"PeriodError\";\n }\n}\n\n/**\n * Error thrown when period is too small\n */\nexport class PeriodTooSmallError extends PeriodError {\n constructor(minPeriod: number) {\n super(`Period must be at least ${minPeriod} second(s)`);\n this.name = \"PeriodTooSmallError\";\n }\n}\n\n/**\n * Error thrown when period is too large\n */\nexport class PeriodTooLargeError extends PeriodError {\n constructor(maxPeriod: number) {\n super(`Period must not exceed ${maxPeriod} seconds`);\n this.name = \"PeriodTooLargeError\";\n }\n}\n\n/**\n * Error thrown when digits value is invalid\n */\nexport class DigitsError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"DigitsError\";\n }\n}\n\n/**\n * Error thrown when hash algorithm is invalid\n */\nexport class AlgorithmError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"AlgorithmError\";\n }\n}\n\n/**\n * Error thrown when token is invalid\n */\nexport class TokenError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"TokenError\";\n }\n}\n\n/**\n * Error thrown when token has incorrect length\n */\nexport class TokenLengthError extends TokenError {\n constructor(expected: number, actual: number) {\n super(`Token must be ${expected} digits, got ${actual}`);\n this.name = \"TokenLengthError\";\n }\n}\n\n/**\n * Error thrown when token contains non-digit characters\n */\nexport class TokenFormatError extends TokenError {\n constructor() {\n super(\"Token must contain only digits\");\n this.name = \"TokenFormatError\";\n }\n}\n\n/**\n * Error thrown when crypto operation fails\n */\nexport class CryptoError extends OTPError {\n constructor(message: string, options?: OTPErrorOptions) {\n super(message, options);\n this.name = \"CryptoError\";\n }\n}\n\n/**\n * Error thrown when HMAC computation fails\n *\n * The original error from the crypto plugin is available via `cause`.\n *\n * @example\n * ```typescript\n * try {\n * await cryptoContext.hmac('sha1', key, data);\n * } catch (error) {\n * if (error instanceof HMACError) {\n * console.log('HMAC failed:', error.message);\n * console.log('Original error:', error.cause);\n * }\n * }\n * ```\n */\nexport class HMACError extends CryptoError {\n constructor(message: string, options?: OTPErrorOptions) {\n super(`HMAC computation failed: ${message}`, options);\n this.name = \"HMACError\";\n }\n}\n\n/**\n * Error thrown when random byte generation fails\n *\n * The original error from the crypto plugin is available via `cause`.\n */\nexport class RandomBytesError extends CryptoError {\n constructor(message: string, options?: OTPErrorOptions) {\n super(`Random byte generation failed: ${message}`, options);\n this.name = \"RandomBytesError\";\n }\n}\n\n/**\n * Error thrown when Base32 operation fails\n */\nexport class Base32Error extends OTPError {\n constructor(message: string, options?: OTPErrorOptions) {\n super(message, options);\n this.name = \"Base32Error\";\n }\n}\n\n/**\n * Error thrown when Base32 encoding fails\n *\n * The original error from the Base32 plugin is available via `cause`.\n *\n * @example\n * ```typescript\n * try {\n * base32Context.encode(data);\n * } catch (error) {\n * if (error instanceof Base32EncodeError) {\n * console.log('Encoding failed:', error.message);\n * console.log('Original error:', error.cause);\n * }\n * }\n * ```\n */\nexport class Base32EncodeError extends Base32Error {\n constructor(message: string, options?: OTPErrorOptions) {\n super(`Base32 encoding failed: ${message}`, options);\n this.name = \"Base32EncodeError\";\n }\n}\n\n/**\n * Error thrown when Base32 decoding fails\n *\n * The original error from the Base32 plugin is available via `cause`.\n *\n * @example\n * ```typescript\n * try {\n * base32Context.decode(invalidString);\n * } catch (error) {\n * if (error instanceof Base32DecodeError) {\n * console.log('Decoding failed:', error.message);\n * console.log('Original error:', error.cause);\n * }\n * }\n * ```\n */\nexport class Base32DecodeError extends Base32Error {\n constructor(message: string, options?: OTPErrorOptions) {\n super(`Base32 decoding failed: ${message}`, options);\n this.name = \"Base32DecodeError\";\n }\n}\n\n/**\n * Error thrown when counter tolerance is invalid\n */\nexport class CounterToleranceError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"CounterToleranceError\";\n }\n}\n\n/**\n * Error thrown when counter tolerance is too large\n */\nexport class CounterToleranceTooLargeError extends CounterToleranceError {\n constructor(maxTolerance: number, actualSize: number) {\n super(\n `Counter tolerance size must not exceed ${maxTolerance}, got ${actualSize}. ` +\n `Large tolerances can cause performance issues.`,\n );\n this.name = \"CounterToleranceTooLargeError\";\n }\n}\n\n/**\n * Error thrown when epoch tolerance is invalid\n */\nexport class EpochToleranceError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"EpochToleranceError\";\n }\n}\n\n/**\n * Error thrown when epoch tolerance contains negative values\n */\nexport class EpochToleranceNegativeError extends EpochToleranceError {\n constructor() {\n super(\"Epoch tolerance cannot contain negative values\");\n this.name = \"EpochToleranceNegativeError\";\n }\n}\n\n/**\n * Error thrown when epoch tolerance is too large\n */\nexport class EpochToleranceTooLargeError extends EpochToleranceError {\n constructor(maxTolerance: number, actualValue: number) {\n super(\n `Epoch tolerance must not exceed ${maxTolerance} seconds, got ${actualValue}. ` +\n `Large tolerances can cause performance issues.`,\n );\n this.name = \"EpochToleranceTooLargeError\";\n }\n}\n\n/**\n * Error thrown when a required plugin is missing\n */\nexport class PluginError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"PluginError\";\n }\n}\n\n/**\n * Error thrown when crypto plugin is not configured\n */\nexport class CryptoPluginMissingError extends PluginError {\n constructor() {\n super(\"Crypto plugin is required.\");\n this.name = \"CryptoPluginMissingError\";\n }\n}\n\n/**\n * Error thrown when Base32 plugin is not configured\n */\nexport class Base32PluginMissingError extends PluginError {\n constructor() {\n super(\"Base32 plugin is required.\");\n this.name = \"Base32PluginMissingError\";\n }\n}\n\n/**\n * Error thrown when required configuration is missing\n */\nexport class ConfigurationError extends OTPError {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigurationError\";\n }\n}\n\n/**\n * Error thrown when secret is not configured\n */\nexport class SecretMissingError extends ConfigurationError {\n constructor() {\n super(\n \"Secret is required. \" +\n \"Use generateSecret() to create one, or provide via { secret: 'YOUR_BASE32_SECRET' }\",\n );\n this.name = \"SecretMissingError\";\n }\n}\n\n/**\n * Error thrown when label is not configured (required for URI generation)\n */\nexport class LabelMissingError extends ConfigurationError {\n constructor() {\n super(\"Label is required for URI generation. Example: { label: 'user@example.com' }\");\n this.name = \"LabelMissingError\";\n }\n}\n\n/**\n * Error thrown when issuer is not configured (required for URI generation)\n */\nexport class IssuerMissingError extends ConfigurationError {\n constructor() {\n super(\"Issuer is required for URI generation. Example: { issuer: 'MyApp' }\");\n this.name = \"IssuerMissingError\";\n }\n}\n\n/**\n * Error thrown when secret must be a Base32 string but is provided as bytes\n */\nexport class SecretTypeError extends ConfigurationError {\n constructor() {\n super(\n \"Class API requires secret to be a Base32 string, not Uint8Array. \" +\n \"Use generateSecret() or provide a Base32-encoded string.\",\n );\n this.name = \"SecretTypeError\";\n }\n}\n","import {\n OTPError,\n SecretTooShortError,\n SecretTooLongError,\n CounterNegativeError,\n CounterOverflowError,\n TimeNegativeError,\n PeriodTooSmallError,\n PeriodTooLargeError,\n TokenLengthError,\n TokenFormatError,\n CounterToleranceTooLargeError,\n EpochToleranceNegativeError,\n EpochToleranceTooLargeError,\n CryptoPluginMissingError,\n Base32PluginMissingError,\n SecretMissingError,\n LabelMissingError,\n IssuerMissingError,\n SecretTypeError,\n} from \"./errors.js\";\n\nimport type {\n HashAlgorithm,\n SecretOptions,\n OTPResultOk,\n OTPResultError,\n OTPResult,\n} from \"./types.js\";\n\n/**\n * Singleton TextEncoder instance to avoid repeated allocations\n */\nconst textEncoder = new TextEncoder();\n\n/**\n * Minimum secret length in bytes (128 bits as per RFC 4226)\n */\nexport const MIN_SECRET_BYTES = 16;\n\n/**\n * Maximum secret length in bytes (512 bits)\n *\n * The 64-byte maximum is not part of the RFCs.\n * This is to prevent excessive memory usage in HMAC operations.\n */\nexport const MAX_SECRET_BYTES = 64;\n\n/**\n * Recommended secret length in bytes (160 bits as per RFC 4226)\n */\nexport const RECOMMENDED_SECRET_BYTES = 20;\n\n/**\n * Minimum period in seconds\n */\nexport const MIN_PERIOD = 1;\n\n/**\n * Maximum period in seconds (1 hour)\n */\nexport const MAX_PERIOD = 3600;\n\n/**\n * Default period in seconds (30 seconds as per RFC 6238)\n */\nexport const DEFAULT_PERIOD = 30;\n\n/**\n * Maximum safe integer for counter (2^53 - 1)\n */\nexport const MAX_COUNTER = Number.MAX_SAFE_INTEGER;\n\n/**\n * Maximum verification window size\n *\n * Limits the number of HMAC computations during verification to prevent DoS attacks.\n * A window of 100 means up to 201 HMAC computations ([-100, +100] range).\n *\n * For TOTP: window=1 is typically sufficient (allows +-30 seconds clock drift)\n * For HOTP: window=10-50 handles reasonable counter desynchronization\n */\nexport const MAX_WINDOW = 100;\n\n/**\n * Configurable guardrails for OTP validation\n *\n * Allows overriding default safety limits for non-standard production requirements.\n * Use with caution - custom guardrails can weaken security.\n */\nexport type OTPGuardrailsConfig = {\n MIN_SECRET_BYTES: number;\n MAX_SECRET_BYTES: number;\n MIN_PERIOD: number;\n MAX_PERIOD: number;\n MAX_COUNTER: number;\n MAX_WINDOW: number;\n};\n\n/**\n * Module-private symbol to track guardrail override status\n *\n * This symbol is used as a property key to store whether guardrails contain custom values.\n * Being module-private and a symbol ensures:\n * - Cannot be accessed outside this module (not exported)\n * - Cannot be recreated (each Symbol() call is unique)\n * - Hidden from normal enumeration (Object.keys, JSON.stringify, for-in)\n * - Minimal memory overhead (~1 byte per object)\n * - No garbage collection concerns\n *\n * @internal\n */\nconst OVERRIDE_SYMBOL = Symbol(\"otplib.guardrails.override\");\n\n/**\n * Complete guardrails configuration\n *\n * This represents the final, immutable configuration used by validation functions.\n * Internally tracks whether any values were overridden from RFC recommendations,\n * enabling security auditing and compliance monitoring without exposing implementation\n * details in the public API.\n *\n * The override status is stored using a module-private Symbol that cannot be accessed\n * or recreated outside this module, providing true encapsulation.\n *\n * @see {@link OTPGuardrailsConfig} for the base configuration structure\n * @see {@link createGuardrails} for creating guardrails instances\n * @see {@link hasGuardrailOverrides} to check if guardrails were customized\n */\nexport type OTPGuardrails = Readonly<OTPGuardrailsConfig> & {\n [OVERRIDE_SYMBOL]?: boolean;\n};\n\n/**\n * Default guardrails matching RFC recommendations\n *\n * Frozen to ensure immutability. Used as default parameter for validation functions.\n * For custom guardrails, use the createGuardrails() factory function.\n */\nconst DEFAULT_GUARDRAILS: OTPGuardrails = Object.freeze({\n MIN_SECRET_BYTES,\n MAX_SECRET_BYTES,\n MIN_PERIOD,\n MAX_PERIOD,\n MAX_COUNTER,\n MAX_WINDOW,\n [OVERRIDE_SYMBOL]: false,\n});\n\n/**\n * Create guardrails configuration object\n *\n * Factory function that merges custom guardrails with defaults and returns\n * an immutable (frozen) object. Validates custom guardrails to ensure they\n * maintain basic safety invariants.\n *\n * When called without arguments or with `undefined`, returns the default guardrails\n * singleton (optimized to avoid unnecessary allocations). When called with custom\n * values, creates a new frozen object and internally marks it as overridden.\n *\n * @param custom - Optional partial guardrails to override defaults\n * @returns Frozen guardrails object\n * @throws {Error} If custom guardrails violate safety invariants\n *\n * @example Basic usage\n * ```ts\n * import { createGuardrails, hasGuardrailOverrides } from '@otplib/core'\n *\n * // Returns default singleton (no overrides)\n * const defaults = createGuardrails();\n * hasGuardrailOverrides(defaults); // false\n *\n * // Creates new object with overrides\n * const custom = createGuardrails({\n * MIN_SECRET_BYTES: 8,\n * MAX_WINDOW: 200\n * });\n * hasGuardrailOverrides(custom); // true\n * ```\n *\n * @example Monitoring custom guardrails\n * ```ts\n * import { createGuardrails, hasGuardrailOverrides } from '@otplib/core';\n *\n * const guardrails = createGuardrails({ MAX_WINDOW: 20 });\n *\n * if (hasGuardrailOverrides(guardrails)) {\n * logger.warn('Non-default guardrails in use', { guardrails });\n * }\n * ```\n *\n * @see {@link hasGuardrailOverrides} to check if guardrails were customized\n */\nexport function createGuardrails(custom?: Partial<OTPGuardrailsConfig>): OTPGuardrails {\n if (!custom) {\n return DEFAULT_GUARDRAILS;\n }\n\n return Object.freeze({\n ...DEFAULT_GUARDRAILS,\n ...custom,\n [OVERRIDE_SYMBOL]: true,\n });\n}\n\n/**\n * Check if guardrails contain custom overrides\n *\n * Returns `true` if the guardrails object was created with custom values,\n * `false` if using RFC-recommended defaults. Useful for security auditing,\n * compliance monitoring, and development warnings.\n *\n * This function accesses a module-private Symbol property that cannot be\n * accessed or modified outside this module, ensuring reliable detection.\n *\n * @param guardrails - The guardrails object to check\n * @returns `true` if guardrails were customized, `false` if using defaults\n *\n * @example Security monitoring\n * ```ts\n * import { createGuardrails, hasGuardrailOverrides } from '@otplib/core';\n *\n * const guardrails = createGuardrails({ MAX_WINDOW: 20 });\n *\n * if (hasGuardrailOverrides(guardrails)) {\n * console.warn('Custom guardrails detected:', guardrails);\n * // Log to security audit system\n * }\n * ```\n *\n * @example Compliance check\n * ```ts\n * function validateGuardrails(guardrails: OTPGuardrails) {\n * if (hasGuardrailOverrides(guardrails)) {\n * throw new Error('Custom guardrails not allowed in production');\n * }\n * }\n * ```\n */\nexport function hasGuardrailOverrides(guardrails: OTPGuardrails): boolean {\n return guardrails[OVERRIDE_SYMBOL] ?? false;\n}\n\n/**\n * Validate secret key\n *\n * @param secret - The secret to validate\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {SecretTooShortError} If secret is too short\n * @throws {SecretTooLongError} If secret is too long\n */\nexport function validateSecret(\n secret: Uint8Array,\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n if (secret.length < guardrails.MIN_SECRET_BYTES) {\n throw new SecretTooShortError(guardrails.MIN_SECRET_BYTES, secret.length);\n }\n\n if (secret.length > guardrails.MAX_SECRET_BYTES) {\n throw new SecretTooLongError(guardrails.MAX_SECRET_BYTES, secret.length);\n }\n}\n\n/**\n * Validate counter value\n *\n * @param counter - The counter to validate\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {CounterNegativeError} If counter is negative\n * @throws {CounterOverflowError} If counter exceeds safe integer\n */\nexport function validateCounter(\n counter: number | bigint,\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n const value = typeof counter === \"bigint\" ? counter : BigInt(counter);\n\n if (value < 0n) {\n throw new CounterNegativeError();\n }\n\n if (value > BigInt(guardrails.MAX_COUNTER)) {\n throw new CounterOverflowError();\n }\n}\n\n/**\n * Validate time value\n *\n * @param time - The time in seconds to validate\n * @throws {TimeNegativeError} If time is negative\n */\nexport function validateTime(time: number): void {\n if (time < 0) {\n throw new TimeNegativeError();\n }\n}\n\n/**\n * Validate period value\n *\n * @param period - The period in seconds to validate\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {PeriodTooSmallError} If period is too small\n * @throws {PeriodTooLargeError} If period is too large\n */\nexport function validatePeriod(\n period: number,\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n if (!Number.isInteger(period) || period < guardrails.MIN_PERIOD) {\n throw new PeriodTooSmallError(guardrails.MIN_PERIOD);\n }\n\n if (period > guardrails.MAX_PERIOD) {\n throw new PeriodTooLargeError(guardrails.MAX_PERIOD);\n }\n}\n\n/**\n * Validate token\n *\n * @param token - The token string to validate\n * @param digits - Expected number of digits\n * @throws {TokenLengthError} If token has incorrect length\n * @throws {TokenFormatError} If token contains non-digit characters\n */\nexport function validateToken(token: string, digits: number): void {\n if (token.length !== digits) {\n throw new TokenLengthError(digits, token.length);\n }\n\n if (!/^\\d+$/.test(token)) {\n throw new TokenFormatError();\n }\n}\n\n/**\n * Validate counter tolerance for HOTP verification\n *\n * Prevents DoS attacks by limiting the number of counter values checked.\n *\n * @param counterTolerance - Counter tolerance specification (number or array of offsets)\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {CounterToleranceTooLargeError} If tolerance size exceeds MAX_WINDOW\n *\n * @example\n * ```ts\n * validateCounterTolerance(1); // OK: 3 offsets [-1, 0, 1]\n * validateCounterTolerance(100); // OK: 201 offsets [-100, ..., 100]\n * validateCounterTolerance(101); // Throws: exceeds MAX_WINDOW\n * validateCounterTolerance([0, 1]); // OK: 2 offsets\n * ```\n */\nexport function validateCounterTolerance(\n counterTolerance: number | number[],\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n const size = Array.isArray(counterTolerance) ? counterTolerance.length : counterTolerance * 2 + 1;\n\n if (size > guardrails.MAX_WINDOW * 2 + 1) {\n throw new CounterToleranceTooLargeError(\n guardrails.MAX_WINDOW,\n Array.isArray(counterTolerance) ? counterTolerance.length : counterTolerance,\n );\n }\n}\n\n/**\n * Validate epoch tolerance for TOTP verification\n *\n * Prevents DoS attacks by limiting the time range checked.\n * Also validates that tolerance values are non-negative.\n *\n * @param epochTolerance - Epoch tolerance specification (number or tuple [past, future])\n * @param period - The TOTP period in seconds (default: 30). Used to calculate max tolerance.\n * @param guardrails - Validation guardrails (defaults to RFC recommendations)\n * @throws {EpochToleranceNegativeError} If tolerance contains negative values\n * @throws {EpochToleranceTooLargeError} If tolerance exceeds MAX_WINDOW periods\n *\n * @example\n * ```ts\n * validateEpochTolerance(30); // OK: 30 seconds (default period 30s)\n * validateEpochTolerance([5, 0]); // OK: 5 seconds past only\n * validateEpochTolerance([-5, 0]); // Throws: negative values not allowed\n * validateEpochTolerance(3600); // Throws: exceeds MAX_WINDOW * period\n * validateEpochTolerance(6000, 60); // OK with 60s period (MAX_WINDOW * 60 = 6000)\n * ```\n */\nexport function validateEpochTolerance(\n epochTolerance: number | [number, number],\n period: number = DEFAULT_PERIOD,\n guardrails: OTPGuardrails = DEFAULT_GUARDRAILS,\n): void {\n const [pastTolerance, futureTolerance] = Array.isArray(epochTolerance)\n ? epochTolerance\n : [epochTolerance, epochTolerance];\n\n // Check for negative values\n if (pastTolerance < 0 || futureTolerance < 0) {\n throw new EpochToleranceNegativeError();\n }\n\n // Check total tolerance doesn't exceed reasonable limits\n // Convert to periods and check against MAX_WINDOW\n const maxToleranceSeconds = guardrails.MAX_WINDOW * period;\n const maxAllowed = Math.max(pastTolerance, futureTolerance);\n\n if (maxAllowed > maxToleranceSeconds) {\n throw new EpochToleranceTooLargeError(maxToleranceSeconds, maxAllowed);\n }\n}\n\n/**\n * Convert counter to 8-byte big-endian array\n *\n * Per RFC 4226 Section 5.1, the counter value is represented as an 8-byte\n * big-endian (network byte order) unsigned integer.\n *\n * @see {@link https://tools.ietf.org/html/rfc4226#section-5.1 | RFC 4226 Section 5.1 - Symbol Descriptions}\n *\n * @param value - The counter value to convert\n * @returns 8-byte big-endian array\n */\nexport function counterToBytes(value: number | bigint): Uint8Array {\n const bigintValue = typeof value === \"bigint\" ? value : BigInt(value);\n const buffer = new ArrayBuffer(8);\n const view = new DataView(buffer);\n\n view.setBigUint64(0, bigintValue, false);\n\n return new Uint8Array(buffer);\n}\n\n/**\n * Perform Dynamic Truncation as per RFC 4226 Section 5.3\n *\n * The algorithm:\n * 1. Take the low-order 4 bits of the last byte as offset\n * 2. Extract 4 bytes starting at offset\n * 3. Mask the most significant bit to get a 31-bit unsigned integer\n *\n * This ensures consistent extraction across different HMAC output sizes\n * while producing a value that fits in a signed 32-bit integer.\n *\n * @see {@link https://tools.ietf.org/html/rfc4226#section-5.3 | RFC 4226 Section 5.3 - Generating an HOTP Value}\n *\n * @param hmacResult - HMAC result (at least 20 bytes for SHA-1)\n * @returns Truncated 31-bit unsigned integer\n */\nexport function dynamicTruncate(hmacResult: Uint8Array): number {\n const offset = hmacResult[hmacResult.length - 1] & 0x0f;\n\n const binary =\n ((hmacResult[offset] & 0x7f) << 24) |\n (hmacResult[offset + 1] << 16) |\n (hmacResult[offset + 2] << 8) |\n hmacResult[offset + 3];\n\n return binary;\n}\n\n/**\n * Convert truncated integer to OTP string with specified digits\n *\n * Computes: Snum mod 10^Digit (RFC 4226 Section 5.3)\n *\n * The result is zero-padded to ensure consistent length,\n * as required for proper token comparison.\n *\n * @see {@link https://tools.ietf.org/html/rfc4226#section-5.3 | RFC 4226 Section 5.3 - Generating an HOTP Value}\n *\n * @param value - The truncated integer value (Snum)\n * @param digits - Number of digits for the OTP (Digit, typically 6-8)\n * @returns OTP string with leading zeros if necessary\n */\nexport function truncateDigits(value: number, digits: number): string {\n const maxOtp = 10 ** digits;\n const otp = value % maxOtp;\n return otp.toString().padStart(digits, \"0\");\n}\n\n/**\n * Validate that two byte arrays have equal length\n *\n * Useful as a preliminary check before performing byte-by-byte comparisons.\n *\n * @param a - First byte array\n * @param b - Second byte array\n * @returns true if arrays have equal length, false otherwise\n */\nexport function validateByteLengthEqual(a: Uint8Array, b: Uint8Array): boolean {\n return a.length === b.length;\n}\n\n/**\n * Constant-time comparison to prevent timing attacks\n *\n * This implements a timing-safe equality check as recommended in\n * RFC 4226 Section 7.2 for token validation to prevent\n * timing side-channel attacks.\n *\n * @see {@link https://tools.ietf.org/html/rfc4226#section-7.2 | RFC 4226 Section 7.2 - Validation and Verification}\n *\n * @param a - First value to compare\n * @param b - Second value to compare\n * @returns true if values are equal, false otherwise\n */\nexport function constantTimeEqual(a: string | Uint8Array, b: string | Uint8Array): boolean {\n const bufA = stringToBytes(a);\n const bufB = stringToBytes(b);\n\n if (!validateByteLengthEqual(bufA, bufB)) {\n return false;\n }\n\n let result = 0;\n for (let i = 0; i < bufA.length; i++) {\n result |= bufA[i] ^ bufB[i];\n }\n\n return result === 0;\n}\n\n/**\n * Get HMAC digest size in bytes for a given algorithm\n *\n * @param algorithm - The hash algorithm\n * @returns Digest size in bytes\n */\nexport function getDigestSize(algorithm: HashAlgorithm): number {\n switch (algorithm) {\n case \"sha1\":\n return 20;\n case \"sha256\":\n return 32;\n case \"sha512\":\n return 64;\n }\n}\n\n/**\n * Convert a string or Uint8Array to Uint8Array\n *\n * This utility function normalizes input to Uint8Array, converting strings\n * using UTF-8 encoding. Uint8Array inputs are returned as-is.\n *\n * @param value - The value to convert (string or Uint8Array)\n * @returns The value as a Uint8Array (UTF-8 encoded for strings)\n *\n * @example\n * ```ts\n * import { stringToBytes } from '@otplib/core'\n *\n * const bytes1 = stringToBytes('1234567890123456')\n * // Returns: Uint8Array([49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54])\n *\n * const bytes2 = stringToBytes(new Uint8Array([1, 2, 3]))\n * // Returns: Uint8Array([1, 2, 3]) - returned as-is\n * ```\n */\nexport function stringToBytes(value: string | Uint8Array): Uint8Array {\n return typeof value === \"string\" ? textEncoder.encode(value) : value;\n}\n\n/**\n * Convert a hex string to a Uint8Array\n *\n * This is useful for working with RFC test vectors and debugging HMAC outputs,\n * which are commonly represented as hexadecimal strings.\n *\n * If your environment supports it, consider using `Uint8Array.fromHex()` instead.\n *\n * @param hex - The hex string to convert (lowercase or uppercase, no 0x prefix)\n * @returns The bytes as a Uint8Array\n *\n * @example\n * ```ts\n * import { hexToBytes } from '@otplib/core'\n *\n * // Convert RFC 4226 HMAC test vector\n * const hmac = hexToBytes('cc93cf18508d94934c64b65d8ba7667fb7cde4b0')\n * // Returns: Uint8Array([0xcc, 0x93, 0xcf, ...])\n * ```\n */\nexport function hexToBytes(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);\n }\n return bytes;\n}\n\n/**\n * Normalize secret input to Uint8Array\n *\n * Accepts either a Base32-encoded string or Uint8Array and returns Uint8Array.\n * If a Base32Plugin is provided, string secrets will be automatically decoded.\n *\n * @param secret - The secret to normalize (string or Uint8Array)\n * @param base32 - Optional Base32Plugin to decode string secrets\n * @returns The secret as Uint8Array\n * @throws {Error} If secret is a string but no Base32Plugin is provided\n *\n * @example\n * ```ts\n * import { normalizeSecret } from '@otplib/core'\n * import { ScureBase32Plugin } from '@otplib/plugin-base32-scure'\n *\n * const base32 = new ScureBase32Plugin()\n *\n * // Uint8Array - returned as-is\n * const secret1 = normalizeSecret(new Uint8Array([1, 2, 3]))\n *\n * // Base32 string - automatically decoded\n * const secret2 = normalizeSecret('JBSWY3DPEHPK3PXP', base32)\n * ```\n */\nexport function normalizeSecret(\n secret: string | Uint8Array,\n base32?: { decode: (str: string) => Uint8Array },\n): Uint8Array {\n if (typeof secret === \"string\") {\n requireBase32Plugin(base32);\n return base32.decode(secret);\n }\n return secret;\n}\n\n/**\n * Generate a random Base32-encoded secret\n *\n * Creates a cryptographically secure random secret suitable for OTP generation.\n * The default length of 20 bytes (160 bits) matches RFC 4226 recommendations\n * and provides good security margin.\n *\n * @param options - Secret generation options\n * @returns Base32-encoded secret string (without padding for Google Authenticator compatibility)\n *\n * @example\n * ```ts\n * import { generateSecret } from '@otplib/core';\n * import { NodeCryptoPlugin } from '@otplib/plugin-crypto-node';\n * import { ScureBase32Plugin } from '@otplib/plugin-base32-scure';\n *\n * const secret = generateSecret({\n * crypto: new NodeCryptoPlugin(),\n * base32: new ScureBase32Plugin(),\n * });\n * // Returns: 'JBSWY3DPEHPK3PXP...' (32 characters)\n * ```\n *\n * @example Custom length\n * ```ts\n * const secret = generateSecret({\n * crypto: new NodeCryptoPlugin(),\n * base32: new ScureBase32Plugin(),\n * length: 32, // 256 bits for SHA-256\n * });\n * ```\n */\nexport function generateSecret(options: SecretOptions): string {\n const { crypto, base32, length = RECOMMENDED_SECRET_BYTES } = options;\n\n requireCryptoPlugin(crypto);\n requireBase32Plugin(base32);\n\n const randomBytes = crypto.randomBytes(length);\n return base32.encode(randomBytes, { padding: false });\n}\n\n/**\n * Normalize counter tolerance to an array of offsets\n *\n * Converts a number or array counter tolerance specification into an array of offsets\n * - Number: creates symmetric range [-tolerance, +tolerance]\n * - Array: uses the array as-is (already contains specific offsets)\n *\n * @param counterTolerance - Counter tolerance specification (number or array of offsets)\n * @returns Array of offsets to check\n *\n * @example\n * ```ts\n * normalizeCounterTolerance(0) // [0]\n * normalizeCounterTolerance(1) // [-1, 0, 1]\n * normalizeCounterTolerance(2) // [-2, -1, 0, 1, 2]\n * normalizeCounterTolerance([0, 1]) // [0, 1]\n * normalizeCounterTolerance([-1, 0, 1]) // [-1, 0, 1]\n * ```\n */\nexport function normalizeCounterTolerance(counterTolerance: number | number[] = 0): number[] {\n if (Array.isArray(counterTolerance)) {\n return counterTolerance;\n }\n\n const result: number[] = [];\n for (let i = -counterTolerance; i <= counterTolerance; i++) {\n // Bitwise OR with 0 converts -0 to 0 and preserves other integers\n result.push(i | 0);\n }\n return result;\n}\n\n/**\n * Normalize epoch tolerance to [past, future] tuple\n *\n * Converts a number or tuple epoch tolerance specification into a [past, future] tuple\n * - Number: creates symmetric tolerance [tolerance, tolerance]\n * - Tuple: uses the tuple as-is\n *\n * @param epochTolerance - Epoch tolerance specification (number or tuple [past, future])\n * @returns Tuple [pastTolerance, futureTolerance] in seconds\n *\n * @example\n * ```ts\n * normalizeEpochTolerance(0) // [0, 0]\n * normalizeEpochTolerance(30) // [30, 30]\n * normalizeEpochTolerance([5, 0]) // [5, 0]\n * normalizeEpochTolerance([10, 5]) // [10, 5]\n * ```\n */\nexport function normalizeEpochTolerance(\n epochTolerance: number | [number, number] = 0,\n): [number, number] {\n return Array.isArray(epochTolerance) ? epochTolerance : [epochTolerance, epochTolerance];\n}\n\n/**\n * Require crypto plugin to be configured\n *\n * @param crypto - The crypto plugin\n * @throws {CryptoPluginMissingError} If crypto plugin is not set\n */\nexport function requireCryptoPlugin<T>(crypto: T | undefined): asserts crypto is T {\n if (!crypto) {\n throw new CryptoPluginMissingError();\n }\n}\n\n/**\n * Require Base32 plugin to be configured\n *\n * @param base32 - The Base32 plugin\n * @throws {Base32PluginMissingError} If Base32 plugin is not set\n */\nexport function requireBase32Plugin<T>(base32: T | undefined): asserts base32 is T {\n if (!base32) {\n throw new Base32PluginMissingError();\n }\n}\n\n/**\n * Require secret to be configured\n *\n * @param secret - The secret value\n * @throws {SecretMissingError} If secret is not set\n */\nexport function requireSecret<T>(secret: T | undefined): asserts secret is T {\n if (!secret) {\n throw new SecretMissingError();\n }\n}\n\n/**\n * Require label to be configured (for URI generation)\n *\n * @param label - The label value\n * @throws {LabelMissingError} If label is not set\n */\nexport function requireLabel(label: string | undefined): asserts label is string {\n if (!label) {\n throw new LabelMissingError();\n }\n}\n\n/**\n * Require issuer to be configured (for URI generation)\n *\n * @param issuer - The issuer value\n * @throws {IssuerMissingError} If issuer is not set\n */\nexport function requireIssuer(issuer: string | undefined): asserts issuer is string {\n if (!issuer) {\n throw new IssuerMissingError();\n }\n}\n\n/**\n * Require secret to be a Base32 string (for URI generation)\n *\n * @param secret - The secret value\n * @throws {SecretTypeError} If secret is not a string\n */\nexport function requireBase32String(secret: string | Uint8Array): asserts secret is string {\n if (typeof secret !== \"string\") {\n throw new SecretTypeError();\n }\n}\n\n/**\n * Create a success result\n * @internal\n */\nfunction ok<T>(value: T): OTPResultOk<T> {\n return { ok: true, value };\n}\n\n/**\n * Create a failure result\n * @internal\n */\nfunction err<E>(error: E): OTPResultError<E> {\n return { ok: false, error };\n}\n\n/**\n * Wrap a synchronous function to return OTPResult instead of throwing\n *\n * Preserves the original OTPError subclass so users can access\n * specific error information via instanceof checks.\n *\n * @internal\n */\nexport function wrapResult<T, Args extends unknown[]>(\n fn: (...args: Args) => T,\n): (...args: Args) => OTPResult<T, OTPError> {\n return (...args: Args): OTPResult<T, OTPError> => {\n try {\n return ok(fn(...args));\n } catch (error) {\n return err(error as OTPError);\n }\n };\n}\n\n/**\n * Wrap an async function to return OTPResult instead of throwing\n *\n * Preserves the original OTPError subclass so users can access\n * specific error information via instanceof checks.\n *\n * @internal\n */\nexport function wrapResultAsync<T, Args extends unknown[]>(\n fn: (...args: Args) => Promise<T>,\n): (...args: Args) => Promise<OTPResult<T, OTPError>> {\n return async (...args: Args): Promise<OTPResult<T, OTPError>> => {\n try {\n return ok(await fn(...args));\n } catch (error) {\n return err(error as OTPError);\n }\n };\n}\n","import { HMACError, RandomBytesError } from \"./errors.js\";\n\nimport type { CryptoPlugin, HashAlgorithm } from \"./types.js\";\n\n/**\n * CryptoContext provides a unified interface for crypto operations\n * using a pluggable crypto backend\n */\nexport class CryptoContext {\n /**\n * Create a new CryptoContext with the given crypto plugin\n *\n * @param crypto - The crypto plugin to use\n */\n constructor(private readonly crypto: CryptoPlugin) {}\n\n /**\n * Get the underlying crypto plugin\n */\n get plugin(): CryptoPlugin {\n return this.crypto;\n }\n\n /**\n * Compute HMAC using the configured crypto plugin\n *\n * @param algorithm - The hash algorithm to use\n * @param key - The secret key as a byte array\n * @param data - The data to authenticate as a byte array\n * @returns HMAC digest as a byte array\n * @throws {HMACError} If HMAC computation fails\n */\n async hmac(algorithm: HashAlgorithm, key: Uint8Array, data: Uint8Array): Promise<Uint8Array> {\n try {\n const result = this.crypto.hmac(algorithm, key, data);\n return result instanceof Promise ? await result : result;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new HMACError(message, { cause: error });\n }\n }\n\n /**\n * Synchronous HMAC computation\n *\n * @param algorithm - The hash algorithm to use\n * @param key - The secret key as a byte array\n * @param data - The data to authenticate as a byte array\n * @returns HMAC digest as a byte array\n * @throws {HMACError} If HMAC computation fails or if crypto plugin doesn't support sync operations\n */\n hmacSync(algorithm: HashAlgorithm, key: Uint8Array, data: Uint8Array): Uint8Array {\n try {\n const result = this.crypto.hmac(algorithm, key, data);\n if (result instanceof Promise) {\n throw new HMACError(\"Crypto plugin does not support synchronous HMAC operations\");\n }\n return result;\n } catch (error) {\n if (error instanceof HMACError) {\n throw error;\n }\n const message = error instanceof Error ? error.message : String(error);\n throw new HMACError(message, { cause: error });\n }\n }\n\n /**\n * Generate cryptographically secure random bytes\n *\n * @param length - Number of random bytes to generate\n * @returns Random bytes\n * @throws {RandomBytesError} If random byte generation fails\n */\n randomBytes(length: number): Uint8Array {\n try {\n return this.crypto.randomBytes(length);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new RandomBytesError(message, { cause: error });\n }\n }\n}\n\n/**\n * Create a CryptoContext from a crypto plugin\n *\n * @param crypto - The crypto plugin to use\n * @returns A new CryptoContext instance\n */\nexport function createCryptoContext(crypto: CryptoPlugin): CryptoContext {\n return new CryptoContext(crypto);\n}\n","import { Base32EncodeError, Base32DecodeError } from \"./errors.js\";\n\nimport type { Base32Plugin, Base32EncodeOptions } from \"./types.js\";\n\n/**\n * Base32Context provides a unified interface for Base32 operations\n * using a pluggable Base32 backend.\n *\n * All errors from the underlying plugin are wrapped in otplib error types\n * with the original error preserved via the `cause` property.\n */\nexport class Base32Context {\n /**\n * Create a new Base32Context with the given Base32 plugin\n *\n * @param base32 - The Base32 plugin to use\n */\n constructor(private readonly base32: Base32Plugin) {}\n\n /**\n * Get the underlying Base32 plugin\n */\n get plugin(): Base32Plugin {\n return this.base32;\n }\n\n /**\n * Encode binary data to Base32 string using the configured plugin\n *\n * @param data - Uint8Array to encode\n * @param options - Encoding options\n * @returns Base32 encoded string\n * @throws {Base32EncodeError} If encoding fails\n */\n encode(data: Uint8Array, options?: Base32EncodeOptions): string {\n try {\n return this.base32.encode(data, options);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Base32EncodeError(message, { cause: error });\n }\n }\n\n /**\n * Decode Base32 string to binary data using the configured plugin\n *\n * @param str - Base32 string to decode\n * @returns Decoded Uint8Array\n * @throws {Base32DecodeError} If string contains invalid characters or decoding fails\n */\n decode(str: string): Uint8Array {\n try {\n return this.base32.decode(str);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Base32DecodeError(message, { cause: error });\n }\n }\n}\n\n/**\n * Create a Base32Context from a Base32 plugin\n *\n * @param base32 - The Base32 plugin to use\n * @returns A new Base32Context instance\n */\nexport function createBase32Context(base32: Base32Plugin): Base32Context {\n return new Base32Context(base32);\n}\n","/**\n * TypeScript Utility Types for otplib\n *\n * These types enhance developer experience by providing:\n * - Branded types for type-safe string handling\n * - Type guards for discriminated unions\n * - Helper types for option extraction\n */\n\nimport type { CryptoPlugin, Base32Plugin } from \"./types.js\";\n\n/**\n * Brand type for creating nominal types from primitives\n *\n * @example\n * ```ts\n * type UserId = Brand<string, 'UserId'>;\n * const id: UserId = 'abc' as UserId;\n * ```\n */\nexport type Brand<T, B extends string> = T & { readonly __brand: B };\n\n/**\n * Branded string type for Base32-encoded secrets\n *\n * Use this type to distinguish Base32-encoded secrets from regular strings\n * at compile time, preventing accidental misuse.\n *\n * @example\n * ```ts\n * import type { Base32Secret } from '@otplib/core';\n *\n * function processSecret(secret: Base32Secret): void {\n * // TypeScript ensures only Base32Secret values are passed\n * }\n *\n * const secret = generateSecret() as Base32Secret;\n * processSecret(secret); // OK\n * processSecret('random-string'); // Type error\n * ```\n */\nexport type Base32Secret = Brand<string, \"Base32Secret\">;\n\n/**\n * Branded string type for OTP tokens\n *\n * Use this type to distinguish OTP tokens from regular strings\n * at compile time, preventing accidental misuse.\n *\n * @example\n * ```ts\n * import type { OTPToken } from '@otplib/core';\n *\n * function validateToken(token: OTPToken): boolean {\n * // TypeScript ensures only OTPToken values are passed\n * }\n *\n * const token = await generate() as OTPToken;\n * validateToken(token); // OK\n * validateToken('123456'); // Type error\n * ```\n */\nexport type OTPToken = Brand<string, \"OTPToken\">;\n\n/**\n * Helper type to make all properties of T required except those in K\n *\n * @example\n * ```ts\n * type Options = { a?: string; b?: number; c?: boolean };\n * type RequiredAB = RequireKeys<Options, 'a' | 'b'>;\n * // { a: string; b: number; c?: boolean }\n * ```\n */\nexport type RequireKeys<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;\n\n/**\n * Helper type to make all properties of T optional except those in K\n *\n * @example\n * ```ts\n * type Options = { a: string; b: number; c: boolean };\n * type OptionalBC = OptionalKeys<Options, 'b' | 'c'>;\n * // { a: string; b?: number; c?: boolean }\n * ```\n */\nexport type OptionalKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;\n\n/**\n * Extract the plugin configuration from an options type\n *\n * @example\n * ```ts\n * type Plugins = PluginConfig<TOTPOptions>;\n * // { crypto: CryptoPlugin; base32?: Base32Plugin }\n * ```\n */\nexport type PluginConfig<T> = T extends { crypto?: CryptoPlugin; base32?: Base32Plugin }\n ? Pick<T, \"crypto\" | \"base32\">\n : never;\n\n/**\n * Ensure an options type has plugins defined\n *\n * @example\n * ```ts\n * type ConfiguredOptions = WithRequiredPlugins<TOTPOptions>;\n * // TOTPOptions with crypto and base32 required\n * ```\n */\nexport type WithRequiredPlugins<T extends { crypto?: CryptoPlugin; base32?: Base32Plugin }> = T & {\n crypto: CryptoPlugin;\n base32: Base32Plugin;\n};\n\n/**\n * Options type for OTP generation (crypto required)\n *\n * @example\n * ```ts\n * type MyGenerateOptions = GenerationReady<HOTPOptions>;\n * // HOTPOptions with crypto required\n * ```\n */\nexport type GenerationReady<T extends { crypto?: CryptoPlugin }> = T & {\n crypto: CryptoPlugin;\n};\n\n/**\n * Type predicate to check if an object has the required plugins\n *\n * @example\n * ```ts\n * const options = getOptions();\n * if (hasPlugins(options)) {\n * // TypeScript knows plugins are defined\n * options.crypto.hmac(...);\n * }\n * ```\n */\nexport function hasPlugins<T extends { crypto?: CryptoPlugin; base32?: Base32Plugin }>(\n options: T,\n): options is T & { crypto: CryptoPlugin; base32: Base32Plugin } {\n return options.crypto !== undefined && options.base32 !== undefined;\n}\n\n/**\n * Type predicate to check if an object has a crypto plugin\n *\n * @example\n * ```ts\n * if (hasCrypto(options)) {\n * await options.crypto.hmac('sha1', key, data);\n * }\n * ```\n */\nexport function hasCrypto<T extends { crypto?: CryptoPlugin }>(\n options: T,\n): options is T & { crypto: CryptoPlugin } {\n return options.crypto !== undefined;\n}\n\n/**\n * Type predicate to check if an object has a base32 plugin\n *\n * @example\n * ```ts\n * if (hasBase32(options)) {\n * const decoded = options.base32.decode(secret);\n * }\n * ```\n */\nexport function hasBase32<T extends { base32?: Base32Plugin }>(\n options: T,\n): options is T & { base32: Base32Plugin } {\n return options.base32 !== undefined;\n}\n\n/**\n * Narrow union type by a specific property value\n *\n * @example\n * ```ts\n * type Result = VerifyResultValid | VerifyResultInvalid;\n * type ValidOnly = NarrowBy<Result, 'valid', true>;\n * // VerifyResultValid\n * ```\n */\nexport type NarrowBy<T, K extends keyof T, V extends T[K]> = T extends { [key in K]: V }\n ? T\n : never;\n"],"mappings":"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","names":["OTPError","message","options","SecretError","SecretTooShortError","minBytes","actualBytes","SecretTooLongError","maxBytes","CounterError","CounterNegativeError","CounterOverflowError","TimeError","TimeNegativeError","PeriodError","PeriodTooSmallError","minPeriod","PeriodTooLargeError","maxPeriod","DigitsError","AlgorithmError","TokenError","TokenLengthError","expected","actual","TokenFormatError","CryptoError","HMACError","RandomBytesError","Base32Error","Base32EncodeError","Base32DecodeError","CounterToleranceError","CounterToleranceTooLargeError","maxTolerance","actualSize","EpochToleranceError","EpochToleranceNegativeError","EpochToleranceTooLargeError","actualValue","PluginError","CryptoPluginMissingError","Base32PluginMissingError","ConfigurationError","SecretMissingError","LabelMissingError","IssuerMissingError","SecretTypeError","textEncoder","MIN_SECRET_BYTES","MAX_SECRET_BYTES","RECOMMENDED_SECRET_BYTES","MIN_PERIOD","MAX_PERIOD","DEFAULT_PERIOD","MAX_COUNTER","MAX_WINDOW","OVERRIDE_SYMBOL","DEFAULT_GUARDRAILS","createGuardrails","custom","hasGuardrailOverrides","guardrails","validateSecret","secret","SecretTooShortError","SecretTooLongError","validateCounter","counter","value","CounterNegativeError","CounterOverflowError","validateTime","time","TimeNegativeError","validatePeriod","period","PeriodTooSmallError","PeriodTooLargeError","validateToken","token","digits","TokenLengthError","TokenFormatError","validateCounterTolerance","counterTolerance","CounterToleranceTooLargeError","validateEpochTolerance","epochTolerance","pastTolerance","futureTolerance","EpochToleranceNegativeError","maxToleranceSeconds","maxAllowed","EpochToleranceTooLargeError","counterToBytes","bigintValue","buffer","dynamicTruncate","hmacResult","offset","truncateDigits","maxOtp","validateByteLengthEqual","a","b","constantTimeEqual","bufA","stringToBytes","bufB","result","getDigestSize","algorithm","hexToBytes","hex","bytes","i","normalizeSecret","base32","requireBase32Plugin","generateSecret","options","crypto","length","requireCryptoPlugin","randomBytes","normalizeCounterTolerance","normalizeEpochTolerance","CryptoPluginMissingError","Base32PluginMissingError","requireSecret","SecretMissingError","requireLabel","label","LabelMissingError","requireIssuer","issuer","IssuerMissingError","requireBase32String","SecretTypeError","ok","err","error","wrapResult","fn","args","wrapResultAsync","CryptoContext","crypto","algorithm","key","data","result","error","message","HMACError","length","RandomBytesError","createCryptoContext","Base32Context","base32","data","options","error","message","Base32EncodeError","str","Base32DecodeError","createBase32Context","hasPlugins","options","hasCrypto","hasBase32"]}
|
package/dist/metafile-cjs.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"src/errors.ts":{"bytes":10514,"imports":[],"format":"esm"},"src/utils.ts":{"bytes":25692,"imports":[{"path":"src/errors.ts","kind":"import-statement","original":"./errors.js"}],"format":"esm"},"src/crypto-context.ts":{"bytes":2926,"imports":[{"path":"src/errors.ts","kind":"import-statement","original":"./errors.js"}],"format":"esm"},"src/base32-context.ts":{"bytes":2029,"imports":[{"path":"src/errors.ts","kind":"import-statement","original":"./errors.js"}],"format":"esm"},"src/utility-types.ts":{"bytes":
|
|
1
|
+
{"inputs":{"src/errors.ts":{"bytes":10514,"imports":[],"format":"esm"},"src/utils.ts":{"bytes":25692,"imports":[{"path":"src/errors.ts","kind":"import-statement","original":"./errors.js"}],"format":"esm"},"src/crypto-context.ts":{"bytes":2926,"imports":[{"path":"src/errors.ts","kind":"import-statement","original":"./errors.js"}],"format":"esm"},"src/base32-context.ts":{"bytes":2029,"imports":[{"path":"src/errors.ts","kind":"import-statement","original":"./errors.js"}],"format":"esm"},"src/utility-types.ts":{"bytes":4869,"imports":[],"format":"esm"},"src/index.ts":{"bytes":2262,"imports":[{"path":"src/errors.ts","kind":"import-statement","original":"./errors.js"},{"path":"src/utils.ts","kind":"import-statement","original":"./utils.js"},{"path":"src/crypto-context.ts","kind":"import-statement","original":"./crypto-context.js"},{"path":"src/base32-context.ts","kind":"import-statement","original":"./base32-context.js"},{"path":"src/utility-types.ts","kind":"import-statement","original":"./utility-types.js"}],"format":"esm"},"src/types.ts":{"bytes":4008,"imports":[],"format":"esm"}},"outputs":{"dist/index.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":65278},"dist/index.cjs":{"imports":[],"exports":[],"entryPoint":"src/index.ts","inputs":{"src/index.ts":{"bytesInOutput":1904},"src/errors.ts":{"bytesInOutput":3842},"src/utils.ts":{"bytesInOutput":2647},"src/crypto-context.ts":{"bytesInOutput":672},"src/base32-context.ts":{"bytesInOutput":356},"src/utility-types.ts":{"bytesInOutput":139}},"bytes":11367},"dist/errors.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":15934},"dist/errors.cjs":{"imports":[],"exports":[],"entryPoint":"src/errors.ts","inputs":{"src/errors.ts":{"bytesInOutput":4729}},"bytes":5812},"dist/utils.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":48323},"dist/utils.cjs":{"imports":[],"exports":[],"entryPoint":"src/utils.ts","inputs":{"src/utils.ts":{"bytesInOutput":3529},"src/errors.ts":{"bytesInOutput":3140}},"bytes":7738},"dist/types.cjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":4341},"dist/types.cjs":{"imports":[],"exports":[],"entryPoint":"src/types.ts","inputs":{"src/types.ts":{"bytesInOutput":29}},"bytes":397}}}
|
package/dist/metafile-esm.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"src/errors.ts":{"bytes":10514,"imports":[],"format":"esm"},"src/utils.ts":{"bytes":25692,"imports":[{"path":"src/errors.ts","kind":"import-statement","original":"./errors.js"}],"format":"esm"},"src/crypto-context.ts":{"bytes":2926,"imports":[{"path":"src/errors.ts","kind":"import-statement","original":"./errors.js"}],"format":"esm"},"src/base32-context.ts":{"bytes":2029,"imports":[{"path":"src/errors.ts","kind":"import-statement","original":"./errors.js"}],"format":"esm"},"src/utility-types.ts":{"bytes":
|
|
1
|
+
{"inputs":{"src/errors.ts":{"bytes":10514,"imports":[],"format":"esm"},"src/utils.ts":{"bytes":25692,"imports":[{"path":"src/errors.ts","kind":"import-statement","original":"./errors.js"}],"format":"esm"},"src/crypto-context.ts":{"bytes":2926,"imports":[{"path":"src/errors.ts","kind":"import-statement","original":"./errors.js"}],"format":"esm"},"src/base32-context.ts":{"bytes":2029,"imports":[{"path":"src/errors.ts","kind":"import-statement","original":"./errors.js"}],"format":"esm"},"src/utility-types.ts":{"bytes":4869,"imports":[],"format":"esm"},"src/index.ts":{"bytes":2262,"imports":[{"path":"src/errors.ts","kind":"import-statement","original":"./errors.js"},{"path":"src/utils.ts","kind":"import-statement","original":"./utils.js"},{"path":"src/crypto-context.ts","kind":"import-statement","original":"./crypto-context.js"},{"path":"src/base32-context.ts","kind":"import-statement","original":"./base32-context.js"},{"path":"src/utility-types.ts","kind":"import-statement","original":"./utility-types.js"}],"format":"esm"},"src/types.ts":{"bytes":4008,"imports":[],"format":"esm"}},"outputs":{"dist/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":60237},"dist/index.js":{"imports":[],"exports":["AlgorithmError","Base32Context","Base32DecodeError","Base32EncodeError","Base32Error","Base32PluginMissingError","ConfigurationError","CounterError","CounterNegativeError","CounterOverflowError","CounterToleranceError","CounterToleranceTooLargeError","CryptoContext","CryptoError","CryptoPluginMissingError","DEFAULT_PERIOD","DigitsError","EpochToleranceError","EpochToleranceNegativeError","EpochToleranceTooLargeError","HMACError","IssuerMissingError","LabelMissingError","MAX_COUNTER","MAX_PERIOD","MAX_SECRET_BYTES","MAX_WINDOW","MIN_PERIOD","MIN_SECRET_BYTES","OTPError","PeriodError","PeriodTooLargeError","PeriodTooSmallError","PluginError","RECOMMENDED_SECRET_BYTES","RandomBytesError","SecretError","SecretMissingError","SecretTooLongError","SecretTooShortError","SecretTypeError","TimeError","TimeNegativeError","TokenError","TokenFormatError","TokenLengthError","constantTimeEqual","counterToBytes","createBase32Context","createCryptoContext","createGuardrails","dynamicTruncate","generateSecret","getDigestSize","hasBase32","hasCrypto","hasGuardrailOverrides","hasPlugins","hexToBytes","normalizeCounterTolerance","normalizeEpochTolerance","normalizeSecret","requireBase32Plugin","requireBase32String","requireCryptoPlugin","requireIssuer","requireLabel","requireSecret","stringToBytes","truncateDigits","validateByteLengthEqual","validateCounter","validateCounterTolerance","validateEpochTolerance","validatePeriod","validateSecret","validateTime","validateToken","wrapResult","wrapResultAsync"],"entryPoint":"src/index.ts","inputs":{"src/errors.ts":{"bytesInOutput":3842},"src/index.ts":{"bytesInOutput":0},"src/utils.ts":{"bytesInOutput":2642},"src/crypto-context.ts":{"bytesInOutput":672},"src/base32-context.ts":{"bytesInOutput":356},"src/utility-types.ts":{"bytesInOutput":139}},"bytes":9444},"dist/errors.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":15391},"dist/errors.js":{"imports":[],"exports":["AlgorithmError","Base32DecodeError","Base32EncodeError","Base32Error","Base32PluginMissingError","ConfigurationError","CounterError","CounterNegativeError","CounterOverflowError","CounterToleranceError","CounterToleranceTooLargeError","CryptoError","CryptoPluginMissingError","DigitsError","EpochToleranceError","EpochToleranceNegativeError","EpochToleranceTooLargeError","HMACError","IssuerMissingError","LabelMissingError","OTPError","PeriodError","PeriodTooLargeError","PeriodTooSmallError","PluginError","RandomBytesError","SecretError","SecretMissingError","SecretTooLongError","SecretTooShortError","SecretTypeError","TimeError","TimeNegativeError","TokenError","TokenFormatError","TokenLengthError"],"entryPoint":"src/errors.ts","inputs":{"src/errors.ts":{"bytesInOutput":3842}},"bytes":4666},"dist/utils.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":47065},"dist/utils.js":{"imports":[],"exports":["DEFAULT_PERIOD","MAX_COUNTER","MAX_PERIOD","MAX_SECRET_BYTES","MAX_WINDOW","MIN_PERIOD","MIN_SECRET_BYTES","RECOMMENDED_SECRET_BYTES","constantTimeEqual","counterToBytes","createGuardrails","dynamicTruncate","generateSecret","getDigestSize","hasGuardrailOverrides","hexToBytes","normalizeCounterTolerance","normalizeEpochTolerance","normalizeSecret","requireBase32Plugin","requireBase32String","requireCryptoPlugin","requireIssuer","requireLabel","requireSecret","stringToBytes","truncateDigits","validateByteLengthEqual","validateCounter","validateCounterTolerance","validateEpochTolerance","validatePeriod","validateSecret","validateTime","validateToken","wrapResult","wrapResultAsync"],"entryPoint":"src/utils.ts","inputs":{"src/errors.ts":{"bytesInOutput":3140},"src/utils.ts":{"bytesInOutput":2624}},"bytes":6598},"dist/types.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":93},"dist/types.js":{"imports":[],"exports":[],"entryPoint":"src/types.ts","inputs":{"src/types.ts":{"bytesInOutput":0}},"bytes":0}}}
|