-
-
Notifications
You must be signed in to change notification settings - Fork 111
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b8f3061
commit decdbdc
Showing
16 changed files
with
428 additions
and
346 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
node_modules | ||
yarn.lock | ||
/distribution |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,20 +10,21 @@ | |
"email": "[email protected]", | ||
"url": "https://sindresorhus.com" | ||
}, | ||
"type": "module", | ||
"bin": { | ||
"fast": "cli.js" | ||
"fast": "./distribution/cli.js" | ||
}, | ||
"engines": { | ||
"node": ">=12.20" | ||
"node": ">=18" | ||
}, | ||
"scripts": { | ||
"test": "xo && ava" | ||
"build": "tsc", | ||
"prepublish": "npm run build", | ||
"pretest": "npm run build", | ||
"test": "xo && NODE_OPTIONS='--import=tsx/esm' ava" | ||
}, | ||
"files": [ | ||
"cli.js", | ||
"api.js", | ||
"ui.js", | ||
"utilities.js" | ||
"distribution" | ||
], | ||
"keywords": [ | ||
"cli-app", | ||
|
@@ -46,30 +47,43 @@ | |
"mbps" | ||
], | ||
"dependencies": { | ||
"delay": "^5.0.0", | ||
"import-jsx": "^4.0.1", | ||
"ink": "^3.2.0", | ||
"ink-spinner": "^4.0.3", | ||
"meow": "^9.0.0", | ||
"puppeteer": "^13.1.3", | ||
"react": "^17.0.2", | ||
"zen-observable": "^0.8.15" | ||
"ink": "^5.0.0", | ||
"ink-spinner": "^5.0.0", | ||
"meow": "^13.2.0", | ||
"puppeteer": "^22.8.1", | ||
"react": "^18.3.1", | ||
"unicorn-magic": "^0.2.0" | ||
}, | ||
"devDependencies": { | ||
"ava": "^2.4.0", | ||
"eslint-config-xo-react": "^0.25.0", | ||
"eslint-plugin-react": "^7.23.2", | ||
"eslint-plugin-react-hooks": "^4.2.0", | ||
"execa": "^5.0.0", | ||
"p-event": "^4.2.0", | ||
"xo": "^0.39.1" | ||
"@sindresorhus/tsconfig": "^5.0.0", | ||
"@types/react": "^18.3.2", | ||
"ava": "^6.1.3", | ||
"eslint-config-xo-react": "^0.27.0", | ||
"eslint-plugin-react": "^7.34.1", | ||
"eslint-plugin-react-hooks": "^4.6.2", | ||
"execa": "^9.1.0", | ||
"p-event": "^6.0.1", | ||
"tsx": "^4.10.2", | ||
"xo": "^0.58.0" | ||
}, | ||
"xo": { | ||
"extends": [ | ||
"xo-react" | ||
], | ||
"rules": { | ||
"react/prop-types": "off" | ||
"react/prop-types": "off", | ||
"react/function-component-definition": "off", | ||
"react/boolean-prop-naming": "off", | ||
"@typescript-eslint/no-non-null-asserted-optional-chain": "off", | ||
"react/no-unused-prop-types": "off" | ||
} | ||
}, | ||
"ava": { | ||
"extensions": { | ||
"ts": "module", | ||
"tsx": "module" | ||
}, | ||
"workerThreads": false, | ||
"timeout": "10m" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import {isDeepStrictEqual} from 'node:util'; | ||
import puppeteer from 'puppeteer'; | ||
import {delay} from 'unicorn-magic'; | ||
|
||
type Options = { | ||
measureUpload?: boolean; | ||
}; | ||
|
||
type Result = { | ||
downloadSpeed: number; | ||
uploadSpeed: number; | ||
downloadUnit: string; | ||
downloaded: number; | ||
uploadUnit: string; | ||
uploaded: number; | ||
latency: number; | ||
bufferBloat: number; | ||
userLocation: string; | ||
userIp: string; | ||
isDone: boolean; | ||
}; | ||
|
||
async function * monitorSpeed(page: puppeteer.Page, options?: Options): AsyncGenerator<Result, void, undefined> { | ||
let previousResult: Result | undefined; | ||
|
||
while (true) { | ||
// eslint-disable-next-line no-await-in-loop, @typescript-eslint/no-loop-func | ||
const result: Result = await page.evaluate((): Result => { | ||
const $ = document.querySelector.bind(document); | ||
|
||
return { | ||
downloadSpeed: Number($('#speed-value')?.textContent), | ||
uploadSpeed: Number($('#upload-value')?.textContent), | ||
downloadUnit: $('#speed-units')?.textContent?.trim()!, | ||
downloaded: Number($('#down-mb-value')?.textContent?.trim()), | ||
uploadUnit: $('#upload-units')?.textContent?.trim()!, | ||
uploaded: Number($('#up-mb-value')?.textContent?.trim()), | ||
latency: Number($('#latency-value')?.textContent?.trim()), | ||
bufferBloat: Number($('#bufferbloat-value')?.textContent?.trim()), | ||
userLocation: $('#user-location')?.textContent?.trim()!, | ||
userIp: $('#user-ip')?.textContent?.trim()!, | ||
isDone: Boolean($('#speed-value.succeeded') && $('#upload-value.succeeded')), | ||
}; | ||
}); | ||
|
||
if (result.downloadSpeed > 0 && !isDeepStrictEqual(result, previousResult)) { | ||
yield result; | ||
} | ||
|
||
if (result.isDone || (options && !options.measureUpload && result.uploadSpeed)) { | ||
return; | ||
} | ||
|
||
previousResult = result; | ||
|
||
// eslint-disable-next-line no-await-in-loop | ||
await delay({seconds: 0.1}); | ||
} | ||
} | ||
|
||
export default async function * api(options?: Options): AsyncGenerator<Result, void, undefined> { | ||
const browser = await puppeteer.launch({args: ['--no-sandbox']}); | ||
const page = await browser.newPage(); | ||
await page.goto('https://fast.com'); | ||
|
||
try { | ||
for await (const result of monitorSpeed(page, options)) { | ||
yield result; | ||
} | ||
} finally { | ||
await browser.close(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
#!/usr/bin/env node | ||
import meow from 'meow'; | ||
import React from 'react'; | ||
import {render} from 'ink'; | ||
import Ui from './ui.js'; | ||
|
||
const cli = meow(` | ||
Usage | ||
$ fast | ||
$ fast > file | ||
Options | ||
--upload, -u Measure upload speed in addition to download speed | ||
--single-line Reduce spacing and output to a single line | ||
--json JSON output | ||
Examples | ||
$ fast --upload > file && cat file | ||
17 Mbps | ||
4.4 Mbps | ||
$ fast --upload --json | ||
`, { | ||
importMeta: import.meta, | ||
flags: { | ||
upload: { | ||
type: 'boolean', | ||
shortFlag: 'u', | ||
}, | ||
singleLine: { | ||
type: 'boolean', | ||
}, | ||
json: { | ||
type: 'boolean', | ||
}, | ||
} as const, | ||
}); | ||
|
||
function App() { | ||
return ( | ||
<Ui | ||
singleLine={cli.flags.singleLine} | ||
upload={cli.flags.upload} | ||
json={cli.flags.json} | ||
/> | ||
); | ||
} | ||
|
||
const app = render(<App/>); | ||
await app.waitUntilExit(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export type SpeedUnit = 'gbps' | 'mbps' | 'kbps' | 'bps'; |
Oops, something went wrong.