Skip to content

add Bun.ms: ms-style duration parse/format API#31613

Open
robobun wants to merge 1 commit into
farm/9555d6db/minimum-release-age-default-and-msfrom
farm/9555d6db/bun-ms
Open

add Bun.ms: ms-style duration parse/format API#31613
robobun wants to merge 1 commit into
farm/9555d6db/minimum-release-age-default-and-msfrom
farm/9555d6db/bun-ms

Conversation

@robobun

@robobun robobun commented May 30, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #30529 (merge that first; this PR's diff is shown against its branch). Reuses the parse_ms landed in #30529.

Requested in #18597; supersedes the earlier attempt #23162 (which predated the Rust rewrite by ~1200 commits).

Note: this was previously stacked on #31608 (the configVersion-gated 2-day default), which was declined because the npm registry's compact Content-Type doesn't carry the time data the age filter needs. Bun.ms doesn't depend on any of that — it's been rebased directly onto #30529.

What

Bun.ms — modeled on the npm ms package:

Bun.ms("2 days");              // 172800000   (parse string → ms)
Bun.ms("1h");                  // 3600000
Bun.ms("-1.5h");               // -5400000
Bun.ms("nonsense");            // undefined   (unparseable → undefined, like ms)
Bun.ms(60000);                 // "1m"        (format ms → string)
Bun.ms(60000, { long: true }); // "1 minute"
Bun.ms(172800000);             // "2d"
Bun.ms(1.5);                   // "1.5ms"

How

  • Parse (string → number) reuses bun_core::parse_ms from install: accept ms-style duration strings for minimumReleaseAge #30529.
  • Format (number → string) adds bun_core::format_ms, kept in f64 throughout and stringified with WTF's dtoa (JS Number.prototype.toString semantics): JavaScript Math.round tie-breaking (round half toward +∞), pluralization at ≥ 1.5× in the long form, and the same unit scale (a year is 365.25 days).

