Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
d6be2e27b0 | |||
d6c0af35fa | |||
bc19c21949 | |||
dba1855eb6 |
66
.gitea/workflows/default_nottags.yaml
Normal file
66
.gitea/workflows/default_nottags.yaml
Normal file
@ -0,0 +1,66 @@
|
||||
name: Default (not tags)
|
||||
|
||||
on:
|
||||
push:
|
||||
tags-ignore:
|
||||
- '**'
|
||||
|
||||
env:
|
||||
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
|
||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
|
||||
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
|
||||
|
||||
jobs:
|
||||
security:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install pnpm and npmci
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @ship.zone/npmci
|
||||
|
||||
- name: Run npm prepare
|
||||
run: npmci npm prepare
|
||||
|
||||
- name: Audit production dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --prod
|
||||
continue-on-error: true
|
||||
|
||||
- name: Audit development dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --dev
|
||||
continue-on-error: true
|
||||
|
||||
test:
|
||||
if: ${{ always() }}
|
||||
needs: security
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Test stable
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm test
|
||||
|
||||
- name: Test build
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm build
|
124
.gitea/workflows/default_tags.yaml
Normal file
124
.gitea/workflows/default_tags.yaml
Normal file
@ -0,0 +1,124 @@
|
||||
name: Default (tags)
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
|
||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
|
||||
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
|
||||
|
||||
jobs:
|
||||
security:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @ship.zone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Audit production dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --prod
|
||||
continue-on-error: true
|
||||
|
||||
- name: Audit development dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --dev
|
||||
continue-on-error: true
|
||||
|
||||
test:
|
||||
if: ${{ always() }}
|
||||
needs: security
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @ship.zone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Test stable
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm test
|
||||
|
||||
- name: Test build
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm build
|
||||
|
||||
release:
|
||||
needs: test
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @ship.zone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Release
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm publish
|
||||
|
||||
metadata:
|
||||
needs: test
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
continue-on-error: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @ship.zone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Code quality
|
||||
run: |
|
||||
npmci command npm install -g typescript
|
||||
npmci npm install
|
||||
|
||||
- name: Trigger
|
||||
run: npmci trigger
|
||||
|
||||
- name: Build docs and upload artifacts
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
pnpm install -g @git.zone/tsdoc
|
||||
npmci command tsdoc
|
||||
continue-on-error: true
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,7 +3,6 @@
|
||||
# artifacts
|
||||
coverage/
|
||||
public/
|
||||
pages/
|
||||
|
||||
# installs
|
||||
node_modules/
|
||||
@ -17,4 +16,4 @@ node_modules/
|
||||
dist/
|
||||
dist_*/
|
||||
|
||||
# custom
|
||||
#------# custom
|
141
.gitlab-ci.yml
141
.gitlab-ci.yml
@ -1,141 +0,0 @@
|
||||
# gitzone ci_default
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- .npmci_cache/
|
||||
key: '$CI_BUILD_STAGE'
|
||||
|
||||
stages:
|
||||
- security
|
||||
- test
|
||||
- release
|
||||
- metadata
|
||||
|
||||
before_script:
|
||||
- npm install -g @shipzone/npmci
|
||||
|
||||
# ====================
|
||||
# security stage
|
||||
# ====================
|
||||
mirror:
|
||||
stage: security
|
||||
script:
|
||||
- npmci git mirror
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
auditProductionDependencies:
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
stage: security
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci command npm install --production --ignore-scripts
|
||||
- npmci command npm config set registry https://registry.npmjs.org
|
||||
- npmci command npm audit --audit-level=high --only=prod --production
|
||||
tags:
|
||||
- docker
|
||||
allow_failure: true
|
||||
|
||||
auditDevDependencies:
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
stage: security
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci command npm install --ignore-scripts
|
||||
- npmci command npm config set registry https://registry.npmjs.org
|
||||
- npmci command npm audit --audit-level=high --only=dev
|
||||
tags:
|
||||
- docker
|
||||
allow_failure: true
|
||||
|
||||
# ====================
|
||||
# test stage
|
||||
# ====================
|
||||
|
||||
testStable:
|
||||
stage: test
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci node install stable
|
||||
- npmci npm install
|
||||
- npmci npm test
|
||||
coverage: /\d+.?\d+?\%\s*coverage/
|
||||
tags:
|
||||
- docker
|
||||
|
||||
testBuild:
|
||||
stage: test
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci node install stable
|
||||
- npmci npm install
|
||||
- npmci command npm run build
|
||||
coverage: /\d+.?\d+?\%\s*coverage/
|
||||
tags:
|
||||
- docker
|
||||
|
||||
release:
|
||||
stage: release
|
||||
script:
|
||||
- npmci node install stable
|
||||
- npmci npm publish
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
# ====================
|
||||
# metadata stage
|
||||
# ====================
|
||||
codequality:
|
||||
stage: metadata
|
||||
allow_failure: true
|
||||
only:
|
||||
- tags
|
||||
script:
|
||||
- npmci command npm install -g tslint typescript
|
||||
- npmci npm prepare
|
||||
- npmci npm install
|
||||
- npmci command "tslint -c tslint.json ./ts/**/*.ts"
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
- priv
|
||||
|
||||
trigger:
|
||||
stage: metadata
|
||||
script:
|
||||
- npmci trigger
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
pages:
|
||||
stage: metadata
|
||||
script:
|
||||
- npmci node install lts
|
||||
- npmci command npm install -g @git.zone/tsdoc
|
||||
- npmci npm prepare
|
||||
- npmci npm install
|
||||
- npmci command tsdoc
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
- notpriv
|
||||
only:
|
||||
- tags
|
||||
artifacts:
|
||||
expire_in: 1 week
|
||||
paths:
|
||||
- public
|
||||
allow_failure: true
|
17
changelog.md
17
changelog.md
@ -1,5 +1,22 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-04-28 - 3.0.5 - fix(core)
|
||||
Improve logging and error handling by introducing custom error classes and a global logging interface while refactoring network diagnostics methods.
|
||||
|
||||
- Added custom error classes (NetworkError, TimeoutError) for network operations.
|
||||
- Introduced a global logging interface to replace direct console logging.
|
||||
- Updated CloudflareSpeed and SmartNetwork classes to use getLogger for improved error reporting.
|
||||
- Disabled connection pooling in HTTP requests to prevent listener accumulation.
|
||||
|
||||
## 2025-04-28 - 3.0.4 - fix(ci/config)
|
||||
Improve CI workflows, update project configuration, and clean up code formatting
|
||||
|
||||
- Added new Gitea workflow files (default_nottags.yaml and default_tags.yaml) to replace GitLab CI
|
||||
- Updated package.json with new buildDocs script, revised homepage URL, bug tracking info, and pnpm overrides
|
||||
- Refined code formatting in TypeScript files, including improved error handling in Cloudflare speed tests and consistent callback structure
|
||||
- Enhanced tsconfig.json by adding baseUrl and paths for better module resolution
|
||||
- Introduced readme.plan.md outlining future improvements and feature enhancements
|
||||
|
||||
## 2025-04-28 - 3.0.3 - fix(deps)
|
||||
Update dependency namespaces and bump package versions in CI configuration and source imports
|
||||
|
||||
|
15
package.json
15
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@push.rocks/smartnetwork",
|
||||
"version": "3.0.3",
|
||||
"version": "3.0.5",
|
||||
"private": false,
|
||||
"description": "A toolkit for network diagnostics including speed tests, port availability checks, and more.",
|
||||
"main": "dist_ts/index.js",
|
||||
@ -10,7 +10,8 @@
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"test": "(tstest test/)",
|
||||
"build": "(tsbuild --web --allowimplicitany)"
|
||||
"build": "(tsbuild --web --allowimplicitany)",
|
||||
"buildDocs": "tsdoc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@git.zone/tsbuild": "^2.1.61",
|
||||
@ -56,10 +57,16 @@
|
||||
"network utility",
|
||||
"TypeScript"
|
||||
],
|
||||
"homepage": "https://code.foss.global/push.rocks/smartnetwork",
|
||||
"homepage": "https://code.foss.global/push.rocks/smartnetwork#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://code.foss.global/push.rocks/smartnetwork.git"
|
||||
},
|
||||
"packageManager": "pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6"
|
||||
"packageManager": "pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6",
|
||||
"bugs": {
|
||||
"url": "https://code.foss.global/push.rocks/smartnetwork/issues"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
# @push.rocks/smartnetwork
|
||||
|
||||
network diagnostics
|
||||
|
||||
## Install
|
||||
@ -58,7 +59,7 @@ const checkLocalPort = async (port: number) => {
|
||||
}
|
||||
};
|
||||
|
||||
checkLocalPort(8080); // Example port number
|
||||
checkLocalPort(8080); // Example port number
|
||||
```
|
||||
|
||||
### Checking Remote Port Availability
|
||||
@ -75,7 +76,7 @@ const checkRemotePort = async (hostname: string, port: number) => {
|
||||
}
|
||||
};
|
||||
|
||||
checkRemotePort('example.com', 443); // Checking HTTPS port on example.com
|
||||
checkRemotePort('example.com', 443); // Checking HTTPS port on example.com
|
||||
```
|
||||
|
||||
### Using Ping
|
||||
@ -131,7 +132,7 @@ These examples offer a glimpse into the module's utility in real-world scenarios
|
||||
|
||||
## License and Legal Information
|
||||
|
||||
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
||||
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
||||
|
||||
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
||||
|
||||
|
47
readme.plan.md
Normal file
47
readme.plan.md
Normal file
@ -0,0 +1,47 @@
|
||||
# Plan to Enhance Code Quality, Feature Set & Documentation
|
||||
|
||||
This plan focuses on three pillars to elevate `@push.rocks/smartnetwork`: 1) Code Quality, 2) Feature Enhancements, and 3) Documentation.
|
||||
|
||||
## 1. Code Quality Improvements
|
||||
- Enable strict TypeScript (`strict`, `noImplicitAny`, `strictNullChecks`).
|
||||
- Enforce linting (ESLint) and formatting (Prettier) with pre-commit hooks.
|
||||
- Audit and refactor core modules for:
|
||||
- Clear separation of concerns (IO, business logic, helpers).
|
||||
- Removal of duplicated logic and dead code.
|
||||
- Consistent use of async/await and error propagation.
|
||||
- Introduce custom error classes (e.g., `NetworkError`, `TimeoutError`) for predictable failure handling.
|
||||
- Augment logging support via injectable logger interface.
|
||||
- Establish a baseline of ≥90% unit-test coverage and enforce via CI.
|
||||
|
||||
## 2. Feature Enhancements
|
||||
- Expand diagnostics:
|
||||
- Traceroute functionality with hop-by-hop latency.
|
||||
- DNS lookup (A, AAAA, MX records).
|
||||
- HTTP(s) endpoint health check (status codes, headers, latency).
|
||||
- Improve existing methods:
|
||||
- `getSpeed`: allow configurable test duration and parallel streams.
|
||||
- `ping`: add statistical summary (min, max, stddev) and continuous mode.
|
||||
- `isRemotePortAvailable`: support TCP/UDP checks with timeout and retry.
|
||||
- Introduce plugin architecture:
|
||||
- Define `Plugin` interface for third-party extensions.
|
||||
- Enable runtime registration/unregistration.
|
||||
- Provide sample plugins (e.g., custom ping strategies, alternate speed providers).
|
||||
- Optional in-memory caching with TTL for expensive calls (`getPublicIps`, `getGateways`).
|
||||
|
||||
## 3. Documentation & Examples
|
||||
- Upgrade README:
|
||||
- Detailed API reference with method signatures and option parameters.
|
||||
- Real-world usage snippets and full example projects.
|
||||
- Add TSDoc comments to all public classes, methods, and types.
|
||||
- Create a `docs/` folder with:
|
||||
- Getting Started guide.
|
||||
- Advanced topics (plugin development, custom error handling).
|
||||
- FAQ and troubleshooting section.
|
||||
- Integrate TypeDoc for automated documentation site generation.
|
||||
- Update `CONTRIBUTING.md` and `CHANGELOG.md` to reflect development and release practices.
|
||||
|
||||
## Next Steps
|
||||
1. Review and prioritize high-impact items per pillar.
|
||||
2. Kick off Phase 1 (Code Quality) with linting, TS config, and core refactor.
|
||||
3. Schedule sprints for Feature and Documentation phases.
|
||||
4. Configure CI pipeline to enforce quality gates and publish docs.
|
@ -20,6 +20,6 @@ tap.test('should state when a ping is not alive ', async () => {
|
||||
|
||||
tap.test('should send a ping to an IP', async () => {
|
||||
await expectAsync(testSmartnetwork.ping('192.168.186.999')).property('alive').toBeFalse();
|
||||
})
|
||||
});
|
||||
|
||||
tap.start();
|
||||
|
@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartnetwork',
|
||||
version: '3.0.3',
|
||||
version: '3.0.5',
|
||||
description: 'A toolkit for network diagnostics including speed tests, port availability checks, and more.'
|
||||
}
|
||||
|
20
ts/errors.ts
Normal file
20
ts/errors.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Custom error classes for network operations
|
||||
*/
|
||||
export class NetworkError extends Error {
|
||||
public code?: string;
|
||||
constructor(message?: string, code?: string) {
|
||||
super(message);
|
||||
this.name = 'NetworkError';
|
||||
this.code = code;
|
||||
Object.setPrototypeOf(this, new.target.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
export class TimeoutError extends NetworkError {
|
||||
constructor(message?: string) {
|
||||
super(message, 'ETIMEOUT');
|
||||
this.name = 'TimeoutError';
|
||||
Object.setPrototypeOf(this, new.target.prototype);
|
||||
}
|
||||
}
|
30
ts/logging.ts
Normal file
30
ts/logging.ts
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Injectable logging interface and global logger
|
||||
*/
|
||||
export interface Logger {
|
||||
/** Debug-level messages */
|
||||
debug?(...args: unknown[]): void;
|
||||
/** Informational messages */
|
||||
info(...args: unknown[]): void;
|
||||
/** Warning messages */
|
||||
warn?(...args: unknown[]): void;
|
||||
/** Error messages */
|
||||
error(...args: unknown[]): void;
|
||||
}
|
||||
|
||||
let globalLogger: Logger = console;
|
||||
|
||||
/**
|
||||
* Replace the global logger implementation
|
||||
* @param logger Custom logger adhering to Logger interface
|
||||
*/
|
||||
export function setLogger(logger: Logger): void {
|
||||
globalLogger = logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current global logger
|
||||
*/
|
||||
export function getLogger(): Logger {
|
||||
return globalLogger;
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
import * as plugins from './smartnetwork.plugins.js';
|
||||
import { getLogger } from './logging.js';
|
||||
import { NetworkError, TimeoutError } from './errors.js';
|
||||
import * as stats from './helpers/stats.js';
|
||||
|
||||
export class CloudflareSpeed {
|
||||
@ -49,8 +51,8 @@ export class CloudflareSpeed {
|
||||
measurements.push(response[4] - response[0] - response[6]);
|
||||
},
|
||||
(error) => {
|
||||
console.log(`Error: ${error}`);
|
||||
}
|
||||
getLogger().error('Error measuring latency:', error);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -73,8 +75,8 @@ export class CloudflareSpeed {
|
||||
measurements.push(await this.measureSpeed(bytes, transferTime));
|
||||
},
|
||||
(error) => {
|
||||
console.log(`Error: ${error}`);
|
||||
}
|
||||
getLogger().error('Error measuring download chunk:', error);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -91,8 +93,8 @@ export class CloudflareSpeed {
|
||||
measurements.push(await this.measureSpeed(bytes, transferTime));
|
||||
},
|
||||
(error) => {
|
||||
console.log(`Error: ${error}`);
|
||||
}
|
||||
getLogger().error('Error measuring upload chunk:', error);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -104,15 +106,16 @@ export class CloudflareSpeed {
|
||||
}
|
||||
|
||||
public async fetchServerLocations(): Promise<{ [key: string]: string }> {
|
||||
const res = JSON.parse(await this.get('speed.cloudflare.com', '/locations'));
|
||||
|
||||
return res.reduce((data: any, optionsArg: { iata: string; city: string }) => {
|
||||
// Bypass prettier "no-assign-param" rules
|
||||
const data1 = data;
|
||||
|
||||
data1[optionsArg.iata] = optionsArg.city;
|
||||
return data1;
|
||||
}, {});
|
||||
const res = JSON.parse(
|
||||
await this.get('speed.cloudflare.com', '/locations'),
|
||||
) as Array<{ iata: string; city: string }>;
|
||||
return res.reduce(
|
||||
(data: Record<string, string>, optionsArg) => {
|
||||
data[optionsArg.iata] = optionsArg.city;
|
||||
return data;
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
);
|
||||
}
|
||||
|
||||
public async get(hostname: string, path: string): Promise<string> {
|
||||
@ -122,6 +125,8 @@ export class CloudflareSpeed {
|
||||
hostname,
|
||||
path,
|
||||
method: 'GET',
|
||||
// disable connection pooling to avoid listener accumulation
|
||||
agent: false,
|
||||
},
|
||||
(res) => {
|
||||
const body: Array<Buffer> = [];
|
||||
@ -135,10 +140,10 @@ export class CloudflareSpeed {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
req.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
}
|
||||
req.on('error', (err: Error & { code?: string }) => {
|
||||
reject(new NetworkError(err.message, err.code));
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
req.end();
|
||||
@ -179,33 +184,36 @@ export class CloudflareSpeed {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
started = plugins.perfHooks.performance.now();
|
||||
const req = plugins.https.request(options, (res) => {
|
||||
// disable connection pooling to avoid listener accumulation across requests
|
||||
const reqOptions = { ...options, agent: false };
|
||||
const req = plugins.https.request(reqOptions, (res) => {
|
||||
res.once('readable', () => {
|
||||
ttfb = plugins.perfHooks.performance.now();
|
||||
});
|
||||
res.on('data', () => {});
|
||||
res.on('end', () => {
|
||||
ended = plugins.perfHooks.performance.now();
|
||||
resolve([
|
||||
started,
|
||||
dnsLookup,
|
||||
tcpHandshake,
|
||||
sslHandshake,
|
||||
ttfb,
|
||||
ended,
|
||||
parseFloat(res.headers['server-timing'].slice(22) as any),
|
||||
]);
|
||||
resolve([
|
||||
started,
|
||||
dnsLookup,
|
||||
tcpHandshake,
|
||||
sslHandshake,
|
||||
ttfb,
|
||||
ended,
|
||||
parseFloat((res.headers['server-timing'] as string).slice(22)),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
req.on('socket', (socket) => {
|
||||
socket.on('lookup', () => {
|
||||
// Listen for timing events once per new socket
|
||||
req.once('socket', (socket) => {
|
||||
socket.once('lookup', () => {
|
||||
dnsLookup = plugins.perfHooks.performance.now();
|
||||
});
|
||||
socket.on('connect', () => {
|
||||
socket.once('connect', () => {
|
||||
tcpHandshake = plugins.perfHooks.performance.now();
|
||||
});
|
||||
socket.on('secureConnect', () => {
|
||||
socket.once('secureConnect', () => {
|
||||
sslHandshake = plugins.perfHooks.performance.now();
|
||||
});
|
||||
});
|
||||
@ -238,20 +246,14 @@ export class CloudflareSpeed {
|
||||
text
|
||||
.split('\n')
|
||||
.map((i) => {
|
||||
const j = i.split('=');
|
||||
|
||||
return [j[0], j[1]];
|
||||
const parts = i.split('=');
|
||||
return [parts[0], parts[1]];
|
||||
})
|
||||
.reduce((data: any, [k, v]) => {
|
||||
.reduce((data: Record<string, string>, [k, v]) => {
|
||||
if (v === undefined) return data;
|
||||
|
||||
// Bypass prettier "no-assign-param" rules
|
||||
const data1 = data;
|
||||
// Object.fromEntries is only supported by Node.js 12 or newer
|
||||
data1[k] = v;
|
||||
|
||||
return data1;
|
||||
}, {});
|
||||
data[k] = v;
|
||||
return data;
|
||||
}, {} as Record<string, string>);
|
||||
|
||||
return this.get('speed.cloudflare.com', '/cdn-cgi/trace').then(parseCfCdnCgiTrace);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as plugins from './smartnetwork.plugins.js';
|
||||
|
||||
import { CloudflareSpeed } from './smartnetwork.classes.cloudflarespeed.js';
|
||||
import { getLogger } from './logging.js';
|
||||
|
||||
/**
|
||||
* SmartNetwork simplifies actions within the network
|
||||
@ -16,7 +17,10 @@ export class SmartNetwork {
|
||||
return test;
|
||||
}
|
||||
|
||||
public async ping(hostArg: string, timeoutArg: number = 500): Promise<ReturnType<typeof plugins.smartping.Smartping.prototype.ping>> {
|
||||
public async ping(
|
||||
hostArg: string,
|
||||
timeoutArg: number = 500,
|
||||
): Promise<ReturnType<typeof plugins.smartping.Smartping.prototype.ping>> {
|
||||
const smartpingInstance = new plugins.smartping.Smartping();
|
||||
const pingResult = await smartpingInstance.ping(hostArg, timeoutArg);
|
||||
return pingResult;
|
||||
@ -34,11 +38,7 @@ export class SmartNetwork {
|
||||
|
||||
// test IPv4 space
|
||||
const ipv4Test = net.createServer();
|
||||
ipv4Test.once('error', (err: any) => {
|
||||
if (err.code !== 'EADDRINUSE') {
|
||||
doneIpV4.resolve(false);
|
||||
return;
|
||||
}
|
||||
ipv4Test.once('error', () => {
|
||||
doneIpV4.resolve(false);
|
||||
});
|
||||
ipv4Test.once('listening', () => {
|
||||
@ -53,11 +53,7 @@ export class SmartNetwork {
|
||||
|
||||
// test IPv6 space
|
||||
const ipv6Test = net.createServer();
|
||||
ipv6Test.once('error', function (err: any) {
|
||||
if (err.code !== 'EADDRINUSE') {
|
||||
doneIpV6.resolve(false);
|
||||
return;
|
||||
}
|
||||
ipv6Test.once('error', () => {
|
||||
doneIpV6.resolve(false);
|
||||
});
|
||||
ipv6Test.once('listening', () => {
|
||||
@ -84,14 +80,15 @@ export class SmartNetwork {
|
||||
const domainPart = domainArg.split(':')[0];
|
||||
const port = portArg ? portArg : parseInt(domainArg.split(':')[1], 10);
|
||||
|
||||
plugins.isopen(domainPart, port, (response: any) => {
|
||||
console.log(response);
|
||||
if (response[port.toString()].isOpen) {
|
||||
done.resolve(true);
|
||||
} else {
|
||||
done.resolve(false);
|
||||
}
|
||||
});
|
||||
plugins.isopen(
|
||||
domainPart,
|
||||
port,
|
||||
(response: Record<string, { isOpen: boolean }>) => {
|
||||
getLogger().debug(response);
|
||||
const portInfo = response[port.toString()];
|
||||
done.resolve(Boolean(portInfo?.isOpen));
|
||||
},
|
||||
);
|
||||
const result = await done.promise;
|
||||
return result;
|
||||
}
|
||||
@ -107,7 +104,7 @@ export class SmartNetwork {
|
||||
}> {
|
||||
const defaultGatewayName = await plugins.systeminformation.networkInterfaceDefault();
|
||||
if (!defaultGatewayName) {
|
||||
console.log('Cannot determine default gateway');
|
||||
getLogger().warn?.('Cannot determine default gateway');
|
||||
return null;
|
||||
}
|
||||
const gateways = await this.getGateways();
|
||||
@ -120,18 +117,22 @@ export class SmartNetwork {
|
||||
|
||||
public async getPublicIps() {
|
||||
return {
|
||||
v4: await plugins.publicIp.publicIpv4({
|
||||
timeout: 1000,
|
||||
onlyHttps: true,
|
||||
}).catch(async (err) => {
|
||||
return null
|
||||
}),
|
||||
v6: await plugins.publicIp.publicIpv6({
|
||||
timeout: 1000,
|
||||
onlyHttps: true,
|
||||
}).catch(async (err) => {
|
||||
return null
|
||||
})
|
||||
v4: await plugins.publicIp
|
||||
.publicIpv4({
|
||||
timeout: 1000,
|
||||
onlyHttps: true,
|
||||
})
|
||||
.catch(async (err) => {
|
||||
return null;
|
||||
}),
|
||||
v6: await plugins.publicIp
|
||||
.publicIpv6({
|
||||
timeout: 1000,
|
||||
onlyHttps: true,
|
||||
})
|
||||
.catch(async (err) => {
|
||||
return null;
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,11 @@
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"esModuleInterop": true,
|
||||
"verbatimModuleSyntax": true
|
||||
"verbatimModuleSyntax": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {}
|
||||
},
|
||||
"exclude": [
|
||||
"dist_*/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user