@push.rocks/smartnetwork 4.4.0 → 4.5.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/readme.hints.md CHANGED
@@ -1,73 +1,99 @@
1
1
  # Project Analysis
2
2
 
3
3
  ## Architecture Overview
4
- This is a comprehensive network diagnostics toolkit that provides various network-related utilities. The main entry point is the `SmartNetwork` class which orchestrates all functionality.
4
+ This is a comprehensive network diagnostics toolkit. The main entry point is the `SmartNetwork` class which orchestrates all functionality. System-dependent operations are delegated to a Rust binary via JSON-over-stdin/stdout IPC using `@push.rocks/smartrust`.
5
5
 
6
6
  Key features:
7
- - Speed testing via Cloudflare (parallelizable with duration support)
8
- - Ping operations with statistics
9
- - Port availability checks (local and remote)
10
- - Network gateway discovery
11
- - Public IP retrieval
12
- - DNS resolution
13
- - HTTP endpoint health checks
14
- - Traceroute functionality (with fallback stub)
7
+ - Speed testing via Cloudflare (parallelizable with duration support) — pure TS
8
+ - ICMP ping with statistics — Rust binary (surge-ping)
9
+ - Port availability checks (local and remote) — Rust binary (tokio TCP / socket2)
10
+ - Network gateway discovery — Rust binary (parses /proc/net/route on Linux)
11
+ - Public IP retrieval — pure TS
12
+ - DNS resolution via smartdns — pure TS
13
+ - HTTP endpoint health checks — pure TS
14
+ - Traceroute — Rust binary (raw ICMP sockets via socket2)
15
+
16
+ ## Rust Binary Architecture (v4.5.0+)
17
+
18
+ ### Binary: `rustnetwork`
19
+ - Located in `rust/crates/rustnetwork/`
20
+ - Cross-compiled for linux_amd64 and linux_arm64 via `@git.zone/tsrust`
21
+ - Output: `dist_rust/rustnetwork_linux_amd64`, `dist_rust/rustnetwork_linux_arm64`
22
+ - IPC mode: `--management` flag for JSON-over-stdin/stdout
23
+
24
+ ### IPC Commands
25
+ | Method | Description |
26
+ |--------|-------------|
27
+ | `healthPing` | Liveness check (returns `{ pong: true }`) |
28
+ | `ping` | ICMP ping with count/timeout, returns stats |
29
+ | `traceroute` | Hop-by-hop latency via raw ICMP sockets |
30
+ | `tcpPortCheck` | TCP connect probe to remote host:port |
31
+ | `isLocalPortFree` | Bind test on IPv4 + IPv6 |
32
+ | `defaultGateway` | Parse /proc/net/route for default interface |
33
+
34
+ ### TypeScript Bridge
35
+ - `ts/smartnetwork.classes.rustbridge.ts` — Singleton `RustNetworkBridge` wrapping `smartrust.RustBridge`
36
+ - `SmartNetwork` class has `start()`/`stop()` lifecycle for the bridge
37
+ - `ensureBridge()` auto-starts on first use
38
+
39
+ ### Build Pipeline
40
+ - `pnpm build` = `tsbuild tsfolders --allowimplicitany && tsrust`
41
+ - Targets configured in `.smartconfig.json` under `@git.zone/tsrust`
15
42
 
16
43
  ## Key Components
17
44
 
18
45
  ### SmartNetwork Class
19
46
  - Central orchestrator for all network operations
47
+ - Requires `start()` before using ping/traceroute/port/gateway operations
20
48
  - Supports caching via `cacheTtl` option for gateway and public IP lookups
21
49
  - Plugin architecture for extensibility
22
50
 
51
+ ### RustNetworkBridge Class
52
+ - Singleton pattern via `getInstance()`
53
+ - Binary search: dist_rust/ (platform-suffixed) > rust/target/release/ > rust/target/debug/
54
+ - `searchSystemPath: false` — only looks in local paths
55
+
23
56
  ### CloudflareSpeed Class
24
57
  - Handles internet speed testing using Cloudflare's infrastructure
25
58
  - Supports parallel streams and customizable test duration
26
- - Measures both download and upload speeds using progressive chunk sizes
27
- - Includes latency measurements (jitter, median, average)
59
+ - Upload speed measurement uses server-timing header with client-side timing fallback
28
60
 
29
61
  ### Error Handling
30
62
  - Custom `NetworkError` and `TimeoutError` classes for better error context
31
63
  - Error codes follow Node.js conventions (ENOTSUP, EINVAL, ETIMEOUT)
64
+ - Ping/port methods gracefully degrade on errors (return alive=false / false)
32
65
 