ms-package behavior matched

  • Unparseable non-empty strings return undefined (not a throw), so Bun.ms(input) ?? fallback works. Empty string / non-string-non-number inputs throw (matching ms's typeof checks — boxed new String(...) throws too).
  • Formatting emits at most days (never weeks/years — those are parse-only); sub-second keeps the raw number (Bun.ms(1.5)"1.5ms"); months are not a unit (Bun.ms("1mo")undefined).

One intentional difference: the underlying parse_ms (shared with bunfig / --minimum-release-age parsing in #30529) is slightly more lenient than ms's anchored regex — it trims surrounding whitespace and accepts a leading +. It's strictly a superset, so anything ms parses, Bun.ms parses identically.

Verification

test/js/bun/util/ms.test.ts — 13 tests pass on debug+ASAN: compact/long/case-insensitive parsing, undefined-on-unparseable, empty-string/boxed-object/wrong-type throwing, compact/long formatting, JS-round tie-breaking, raw sub-second, parse↔format round-trips, and non-finite rejection. cargo clippy clean on bun_core + bun_runtime; debug + release both build clean. Docs added to docs/runtime/utils.mdx + bun-apis.mdx.

@robobun robobun requested a review from alii as a code owner May 30, 2026 13:28
@robobun

robobun commented May 30, 2026

Copy link
Copy Markdown
Collaborator Author

@github-actions

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. implement Bun.ms #23162 - Earlier Zig-based implementation of the same Bun.ms() parse/format API
  2. Cleaner way to implement Bun.ms #23724 - Alternative implementation of Bun.ms()

🤖 Generated with Claude Code

@robobun

robobun commented May 30, 2026

Copy link
Copy Markdown
Collaborator Author

Yep — this intentionally supersedes #23162 (and its follow-up #23724), which implemented the same Bun.ms API in Zig before the Rust rewrite (~1200 commits ago) and never merged. This PR is the Rust port: it reuses the parse_ms already landed in this stack (#30529) and adds format_ms, matching the npm ms package exactly. Noted in the PR description; closes #18597.

Comment thread src/bun_core/fmt.rs Outdated
Comment thread src/runtime/api/BunObject.rs Outdated
Comment thread src/bun_core/fmt.rs Outdated
@robobun robobun force-pushed the farm/9555d6db/bun-ms branch from 01cc9b1 to 53897e2 Compare May 30, 2026 13:46
@robobun robobun force-pushed the farm/9555d6db/minimum-release-age-2d-default branch from 46d53b6 to e78489e Compare May 30, 2026 13:47
@robobun robobun force-pushed the farm/9555d6db/bun-ms branch from 53897e2 to 0be36c9 Compare May 30, 2026 13:48
Comment thread src/runtime/api/BunObject.rs Outdated
Comment thread src/runtime/api/BunObject.rs
Comment thread src/runtime/api/BunObject.rs Outdated
Comment thread src/runtime/api/BunObject.rs
@robobun robobun force-pushed the farm/9555d6db/bun-ms branch from 0be36c9 to 37e71ed Compare May 30, 2026 14:09
Comment thread packages/bun-types/bun.d.ts Outdated
@robobun robobun force-pushed the farm/9555d6db/bun-ms branch from 37e71ed to f18b3df Compare May 30, 2026 14:26
Comment thread test/js/bun/util/ms.test.ts Outdated
@robobun robobun force-pushed the farm/9555d6db/bun-ms branch from b9effe8 to cbb40a7 Compare May 30, 2026 14:47
Comment thread docs/runtime/utils.mdx Outdated
@robobun robobun force-pushed the farm/9555d6db/bun-ms branch from cbb40a7 to a30583f Compare May 30, 2026 14:55

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All prior feedback addressed and the implementation looks solid — deferring to a human since this ships a new public Bun.* API surface (and is stacked on two unmerged PRs), which is a product call worth a maintainer's sign-off.

Extended reasoning...

Overview

This PR adds Bun.ms(), a new public utility on the Bun global modeled on the npm ms package: string → milliseconds parsing (reusing bun_core::parse_ms from earlier in the stack) and milliseconds → string formatting (new bun_core::format_ms). Touches: ~76 lines of new Rust in fmt.rs + a ~67-line host fn in BunObject.rs, one-line wiring in the C++ LUT (BunObject.cpp / BunObject+exports.h), a re-export in lib.rs, two .d.ts overloads, two docs pages, and a 165-line test file (13 tests).

Prior review history

I left five rounds of inline comments on earlier revisions (sub-second truncation, non-number coercion, i64 saturation, undefined-vs-throw on unparseable strings, boxed-string handling, dead exception check, missing docs, a TS2769 in the test, and a broken docs example). Every one was addressed in follow-up commits (53897e2, 37e71ed, f18b3df, cbb40a7, a30583f) and all threads are resolved. The current bug-hunting pass found nothing new.

Security risks

None identified. The function is a pure string/number converter with no I/O, no filesystem/network access, and no auth/crypto/permission implications. Inputs are bounded (parse_ms operates on the byte slice directly with no regex backtracking; format_ms is constant-time per call). The one unsafe block (from_utf8_unchecked on dtoa output) is sound — WTF's dtoa only emits ASCII.

Level of scrutiny

Moderate. The implementation itself is small, well-commented, and now thoroughly tested. What pushes this above my auto-approval bar is that it introduces new public API surface on the Bun global — the overload shapes, the undefined-on-unparseable contract, and the documented intentional divergence from npm ms (lenient whitespace/+ parsing) are product decisions that become hard to change once shipped. That's a maintainer's call, not a bot's.

Other factors

  • Stacked on #31608#30529, both of which must merge first; merge order is a human concern.
  • CI shows musl LTO link failures and one Windows bun-install.test.ts failure that look unrelated/infrastructural, but a human should confirm before merge.
  • Test coverage is good: parsing (compact/long/case-insensitive/negatives), formatting (compact/long/rounding/pluralization/sub-second), strict type rejection, round-trips, and non-finite rejection.

@robobun

robobun commented May 30, 2026

Copy link
Copy Markdown
Collaborator Author

CI status

Build 60210 (rebased tip, sha d3ea742): 269+ jobs passed; all 15 failing lanes fail on the same single testtest/cli/install/bunx.test.ts → "should handle package that requires node 24", which runs live bunx --bun @angular/cli@latest against the real npm registry. The latest Angular publish exits 3 under --bun, breaking that test tree-wide on every platform: it fails identically on plain main (bf70cc79c4) with zero changes from this stack. test/js/bun/util/ms.test.ts is not in any failure set.

This needs a tree-wide fix (pin the Angular version in the test, or investigate the --bun incompatibility) — outside this PR's scope.

Bun.ms builds clean in debug + release, passes 13/13 tests locally, clippy clean. Stacked on #30529merge that first. Diff is green; ready for review/merge + maintainer sign-off on the new public API.

@robobun robobun changed the base branch from farm/9555d6db/minimum-release-age-2d-default to farm/9555d6db/minimum-release-age-default-and-ms June 1, 2026 23:30
@robobun robobun force-pushed the farm/9555d6db/bun-ms branch from aeb24c9 to 79616b0 Compare June 1, 2026 23:30
`Bun.ms` mirrors the npm `ms` package:
- `Bun.ms("2 days")` → 172800000 (parse a duration string to milliseconds)
- `Bun.ms(60000)` → "1m" (format milliseconds to a compact string)
- `Bun.ms(60000, { long: true })` → "1 minute" (verbose form)

Parsing reuses `bun_core::parse_ms`; formatting adds `bun_core::format_ms`
with JavaScript `Math.round` tie-breaking (round half toward +∞) and the same
unit scale as the `ms` package (a year is 365.25 days; formatting emits at most
days, never weeks/years). Invalid strings and non-finite numbers throw a
TypeError.

Wires the host function into BunObject (Rust `ms` + LUT entry + exports),
adds the `Bun.ms` TypeScript overloads, and tests the parse/format/rounding
behavior against the `ms` package.

Closes #18597
@robobun robobun force-pushed the farm/9555d6db/minimum-release-age-default-and-ms branch from 29c0f0c to fa4b4b2 Compare June 3, 2026 23:40
@robobun robobun force-pushed the farm/9555d6db/bun-ms branch from 79616b0 to d3ea742 Compare June 3, 2026 23:42

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All my earlier feedback has been addressed and the implementation looks solid; deferring for a human to sign off on adding Bun.ms to the public API surface (and on the stacked #30529 dependency).

Extended reasoning...

Overview

This PR adds Bun.ms() — an ms-package-compatible duration parse/format utility — as a new public function on the Bun global. It touches: src/bun_core/fmt.rs (new format_ms + js_math_round + write_js_number helpers), src/runtime/api/BunObject.rs (the host fn), src/jsc/bindings/BunObject.cpp / BunObject+exports.h (LUT wiring), packages/bun-types/bun.d.ts (two overloads + MsOptions), docs (bun-apis.mdx, utils.mdx), and a new 165-line test file. The PR is stacked on #30529.

Security risks

None identified. The function is pure string/number conversion with no I/O, no auth, no filesystem/network access. Inputs are strictly type-checked (primitive string or finite number only); the parser operates on a bounded byte slice with no allocation proportional to untrusted input beyond the input string itself. The one unsafe block (from_utf8_unchecked over WTF dtoa output) is sound — dtoa only emits ASCII.

Level of scrutiny

Moderate-to-high, primarily because this adds new public API surface to the Bun global. The implementation itself is small, self-contained, and well-tested (13 tests covering parse/format/edge cases/type strictness), and went through five rounds of review feedback in this thread — all of which the author addressed (sub-second truncation, i64 saturation, type coercion, undefined-on-unparseable, boxed-string handling, dead-code cleanup, docs, doc-example correctness, TS overload typecheck). The remaining decision is product/API-shape, not correctness: whether Bun.ms belongs on the global, and whether the documented one intentional divergence (slightly more lenient whitespace/+ parsing than npm ms) is acceptable.

Other factors

  • The author's own final comment explicitly requests "maintainer sign-off on the new Bun.ms public API."
  • Stacked on #30529, which must merge first.
  • CI is green on Linux/Windows; darwin failures are unrelated infra issues (missing Postgres role, OOM-killed runner).
  • No CODEOWNERS conflict apparent for these paths.

Given that new public API surface is a design decision a human maintainer should ratify, I'm deferring rather than approving.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant