@hixbe/time 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Hixbe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,446 @@
1
+ # @hixbe/time
2
+
3
+ **High-precision NTP time synchronization package with powerful CLI tools**
4
+
5
+ A professional-grade TypeScript package for querying NTP servers and synchronizing system time with network time servers. Built with Hixbe's primary server `time.hixbe.com` for ultra-reliable time synchronization.
6
+
7
+ [![npm version](https://img.shields.io/npm/v/@hixbe/time.svg)](https://www.npmjs.com/package/@hixbe/time)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue)](https://www.typescriptlang.org/)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
+
11
+ ## Features
12
+
13
+ ✨ **Rich Feature Set**
14
+ - 🚀 High-performance NTP client
15
+ - 📊 Raw packet inspection & analysis
16
+ - ⏱️ Precise timestamp conversion (NTP to Unix)
17
+ - 🔄 Continuous sync mode with configurable intervals
18
+ - 📡 Multiple server support (Hixbe, pool.ntp.org, etc.)
19
+ - 📋 Detailed verbose reporting
20
+ - 🎨 Beautiful CLI with multiple output formats
21
+ - 🔐 Type-safe TypeScript implementation
22
+ - ⚡ Zero external dependencies (uses Node.js built-ins)
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install @hixbe/time
28
+ ```
29
+
30
+ ### Global CLI Installation
31
+
32
+ ```bash
33
+ npm install -g @hixbe/time
34
+ hixbe-time --help
35
+ ```
36
+
37
+ ## Quick Start
38
+
39
+ ### CLI Usage
40
+
41
+ ```bash
42
+ # Get current time from Hixbe server
43
+ hixbe-time
44
+
45
+ # Verbose mode with raw packet details
46
+ hixbe-time --verbose
47
+
48
+ # Output as JSON
49
+ hixbe-time --json
50
+
51
+ # Get time offset (useful for scripts)
52
+ hixbe-time --offset
53
+
54
+ # Continuous synchronization (every 5 seconds)
55
+ hixbe-time --continuous
56
+
57
+ # Custom interval (every 2 seconds)
58
+ hixbe-time --continuous --interval 2000
59
+
60
+ # Query different server
61
+ hixbe-time --server pool.ntp.org --verbose
62
+
63
+ # Show only the offset
64
+ hixbe-time --offset
65
+ ```
66
+
67
+ ### Programmatic Usage
68
+
69
+ ```typescript
70
+ import { NTPClient } from '@hixbe/time';
71
+
72
+ // Basic usage
73
+ const client = new NTPClient();
74
+ const result = await client.query();
75
+ console.log(result.parsed.timestamps.transmit.date);
76
+
77
+ // Get current time
78
+ const time = await client.getTime();
79
+ console.log(time); // Date object
80
+
81
+ // Get offset between local and server time
82
+ const offsetMs = await client.getOffset();
83
+ console.log(`System is ${offsetMs > 0 ? 'slow' : 'fast'} by ${Math.abs(offsetMs)}ms`);
84
+
85
+ // Custom server
86
+ const customClient = new NTPClient({
87
+ host: 'time.google.com',
88
+ timeout: 3000
89
+ });
90
+ const time = await customClient.getTime();
91
+ ```
92
+
93
+ ## API Reference
94
+
95
+ ### `NTPClient`
96
+
97
+ Main class for NTP queries.
98
+
99
+ ```typescript
100
+ class NTPClient {
101
+ constructor(config?: NTPClientConfig);
102
+ async query(): Promise<NTPQueryResult>;
103
+ async getTime(): Promise<Date>;
104
+ async getOffset(): Promise<number>;
105
+ }
106
+ ```
107
+
108
+ #### `NTPClientConfig`
109
+
110
+ ```typescript
111
+ interface NTPClientConfig {
112
+ host?: string; // Default: 'time.hixbe.com'
113
+ port?: number; // Default: 123
114
+ timeout?: number; // Default: 5000ms
115
+ }
116
+ ```
117
+
118
+ #### `NTPQueryResult`
119
+
120
+ ```typescript
121
+ interface NTPQueryResult {
122
+ buffer: Buffer; // Raw NTP packet
123
+ hexDump: string; // Formatted hex dump
124
+ parsed: ParsedNTPPacket; // Parsed packet
125
+ serverAddress: string; // Server IP
126
+ clientReceiveTime: Date; // When response arrived
127
+ clientOriginateTime: Date; // When request sent
128
+ }
129
+ ```
130
+
131
+ ### `ParsedNTPPacket`
132
+
133
+ ```typescript
134
+ interface ParsedNTPPacket {
135
+ header: NTPPacketHeader;
136
+ timestamps: NTPTimestamps;
137
+ roundTripDelay: number | null;
138
+ clockOffset: number | null;
139
+ }
140
+ ```
141
+
142
+ #### `NTPPacketHeader`
143
+
144
+ ```typescript
145
+ interface NTPPacketHeader {
146
+ leapIndicator: number; // 0-3: Normal, +1sec, -1sec, unsync
147
+ versionNumber: number; // NTP version (3 or 4)
148
+ mode: number; // Client/Server mode
149
+ stratum: number; // Clock distance from reference
150
+ pollInterval: number; // Poll interval (2^x seconds)
151
+ precision: number; // Precision (2^x seconds)
152
+ rootDelay: number; // Root delay in milliseconds
153
+ rootDispersion: number; // Root dispersion in milliseconds
154
+ referenceId: string; // Reference clock identifier
155
+ }
156
+ ```
157
+
158
+ #### `NTPTimestamps`
159
+
160
+ ```typescript
161
+ interface NTPTimestamps {
162
+ reference: ParsedNTPTimestamp; // Reference time
163
+ originate: ParsedNTPTimestamp; // Client's transmit time
164
+ receive: ParsedNTPTimestamp; // Server's receive time
165
+ transmit: ParsedNTPTimestamp; // Server's transmit time
166
+ }
167
+ ```
168
+
169
+ #### `ParsedNTPTimestamp`
170
+
171
+ ```typescript
172
+ interface ParsedNTPTimestamp {
173
+ raw: {
174
+ seconds: number; // NTP seconds (since 1900)
175
+ fraction: number; // Fraction (32-bit fixed-point)
176
+ };
177
+ unix: {
178
+ seconds: number; // Unix seconds (since 1970)
179
+ milliseconds: number; // Fractional milliseconds
180
+ };
181
+ date: Date; // JavaScript Date object
182
+ iso: string; // ISO 8601 string
183
+ local: string; // Local timezone string
184
+ timestamp: number; // Unix timestamp in ms
185
+ }
186
+ ```
187
+
188
+ ## Understanding NTP Timestamps
189
+
190
+ ### The Conversion Process
191
+
192
+ NTP uses a unique timestamp format:
193
+ - **NTP Epoch**: January 1, 1900
194
+ - **Unix Epoch**: January 1, 1970
195
+ - **Offset**: 2,208,988,800 seconds
196
+
197
+ Each timestamp is 8 bytes (64-bit):
198
+ - **First 4 bytes**: Seconds since 1900
199
+ - **Last 4 bytes**: Fractional seconds (32-bit fixed-point)
200
+
201
+ ### Example Conversion
202
+
203
+ ```
204
+ Raw NTP packet (Bytes 40-47, Transmit):
205
+ Hex: EC EB 5C 3D 33 88 C9 16
206
+
207
+ Breakdown:
208
+ Seconds (bytes 0-3): 0xECEB5C3D = 3,974,847,549
209
+ Fraction (bytes 4-7): 0x3388C916 = 864,602,390
210
+
211
+ Conversion:
212
+ NTP → Unix: 3,974,847,549 - 2,208,988,800 = 1,765,858,749 sec
213
+ Fraction: 864,602,390 ÷ 0x100000000 = 0.201306 sec = 201.306 ms
214
+
215
+ Result:
216
+ Unix timestamp: 1,765,858,749.201 seconds
217
+ Date: 2025-12-16T04:19:09.201Z
218
+ ```
219
+
220
+ ## CLI Examples
221
+
222
+ ### Default Output
223
+ ```bash
224
+ $ hixbe-time
225
+ ======================================================================
226
+ 🕐 HIXBE TIME SYNC
227
+ ======================================================================
228
+
229
+ Server: 154.26.137.94 (time.hixbe.com)
230
+ UTC Time: 2025-12-16T04:19:09.201Z
231
+ Local Time: Tue Dec 16 2025 10:19:09 GMT+0600
232
+ Offset: +0.468 seconds
233
+ Precision: ±231 (2^x sec)
234
+ Stratum: 2
235
+ ======================================================================
236
+ ```
237
+
238
+ ### Verbose Mode
239
+ ```bash
240
+ $ hixbe-time --verbose
241
+ 📡 TIMESTAMPS:
242
+ Reference: 2025-12-16T04:18:37.660Z
243
+ Transmit: 2025-12-16T04:19:09.201Z
244
+ Receive: 2025-12-16T04:19:09.200Z
245
+
246
+ 💾 RAW TRANSMIT TIMESTAMP (Bytes 40-47):
247
+ Hex: ECEB5C3D3388C916
248
+ Seconds (NTP): 3974847549 → Unix: 1765858749
249
+ Fraction: 0x3388C916 = 201.306ms
250
+ ```
251
+
252
+ ### JSON Output
253
+ ```bash
254
+ $ hixbe-time --json
255
+ {
256
+ "timestamp": 1765858749201,
257
+ "iso": "2025-12-16T04:19:09.201Z",
258
+ "server": {
259
+ "address": "154.26.137.94",
260
+ "stratum": 2,
261
+ "referenceId": "0xD8EF230C"
262
+ },
263
+ "offset": 468,
264
+ "precision": -23,
265
+ "version": 4
266
+ }
267
+ ```
268
+
269
+ ### Continuous Sync
270
+ ```bash
271
+ $ hixbe-time --continuous --interval 1000
272
+ ⏱️ Starting continuous sync with time.hixbe.com
273
+ 📊 Interval: 1000ms (1.0s)
274
+ Press Ctrl+C to stop
275
+
276
+ [1] ✅ 2025-12-16T04:19:09.201Z | Offset: +468ms
277
+ [2] ✅ 2025-12-16T04:19:10.205Z | Offset: +456ms
278
+ [3] ✅ 2025-12-16T04:19:11.210Z | Offset: +442ms
279
+ ```
280
+
281
+ ## Advanced Usage
282
+
283
+ ### Creating a Time Sync Daemon
284
+
285
+ ```typescript
286
+ import { NTPClient } from '@hixbe/time';
287
+
288
+ async function syncClock() {
289
+ const client = new NTPClient({ host: 'time.hixbe.com' });
290
+ const offsetMs = await client.getOffset();
291
+
292
+ if (Math.abs(offsetMs) > 1000) {
293
+ console.warn(`⚠️ Large offset detected: ${offsetMs}ms`);
294
+ // Could trigger system time adjustment
295
+ }
296
+
297
+ console.log(`Clock offset: ${offsetMs}ms`);
298
+ }
299
+
300
+ // Sync every 60 seconds
301
+ setInterval(syncClock, 60000);
302
+ ```
303
+
304
+ ### Monitoring Multiple Servers
305
+
306
+ ```typescript
307
+ import { NTPClient } from '@hixbe/time';
308
+
309
+ const servers = [
310
+ 'time.hixbe.com',
311
+ 'pool.ntp.org',
312
+ 'time.google.com'
313
+ ];
314
+
315
+ for (const server of servers) {
316
+ const client = new NTPClient({ host: server });
317
+ try {
318
+ const time = await client.getTime();
319
+ console.log(`${server}: ${time.toISOString()}`);
320
+ } catch (error) {
321
+ console.error(`${server}: Failed`);
322
+ }
323
+ }
324
+ ```
325
+
326
+ ### Raw Packet Analysis
327
+
328
+ ```typescript
329
+ import { NTPClient, NTPParser } from '@hixbe/time';
330
+
331
+ const client = new NTPClient();
332
+ const result = await client.query();
333
+
334
+ // Access raw bytes
335
+ console.log(result.hexDump);
336
+
337
+ // Parse header
338
+ const header = result.parsed.header;
339
+ console.log(`Stratum: ${header.stratum}`);
340
+ console.log(`Reference ID: ${header.referenceId}`);
341
+
342
+ // Access all timestamps
343
+ const ts = result.parsed.timestamps;
344
+ console.log(`Server said time is: ${ts.transmit.iso}`);
345
+ ```
346
+
347
+ ## Supported NTP Servers
348
+
349
+ ```typescript
350
+ // Hixbe (recommended)
351
+ new NTPClient({ host: 'time.hixbe.com' })
352
+
353
+ // Public pools and servers
354
+ new NTPClient({ host: 'pool.ntp.org' })
355
+ new NTPClient({ host: 'time.nist.gov' })
356
+ new NTPClient({ host: 'time.google.com' })
357
+ new NTPClient({ host: 'time.cloudflare.com' })
358
+ new NTPClient({ host: 'time.apple.com' })
359
+ ```
360
+
361
+ ## Performance & Reliability
362
+
363
+ - ⚡ **Fast**: < 100ms typical response time
364
+ - 🔒 **Reliable**: Works with standard NTP servers
365
+ - 📊 **Accurate**: Microsecond precision
366
+ - 🌐 **Network Safe**: Standard UDP port 123
367
+ - 💾 **Lightweight**: No dependencies beyond Node.js
368
+
369
+ ## Troubleshooting
370
+
371
+ ### "NTP request timeout"
372
+ - Server may be unreachable
373
+ - Firewall blocking UDP port 123
374
+ - Try a different server: `--server pool.ntp.org`
375
+
376
+ ### Offset seems large
377
+ - Normal variation can be several hundred milliseconds
378
+ - Check with `--verbose` for server stratum
379
+ - Try lower latency server
380
+
381
+ ### "Invalid NTP packet"
382
+ - Packet may be corrupted
383
+ - Server may not be responding with valid NTP
384
+ - Try different server
385
+
386
+ ## FAQ
387
+
388
+ **Q: Can I use this in production?**
389
+ A: Yes! This is a robust, type-safe implementation suitable for production clock synchronization.
390
+
391
+ **Q: What's the precision?**
392
+ A: Typically ±50-200ms depending on network latency and server stratum.
393
+
394
+ **Q: Can I set system time?**
395
+ A: This package only queries and reports time. OS permissions are needed for actual system time adjustment.
396
+
397
+ **Q: Why is my offset negative?**
398
+ A: Your system time is ahead of the NTP server time.
399
+
400
+ **Q: Can I use IPv6 servers?**
401
+ A: Currently uses UDP4. IPv6 support coming in v2.0.
402
+
403
+ ## Security
404
+
405
+ - No authentication required for NTP (protocol design)
406
+ - Uses standard UDP port 123
407
+ - Validates packet structure
408
+ - TypeScript type safety prevents injection attacks
409
+
410
+ ## Development
411
+
412
+ ```bash
413
+ # Install dependencies
414
+ npm install
415
+
416
+ # Build TypeScript
417
+ npm run build
418
+
419
+ # Watch mode
420
+ npm run dev
421
+
422
+ # Run tests
423
+ npm test
424
+ ```
425
+
426
+ ## License
427
+
428
+ MIT © 2025 Hixbe
429
+
430
+ ## Contributing
431
+
432
+ Contributions welcome! Please submit pull requests to improve:
433
+ - IPv6 support
434
+ - Performance optimizations
435
+ - Additional time servers
436
+ - Better documentation
437
+
438
+ ## Support
439
+
440
+ - GitHub Issues: https://github.com/hixbehq/nodejs-time/issues
441
+ - Documentation: https://github.com/hixbehq/nodejs-time#readme
442
+ - Contact: support@hixbe.com
443
+
444
+ ---
445
+
446
+ Made with ❤️ by **Hixbe** - Precision Time Solutions
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,225 @@
1
+ #!/usr/bin/env node
2
+ import { NTPClient } from '../core/client.js';
3
+ class HixbeTimeCLI {
4
+ constructor() {
5
+ this.options = {
6
+ server: 'time.hixbe.com',
7
+ json: false,
8
+ verbose: false,
9
+ offset: false,
10
+ continuous: false,
11
+ interval: 5000,
12
+ };
13
+ }
14
+ async run() {
15
+ this.parseArgs();
16
+ if (this.options.continuous) {
17
+ await this.continuousMode();
18
+ }
19
+ else {
20
+ await this.singleQuery();
21
+ }
22
+ }
23
+ parseArgs() {
24
+ const args = process.argv.slice(2);
25
+ for (let i = 0; i < args.length; i++) {
26
+ const arg = args[i];
27
+ switch (arg) {
28
+ case '-s':
29
+ case '--server':
30
+ this.options.server = args[++i];
31
+ break;
32
+ case '-j':
33
+ case '--json':
34
+ this.options.json = true;
35
+ break;
36
+ case '-v':
37
+ case '--verbose':
38
+ this.options.verbose = true;
39
+ break;
40
+ case '-o':
41
+ case '--offset':
42
+ this.options.offset = true;
43
+ break;
44
+ case '-c':
45
+ case '--continuous':
46
+ this.options.continuous = true;
47
+ break;
48
+ case '-i':
49
+ case '--interval':
50
+ this.options.interval = parseInt(args[++i], 10);
51
+ break;
52
+ case '-h':
53
+ case '--help':
54
+ this.printHelp();
55
+ process.exit(0);
56
+ break;
57
+ case '--version':
58
+ this.printVersion();
59
+ process.exit(0);
60
+ break;
61
+ }
62
+ }
63
+ }
64
+ async singleQuery() {
65
+ try {
66
+ const client = new NTPClient({ host: this.options.server });
67
+ const result = await client.query();
68
+ if (this.options.json) {
69
+ this.outputJSON(result);
70
+ }
71
+ else if (this.options.offset) {
72
+ this.outputOffset(result);
73
+ }
74
+ else if (this.options.verbose) {
75
+ this.outputVerbose(result);
76
+ }
77
+ else {
78
+ this.outputDefault(result);
79
+ }
80
+ }
81
+ catch (error) {
82
+ this.handleError(error);
83
+ }
84
+ }
85
+ async continuousMode() {
86
+ let count = 0;
87
+ console.log(`⏱️ Starting continuous sync with ${this.options.server}`);
88
+ console.log(`📊 Interval: ${this.options.interval}ms (${(this.options.interval / 1000).toFixed(1)}s)`);
89
+ console.log('Press Ctrl+C to stop\n');
90
+ const displayBars = () => {
91
+ process.stdout.write('');
92
+ };
93
+ while (true) {
94
+ try {
95
+ const client = new NTPClient({ host: this.options.server });
96
+ const result = await client.query();
97
+ const offset = result.parsed.timestamps.transmit.date.getTime() - result.clientReceiveTime.getTime();
98
+ count++;
99
+ const time = result.parsed.timestamps.transmit.date;
100
+ const status = offset > 500 ? '⚠️ ' : offset < -500 ? '⚠️ ' : '✅';
101
+ console.log(`[${count}] ${status} ${time.toISOString()} | Offset: ${offset > 0 ? '+' : ''}${offset.toFixed(0)}ms`);
102
+ await this.sleep(this.options.interval);
103
+ }
104
+ catch (error) {
105
+ console.error(`❌ Error: ${error.message}`);
106
+ await this.sleep(this.options.interval);
107
+ }
108
+ }
109
+ }
110
+ outputDefault(result) {
111
+ const tx = result.parsed.timestamps.transmit;
112
+ const offset = result.parsed.timestamps.transmit.timestamp - result.clientReceiveTime.getTime();
113
+ console.log('═'.repeat(70));
114
+ console.log('🕐 HIXBE TIME SYNC');
115
+ console.log('═'.repeat(70));
116
+ console.log(`\nServer: ${result.serverAddress} (${this.options.server})`);
117
+ console.log(`UTC Time: ${tx.iso}`);
118
+ console.log(`Local Time: ${tx.local}`);
119
+ console.log(`Offset: ${offset > 0 ? '+' : ''}${(offset / 1000).toFixed(3)} seconds`);
120
+ console.log(`Precision: ±${result.parsed.header.precision} (2^x sec)`);
121
+ console.log(`Stratum: ${result.parsed.header.stratum}`);
122
+ console.log('═'.repeat(70) + '\n');
123
+ }
124
+ outputOffset(result) {
125
+ const offset = result.parsed.timestamps.transmit.timestamp - result.clientReceiveTime.getTime();
126
+ console.log(offset > 0 ? '+' : '');
127
+ console.log(offset);
128
+ }
129
+ outputJSON(result) {
130
+ const output = {
131
+ timestamp: result.parsed.timestamps.transmit.timestamp,
132
+ iso: result.parsed.timestamps.transmit.iso,
133
+ server: {
134
+ address: result.serverAddress,
135
+ stratum: result.parsed.header.stratum,
136
+ referenceId: result.parsed.header.referenceId,
137
+ },
138
+ offset: result.parsed.timestamps.transmit.timestamp - result.clientReceiveTime.getTime(),
139
+ precision: result.parsed.header.precision,
140
+ version: result.parsed.header.versionNumber,
141
+ };
142
+ console.log(JSON.stringify(output, null, 2));
143
+ }
144
+ outputVerbose(result) {
145
+ const tx = result.parsed.timestamps.transmit;
146
+ const h = result.parsed.header;
147
+ console.log('═'.repeat(70));
148
+ console.log('🕐 HIXBE TIME - DETAILED REPORT');
149
+ console.log('═'.repeat(70));
150
+ // Timestamps
151
+ console.log('\n📡 TIMESTAMPS:');
152
+ console.log(` Reference: ${result.parsed.timestamps.reference.iso}`);
153
+ console.log(` Transmit: ${tx.iso}`);
154
+ console.log(` Receive: ${result.parsed.timestamps.receive.iso}`);
155
+ // Raw bytes
156
+ console.log('\n💾 RAW TRANSMIT TIMESTAMP (Bytes 40-47):');
157
+ const txOffset = 40;
158
+ const txBuffer = result.buffer.slice(txOffset, txOffset + 8);
159
+ console.log(` Hex: ${txBuffer.toString('hex').toUpperCase()}`);
160
+ const ntpSeconds = txBuffer.readUInt32BE(0);
161
+ const fraction = txBuffer.readUInt32BE(4);
162
+ const unixSeconds = ntpSeconds - 2208988800;
163
+ const fracMs = (fraction / 0x100000000) * 1000;
164
+ console.log(` Seconds (NTP): ${ntpSeconds} → Unix: ${unixSeconds}`);
165
+ console.log(` Fraction: 0x${fraction.toString(16).toUpperCase()} = ${fracMs.toFixed(3)}ms`);
166
+ // Header
167
+ console.log('\n📋 PACKET HEADER:');
168
+ console.log(` Leap Indicator: ${h.leapIndicator}`);
169
+ console.log(` Version: ${h.versionNumber}`);
170
+ console.log(` Mode: ${h.mode}`);
171
+ console.log(` Stratum: ${h.stratum}`);
172
+ console.log(` Poll Interval: 2^${h.pollInterval}`);
173
+ console.log(` Precision: 2^${h.precision}`);
174
+ console.log(` Root Delay: ${h.rootDelay / 65536} ms`);
175
+ console.log(` Root Dispersion: ${h.rootDispersion / 65536} ms`);
176
+ console.log(` Reference ID: ${h.referenceId}`);
177
+ console.log('\n═'.repeat(70) + '\n');
178
+ }
179
+ handleError(error) {
180
+ const message = error instanceof Error ? error.message : String(error);
181
+ console.error(`❌ Error: ${message}`);
182
+ process.exit(1);
183
+ }
184
+ printHelp() {
185
+ console.log(`
186
+ @hixbe/time - High-precision NTP Time Synchronization
187
+
188
+ USAGE:
189
+ hixbe-time [OPTIONS]
190
+
191
+ OPTIONS:
192
+ -s, --server <host> NTP server (default: time.hixbe.com)
193
+ -j, --json Output as JSON
194
+ -v, --verbose Detailed output with raw bytes
195
+ -o, --offset Output only the offset in milliseconds
196
+ -c, --continuous Continuous sync mode
197
+ -i, --interval <ms> Interval between syncs (default: 5000ms)
198
+ -h, --help Show this help
199
+ --version Show version
200
+
201
+ EXAMPLES:
202
+ hixbe-time
203
+ hixbe-time --verbose
204
+ hixbe-time --json
205
+ hixbe-time --continuous --interval 1000
206
+ hixbe-time --offset
207
+ hixbe-time --server pool.ntp.org
208
+
209
+ Learn more: https://github.com/hixbehq/nodejs-time
210
+ `);
211
+ }
212
+ printVersion() {
213
+ console.log('@hixbe/time v1.0.0');
214
+ }
215
+ sleep(ms) {
216
+ return new Promise((resolve) => setTimeout(resolve, ms));
217
+ }
218
+ }
219
+ // Run CLI
220
+ const cli = new HixbeTimeCLI();
221
+ cli.run().catch((error) => {
222
+ console.error(`Fatal error: ${error.message}`);
223
+ process.exit(1);
224
+ });
225
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import { ParsedNTPPacket } from './parser.js';
2
+ export interface NTPClientConfig {
3
+ host?: string;
4
+ port?: number;
5
+ timeout?: number;
6
+ }
7
+ export interface NTPQueryResult {
8
+ buffer: Buffer;
9
+ hexDump: string;
10
+ parsed: ParsedNTPPacket;
11
+ serverAddress: string;
12
+ clientReceiveTime: Date;
13
+ clientOriginateTime: Date;
14
+ }
15
+ /**
16
+ * NTP Client - Sends NTP requests and retrieves responses
17
+ */
18
+ export declare class NTPClient {
19
+ private host;
20
+ private port;
21
+ private timeout;
22
+ constructor(config?: NTPClientConfig);
23
+ /**
24
+ * Send NTP request and receive response
25
+ */
26
+ query(): Promise<NTPQueryResult>;
27
+ /**
28
+ * Get current NTP time from server
29
+ */
30
+ getTime(): Promise<Date>;
31
+ /**
32
+ * Get time offset between local and NTP server
33
+ */
34
+ getOffset(): Promise<number>;
35
+ /**
36
+ * Create NTP request packet (client mode)
37
+ */
38
+ private createRequestPacket;
39
+ /**
40
+ * Generate hex dump of buffer
41
+ */
42
+ private hexDump;
43
+ }
44
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,eAAe,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,eAAe,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,IAAI,CAAC;IACxB,mBAAmB,EAAE,IAAI,CAAC;CAC3B;AAED;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,GAAE,eAAoB;IAMxC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,cAAc,CAAC;IAsDtC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAK9B;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IAOlC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAkB3B;;OAEG;IACH,OAAO,CAAC,OAAO;CAgBhB"}
@@ -0,0 +1,116 @@
1
+ import dgram from 'dgram';
2
+ import { NTPParser } from './parser.js';
3
+ /**
4
+ * NTP Client - Sends NTP requests and retrieves responses
5
+ */
6
+ export class NTPClient {
7
+ constructor(config = {}) {
8
+ this.host = config.host || 'time.hixbe.com';
9
+ this.port = config.port || 123;
10
+ this.timeout = config.timeout || 5000;
11
+ }
12
+ /**
13
+ * Send NTP request and receive response
14
+ */
15
+ async query() {
16
+ return new Promise((resolve, reject) => {
17
+ const client = dgram.createSocket('udp4');
18
+ const timeoutId = setTimeout(() => {
19
+ client.close();
20
+ reject(new Error(`NTP request timeout after ${this.timeout}ms`));
21
+ }, this.timeout);
22
+ client.on('message', (buffer, rinfo) => {
23
+ clearTimeout(timeoutId);
24
+ client.close();
25
+ try {
26
+ const clientReceiveTime = new Date();
27
+ const parsed = NTPParser.parsePacket(buffer);
28
+ resolve({
29
+ buffer,
30
+ hexDump: this.hexDump(buffer),
31
+ parsed,
32
+ serverAddress: rinfo.address,
33
+ clientReceiveTime,
34
+ clientOriginateTime: new Date(Date.now() - this.timeout / 2),
35
+ });
36
+ }
37
+ catch (error) {
38
+ reject(error);
39
+ }
40
+ });
41
+ client.on('error', (error) => {
42
+ clearTimeout(timeoutId);
43
+ client.close();
44
+ reject(error);
45
+ });
46
+ // Create NTP request packet
47
+ const ntpPacket = this.createRequestPacket();
48
+ try {
49
+ client.send(ntpPacket, 0, ntpPacket.length, this.port, this.host, (error) => {
50
+ if (error) {
51
+ clearTimeout(timeoutId);
52
+ client.close();
53
+ reject(error);
54
+ }
55
+ });
56
+ }
57
+ catch (error) {
58
+ clearTimeout(timeoutId);
59
+ client.close();
60
+ reject(error);
61
+ }
62
+ });
63
+ }
64
+ /**
65
+ * Get current NTP time from server
66
+ */
67
+ async getTime() {
68
+ const result = await this.query();
69
+ return result.parsed.timestamps.transmit.date;
70
+ }
71
+ /**
72
+ * Get time offset between local and NTP server
73
+ */
74
+ async getOffset() {
75
+ const result = await this.query();
76
+ const serverTime = result.parsed.timestamps.transmit.date.getTime();
77
+ const localTime = result.clientReceiveTime.getTime();
78
+ return serverTime - localTime;
79
+ }
80
+ /**
81
+ * Create NTP request packet (client mode)
82
+ */
83
+ createRequestPacket() {
84
+ const packet = Buffer.alloc(48, 0);
85
+ // First byte: LI (0), VN (3), Mode (3 = client)
86
+ packet[0] = 0x23;
87
+ // Add transmit timestamp (current time)
88
+ const now = new Date();
89
+ const unixSeconds = Math.floor(now.getTime() / 1000);
90
+ const ntpSeconds = unixSeconds + 2208988800;
91
+ const fraction = Math.random() * 0x100000000;
92
+ packet.writeUInt32BE(ntpSeconds, 40);
93
+ packet.writeUInt32BE(Math.floor(fraction), 44);
94
+ return packet;
95
+ }
96
+ /**
97
+ * Generate hex dump of buffer
98
+ */
99
+ hexDump(buffer) {
100
+ let dump = '';
101
+ for (let i = 0; i < buffer.length; i += 16) {
102
+ const hex = buffer
103
+ .slice(i, i + 16)
104
+ .toString('hex')
105
+ .match(/.{1,2}/g)
106
+ ?.join(' ');
107
+ const ascii = buffer
108
+ .slice(i, i + 16)
109
+ .toString('ascii')
110
+ .replace(/[^\x20-\x7E]/g, '.');
111
+ dump += `${String(i).padStart(4, '0')}: ${hex?.padEnd(48)} ${ascii}\n`;
112
+ }
113
+ return dump;
114
+ }
115
+ }
116
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { NTPClient } from './client.js';
2
+ import { NTPParser } from './parser.js';
3
+ export { NTPClient, NTPParser };
4
+ export type { NTPClientConfig, NTPQueryResult } from './client.js';
5
+ export type { RawNTPTimestamp, ParsedNTPTimestamp, NTPPacketHeader, NTPTimestamps, ParsedNTPPacket, } from './parser.js';
6
+ declare const _default: {
7
+ getTime(host?: string): Promise<Date>;
8
+ getOffset(host?: string): Promise<number>;
9
+ query(host?: string): Promise<import("./client.js").NTPQueryResult>;
10
+ NTPClient: typeof NTPClient;
11
+ NTPParser: typeof NTPParser;
12
+ };
13
+ export default _default;
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAChC,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACnE,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,eAAe,GAChB,MAAM,aAAa,CAAC;;4BAIqB,OAAO,CAAC,IAAI,CAAC;8BAKX,OAAO,CAAC,MAAM,CAAC;;;;;AAN3D,wBAkBE"}
@@ -0,0 +1,21 @@
1
+ import { NTPClient } from './client.js';
2
+ import { NTPParser } from './parser.js';
3
+ export { NTPClient, NTPParser };
4
+ // Default export with convenience functions
5
+ export default {
6
+ async getTime(host = 'time.hixbe.com') {
7
+ const client = new NTPClient({ host });
8
+ return client.getTime();
9
+ },
10
+ async getOffset(host = 'time.hixbe.com') {
11
+ const client = new NTPClient({ host });
12
+ return client.getOffset();
13
+ },
14
+ async query(host = 'time.hixbe.com') {
15
+ const client = new NTPClient({ host });
16
+ return client.query();
17
+ },
18
+ NTPClient,
19
+ NTPParser,
20
+ };
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAUhC,4CAA4C;AAC5C,eAAe;IACb,KAAK,CAAC,OAAO,CAAC,IAAI,GAAG,gBAAgB;QACnC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,IAAI,GAAG,gBAAgB;QACrC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,OAAO,MAAM,CAAC,SAAS,EAAE,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,gBAAgB;QACjC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,OAAO,MAAM,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAED,SAAS;IACT,SAAS;CACV,CAAC"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * NTP Timestamp Parser
3
+ * Handles conversion between NTP timestamps and JavaScript Date objects
4
+ */
5
+ export interface RawNTPTimestamp {
6
+ seconds: number;
7
+ fraction: number;
8
+ }
9
+ export interface ParsedNTPTimestamp {
10
+ raw: RawNTPTimestamp;
11
+ unix: {
12
+ seconds: number;
13
+ milliseconds: number;
14
+ };
15
+ date: Date;
16
+ iso: string;
17
+ local: string;
18
+ timestamp: number;
19
+ }
20
+ export interface NTPPacketHeader {
21
+ leapIndicator: number;
22
+ versionNumber: number;
23
+ mode: number;
24
+ stratum: number;
25
+ pollInterval: number;
26
+ precision: number;
27
+ rootDelay: number;
28
+ rootDispersion: number;
29
+ referenceId: string;
30
+ }
31
+ export interface NTPTimestamps {
32
+ reference: ParsedNTPTimestamp;
33
+ originate: ParsedNTPTimestamp;
34
+ receive: ParsedNTPTimestamp;
35
+ transmit: ParsedNTPTimestamp;
36
+ }
37
+ export interface ParsedNTPPacket {
38
+ header: NTPPacketHeader;
39
+ timestamps: NTPTimestamps;
40
+ roundTripDelay: number | null;
41
+ clockOffset: number | null;
42
+ }
43
+ export declare class NTPParser {
44
+ /**
45
+ * NTP epoch starts on January 1, 1900
46
+ * Unix epoch starts on January 1, 1970
47
+ * The difference is 2,208,988,800 seconds
48
+ */
49
+ private static readonly NTP_EPOCH_OFFSET;
50
+ /**
51
+ * Parse raw NTP response buffer (48 bytes)
52
+ *
53
+ * Packet structure:
54
+ * - Bytes 0-3: LI, VN, Mode, Stratum, Poll, Precision
55
+ * - Bytes 4-7: Root Delay
56
+ * - Bytes 8-11: Root Dispersion
57
+ * - Bytes 12-15: Reference ID
58
+ * - Bytes 16-23: Reference Timestamp
59
+ * - Bytes 24-31: Originate Timestamp
60
+ * - Bytes 32-39: Receive Timestamp
61
+ * - Bytes 40-47: Transmit Timestamp
62
+ */
63
+ static parsePacket(buffer: Buffer): ParsedNTPPacket;
64
+ /**
65
+ * Parse 8-byte NTP timestamp (64-bit fixed-point: 32-bit seconds, 32-bit fraction)
66
+ */
67
+ private static parseNTPTimestamp;
68
+ /**
69
+ * Format reference ID based on stratum
70
+ */
71
+ private static formatReferenceId;
72
+ /**
73
+ * Calculate round-trip delay and clock offset
74
+ */
75
+ static calculateMetrics(parsed: ParsedNTPPacket, clientOriginateTime: Date, clientReceiveTime: Date): {
76
+ roundTripDelay: number;
77
+ clockOffset: number;
78
+ roundTripDelayMs: number;
79
+ clockOffsetMs: number;
80
+ };
81
+ }
82
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/core/parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,eAAe,CAAC;IACrB,IAAI,EAAE;QACJ,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,IAAI,EAAE,IAAI,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,kBAAkB,CAAC;IAC9B,SAAS,EAAE,kBAAkB,CAAC;IAC9B,OAAO,EAAE,kBAAkB,CAAC;IAC5B,QAAQ,EAAE,kBAAkB,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,EAAE,aAAa,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,qBAAa,SAAS;IACpB;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAc;IAEtD;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe;IAgDnD;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAwBhC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAWhC;;OAEG;IACH,MAAM,CAAC,gBAAgB,CACrB,MAAM,EAAE,eAAe,EACvB,mBAAmB,EAAE,IAAI,EACzB,iBAAiB,EAAE,IAAI,GACtB;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE;CAgBpG"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * NTP Timestamp Parser
3
+ * Handles conversion between NTP timestamps and JavaScript Date objects
4
+ */
5
+ export class NTPParser {
6
+ /**
7
+ * Parse raw NTP response buffer (48 bytes)
8
+ *
9
+ * Packet structure:
10
+ * - Bytes 0-3: LI, VN, Mode, Stratum, Poll, Precision
11
+ * - Bytes 4-7: Root Delay
12
+ * - Bytes 8-11: Root Dispersion
13
+ * - Bytes 12-15: Reference ID
14
+ * - Bytes 16-23: Reference Timestamp
15
+ * - Bytes 24-31: Originate Timestamp
16
+ * - Bytes 32-39: Receive Timestamp
17
+ * - Bytes 40-47: Transmit Timestamp
18
+ */
19
+ static parsePacket(buffer) {
20
+ if (buffer.length < 48) {
21
+ throw new Error('Invalid NTP packet: minimum 48 bytes required');
22
+ }
23
+ // Parse header
24
+ const firstByte = buffer[0];
25
+ const leapIndicator = (firstByte >> 6) & 0b11;
26
+ const versionNumber = (firstByte >> 3) & 0b111;
27
+ const mode = firstByte & 0b111;
28
+ const stratum = buffer[1];
29
+ const pollInterval = buffer[2];
30
+ const precision = buffer[3];
31
+ // Parse timestamps
32
+ const referenceTimestamp = this.parseNTPTimestamp(buffer, 16);
33
+ const originateTimestamp = this.parseNTPTimestamp(buffer, 24);
34
+ const receiveTimestamp = this.parseNTPTimestamp(buffer, 32);
35
+ const transmitTimestamp = this.parseNTPTimestamp(buffer, 40);
36
+ // Parse root delay and dispersion
37
+ const rootDelay = buffer.readUInt32BE(4);
38
+ const rootDispersion = buffer.readUInt32BE(8);
39
+ const referenceId = buffer.readUInt32BE(12);
40
+ return {
41
+ header: {
42
+ leapIndicator,
43
+ versionNumber,
44
+ mode,
45
+ stratum,
46
+ pollInterval,
47
+ precision,
48
+ rootDelay,
49
+ rootDispersion,
50
+ referenceId: this.formatReferenceId(referenceId, stratum),
51
+ },
52
+ timestamps: {
53
+ reference: referenceTimestamp,
54
+ originate: originateTimestamp,
55
+ receive: receiveTimestamp,
56
+ transmit: transmitTimestamp,
57
+ },
58
+ roundTripDelay: null,
59
+ clockOffset: null,
60
+ };
61
+ }
62
+ /**
63
+ * Parse 8-byte NTP timestamp (64-bit fixed-point: 32-bit seconds, 32-bit fraction)
64
+ */
65
+ static parseNTPTimestamp(buffer, offset) {
66
+ const seconds = buffer.readUInt32BE(offset);
67
+ const fraction = buffer.readUInt32BE(offset + 4);
68
+ // Convert fraction to milliseconds (32-bit fraction / 2^32 * 1000)
69
+ const milliseconds = (fraction / 0x100000000) * 1000;
70
+ // Convert NTP timestamp to Unix timestamp
71
+ const unixSeconds = seconds - this.NTP_EPOCH_OFFSET;
72
+ const date = new Date((unixSeconds * 1000) + milliseconds);
73
+ return {
74
+ raw: { seconds, fraction },
75
+ unix: {
76
+ seconds: unixSeconds,
77
+ milliseconds,
78
+ },
79
+ date,
80
+ iso: date.toISOString(),
81
+ local: date.toString(),
82
+ timestamp: date.getTime(),
83
+ };
84
+ }
85
+ /**
86
+ * Format reference ID based on stratum
87
+ */
88
+ static formatReferenceId(id, stratum) {
89
+ if (stratum === 0)
90
+ return 'KISS';
91
+ if (stratum === 1) {
92
+ // First 4 bytes are ASCII
93
+ const bytes = Buffer.alloc(4);
94
+ bytes.writeUInt32BE(id);
95
+ return bytes.toString('ascii').replace(/\x00/g, '');
96
+ }
97
+ return `0x${id.toString(16).toUpperCase().padStart(8, '0')}`;
98
+ }
99
+ /**
100
+ * Calculate round-trip delay and clock offset
101
+ */
102
+ static calculateMetrics(parsed, clientOriginateTime, clientReceiveTime) {
103
+ const t1 = clientOriginateTime.getTime() / 1000;
104
+ const t2 = parsed.timestamps.receive.unix.seconds + parsed.timestamps.receive.unix.milliseconds / 1000;
105
+ const t3 = parsed.timestamps.transmit.unix.seconds + parsed.timestamps.transmit.unix.milliseconds / 1000;
106
+ const t4 = clientReceiveTime.getTime() / 1000;
107
+ const roundTripDelay = (t4 - t1) - (t3 - t2);
108
+ const clockOffset = ((t2 - t1) + (t3 - t4)) / 2;
109
+ return {
110
+ roundTripDelay,
111
+ clockOffset,
112
+ roundTripDelayMs: roundTripDelay * 1000,
113
+ clockOffsetMs: clockOffset * 1000,
114
+ };
115
+ }
116
+ }
117
+ /**
118
+ * NTP epoch starts on January 1, 1900
119
+ * Unix epoch starts on January 1, 1970
120
+ * The difference is 2,208,988,800 seconds
121
+ */
122
+ NTPParser.NTP_EPOCH_OFFSET = 2208988800;
123
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/core/parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { default, NTPClient, NTPParser } from './core/index.js';
2
+ export type { NTPClientConfig, NTPQueryResult } from './core/client.js';
3
+ export type { RawNTPTimestamp, ParsedNTPTimestamp, NTPPacketHeader, NTPTimestamps, ParsedNTPPacket, } from './core/parser.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACxE,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,eAAe,GAChB,MAAM,kBAAkB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { default, NTPClient, NTPParser } from './core/index.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@hixbe/time",
3
+ "version": "1.0.0",
4
+ "description": "High-precision NTP time synchronization package with CLI tools",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "hixbe-time": "dist/cli/index.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "dev": "tsc --watch",
14
+ "start": "node dist/cli/index.js",
15
+ "cli": "node dist/cli/index.js",
16
+ "test": "node --test dist/**/*.test.js",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "ntp",
21
+ "network-time-protocol",
22
+ "time",
23
+ "synchronization",
24
+ "time-server",
25
+ "hixbe"
26
+ ],
27
+ "author": "Hixbe",
28
+ "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/hixbehq/nodejs-time.git"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^20.0.0",
35
+ "typescript": "^5.0.0"
36
+ },
37
+ "engines": {
38
+ "node": ">=18.0.0"
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "README.md",
43
+ "LICENSE"
44
+ ],
45
+ "publishConfig": {
46
+ "access": "public",
47
+ "provenance": true
48
+ }
49
+ }