33
66
  ### Logging
34
67
  - Global logger interface for consistent logging across the codebase
35
68
  - Replaceable logger implementation (defaults to console)
36
- - Used primarily for error reporting in speed tests
37
69
 
38
70
  ### Statistics Helpers
39
71
  - Utility functions for statistical calculations (average, median, quartile, jitter)
40
- - Used extensively by speed testing and ping operations
72
+ - Used by speed testing; ping statistics now computed server-side in Rust
41
73
 
42
- ## Recent Changes (v4.0.0 - v4.0.1)
43
- - Added configurable speed test options (parallelStreams, duration)
44
- - Introduced plugin architecture for runtime extensibility
45
- - Enhanced error handling with custom error classes
46
- - Added global logging interface
47
- - Improved connection management by disabling HTTP connection pooling
48
- - Fixed memory leaks from listener accumulation
49
- - Minor formatting fixes for consistency
74
+ ## Dependencies
75
+ - **Runtime**: `@push.rocks/smartrust` (IPC bridge), `@push.rocks/smartdns` (DNS resolution)
76
+ - **Removed in v4.5.0**: `@push.rocks/smartping`, `@push.rocks/smartstring`, `isopen`, `systeminformation`
77
+ - **Dev**: `@git.zone/tsrust` (Rust cross-compilation)
50
78
 
51
79
  ## Testing
52
- - Comprehensive test suite covering all major features
53
- - Tests run on both browser and node environments
54
- - Uses @push.rocks/tapbundle for testing with expectAsync
55
- - Performance tests for speed testing functionality
56
- - Edge case handling for network errors and timeouts
80
+ - `test/test.ts` — Core smoke tests (speed, ports, gateways, public IPs)
81
+ - `test/test.ping.ts` — ICMP ping tests (requires CAP_NET_RAW for alive=true)
82
+ - `test/test.ports.ts` — Comprehensive port testing (27 tests)
83
+ - `test/test.features.ts` — DNS, HTTP health check, traceroute, multi-ping, plugins, caching
84
+ - All tests require `start()`/`stop()` lifecycle for the Rust bridge
57
85
 
58
86
  ## Technical Details
59
87
  - ESM-only package (module type)
60
88
  - TypeScript with strict typing
61
- - Depends on external modules for specific functionality:
62
- - @push.rocks/smartping for ICMP operations
63
- - public-ip for external IP discovery
64
- - systeminformation for network interface details
65
- - isopen for remote port checking
66
- - Uses native Node.js modules for DNS, HTTP/HTTPS, and network operations
89
+ - Node built-in imports use `node:` prefix throughout
90
+ - Uses native Node.js modules for HTTP/HTTPS and os.networkInterfaces()
91
+ - Rust binary requires no elevated privileges for port checks; ICMP ping needs CAP_NET_RAW or appropriate ping_group_range
67
92
 
68
93
  ## Design Patterns
94
+ - Singleton pattern for RustNetworkBridge
69
95
  - Factory pattern for plugin registration
70
96
  - Caching pattern with TTL for expensive operations
71
97
  - Promise-based async/await throughout
72
- - Deferred promises for complex async coordination
73
- - Error propagation with custom error types
98
+ - Error propagation with custom error types
99
+ - Graceful degradation when ICMP permissions unavailable
package/readme.md CHANGED
@@ -37,6 +37,14 @@ import { SmartNetwork } from '@push.rocks/smartnetwork';
37
37
  // Basic instance
38
38
  const network = new SmartNetwork();
39
39
 
40
+ // Start the Rust bridge (auto-started on first use, but explicit start is recommended)
41
+ await network.start();
42
+
43
+ // ... use the network instance ...
44
+
45
+ // Clean up when done (stops the Rust bridge process)
46
+ await network.stop();
47
+
40
48
  // With caching enabled (60 seconds TTL)
41
49
  const cachedNetwork = new SmartNetwork({ cacheTtl: 60000 });
