Architecture
This page describes the high-level architecture of the ms library, including its module structure, internal parsing and formatting mechanisms, and the key data flows that transform time strings to milliseconds and back.
System Overview
ms is a single-purpose utility library with no runtime processes, network services, or persistent state. It consists of two modules:
src– the core library implementing all public functions (parse, format, ms, parseStrict)- Project Root (
"") – development tooling configuration (Jest, lint-staged, tsdown)
The library has no dependencies and runs entirely within the host process (Node.js, browser, Deno, Bun, or Edge Runtime).
Module Layering
src Module (Core Library)
The src module is the sole runtime layer. It contains a single source file (src/index.ts) that exports all public functions and types, plus four test files that validate behavior. There are no internal submodules, services, or intermediate layers.
Public API surface:
| Symbol | Signature | Purpose |
|---|---|---|
ms | (value: string | number, options?: MsOptions) => string | number | undefined | Main entry point: parses strings to milliseconds or formats numbers to strings |
parse | (value: string) => number | undefined | Parses a time string to milliseconds; returns undefined on invalid input |
parseStrict | (value: StringValue) => number | Type-safe parsing that accepts only valid template literal patterns at compile time; throws on invalid input |
format | (value: number, options?: MsOptions) => string | Formats milliseconds to a human-readable string (short or long form) |
StringValue | Type | Template literal type representing all valid time string patterns (e.g., "2 hours", "1d") |
MsOptions | Interface | Options object with optional long: boolean property |
Project Root Module (Configuration)
The root module contains three configuration files that are never loaded at runtime:
jest.config.ts– test runner configurationlint-staged.config.ts– pre-commit linting setuptsdown.config.ts– bundler configuration for producing the npm package
These files govern the development pipeline only. They do not affect the library's behavior in production.
Key Flows
Flow 1: Parsing a Time String to Milliseconds
When a consumer calls ms("2 hours") or parse("2 days"), the library performs the following steps:
-
Input normalization – The string is trimmed and converted to lowercase. The numeric portion and unit portion are extracted using a regular expression.
-
Unit resolution – The unit string (e.g.,
"hours","h","hour") is matched against a lookup table of supported units. Each unit maps to a millisecond multiplier:
| Unit | Multiplier (ms) |
|---|---|
| second / seconds / s | 1,000 |
| minute / minutes / m | 60,000 |
| hour / hours / h | 3,600,000 |
| day / days / d | 86,400,000 |
| week / weeks / w | 604,800,000 |
| month / months / mo | 2,592,000,000 (30 days) |
| year / years / y | 31,536,000,000 (365 days) |
-
Multiplication – The numeric value is multiplied by the unit's millisecond constant. Fractional values (e.g.,
"1.5 hours") are supported. -
Return – The result is returned as a number. If the input string is invalid (unrecognized unit, non-numeric prefix, empty string),
parse()returnsundefinedandms()returnsundefined.parseStrict()throws aRangeErrorinstead.
The parseStrict() function adds a compile-time constraint: its parameter type StringValue is a TypeScript template literal type that only accepts strings matching the allowed patterns. This prevents passing arbitrary strings at compile time.
Flow 2: Formatting Milliseconds to a String
When a consumer calls ms(60000) or format(86400000, { long: true }), the library:
-
Determines the sign – If the value is negative, the output is prefixed with
-. -
Finds the best unit – The absolute value is compared against unit thresholds in descending order (years, months, weeks, days, hours, minutes, seconds). The largest unit that produces a whole number (or a value >= 1) is selected. For example,
60000matches minutes (1 minute), while90000matches minutes (1.5 minutes). -
Formats the output – The value is divided by the unit's millisecond constant and rounded to one decimal place if fractional. The unit abbreviation (e.g.,
"m") or full name (e.g.," minute") is appended based on thelongoption. -
Pluralization – In long format, the unit name is pluralized when the value is not exactly 1 (e.g.,
"2 minutes"vs"1 minute").
Flow 3: Combined Parse and Format via ms()
The ms() function acts as a dispatcher:
- If the input is a string, it delegates to the parsing logic (same as
parse()). - If the input is a number, it delegates to the formatting logic (same as
format()).
This allows consumers to use a single import for both directions.
Runtime Topology
The library has no runtime topology. It is a pure function library that executes entirely within the caller's process. There are no:
- Network services or APIs
- Background processes or workers
- Caches or state stores
- Configuration files loaded at runtime
The only "runtime" is the host environment (Node.js, browser, Deno, Bun, Edge Runtime) that imports and invokes the exported functions.
Edge Runtime Compatibility
Because ms has no dependencies and uses no Node.js-specific APIs (no fs, net, process), it runs in any JavaScript environment including Vercel Edge Functions, Cloudflare Workers, and Deno. The package's package.json exports both CommonJS and ESM builds to support all module systems.
Related Pages
- Overview
- Getting Started
- Architecture (this page)