Pre-release hardening pass over the core NTP client, plus a full offline test suite and packaging verification in CI.
Fixes & hardening
- Instance-scoped state — cache/singleton state moved off module scope; independent
new NtpTimeSync(...)instances no longer cross-contaminate. - Signed clock offset (RFC 5905) — offset now uses
((T2-T1) + (T3-T4))/2instead of anabs()+heuristic-sign form that returned the wrong result on asymmetric paths. - IPv6-aware server parsing — supports bracketed IPv6
[::1][:port], bare IPv6 literals, and the existinghost/host:port/IPv4[:port]forms with port-range validation. - Stricter option validation — unknown constructor options now throw
Invalid option: <key>instead of being silently ignored. - Robustness — register the message listener before
send(fixes a fast-reply race), count retries on no-progress rounds, and harden socket/listener cleanup on error paths. createPacketrewrite — usesBuffer.writeBigUInt64BE, dropping deprecatedsubstrand per-packet string allocations.
Public API
src/index.tsuses explicit named exports andNtpTimeSyncDefaultOptionsis deep-frozen. Same 5 names, runtime mutation blocked.
Dependencies
ntp-packet-parserbumped to^0.6.1.
Tooling / CI
- 32 offline unit tests (in-process UDP fixture) covering packet construction, response validation, sample math, instance isolation, retry bounds, socket-leak regression, option validation, and server parsing.
- New
verify-packageCI job runsnpm pack,publint, andattw(0.18.4) to catchexports/files/types regressions before publish.
⚠️ Breaking changes
- Unknown constructor options now throw instead of being ignored.
- Computed offsets change for asymmetric network paths (signed RFC 5905 formula).
src/index.tsno longer re-exports viaexport *:RecursivePartialis no longer exported andNtpTimeResultis type-only.
Full changelog: 0.5.0...0.6.0