42
50
  ```
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartnetwork',
6
- version: '4.4.0',
6
+ version: '4.5.0',
7
7
  description: 'A toolkit for network diagnostics including speed tests, port availability checks, and more.'
8
8
  }
@@ -33,7 +33,7 @@ export function quartile(values: number[], percentile: number) {
33
33
 
34
34
  export function jitter(values: number[]) {
35
35
  // Average distance between consecutive latency measurements...
36
- let jitters = [];
36
+ let jitters: number[] = [];
37
37
 
38
38
  for (let i = 0; i < values.length - 1; i += 1) {
39
39
  jitters.push(Math.abs(values[i] - values[i + 1]));
package/ts/index.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  export * from './smartnetwork.classes.smartnetwork.js';
2
2
  export type { SmartNetworkOptions, Hop } from './smartnetwork.classes.smartnetwork.js';
3
+ export { RustNetworkBridge } from './smartnetwork.classes.rustbridge.js';
3
4
  export { PublicIp } from './smartnetwork.classes.publicip.js';
5
+ export { IpIntelligence } from './smartnetwork.classes.ipintelligence.js';
6
+ export type { IIpIntelligenceResult, IIpIntelligenceOptions } from './smartnetwork.classes.ipintelligence.js';
4
7
  export { setLogger, getLogger } from './logging.js';
5
8
  export { NetworkError, TimeoutError } from './errors.js';
@@ -53,7 +53,7 @@ export class CloudflareSpeed {
53
53
  const t5 = await measureDownloadParallel(100001000, 1);
54
54
  downloadTests = [...t1, ...t2, ...t3, ...t4, ...t5];
55
55
  }
56
- const speedDownload = stats.quartile(downloadTests, 0.9).toFixed(2);
56
+ const speedDownload = downloadTests.length > 0 ? stats.quartile(downloadTests, 0.9).toFixed(2) : '0.00';
57
57
 
58
58
  // lets test the upload speed with configurable parallel streams
59
59
  const measureUploadParallel = (bytes: number, iterations: number) => {
@@ -84,7 +84,7 @@ export class CloudflareSpeed {
84
84
  const u3 = await measureUploadParallel(1001000, 8);
85
85
  uploadTests = [...u1, ...u2, ...u3];
86
86
  }
87
- const speedUpload = stats.quartile(uploadTests, 0.9).toFixed(2);
87
+ const speedUpload = uploadTests.length > 0 ? stats.quartile(uploadTests, 0.9).toFixed(2) : '0.00';
88
88
 
89
89
  return {
90
90
  ...latency,
@@ -147,8 +147,14 @@ export class CloudflareSpeed {
147
147
  for (let i = 0; i < iterations; i += 1) {
148
148
  await this.upload(bytes).then(
149
149
  async (response) => {
150
- const transferTime = response[6];
151
- measurements.push(await this.measureSpeed(bytes, transferTime));
150
+ // Prefer server-timing duration; fall back to client-side transfer time
151
+ let transferTime = response[6];
152
+ if (!transferTime || !isFinite(transferTime)) {
153
+ transferTime = response[5] - response[4]; // ended - ttfb
154
+ }
155
+ if (transferTime > 0) {
156
+ measurements.push(await this.measureSpeed(bytes, transferTime));
157
+ }
152
158
  },
153
159
  (error) => {
154
160
  getLogger().error('Error measuring upload chunk:', error);
@@ -164,17 +170,22 @@ export class CloudflareSpeed {
164
170
  }
165
171
 
166
172
  public async fetchServerLocations(): Promise<{ [key: string]: string }> {
167
- const res = JSON.parse(await this.get('speed.cloudflare.com', '/locations')) as Array<{
168
- iata: string;
169
- city: string;
170
- }>;
171
- return res.reduce(
172
- (data: Record<string, string>, optionsArg) => {
173
- data[optionsArg.iata] = optionsArg.city;
174
- return data;
175
- },
176
- {} as Record<string, string>,
177
- );
173
+ try {
174
+ const raw = await this.get('speed.cloudflare.com', '/locations');
175
+ const parsed = JSON.parse(raw);
176
+ if (!Array.isArray(parsed)) {
177
+ return {};
178
+ }
179
+ return (parsed as Array<{ iata: string; city: string }>).reduce(
180
+ (data: Record<string, string>, entry) => {
181
+ data[entry.iata] = entry.city;
182
+ return data;
183
+ },
184
+ {} as Record<string, string>,
185
+ );
186
+ } catch {
187
+ return {};
188
+ }
178
189
  }
179
190
 
180
191
  public async get(hostname: string, path: string): Promise<string> {
@@ -259,7 +270,12 @@ export class CloudflareSpeed {
259
270
  sslHandshake,
260
271
  ttfb,
261
272
  ended,
262
- parseFloat((res.headers['server-timing'] as string).slice(22)),
273
+ (() => {
274
+ const serverTiming = res.headers['server-timing'] as string | undefined;
275
+ if (!serverTiming) return 0;
276
+ const match = serverTiming.match(/dur=([\d.]+)/);
277
+ return match ? parseFloat(match[1]) : parseFloat(serverTiming.slice(22)) || 0;
278
+ })(),
263
279
  ]);
264
280
  });
265
281
  });