Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"@types/node": "^20 || ^22 || ^24 || ^25",
"prettier": "3.8.3",
"ts-node": "10.9.2",
"typescript": "5.9.3"
"typescript": "6.0.3"
},
"scripts": {
"prepublishOnly": "yarn prettier:lint && yarn build",
Expand Down
66 changes: 41 additions & 25 deletions src/NtpTimeSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,15 @@ export class NtpTimeSync {

this.options = {
...mergedConfig,
servers: serverConfig.map((server) => {
return {
host: server.split(":", 2)[0],
port: Number(server.split(":", 2)[1]) || mergedConfig.ntpDefaults.port,
};
}),
servers: serverConfig
.filter((server): server is string => server !== undefined)
.map((server) => {
const parts = server.split(":", 2);
return {
host: parts[0] ?? server,
port: Number(parts[1]) || mergedConfig.ntpDefaults.port,
};
}),
};
}

Expand Down Expand Up @@ -181,25 +184,30 @@ export class NtpTimeSync {
// filter erroneous responses, use valid ones as samples
let samples: SampleData[] = [];
ntpResults.forEach((data) => {
const offsetSign = data.transmitTimestamp.getTime() > data.destinationTimestamp.getTime() ? 1 : -1;
const transmitTimestamp = data.transmitTimestamp!;
const receiveTimestamp = data.receiveTimestamp!;
const originTimestamp = data.originTimestamp!;
const precision = data.precision!;

const offsetSign = transmitTimestamp.getTime() > data.destinationTimestamp.getTime() ? 1 : -1;

const offset =
((Math.abs(data.receiveTimestamp.getTime() - data.originTimestamp.getTime()) +
Math.abs(data.transmitTimestamp.getTime() - data.destinationTimestamp.getTime())) /
((Math.abs(receiveTimestamp.getTime() - originTimestamp.getTime()) +
Math.abs(transmitTimestamp.getTime() - data.destinationTimestamp.getTime())) /
2) *
offsetSign;

const delay = Math.max(
data.destinationTimestamp.getTime() -
data.originTimestamp.getTime() -
(data.receiveTimestamp.getTime() - data.transmitTimestamp.getTime()),
originTimestamp.getTime() -
(receiveTimestamp.getTime() - transmitTimestamp.getTime()),
Math.pow(2, this.options.ntpDefaults.precision)
);

const dispersion =
Math.pow(2, data.precision) +
Math.pow(2, precision) +
Math.pow(2, this.options.ntpDefaults.precision) +
this.options.ntpDefaults.tolerance * (data.destinationTimestamp.getTime() - data.originTimestamp.getTime());
this.options.ntpDefaults.tolerance * (data.destinationTimestamp.getTime() - originTimestamp.getTime());

samples.push({
data: data,
Expand All @@ -222,7 +230,12 @@ export class NtpTimeSync {
* @param {boolean} force Force NTP update
*/
async getTime(force = false): Promise<NtpTimeResult> {
if (!force && lastPoll && Date.now() - lastPoll < Math.pow(2, this.options.ntpDefaults.minPoll) * 1000) {
if (
!force &&
lastPoll &&
lastResult &&
Date.now() - lastPoll < Math.pow(2, this.options.ntpDefaults.minPoll) * 1000
) {
let date = new Date();
date.setUTCMilliseconds(date.getUTCMilliseconds() + lastResult.offset);

Expand Down Expand Up @@ -285,7 +298,7 @@ export class NtpTimeSync {
* @param {Integer} mode, defaults to 3 (client)
* @return {Buffer}
*/
private createPacket(leapIndicator = 3, ntpVersion: number = null, mode = 3): Buffer {
private createPacket(leapIndicator = 3, ntpVersion: number | undefined = undefined, mode = 3): Buffer {
ntpVersion = ntpVersion || this.options.ntpDefaults.version;

// generate NTP packet
Expand Down Expand Up @@ -344,9 +357,9 @@ export class NtpTimeSync {
let hasFinished = false;

const errorCallback = (err: Error) => {
if (timeoutHandler) {
if (timeoutHandler !== undefined) {
clearTimeout(timeoutHandler);
timeoutHandler = null;
timeoutHandler = undefined;
}

if (hasFinished) {
Expand All @@ -359,14 +372,14 @@ export class NtpTimeSync {
reject(err);
};

client.on("error", (err) => errorCallback);
client.on("error", (err: Error) => errorCallback(err));

// setup timeout
let timeoutHandler = setTimeout(() => {
let timeoutHandler: ReturnType<typeof setTimeout> | undefined = setTimeout(() => {
errorCallback(new Error("Timeout waiting for NTP response."));
}, this.options.replyTimeout);

client.send(this.createPacket(), port, server, (err) => {
client.send(this.createPacket(), port, server, (err: Error | null) => {
if (hasFinished) {
return;
}
Expand All @@ -376,13 +389,13 @@ export class NtpTimeSync {
return;
}

client.once("message", function (msg) {
client.once("message", function (msg: Buffer) {
if (hasFinished) {
return;
}

clearTimeout(timeoutHandler);
timeoutHandler = null;
timeoutHandler = undefined;
client.close();

let parsed: Partial<NtpPacket>;
Expand Down Expand Up @@ -413,21 +426,24 @@ export class NtpTimeSync {
/*
* Format error
*/
if (data.version > this.options.ntpDefaults.version) {
if (data.version === undefined || data.version > this.options.ntpDefaults.version) {
throw new Error("Format error: Expected version " + this.options.ntpDefaults.version + ", got " + data.version);
}

/*
* A stratum error occurs if (1) the server has never been
* synchronized, (2) the server stratum is invalid.
*/
if (data.leapIndicator === 3 || data.stratum >= this.options.ntpDefaults.maxStratum) {
if (data.leapIndicator === 3 || data.stratum === undefined || data.stratum >= this.options.ntpDefaults.maxStratum) {
throw new Error("Stratum error: Remote clock is unsynchronized");
}

/*
* Verify valid root distance.
*/
if (data.rootDelay === undefined || data.rootDispersion === undefined) {
throw new Error("Format error: Missing root delay or root dispersion");
}
const rootDelay = (data.rootDelay.getTime() - this.options.ntpDefaults.referenceDate.getTime()) / 1000;
const rootDispersion = (data.rootDispersion.getTime() - this.options.ntpDefaults.referenceDate.getTime()) / 1000;
if (rootDelay / 2 + rootDispersion >= this.options.ntpDefaults.maxDispersion) {
Expand All @@ -437,7 +453,7 @@ export class NtpTimeSync {
/*
* Verify origin timestamp
*/
if (data.originTimestamp.getTime() > new Date().getTime()) {
if (data.originTimestamp === undefined || data.originTimestamp.getTime() > new Date().getTime()) {
throw new Error("Format error: Origin timestamp is from the future");
}
}
Expand Down
7 changes: 6 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
{
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"allowJs": true,
"lib": [
"es2019"
],
"target": "es5",
"module": "commonjs",
"target": "es2019",
"types": [
"node"
],
"noEmitOnError": true,
"noImplicitAny": true,
"noImplicitReturns": true,
Expand Down
64 changes: 32 additions & 32 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,99 +4,99 @@

"@cspotcode/source-map-support@^0.8.0":
version "0.8.1"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz"
integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
dependencies:
"@jridgewell/trace-mapping" "0.3.9"

"@jridgewell/resolve-uri@^3.0.3":
version "3.0.7"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe"
resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz"
integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==

"@jridgewell/sourcemap-codec@^1.4.10":
version "1.4.13"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c"
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz"
integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==

"@jridgewell/trace-mapping@0.3.9":
version "0.3.9"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz"
integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
dependencies:
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"

"@tsconfig/node10@^1.0.7":
version "1.0.8"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9"
resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz"
integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==

"@tsconfig/node12@^1.0.7":
version "1.0.9"
resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c"
resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz"
integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==

"@tsconfig/node14@^1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2"
resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz"
integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==

"@tsconfig/node16@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e"
resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz"
integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==

"@types/node@^18 || ^20 || ^22":
version "22.14.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.14.0.tgz#d3bfa3936fef0dbacd79ea3eb17d521c628bb47e"
integrity sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==
"@types/node@*", "@types/node@^20 || ^22 || ^24 || ^25":
version "25.6.0"
resolved "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz"
integrity sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==
dependencies:
undici-types "~6.21.0"
undici-types "~7.19.0"

acorn-walk@^8.1.1:
version "8.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==

acorn@^8.4.1:
version "8.7.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz"
integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==

arg@^4.1.0:
version "4.1.3"
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz"
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==

create-require@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==

diff@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz"
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==

make-error@^1.1.1:
version "1.3.6"
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz"
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==

ntp-packet-parser@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/ntp-packet-parser/-/ntp-packet-parser-0.5.0.tgz#2eb098ec3a3a9f8d5388b066d2fa0d87ddc3a2e1"
resolved "https://registry.npmjs.org/ntp-packet-parser/-/ntp-packet-parser-0.5.0.tgz"
integrity sha512-W18iLyaV17jH4inpvSu2gPep2f1Gs3oABra5NXXzG7MXhLcdHqJrJBO23bTMLkRf4HP9SWHL7FEwwtUf6R1q+g==

prettier@3.8.3:
version "3.8.3"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.3.tgz#560f2de55bf01b4c0503bc629d5df99b9a1d09b0"
resolved "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz"
integrity sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==

ts-node@10.9.2:
version "10.9.2"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f"
resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz"
integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==
dependencies:
"@cspotcode/source-map-support" "^0.8.0"
Expand All @@ -113,22 +113,22 @@ ts-node@10.9.2:
v8-compile-cache-lib "^3.0.1"
yn "3.1.1"

typescript@5.9.3:
version "5.9.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f"
integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==
typescript@>=2.7, typescript@6.0.3:
version "6.0.3"
resolved "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz"
integrity sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==

undici-types@~6.21.0:
version "6.21.0"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb"
integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==
undici-types@~7.19.0:
version "7.19.2"
resolved "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz"
integrity sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==

v8-compile-cache-lib@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz"
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==

yn@3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz"
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
Loading