@qodalis/cli-speed-test 2.0.0-beta.11 → 2.0.0-beta.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qodalis/cli-speed-test",
3
- "version": "2.0.0-beta.11",
3
+ "version": "2.0.0-beta.13",
4
4
  "description": "@qodalis/cli extension for running network speed tests.",
5
5
  "author": "Nicolae Lupei, Qodalis Solutions",
6
6
  "license": "MIT",
@@ -20,7 +20,7 @@
20
20
  "umd": "./umd/index.global.js",
21
21
  "unpkg": "./umd/index.global.js",
22
22
  "dependencies": {
23
- "@qodalis/cli-core": "2.0.0-beta.11"
23
+ "@qodalis/cli-core": "2.0.0-beta.13"
24
24
  },
25
25
  "sideEffects": false,
26
26
  "main": "./public-api.js",
package/public-api.js CHANGED
@@ -5,7 +5,7 @@ var cliCore = require('@qodalis/cli-core');
5
5
  // src/lib/processors/cli-speed-test-command-processor.ts
6
6
 
7
7
  // src/lib/version.ts
8
- var LIBRARY_VERSION = "2.0.0-beta.11";
8
+ var LIBRARY_VERSION = "2.0.0-beta.13";
9
9
  var API_VERSION = 2;
10
10
 
11
11
  // src/lib/processors/cli-speed-test-command-processor.ts
@@ -104,47 +104,41 @@ var CliSpeedTestCommandProcessor = class {
104
104
  async runSpeedTest(command, context) {
105
105
  const downloadOnly = !!command.args["download-only"];
106
106
  const uploadOnly = !!command.args["upload-only"];
107
- const controller = new AbortController();
108
- const subscription = context.onAbort.subscribe(() => {
109
- controller.abort();
110
- });
111
107
  const results = {};
112
108
  try {
113
109
  if (!uploadOnly) {
114
110
  const downloadUrl = command.args["download-url"] || DEFAULT_DOWNLOAD_URL;
111
+ context.notifier.info("Speed test: download");
115
112
  results.download = await this.testDownload(
116
113
  downloadUrl,
117
- context,
118
- controller.signal
114
+ context
119
115
  );
120
116
  }
121
117
  if (!downloadOnly) {
122
118
  const uploadUrl = command.args["upload-url"] || DEFAULT_UPLOAD_URL;
119
+ context.notifier.info("Speed test: upload");
123
120
  results.upload = await this.testUpload(
124
121
  uploadUrl,
125
- context,
126
- controller.signal
122
+ context
127
123
  );
128
124
  }
129
125
  this.printSummary(results, context);
130
126
  } catch (error) {
131
- if (error.name === "AbortError" || controller.signal.aborted) {
127
+ if (error.name === "AbortError" || context.signal?.aborted) {
132
128
  context.writer.writeWarning("Speed test aborted");
133
129
  } else {
134
130
  context.writer.writeError(
135
131
  `Speed test failed: ${error.message || error}`
136
132
  );
137
133
  }
138
- } finally {
139
- subscription.unsubscribe();
140
134
  }
141
135
  }
142
- async testDownload(url, context, signal) {
136
+ async testDownload(url, context) {
143
137
  context.writer.writeln();
144
138
  const progressBar = context.progressBar;
145
139
  const spinner = context.spinner;
146
140
  spinner?.show("Connecting...");
147
- const response = await fetch(url, { signal });
141
+ const response = await context.http.fetch(url);
148
142
  if (!response.ok) {
149
143
  spinner?.hide();
150
144
  throw new Error(`Download failed: HTTP ${response.status}`);
@@ -179,10 +173,12 @@ var CliSpeedTestCommandProcessor = class {
179
173
  progressBar.setText(
180
174
  `${formatSpeed(avgSpeed2)} | ${formatBytes(totalBytes)} / ${formatBytes(contentLength)}`
181
175
  );
176
+ context.notifier.info(`Download: ${Math.round(pct)}% ${formatSpeed(avgSpeed2)}`);
182
177
  } else {
183
178
  progressBar.setText(
184
179
  `${formatSpeed(avgSpeed2)} | ${formatBytes(totalBytes)}`
185
180
  );
181
+ context.notifier.info(`Download: ${formatSpeed(avgSpeed2)}`);
186
182
  }
187
183
  }
188
184
  }
@@ -195,7 +191,7 @@ var CliSpeedTestCommandProcessor = class {
195
191
  );
196
192
  return avgSpeed;
197
193
  }
198
- async testUpload(url, context, signal) {
194
+ async testUpload(url, context) {
199
195
  context.writer.writeln();
200
196
  context.writer.writeInfo(
201
197
  `Running upload test (${formatBytes(UPLOAD_SIZE)})...`
@@ -214,16 +210,15 @@ var CliSpeedTestCommandProcessor = class {
214
210
  let uploaded = 0;
215
211
  const startTime = performance.now();
216
212
  for (let i = 0; i < totalChunks; i++) {
217
- if (signal.aborted) {
213
+ if (context.signal?.aborted) {
218
214
  throw new DOMException("Upload aborted", "AbortError");
219
215
  }
220
216
  const start = i * CHUNK;
221
217
  const end = Math.min(start + CHUNK, UPLOAD_SIZE);
222
218
  const chunk = payload.slice(start, end);
223
- await fetch(url, {
219
+ await context.http.fetch(url, {
224
220
  method: "POST",
225
- body: chunk,
226
- signal
221
+ body: chunk
227
222
  });
228
223
  uploaded += chunk.length;
229
224
  const elapsed = (performance.now() - startTime) / 1e3;
@@ -233,6 +228,7 @@ var CliSpeedTestCommandProcessor = class {
233
228
  progressBar.setText(
234
229
  `${formatSpeed(speed)} | ${formatBytes(uploaded)} / ${formatBytes(UPLOAD_SIZE)}`
235
230
  );
231
+ context.notifier.info(`Upload: ${Math.round(pct)}% ${formatSpeed(speed)}`);
236
232
  }
237
233
  const totalSec = (performance.now() - startTime) / 1e3;
238
234
  const avgSpeed = UPLOAD_SIZE / totalSec;
package/public-api.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../packages/plugins/speed-test/src/lib/version.ts","../../packages/plugins/speed-test/src/lib/processors/cli-speed-test-command-processor.ts","../../packages/plugins/speed-test/src/public-api.ts"],"names":["DefaultLibraryAuthor","CliForegroundColor","avgSpeed"],"mappings":"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","file":"public-api.js","sourcesContent":["\n// Automatically generated during build\nexport const LIBRARY_VERSION = '2.0.0-beta.11';\nexport const API_VERSION = 2;\n ","import {\n CliForegroundColor,\n CliProcessorMetadata,\n DefaultLibraryAuthor,\n CliProcessCommand,\n ICliCommandProcessor,\n ICliExecutionContext,\n} from '@qodalis/cli-core';\nimport { LIBRARY_VERSION } from '../version';\n\n/**\n * Cloudflare speed test endpoints — edge-served, CORS-enabled, fast worldwide.\n * The `bytes` parameter controls the download size.\n */\nconst DEFAULT_DOWNLOAD_URL =\n 'https://speed.cloudflare.com/__down?bytes=26214400';\n\nconst DEFAULT_UPLOAD_URL = 'https://speed.cloudflare.com/__up';\n\n/** Size of upload test payload in bytes (10 MB). */\nconst UPLOAD_SIZE = 10 * 1024 * 1024;\n\n/** Format bytes/second as a human-readable speed string. */\nfunction formatSpeed(bytesPerSecond: number): string {\n const mbps = (bytesPerSecond * 8) / (1024 * 1024);\n if (mbps >= 1) return `${mbps.toFixed(2)} Mbps`;\n const kbps = (bytesPerSecond * 8) / 1024;\n if (kbps >= 1) return `${kbps.toFixed(1)} Kbps`;\n return `${(bytesPerSecond * 8).toFixed(0)} bps`;\n}\n\n/** Format bytes as a human-readable size string. */\nfunction formatBytes(bytes: number): string {\n if (bytes >= 1024 * 1024)\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${bytes} B`;\n}\n\nexport class CliSpeedTestCommandProcessor implements ICliCommandProcessor {\n command = 'speed-test';\n\n description = 'Run an internet speed test';\n\n author = DefaultLibraryAuthor;\n\n version = LIBRARY_VERSION;\n\n processors?: ICliCommandProcessor[] = [];\n\n parameters = [\n {\n name: 'download-url',\n description: 'Custom URL for the download test',\n type: 'string' as const,\n required: false,\n },\n {\n name: 'upload-url',\n description: 'Custom URL for the upload test',\n type: 'string' as const,\n required: false,\n },\n {\n name: 'download-only',\n description: 'Only run the download test',\n type: 'boolean' as const,\n required: false,\n },\n {\n name: 'upload-only',\n description: 'Only run the upload test',\n type: 'boolean' as const,\n required: false,\n },\n ];\n\n metadata?: CliProcessorMetadata = {\n icon: '🚀',\n requiredCoreVersion: '>=2.0.0 <3.0.0',\n requiredCliVersion: '>=2.0.0 <3.0.0',\n };\n\n constructor() {\n this.processors = [\n {\n command: 'run',\n description: 'Run the internet speed test',\n processCommand: async (command, context) => {\n await this.runSpeedTest(command, context);\n },\n },\n ];\n }\n\n async processCommand(\n command: CliProcessCommand,\n context: ICliExecutionContext,\n ): Promise<void> {\n await this.runSpeedTest(command, context);\n }\n\n writeDescription(context: ICliExecutionContext): void {\n const { writer } = context;\n writer.writeln(this.description);\n writer.writeln();\n writer.writeln('Usage:');\n writer.writeln(\n ` ${writer.wrapInColor('speed-test', CliForegroundColor.Cyan)} Run download & upload test`,\n );\n writer.writeln(\n ` ${writer.wrapInColor('speed-test --download-only', CliForegroundColor.Cyan)} Download only`,\n );\n writer.writeln(\n ` ${writer.wrapInColor('speed-test --upload-only', CliForegroundColor.Cyan)} Upload only`,\n );\n writer.writeln(\n ` ${writer.wrapInColor('speed-test --download-url=<url>', CliForegroundColor.Cyan)} Custom download URL`,\n );\n writer.writeln(\n ` ${writer.wrapInColor('speed-test --upload-url=<url>', CliForegroundColor.Cyan)} Custom upload URL`,\n );\n writer.writeln();\n writer.writeln(\n `Press ${writer.wrapInColor('Ctrl+C', CliForegroundColor.Yellow)} to abort the test`,\n );\n }\n\n private async runSpeedTest(\n command: CliProcessCommand,\n context: ICliExecutionContext,\n ): Promise<void> {\n const downloadOnly = !!command.args['download-only'];\n const uploadOnly = !!command.args['upload-only'];\n\n const controller = new AbortController();\n const subscription = context.onAbort.subscribe(() => {\n controller.abort();\n });\n\n const results: { download?: number; upload?: number } = {};\n\n try {\n if (!uploadOnly) {\n const downloadUrl =\n (command.args['download-url'] as string) ||\n DEFAULT_DOWNLOAD_URL;\n results.download = await this.testDownload(\n downloadUrl,\n context,\n controller.signal,\n );\n }\n\n if (!downloadOnly) {\n const uploadUrl =\n (command.args['upload-url'] as string) ||\n DEFAULT_UPLOAD_URL;\n results.upload = await this.testUpload(\n uploadUrl,\n context,\n controller.signal,\n );\n }\n\n this.printSummary(results, context);\n } catch (error: any) {\n if (error.name === 'AbortError' || controller.signal.aborted) {\n context.writer.writeWarning('Speed test aborted');\n } else {\n context.writer.writeError(\n `Speed test failed: ${error.message || error}`,\n );\n }\n } finally {\n subscription.unsubscribe();\n }\n }\n\n private async testDownload(\n url: string,\n context: ICliExecutionContext,\n signal: AbortSignal,\n ): Promise<number> {\n context.writer.writeln();\n\n const progressBar = context.progressBar;\n const spinner = context.spinner;\n\n // Show spinner while waiting for connection (DNS, TLS, TTFB)\n spinner?.show('Connecting...');\n\n const response = await fetch(url, { signal });\n\n if (!response.ok) {\n spinner?.hide();\n throw new Error(`Download failed: HTTP ${response.status}`);\n }\n\n spinner?.hide();\n context.writer.writeInfo('Running download test...');\n\n const contentLength = parseInt(\n response.headers.get('content-length') || '0',\n 10,\n );\n\n progressBar.show();\n progressBar.update(0);\n\n // Start timing AFTER connection is established (exclude TTFB)\n const startTime = performance.now();\n let totalBytes = 0;\n\n const reader = response.body!.getReader();\n let lastUpdateTime = startTime;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n totalBytes += value.length;\n\n const now = performance.now();\n if (now - lastUpdateTime > 250) {\n lastUpdateTime = now;\n const elapsedSec = (now - startTime) / 1000;\n const avgSpeed = totalBytes / elapsedSec;\n\n if (contentLength > 0) {\n const pct = Math.min(\n (totalBytes / contentLength) * 100,\n 100,\n );\n progressBar.update(pct);\n progressBar.setText(\n `${formatSpeed(avgSpeed)} | ${formatBytes(totalBytes)} / ${formatBytes(contentLength)}`,\n );\n } else {\n progressBar.setText(\n `${formatSpeed(avgSpeed)} | ${formatBytes(totalBytes)}`,\n );\n }\n }\n }\n\n progressBar.complete();\n progressBar.hide();\n\n const totalSec = (performance.now() - startTime) / 1000;\n const avgSpeed = totalBytes / totalSec;\n\n context.writer.writeSuccess(\n `Download: ${formatSpeed(avgSpeed)} (${formatBytes(totalBytes)} in ${totalSec.toFixed(1)}s)`,\n );\n\n return avgSpeed;\n }\n\n private async testUpload(\n url: string,\n context: ICliExecutionContext,\n signal: AbortSignal,\n ): Promise<number> {\n context.writer.writeln();\n context.writer.writeInfo(\n `Running upload test (${formatBytes(UPLOAD_SIZE)})...`,\n );\n\n const progressBar = context.progressBar;\n progressBar.show();\n progressBar.update(0);\n\n // Fill with pseudo-random data to prevent compression from skewing results.\n // crypto.getRandomValues() is limited to 65536 bytes per call.\n const payload = new Uint8Array(UPLOAD_SIZE);\n const chunkSize = 65536;\n for (let offset = 0; offset < UPLOAD_SIZE; offset += chunkSize) {\n const length = Math.min(chunkSize, UPLOAD_SIZE - offset);\n crypto.getRandomValues(new Uint8Array(payload.buffer, offset, length));\n }\n\n // Use multiple smaller fetch() calls to measure upload speed.\n // A single large POST to Cloudflare's __up may be blocked by CORS\n // when custom headers trigger a preflight. Chunking also lets us\n // report progress without XHR.\n const CHUNK = 1024 * 1024; // 1 MB per request\n const totalChunks = Math.ceil(UPLOAD_SIZE / CHUNK);\n let uploaded = 0;\n const startTime = performance.now();\n\n for (let i = 0; i < totalChunks; i++) {\n if (signal.aborted) {\n throw new DOMException('Upload aborted', 'AbortError');\n }\n\n const start = i * CHUNK;\n const end = Math.min(start + CHUNK, UPLOAD_SIZE);\n const chunk = payload.slice(start, end);\n\n await fetch(url, {\n method: 'POST',\n body: chunk,\n signal,\n });\n\n uploaded += chunk.length;\n\n const elapsed = (performance.now() - startTime) / 1000;\n const speed = uploaded / elapsed;\n const pct = (uploaded / UPLOAD_SIZE) * 100;\n progressBar.update(pct);\n progressBar.setText(\n `${formatSpeed(speed)} | ${formatBytes(uploaded)} / ${formatBytes(UPLOAD_SIZE)}`,\n );\n }\n\n const totalSec = (performance.now() - startTime) / 1000;\n const avgSpeed = UPLOAD_SIZE / totalSec;\n\n progressBar.complete();\n progressBar.hide();\n\n context.writer.writeSuccess(\n `Upload: ${formatSpeed(avgSpeed)} (${formatBytes(UPLOAD_SIZE)} in ${totalSec.toFixed(1)}s)`,\n );\n\n return avgSpeed;\n }\n\n private printSummary(\n results: { download?: number; upload?: number },\n context: ICliExecutionContext,\n ): void {\n context.writer.writeln();\n\n const headers = ['Test', 'Speed'];\n const rows: string[][] = [];\n\n if (results.download !== undefined) {\n rows.push(['Download', formatSpeed(results.download)]);\n }\n if (results.upload !== undefined) {\n rows.push(['Upload', formatSpeed(results.upload)]);\n }\n\n context.writer.writeTable(headers, rows);\n context.process.output(results);\n }\n}\n","/*\n * Public API Surface of speed-test\n */\n\nexport * from './lib/processors/cli-speed-test-command-processor';\n\nimport { ICliModule } from '@qodalis/cli-core';\nimport { CliSpeedTestCommandProcessor } from './lib/processors/cli-speed-test-command-processor';\nimport { API_VERSION } from './lib/version';\n\nexport const speedTestModule: ICliModule = {\n apiVersion: API_VERSION,\n name: '@qodalis/cli-speed-test',\n processors: [new CliSpeedTestCommandProcessor()],\n translations: {\n es: { 'cli.speed-test.description': 'Ejecutar una prueba de velocidad de internet' },\n fr: { 'cli.speed-test.description': 'Lancer un test de débit internet' },\n de: { 'cli.speed-test.description': 'Internet-Geschwindigkeitstest durchführen' },\n pt: { 'cli.speed-test.description': 'Executar um teste de velocidade de internet' },\n it: { 'cli.speed-test.description': 'Eseguire un test di velocità internet' },\n ja: { 'cli.speed-test.description': 'インターネット速度テストを実行' },\n ko: { 'cli.speed-test.description': '인터넷 속도 테스트 실행' },\n zh: { 'cli.speed-test.description': '运行网络速度测试' },\n ru: { 'cli.speed-test.description': 'Запустить тест скорости интернета' },\n ro: { 'cli.speed-test.description': 'Rulează un test de viteză a internetului' },\n },\n};\n"]}
1
+ {"version":3,"sources":["../../packages/plugins/speed-test/src/lib/version.ts","../../packages/plugins/speed-test/src/lib/processors/cli-speed-test-command-processor.ts","../../packages/plugins/speed-test/src/public-api.ts"],"names":["DefaultLibraryAuthor","CliForegroundColor","avgSpeed"],"mappings":"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","file":"public-api.js","sourcesContent":["\n// Automatically generated during build\nexport const LIBRARY_VERSION = '2.0.0-beta.13';\nexport const API_VERSION = 2;\n ","import {\n CliForegroundColor,\n CliProcessorMetadata,\n DefaultLibraryAuthor,\n CliProcessCommand,\n ICliCommandProcessor,\n ICliExecutionContext,\n} from '@qodalis/cli-core';\nimport { LIBRARY_VERSION } from '../version';\n\n/**\n * Cloudflare speed test endpoints — edge-served, CORS-enabled, fast worldwide.\n * The `bytes` parameter controls the download size.\n */\nconst DEFAULT_DOWNLOAD_URL =\n 'https://speed.cloudflare.com/__down?bytes=26214400';\n\nconst DEFAULT_UPLOAD_URL = 'https://speed.cloudflare.com/__up';\n\n/** Size of upload test payload in bytes (10 MB). */\nconst UPLOAD_SIZE = 10 * 1024 * 1024;\n\n/** Format bytes/second as a human-readable speed string. */\nfunction formatSpeed(bytesPerSecond: number): string {\n const mbps = (bytesPerSecond * 8) / (1024 * 1024);\n if (mbps >= 1) return `${mbps.toFixed(2)} Mbps`;\n const kbps = (bytesPerSecond * 8) / 1024;\n if (kbps >= 1) return `${kbps.toFixed(1)} Kbps`;\n return `${(bytesPerSecond * 8).toFixed(0)} bps`;\n}\n\n/** Format bytes as a human-readable size string. */\nfunction formatBytes(bytes: number): string {\n if (bytes >= 1024 * 1024)\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${bytes} B`;\n}\n\nexport class CliSpeedTestCommandProcessor implements ICliCommandProcessor {\n command = 'speed-test';\n\n description = 'Run an internet speed test';\n\n author = DefaultLibraryAuthor;\n\n version = LIBRARY_VERSION;\n\n processors?: ICliCommandProcessor[] = [];\n\n parameters = [\n {\n name: 'download-url',\n description: 'Custom URL for the download test',\n type: 'string' as const,\n required: false,\n },\n {\n name: 'upload-url',\n description: 'Custom URL for the upload test',\n type: 'string' as const,\n required: false,\n },\n {\n name: 'download-only',\n description: 'Only run the download test',\n type: 'boolean' as const,\n required: false,\n },\n {\n name: 'upload-only',\n description: 'Only run the upload test',\n type: 'boolean' as const,\n required: false,\n },\n ];\n\n metadata?: CliProcessorMetadata = {\n icon: '🚀',\n requiredCoreVersion: '>=2.0.0 <3.0.0',\n requiredCliVersion: '>=2.0.0 <3.0.0',\n };\n\n constructor() {\n this.processors = [\n {\n command: 'run',\n description: 'Run the internet speed test',\n processCommand: async (command, context) => {\n await this.runSpeedTest(command, context);\n },\n },\n ];\n }\n\n async processCommand(\n command: CliProcessCommand,\n context: ICliExecutionContext,\n ): Promise<void> {\n await this.runSpeedTest(command, context);\n }\n\n writeDescription(context: ICliExecutionContext): void {\n const { writer } = context;\n writer.writeln(this.description);\n writer.writeln();\n writer.writeln('Usage:');\n writer.writeln(\n ` ${writer.wrapInColor('speed-test', CliForegroundColor.Cyan)} Run download & upload test`,\n );\n writer.writeln(\n ` ${writer.wrapInColor('speed-test --download-only', CliForegroundColor.Cyan)} Download only`,\n );\n writer.writeln(\n ` ${writer.wrapInColor('speed-test --upload-only', CliForegroundColor.Cyan)} Upload only`,\n );\n writer.writeln(\n ` ${writer.wrapInColor('speed-test --download-url=<url>', CliForegroundColor.Cyan)} Custom download URL`,\n );\n writer.writeln(\n ` ${writer.wrapInColor('speed-test --upload-url=<url>', CliForegroundColor.Cyan)} Custom upload URL`,\n );\n writer.writeln();\n writer.writeln(\n `Press ${writer.wrapInColor('Ctrl+C', CliForegroundColor.Yellow)} to abort the test`,\n );\n }\n\n private async runSpeedTest(\n command: CliProcessCommand,\n context: ICliExecutionContext,\n ): Promise<void> {\n const downloadOnly = !!command.args['download-only'];\n const uploadOnly = !!command.args['upload-only'];\n\n const results: { download?: number; upload?: number } = {};\n\n try {\n if (!uploadOnly) {\n const downloadUrl =\n (command.args['download-url'] as string) ||\n DEFAULT_DOWNLOAD_URL;\n context.notifier.info('Speed test: download');\n results.download = await this.testDownload(\n downloadUrl,\n context,\n );\n }\n\n if (!downloadOnly) {\n const uploadUrl =\n (command.args['upload-url'] as string) ||\n DEFAULT_UPLOAD_URL;\n context.notifier.info('Speed test: upload');\n results.upload = await this.testUpload(\n uploadUrl,\n context,\n );\n }\n\n this.printSummary(results, context);\n } catch (error: any) {\n if (error.name === 'AbortError' || context.signal?.aborted) {\n context.writer.writeWarning('Speed test aborted');\n } else {\n context.writer.writeError(\n `Speed test failed: ${error.message || error}`,\n );\n }\n }\n }\n\n private async testDownload(\n url: string,\n context: ICliExecutionContext,\n ): Promise<number> {\n context.writer.writeln();\n\n const progressBar = context.progressBar;\n const spinner = context.spinner;\n\n // Show spinner while waiting for connection (DNS, TLS, TTFB)\n spinner?.show('Connecting...');\n\n const response = await context.http.fetch(url);\n\n if (!response.ok) {\n spinner?.hide();\n throw new Error(`Download failed: HTTP ${response.status}`);\n }\n\n spinner?.hide();\n context.writer.writeInfo('Running download test...');\n\n const contentLength = parseInt(\n response.headers.get('content-length') || '0',\n 10,\n );\n\n progressBar.show();\n progressBar.update(0);\n\n // Start timing AFTER connection is established (exclude TTFB)\n const startTime = performance.now();\n let totalBytes = 0;\n\n const reader = response.body!.getReader();\n let lastUpdateTime = startTime;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n totalBytes += value.length;\n\n const now = performance.now();\n if (now - lastUpdateTime > 250) {\n lastUpdateTime = now;\n const elapsedSec = (now - startTime) / 1000;\n const avgSpeed = totalBytes / elapsedSec;\n\n if (contentLength > 0) {\n const pct = Math.min(\n (totalBytes / contentLength) * 100,\n 100,\n );\n progressBar.update(pct);\n progressBar.setText(\n `${formatSpeed(avgSpeed)} | ${formatBytes(totalBytes)} / ${formatBytes(contentLength)}`,\n );\n context.notifier.info(`Download: ${Math.round(pct)}% ${formatSpeed(avgSpeed)}`);\n } else {\n progressBar.setText(\n `${formatSpeed(avgSpeed)} | ${formatBytes(totalBytes)}`,\n );\n context.notifier.info(`Download: ${formatSpeed(avgSpeed)}`);\n }\n }\n }\n\n progressBar.complete();\n progressBar.hide();\n\n const totalSec = (performance.now() - startTime) / 1000;\n const avgSpeed = totalBytes / totalSec;\n\n context.writer.writeSuccess(\n `Download: ${formatSpeed(avgSpeed)} (${formatBytes(totalBytes)} in ${totalSec.toFixed(1)}s)`,\n );\n\n return avgSpeed;\n }\n\n private async testUpload(\n url: string,\n context: ICliExecutionContext,\n ): Promise<number> {\n context.writer.writeln();\n context.writer.writeInfo(\n `Running upload test (${formatBytes(UPLOAD_SIZE)})...`,\n );\n\n const progressBar = context.progressBar;\n progressBar.show();\n progressBar.update(0);\n\n // Fill with pseudo-random data to prevent compression from skewing results.\n // crypto.getRandomValues() is limited to 65536 bytes per call.\n const payload = new Uint8Array(UPLOAD_SIZE);\n const chunkSize = 65536;\n for (let offset = 0; offset < UPLOAD_SIZE; offset += chunkSize) {\n const length = Math.min(chunkSize, UPLOAD_SIZE - offset);\n crypto.getRandomValues(new Uint8Array(payload.buffer, offset, length));\n }\n\n // Use multiple smaller fetch() calls to measure upload speed.\n // A single large POST to Cloudflare's __up may be blocked by CORS\n // when custom headers trigger a preflight. Chunking also lets us\n // report progress without XHR.\n const CHUNK = 1024 * 1024; // 1 MB per request\n const totalChunks = Math.ceil(UPLOAD_SIZE / CHUNK);\n let uploaded = 0;\n const startTime = performance.now();\n\n for (let i = 0; i < totalChunks; i++) {\n if (context.signal?.aborted) {\n throw new DOMException('Upload aborted', 'AbortError');\n }\n\n const start = i * CHUNK;\n const end = Math.min(start + CHUNK, UPLOAD_SIZE);\n const chunk = payload.slice(start, end);\n\n await context.http.fetch(url, {\n method: 'POST',\n body: chunk,\n });\n\n uploaded += chunk.length;\n\n const elapsed = (performance.now() - startTime) / 1000;\n const speed = uploaded / elapsed;\n const pct = (uploaded / UPLOAD_SIZE) * 100;\n progressBar.update(pct);\n progressBar.setText(\n `${formatSpeed(speed)} | ${formatBytes(uploaded)} / ${formatBytes(UPLOAD_SIZE)}`,\n );\n context.notifier.info(`Upload: ${Math.round(pct)}% ${formatSpeed(speed)}`);\n }\n\n const totalSec = (performance.now() - startTime) / 1000;\n const avgSpeed = UPLOAD_SIZE / totalSec;\n\n progressBar.complete();\n progressBar.hide();\n\n context.writer.writeSuccess(\n `Upload: ${formatSpeed(avgSpeed)} (${formatBytes(UPLOAD_SIZE)} in ${totalSec.toFixed(1)}s)`,\n );\n\n return avgSpeed;\n }\n\n private printSummary(\n results: { download?: number; upload?: number },\n context: ICliExecutionContext,\n ): void {\n context.writer.writeln();\n\n const headers = ['Test', 'Speed'];\n const rows: string[][] = [];\n\n if (results.download !== undefined) {\n rows.push(['Download', formatSpeed(results.download)]);\n }\n if (results.upload !== undefined) {\n rows.push(['Upload', formatSpeed(results.upload)]);\n }\n\n context.writer.writeTable(headers, rows);\n context.process.output(results);\n }\n}\n","/*\n * Public API Surface of speed-test\n */\n\nexport * from './lib/processors/cli-speed-test-command-processor';\n\nimport { ICliModule } from '@qodalis/cli-core';\nimport { CliSpeedTestCommandProcessor } from './lib/processors/cli-speed-test-command-processor';\nimport { API_VERSION } from './lib/version';\n\nexport const speedTestModule: ICliModule = {\n apiVersion: API_VERSION,\n name: '@qodalis/cli-speed-test',\n processors: [new CliSpeedTestCommandProcessor()],\n translations: {\n es: { 'cli.speed-test.description': 'Ejecutar una prueba de velocidad de internet' },\n fr: { 'cli.speed-test.description': 'Lancer un test de débit internet' },\n de: { 'cli.speed-test.description': 'Internet-Geschwindigkeitstest durchführen' },\n pt: { 'cli.speed-test.description': 'Executar um teste de velocidade de internet' },\n it: { 'cli.speed-test.description': 'Eseguire un test di velocità internet' },\n ja: { 'cli.speed-test.description': 'インターネット速度テストを実行' },\n ko: { 'cli.speed-test.description': '인터넷 속도 테스트 실행' },\n zh: { 'cli.speed-test.description': '运行网络速度测试' },\n ru: { 'cli.speed-test.description': 'Запустить тест скорости интернета' },\n ro: { 'cli.speed-test.description': 'Rulează un test de viteză a internetului' },\n },\n};\n"]}
package/public-api.mjs CHANGED
@@ -3,7 +3,7 @@ import { DefaultLibraryAuthor, CliForegroundColor } from '@qodalis/cli-core';
3
3
  // src/lib/processors/cli-speed-test-command-processor.ts
4
4
 
5
5
  // src/lib/version.ts
6
- var LIBRARY_VERSION = "2.0.0-beta.11";
6
+ var LIBRARY_VERSION = "2.0.0-beta.13";
7
7
  var API_VERSION = 2;
8
8
 
9
9
  // src/lib/processors/cli-speed-test-command-processor.ts
@@ -102,47 +102,41 @@ var CliSpeedTestCommandProcessor = class {
102
102
  async runSpeedTest(command, context) {
103
103
  const downloadOnly = !!command.args["download-only"];
104
104
  const uploadOnly = !!command.args["upload-only"];
105
- const controller = new AbortController();
106
- const subscription = context.onAbort.subscribe(() => {
107
- controller.abort();
108
- });
109
105
  const results = {};
110
106
  try {
111
107
  if (!uploadOnly) {
112
108
  const downloadUrl = command.args["download-url"] || DEFAULT_DOWNLOAD_URL;
109
+ context.notifier.info("Speed test: download");
113
110
  results.download = await this.testDownload(
114
111
  downloadUrl,
115
- context,
116
- controller.signal
112
+ context
117
113
  );
118
114
  }
119
115
  if (!downloadOnly) {
120
116
  const uploadUrl = command.args["upload-url"] || DEFAULT_UPLOAD_URL;
117
+ context.notifier.info("Speed test: upload");
121
118
  results.upload = await this.testUpload(
122
119
  uploadUrl,
123
- context,
124
- controller.signal
120
+ context
125
121
  );
126
122
  }
127
123
  this.printSummary(results, context);
128
124
  } catch (error) {
129
- if (error.name === "AbortError" || controller.signal.aborted) {
125
+ if (error.name === "AbortError" || context.signal?.aborted) {
130
126
  context.writer.writeWarning("Speed test aborted");
131
127
  } else {
132
128
  context.writer.writeError(
133
129
  `Speed test failed: ${error.message || error}`
134
130
  );
135
131
  }
136
- } finally {
137
- subscription.unsubscribe();
138
132
  }
139
133
  }
140
- async testDownload(url, context, signal) {
134
+ async testDownload(url, context) {
141
135
  context.writer.writeln();
142
136
  const progressBar = context.progressBar;
143
137
  const spinner = context.spinner;
144
138
  spinner?.show("Connecting...");
145
- const response = await fetch(url, { signal });
139
+ const response = await context.http.fetch(url);
146
140
  if (!response.ok) {
147
141
  spinner?.hide();
148
142
  throw new Error(`Download failed: HTTP ${response.status}`);
@@ -177,10 +171,12 @@ var CliSpeedTestCommandProcessor = class {
177
171
  progressBar.setText(
178
172
  `${formatSpeed(avgSpeed2)} | ${formatBytes(totalBytes)} / ${formatBytes(contentLength)}`
179
173
  );
174
+ context.notifier.info(`Download: ${Math.round(pct)}% ${formatSpeed(avgSpeed2)}`);
180
175
  } else {
181
176
  progressBar.setText(
182
177
  `${formatSpeed(avgSpeed2)} | ${formatBytes(totalBytes)}`
183
178
  );
179
+ context.notifier.info(`Download: ${formatSpeed(avgSpeed2)}`);
184
180
  }
185
181
  }
186
182
  }
@@ -193,7 +189,7 @@ var CliSpeedTestCommandProcessor = class {
193
189
  );
194
190
  return avgSpeed;
195
191
  }
196
- async testUpload(url, context, signal) {
192
+ async testUpload(url, context) {
197
193
  context.writer.writeln();
198
194
  context.writer.writeInfo(
199
195
  `Running upload test (${formatBytes(UPLOAD_SIZE)})...`
@@ -212,16 +208,15 @@ var CliSpeedTestCommandProcessor = class {
212
208
  let uploaded = 0;
213
209
  const startTime = performance.now();
214
210
  for (let i = 0; i < totalChunks; i++) {
215
- if (signal.aborted) {
211
+ if (context.signal?.aborted) {
216
212
  throw new DOMException("Upload aborted", "AbortError");
217
213
  }
218
214
  const start = i * CHUNK;
219
215
  const end = Math.min(start + CHUNK, UPLOAD_SIZE);
220
216
  const chunk = payload.slice(start, end);
221
- await fetch(url, {
217
+ await context.http.fetch(url, {
222
218
  method: "POST",
223
- body: chunk,
224
- signal
219
+ body: chunk
225
220
  });
226
221
  uploaded += chunk.length;
227
222
  const elapsed = (performance.now() - startTime) / 1e3;
@@ -231,6 +226,7 @@ var CliSpeedTestCommandProcessor = class {
231
226
  progressBar.setText(
232
227
  `${formatSpeed(speed)} | ${formatBytes(uploaded)} / ${formatBytes(UPLOAD_SIZE)}`
233
228
  );
229
+ context.notifier.info(`Upload: ${Math.round(pct)}% ${formatSpeed(speed)}`);
234
230
  }
235
231
  const totalSec = (performance.now() - startTime) / 1e3;
236
232
  const avgSpeed = UPLOAD_SIZE / totalSec;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../packages/plugins/speed-test/src/lib/version.ts","../../packages/plugins/speed-test/src/lib/processors/cli-speed-test-command-processor.ts","../../packages/plugins/speed-test/src/public-api.ts"],"names":["avgSpeed"],"mappings":"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","file":"public-api.mjs","sourcesContent":["\n// Automatically generated during build\nexport const LIBRARY_VERSION = '2.0.0-beta.11';\nexport const API_VERSION = 2;\n ","import {\n CliForegroundColor,\n CliProcessorMetadata,\n DefaultLibraryAuthor,\n CliProcessCommand,\n ICliCommandProcessor,\n ICliExecutionContext,\n} from '@qodalis/cli-core';\nimport { LIBRARY_VERSION } from '../version';\n\n/**\n * Cloudflare speed test endpoints — edge-served, CORS-enabled, fast worldwide.\n * The `bytes` parameter controls the download size.\n */\nconst DEFAULT_DOWNLOAD_URL =\n 'https://speed.cloudflare.com/__down?bytes=26214400';\n\nconst DEFAULT_UPLOAD_URL = 'https://speed.cloudflare.com/__up';\n\n/** Size of upload test payload in bytes (10 MB). */\nconst UPLOAD_SIZE = 10 * 1024 * 1024;\n\n/** Format bytes/second as a human-readable speed string. */\nfunction formatSpeed(bytesPerSecond: number): string {\n const mbps = (bytesPerSecond * 8) / (1024 * 1024);\n if (mbps >= 1) return `${mbps.toFixed(2)} Mbps`;\n const kbps = (bytesPerSecond * 8) / 1024;\n if (kbps >= 1) return `${kbps.toFixed(1)} Kbps`;\n return `${(bytesPerSecond * 8).toFixed(0)} bps`;\n}\n\n/** Format bytes as a human-readable size string. */\nfunction formatBytes(bytes: number): string {\n if (bytes >= 1024 * 1024)\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${bytes} B`;\n}\n\nexport class CliSpeedTestCommandProcessor implements ICliCommandProcessor {\n command = 'speed-test';\n\n description = 'Run an internet speed test';\n\n author = DefaultLibraryAuthor;\n\n version = LIBRARY_VERSION;\n\n processors?: ICliCommandProcessor[] = [];\n\n parameters = [\n {\n name: 'download-url',\n description: 'Custom URL for the download test',\n type: 'string' as const,\n required: false,\n },\n {\n name: 'upload-url',\n description: 'Custom URL for the upload test',\n type: 'string' as const,\n required: false,\n },\n {\n name: 'download-only',\n description: 'Only run the download test',\n type: 'boolean' as const,\n required: false,\n },\n {\n name: 'upload-only',\n description: 'Only run the upload test',\n type: 'boolean' as const,\n required: false,\n },\n ];\n\n metadata?: CliProcessorMetadata = {\n icon: '🚀',\n requiredCoreVersion: '>=2.0.0 <3.0.0',\n requiredCliVersion: '>=2.0.0 <3.0.0',\n };\n\n constructor() {\n this.processors = [\n {\n command: 'run',\n description: 'Run the internet speed test',\n processCommand: async (command, context) => {\n await this.runSpeedTest(command, context);\n },\n },\n ];\n }\n\n async processCommand(\n command: CliProcessCommand,\n context: ICliExecutionContext,\n ): Promise<void> {\n await this.runSpeedTest(command, context);\n }\n\n writeDescription(context: ICliExecutionContext): void {\n const { writer } = context;\n writer.writeln(this.description);\n writer.writeln();\n writer.writeln('Usage:');\n writer.writeln(\n ` ${writer.wrapInColor('speed-test', CliForegroundColor.Cyan)} Run download & upload test`,\n );\n writer.writeln(\n ` ${writer.wrapInColor('speed-test --download-only', CliForegroundColor.Cyan)} Download only`,\n );\n writer.writeln(\n ` ${writer.wrapInColor('speed-test --upload-only', CliForegroundColor.Cyan)} Upload only`,\n );\n writer.writeln(\n ` ${writer.wrapInColor('speed-test --download-url=<url>', CliForegroundColor.Cyan)} Custom download URL`,\n );\n writer.writeln(\n ` ${writer.wrapInColor('speed-test --upload-url=<url>', CliForegroundColor.Cyan)} Custom upload URL`,\n );\n writer.writeln();\n writer.writeln(\n `Press ${writer.wrapInColor('Ctrl+C', CliForegroundColor.Yellow)} to abort the test`,\n );\n }\n\n private async runSpeedTest(\n command: CliProcessCommand,\n context: ICliExecutionContext,\n ): Promise<void> {\n const downloadOnly = !!command.args['download-only'];\n const uploadOnly = !!command.args['upload-only'];\n\n const controller = new AbortController();\n const subscription = context.onAbort.subscribe(() => {\n controller.abort();\n });\n\n const results: { download?: number; upload?: number } = {};\n\n try {\n if (!uploadOnly) {\n const downloadUrl =\n (command.args['download-url'] as string) ||\n DEFAULT_DOWNLOAD_URL;\n results.download = await this.testDownload(\n downloadUrl,\n context,\n controller.signal,\n );\n }\n\n if (!downloadOnly) {\n const uploadUrl =\n (command.args['upload-url'] as string) ||\n DEFAULT_UPLOAD_URL;\n results.upload = await this.testUpload(\n uploadUrl,\n context,\n controller.signal,\n );\n }\n\n this.printSummary(results, context);\n } catch (error: any) {\n if (error.name === 'AbortError' || controller.signal.aborted) {\n context.writer.writeWarning('Speed test aborted');\n } else {\n context.writer.writeError(\n `Speed test failed: ${error.message || error}`,\n );\n }\n } finally {\n subscription.unsubscribe();\n }\n }\n\n private async testDownload(\n url: string,\n context: ICliExecutionContext,\n signal: AbortSignal,\n ): Promise<number> {\n context.writer.writeln();\n\n const progressBar = context.progressBar;\n const spinner = context.spinner;\n\n // Show spinner while waiting for connection (DNS, TLS, TTFB)\n spinner?.show('Connecting...');\n\n const response = await fetch(url, { signal });\n\n if (!response.ok) {\n spinner?.hide();\n throw new Error(`Download failed: HTTP ${response.status}`);\n }\n\n spinner?.hide();\n context.writer.writeInfo('Running download test...');\n\n const contentLength = parseInt(\n response.headers.get('content-length') || '0',\n 10,\n );\n\n progressBar.show();\n progressBar.update(0);\n\n // Start timing AFTER connection is established (exclude TTFB)\n const startTime = performance.now();\n let totalBytes = 0;\n\n const reader = response.body!.getReader();\n let lastUpdateTime = startTime;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n totalBytes += value.length;\n\n const now = performance.now();\n if (now - lastUpdateTime > 250) {\n lastUpdateTime = now;\n const elapsedSec = (now - startTime) / 1000;\n const avgSpeed = totalBytes / elapsedSec;\n\n if (contentLength > 0) {\n const pct = Math.min(\n (totalBytes / contentLength) * 100,\n 100,\n );\n progressBar.update(pct);\n progressBar.setText(\n `${formatSpeed(avgSpeed)} | ${formatBytes(totalBytes)} / ${formatBytes(contentLength)}`,\n );\n } else {\n progressBar.setText(\n `${formatSpeed(avgSpeed)} | ${formatBytes(totalBytes)}`,\n );\n }\n }\n }\n\n progressBar.complete();\n progressBar.hide();\n\n const totalSec = (performance.now() - startTime) / 1000;\n const avgSpeed = totalBytes / totalSec;\n\n context.writer.writeSuccess(\n `Download: ${formatSpeed(avgSpeed)} (${formatBytes(totalBytes)} in ${totalSec.toFixed(1)}s)`,\n );\n\n return avgSpeed;\n }\n\n private async testUpload(\n url: string,\n context: ICliExecutionContext,\n signal: AbortSignal,\n ): Promise<number> {\n context.writer.writeln();\n context.writer.writeInfo(\n `Running upload test (${formatBytes(UPLOAD_SIZE)})...`,\n );\n\n const progressBar = context.progressBar;\n progressBar.show();\n progressBar.update(0);\n\n // Fill with pseudo-random data to prevent compression from skewing results.\n // crypto.getRandomValues() is limited to 65536 bytes per call.\n const payload = new Uint8Array(UPLOAD_SIZE);\n const chunkSize = 65536;\n for (let offset = 0; offset < UPLOAD_SIZE; offset += chunkSize) {\n const length = Math.min(chunkSize, UPLOAD_SIZE - offset);\n crypto.getRandomValues(new Uint8Array(payload.buffer, offset, length));\n }\n\n // Use multiple smaller fetch() calls to measure upload speed.\n // A single large POST to Cloudflare's __up may be blocked by CORS\n // when custom headers trigger a preflight. Chunking also lets us\n // report progress without XHR.\n const CHUNK = 1024 * 1024; // 1 MB per request\n const totalChunks = Math.ceil(UPLOAD_SIZE / CHUNK);\n let uploaded = 0;\n const startTime = performance.now();\n\n for (let i = 0; i < totalChunks; i++) {\n if (signal.aborted) {\n throw new DOMException('Upload aborted', 'AbortError');\n }\n\n const start = i * CHUNK;\n const end = Math.min(start + CHUNK, UPLOAD_SIZE);\n const chunk = payload.slice(start, end);\n\n await fetch(url, {\n method: 'POST',\n body: chunk,\n signal,\n });\n\n uploaded += chunk.length;\n\n const elapsed = (performance.now() - startTime) / 1000;\n const speed = uploaded / elapsed;\n const pct = (uploaded / UPLOAD_SIZE) * 100;\n progressBar.update(pct);\n progressBar.setText(\n `${formatSpeed(speed)} | ${formatBytes(uploaded)} / ${formatBytes(UPLOAD_SIZE)}`,\n );\n }\n\n const totalSec = (performance.now() - startTime) / 1000;\n const avgSpeed = UPLOAD_SIZE / totalSec;\n\n progressBar.complete();\n progressBar.hide();\n\n context.writer.writeSuccess(\n `Upload: ${formatSpeed(avgSpeed)} (${formatBytes(UPLOAD_SIZE)} in ${totalSec.toFixed(1)}s)`,\n );\n\n return avgSpeed;\n }\n\n private printSummary(\n results: { download?: number; upload?: number },\n context: ICliExecutionContext,\n ): void {\n context.writer.writeln();\n\n const headers = ['Test', 'Speed'];\n const rows: string[][] = [];\n\n if (results.download !== undefined) {\n rows.push(['Download', formatSpeed(results.download)]);\n }\n if (results.upload !== undefined) {\n rows.push(['Upload', formatSpeed(results.upload)]);\n }\n\n context.writer.writeTable(headers, rows);\n context.process.output(results);\n }\n}\n","/*\n * Public API Surface of speed-test\n */\n\nexport * from './lib/processors/cli-speed-test-command-processor';\n\nimport { ICliModule } from '@qodalis/cli-core';\nimport { CliSpeedTestCommandProcessor } from './lib/processors/cli-speed-test-command-processor';\nimport { API_VERSION } from './lib/version';\n\nexport const speedTestModule: ICliModule = {\n apiVersion: API_VERSION,\n name: '@qodalis/cli-speed-test',\n processors: [new CliSpeedTestCommandProcessor()],\n translations: {\n es: { 'cli.speed-test.description': 'Ejecutar una prueba de velocidad de internet' },\n fr: { 'cli.speed-test.description': 'Lancer un test de débit internet' },\n de: { 'cli.speed-test.description': 'Internet-Geschwindigkeitstest durchführen' },\n pt: { 'cli.speed-test.description': 'Executar um teste de velocidade de internet' },\n it: { 'cli.speed-test.description': 'Eseguire un test di velocità internet' },\n ja: { 'cli.speed-test.description': 'インターネット速度テストを実行' },\n ko: { 'cli.speed-test.description': '인터넷 속도 테스트 실행' },\n zh: { 'cli.speed-test.description': '运行网络速度测试' },\n ru: { 'cli.speed-test.description': 'Запустить тест скорости интернета' },\n ro: { 'cli.speed-test.description': 'Rulează un test de viteză a internetului' },\n },\n};\n"]}
1
+ {"version":3,"sources":["../../packages/plugins/speed-test/src/lib/version.ts","../../packages/plugins/speed-test/src/lib/processors/cli-speed-test-command-processor.ts","../../packages/plugins/speed-test/src/public-api.ts"],"names":["avgSpeed"],"mappings":"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","file":"public-api.mjs","sourcesContent":["\n// Automatically generated during build\nexport const LIBRARY_VERSION = '2.0.0-beta.13';\nexport const API_VERSION = 2;\n ","import {\n CliForegroundColor,\n CliProcessorMetadata,\n DefaultLibraryAuthor,\n CliProcessCommand,\n ICliCommandProcessor,\n ICliExecutionContext,\n} from '@qodalis/cli-core';\nimport { LIBRARY_VERSION } from '../version';\n\n/**\n * Cloudflare speed test endpoints — edge-served, CORS-enabled, fast worldwide.\n * The `bytes` parameter controls the download size.\n */\nconst DEFAULT_DOWNLOAD_URL =\n 'https://speed.cloudflare.com/__down?bytes=26214400';\n\nconst DEFAULT_UPLOAD_URL = 'https://speed.cloudflare.com/__up';\n\n/** Size of upload test payload in bytes (10 MB). */\nconst UPLOAD_SIZE = 10 * 1024 * 1024;\n\n/** Format bytes/second as a human-readable speed string. */\nfunction formatSpeed(bytesPerSecond: number): string {\n const mbps = (bytesPerSecond * 8) / (1024 * 1024);\n if (mbps >= 1) return `${mbps.toFixed(2)} Mbps`;\n const kbps = (bytesPerSecond * 8) / 1024;\n if (kbps >= 1) return `${kbps.toFixed(1)} Kbps`;\n return `${(bytesPerSecond * 8).toFixed(0)} bps`;\n}\n\n/** Format bytes as a human-readable size string. */\nfunction formatBytes(bytes: number): string {\n if (bytes >= 1024 * 1024)\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${bytes} B`;\n}\n\nexport class CliSpeedTestCommandProcessor implements ICliCommandProcessor {\n command = 'speed-test';\n\n description = 'Run an internet speed test';\n\n author = DefaultLibraryAuthor;\n\n version = LIBRARY_VERSION;\n\n processors?: ICliCommandProcessor[] = [];\n\n parameters = [\n {\n name: 'download-url',\n description: 'Custom URL for the download test',\n type: 'string' as const,\n required: false,\n },\n {\n name: 'upload-url',\n description: 'Custom URL for the upload test',\n type: 'string' as const,\n required: false,\n },\n {\n name: 'download-only',\n description: 'Only run the download test',\n type: 'boolean' as const,\n required: false,\n },\n {\n name: 'upload-only',\n description: 'Only run the upload test',\n type: 'boolean' as const,\n required: false,\n },\n ];\n\n metadata?: CliProcessorMetadata = {\n icon: '🚀',\n requiredCoreVersion: '>=2.0.0 <3.0.0',\n requiredCliVersion: '>=2.0.0 <3.0.0',\n };\n\n constructor() {\n this.processors = [\n {\n command: 'run',\n description: 'Run the internet speed test',\n processCommand: async (command, context) => {\n await this.runSpeedTest(command, context);\n },\n },\n ];\n }\n\n async processCommand(\n command: CliProcessCommand,\n context: ICliExecutionContext,\n ): Promise<void> {\n await this.runSpeedTest(command, context);\n }\n\n writeDescription(context: ICliExecutionContext): void {\n const { writer } = context;\n writer.writeln(this.description);\n writer.writeln();\n writer.writeln('Usage:');\n writer.writeln(\n ` ${writer.wrapInColor('speed-test', CliForegroundColor.Cyan)} Run download & upload test`,\n );\n writer.writeln(\n ` ${writer.wrapInColor('speed-test --download-only', CliForegroundColor.Cyan)} Download only`,\n );\n writer.writeln(\n ` ${writer.wrapInColor('speed-test --upload-only', CliForegroundColor.Cyan)} Upload only`,\n );\n writer.writeln(\n ` ${writer.wrapInColor('speed-test --download-url=<url>', CliForegroundColor.Cyan)} Custom download URL`,\n );\n writer.writeln(\n ` ${writer.wrapInColor('speed-test --upload-url=<url>', CliForegroundColor.Cyan)} Custom upload URL`,\n );\n writer.writeln();\n writer.writeln(\n `Press ${writer.wrapInColor('Ctrl+C', CliForegroundColor.Yellow)} to abort the test`,\n );\n }\n\n private async runSpeedTest(\n command: CliProcessCommand,\n context: ICliExecutionContext,\n ): Promise<void> {\n const downloadOnly = !!command.args['download-only'];\n const uploadOnly = !!command.args['upload-only'];\n\n const results: { download?: number; upload?: number } = {};\n\n try {\n if (!uploadOnly) {\n const downloadUrl =\n (command.args['download-url'] as string) ||\n DEFAULT_DOWNLOAD_URL;\n context.notifier.info('Speed test: download');\n results.download = await this.testDownload(\n downloadUrl,\n context,\n );\n }\n\n if (!downloadOnly) {\n const uploadUrl =\n (command.args['upload-url'] as string) ||\n DEFAULT_UPLOAD_URL;\n context.notifier.info('Speed test: upload');\n results.upload = await this.testUpload(\n uploadUrl,\n context,\n );\n }\n\n this.printSummary(results, context);\n } catch (error: any) {\n if (error.name === 'AbortError' || context.signal?.aborted) {\n context.writer.writeWarning('Speed test aborted');\n } else {\n context.writer.writeError(\n `Speed test failed: ${error.message || error}`,\n );\n }\n }\n }\n\n private async testDownload(\n url: string,\n context: ICliExecutionContext,\n ): Promise<number> {\n context.writer.writeln();\n\n const progressBar = context.progressBar;\n const spinner = context.spinner;\n\n // Show spinner while waiting for connection (DNS, TLS, TTFB)\n spinner?.show('Connecting...');\n\n const response = await context.http.fetch(url);\n\n if (!response.ok) {\n spinner?.hide();\n throw new Error(`Download failed: HTTP ${response.status}`);\n }\n\n spinner?.hide();\n context.writer.writeInfo('Running download test...');\n\n const contentLength = parseInt(\n response.headers.get('content-length') || '0',\n 10,\n );\n\n progressBar.show();\n progressBar.update(0);\n\n // Start timing AFTER connection is established (exclude TTFB)\n const startTime = performance.now();\n let totalBytes = 0;\n\n const reader = response.body!.getReader();\n let lastUpdateTime = startTime;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n totalBytes += value.length;\n\n const now = performance.now();\n if (now - lastUpdateTime > 250) {\n lastUpdateTime = now;\n const elapsedSec = (now - startTime) / 1000;\n const avgSpeed = totalBytes / elapsedSec;\n\n if (contentLength > 0) {\n const pct = Math.min(\n (totalBytes / contentLength) * 100,\n 100,\n );\n progressBar.update(pct);\n progressBar.setText(\n `${formatSpeed(avgSpeed)} | ${formatBytes(totalBytes)} / ${formatBytes(contentLength)}`,\n );\n context.notifier.info(`Download: ${Math.round(pct)}% ${formatSpeed(avgSpeed)}`);\n } else {\n progressBar.setText(\n `${formatSpeed(avgSpeed)} | ${formatBytes(totalBytes)}`,\n );\n context.notifier.info(`Download: ${formatSpeed(avgSpeed)}`);\n }\n }\n }\n\n progressBar.complete();\n progressBar.hide();\n\n const totalSec = (performance.now() - startTime) / 1000;\n const avgSpeed = totalBytes / totalSec;\n\n context.writer.writeSuccess(\n `Download: ${formatSpeed(avgSpeed)} (${formatBytes(totalBytes)} in ${totalSec.toFixed(1)}s)`,\n );\n\n return avgSpeed;\n }\n\n private async testUpload(\n url: string,\n context: ICliExecutionContext,\n ): Promise<number> {\n context.writer.writeln();\n context.writer.writeInfo(\n `Running upload test (${formatBytes(UPLOAD_SIZE)})...`,\n );\n\n const progressBar = context.progressBar;\n progressBar.show();\n progressBar.update(0);\n\n // Fill with pseudo-random data to prevent compression from skewing results.\n // crypto.getRandomValues() is limited to 65536 bytes per call.\n const payload = new Uint8Array(UPLOAD_SIZE);\n const chunkSize = 65536;\n for (let offset = 0; offset < UPLOAD_SIZE; offset += chunkSize) {\n const length = Math.min(chunkSize, UPLOAD_SIZE - offset);\n crypto.getRandomValues(new Uint8Array(payload.buffer, offset, length));\n }\n\n // Use multiple smaller fetch() calls to measure upload speed.\n // A single large POST to Cloudflare's __up may be blocked by CORS\n // when custom headers trigger a preflight. Chunking also lets us\n // report progress without XHR.\n const CHUNK = 1024 * 1024; // 1 MB per request\n const totalChunks = Math.ceil(UPLOAD_SIZE / CHUNK);\n let uploaded = 0;\n const startTime = performance.now();\n\n for (let i = 0; i < totalChunks; i++) {\n if (context.signal?.aborted) {\n throw new DOMException('Upload aborted', 'AbortError');\n }\n\n const start = i * CHUNK;\n const end = Math.min(start + CHUNK, UPLOAD_SIZE);\n const chunk = payload.slice(start, end);\n\n await context.http.fetch(url, {\n method: 'POST',\n body: chunk,\n });\n\n uploaded += chunk.length;\n\n const elapsed = (performance.now() - startTime) / 1000;\n const speed = uploaded / elapsed;\n const pct = (uploaded / UPLOAD_SIZE) * 100;\n progressBar.update(pct);\n progressBar.setText(\n `${formatSpeed(speed)} | ${formatBytes(uploaded)} / ${formatBytes(UPLOAD_SIZE)}`,\n );\n context.notifier.info(`Upload: ${Math.round(pct)}% ${formatSpeed(speed)}`);\n }\n\n const totalSec = (performance.now() - startTime) / 1000;\n const avgSpeed = UPLOAD_SIZE / totalSec;\n\n progressBar.complete();\n progressBar.hide();\n\n context.writer.writeSuccess(\n `Upload: ${formatSpeed(avgSpeed)} (${formatBytes(UPLOAD_SIZE)} in ${totalSec.toFixed(1)}s)`,\n );\n\n return avgSpeed;\n }\n\n private printSummary(\n results: { download?: number; upload?: number },\n context: ICliExecutionContext,\n ): void {\n context.writer.writeln();\n\n const headers = ['Test', 'Speed'];\n const rows: string[][] = [];\n\n if (results.download !== undefined) {\n rows.push(['Download', formatSpeed(results.download)]);\n }\n if (results.upload !== undefined) {\n rows.push(['Upload', formatSpeed(results.upload)]);\n }\n\n context.writer.writeTable(headers, rows);\n context.process.output(results);\n }\n}\n","/*\n * Public API Surface of speed-test\n */\n\nexport * from './lib/processors/cli-speed-test-command-processor';\n\nimport { ICliModule } from '@qodalis/cli-core';\nimport { CliSpeedTestCommandProcessor } from './lib/processors/cli-speed-test-command-processor';\nimport { API_VERSION } from './lib/version';\n\nexport const speedTestModule: ICliModule = {\n apiVersion: API_VERSION,\n name: '@qodalis/cli-speed-test',\n processors: [new CliSpeedTestCommandProcessor()],\n translations: {\n es: { 'cli.speed-test.description': 'Ejecutar una prueba de velocidad de internet' },\n fr: { 'cli.speed-test.description': 'Lancer un test de débit internet' },\n de: { 'cli.speed-test.description': 'Internet-Geschwindigkeitstest durchführen' },\n pt: { 'cli.speed-test.description': 'Executar um teste de velocidade de internet' },\n it: { 'cli.speed-test.description': 'Eseguire un test di velocità internet' },\n ja: { 'cli.speed-test.description': 'インターネット速度テストを実行' },\n ko: { 'cli.speed-test.description': '인터넷 속도 테스트 실행' },\n zh: { 'cli.speed-test.description': '运行网络速度测试' },\n ru: { 'cli.speed-test.description': 'Запустить тест скорости интернета' },\n ro: { 'cli.speed-test.description': 'Rulează un test de viteză a internetului' },\n },\n};\n"]}
@@ -1833,6 +1833,10 @@ var speedTest = (() => {
1833
1833
  CliForegroundColor2["Reset"] = "\x1B[0m";
1834
1834
  return CliForegroundColor2;
1835
1835
  })(CliForegroundColor || {});
1836
+ function resolveHeaders(provider) {
1837
+ if (!provider) return {};
1838
+ return typeof provider === "function" ? provider() : provider;
1839
+ }
1836
1840
  var DefaultLibraryAuthor = {
1837
1841
  name: "Nicolae Lupei",
1838
1842
  email: "nicolae.lupei@qodalis.com"
@@ -1859,9 +1863,12 @@ var speedTest = (() => {
1859
1863
  * Returns the negotiated API version and the base path for all subsequent calls,
1860
1864
  * or null if the server is unreachable or incompatible.
1861
1865
  */
1862
- static async discover(baseUrl) {
1866
+ static async discover(baseUrl, headers) {
1863
1867
  try {
1864
- const response = await fetch(`${baseUrl}${VERSIONS_ENDPOINT}`);
1868
+ const resolved = resolveHeaders(headers);
1869
+ const response = await fetch(`${baseUrl}${VERSIONS_ENDPOINT}`, {
1870
+ headers: Object.keys(resolved).length > 0 ? resolved : void 0
1871
+ });
1865
1872
  if (!response.ok) return null;
1866
1873
  const info = await response.json();
1867
1874
  const version = this.negotiate(info);
@@ -2693,7 +2700,7 @@ var speedTest = (() => {
2693
2700
  };
2694
2701
 
2695
2702
  // src/lib/version.ts
2696
- var LIBRARY_VERSION = "2.0.0-beta.11";
2703
+ var LIBRARY_VERSION = "2.0.0-beta.13";
2697
2704
  var API_VERSION2 = 2;
2698
2705
 
2699
2706
  // src/lib/processors/cli-speed-test-command-processor.ts
@@ -2792,47 +2799,41 @@ var speedTest = (() => {
2792
2799
  async runSpeedTest(command, context) {
2793
2800
  const downloadOnly = !!command.args["download-only"];
2794
2801
  const uploadOnly = !!command.args["upload-only"];
2795
- const controller = new AbortController();
2796
- const subscription = context.onAbort.subscribe(() => {
2797
- controller.abort();
2798
- });
2799
2802
  const results = {};
2800
2803
  try {
2801
2804
  if (!uploadOnly) {
2802
2805
  const downloadUrl = command.args["download-url"] || DEFAULT_DOWNLOAD_URL;
2806
+ context.notifier.info("Speed test: download");
2803
2807
  results.download = await this.testDownload(
2804
2808
  downloadUrl,
2805
- context,
2806
- controller.signal
2809
+ context
2807
2810
  );
2808
2811
  }
2809
2812
  if (!downloadOnly) {
2810
2813
  const uploadUrl = command.args["upload-url"] || DEFAULT_UPLOAD_URL;
2814
+ context.notifier.info("Speed test: upload");
2811
2815
  results.upload = await this.testUpload(
2812
2816
  uploadUrl,
2813
- context,
2814
- controller.signal
2817
+ context
2815
2818
  );
2816
2819
  }
2817
2820
  this.printSummary(results, context);
2818
2821
  } catch (error) {
2819
- if (error.name === "AbortError" || controller.signal.aborted) {
2822
+ if (error.name === "AbortError" || context.signal?.aborted) {
2820
2823
  context.writer.writeWarning("Speed test aborted");
2821
2824
  } else {
2822
2825
  context.writer.writeError(
2823
2826
  `Speed test failed: ${error.message || error}`
2824
2827
  );
2825
2828
  }
2826
- } finally {
2827
- subscription.unsubscribe();
2828
2829
  }
2829
2830
  }
2830
- async testDownload(url, context, signal) {
2831
+ async testDownload(url, context) {
2831
2832
  context.writer.writeln();
2832
2833
  const progressBar = context.progressBar;
2833
2834
  const spinner = context.spinner;
2834
2835
  spinner?.show("Connecting...");
2835
- const response = await fetch(url, { signal });
2836
+ const response = await context.http.fetch(url);
2836
2837
  if (!response.ok) {
2837
2838
  spinner?.hide();
2838
2839
  throw new Error(`Download failed: HTTP ${response.status}`);
@@ -2867,10 +2868,12 @@ var speedTest = (() => {
2867
2868
  progressBar.setText(
2868
2869
  `${formatSpeed(avgSpeed2)} | ${formatBytes(totalBytes)} / ${formatBytes(contentLength)}`
2869
2870
  );
2871
+ context.notifier.info(`Download: ${Math.round(pct)}% ${formatSpeed(avgSpeed2)}`);
2870
2872
  } else {
2871
2873
  progressBar.setText(
2872
2874
  `${formatSpeed(avgSpeed2)} | ${formatBytes(totalBytes)}`
2873
2875
  );
2876
+ context.notifier.info(`Download: ${formatSpeed(avgSpeed2)}`);
2874
2877
  }
2875
2878
  }
2876
2879
  }
@@ -2883,7 +2886,7 @@ var speedTest = (() => {
2883
2886
  );
2884
2887
  return avgSpeed;
2885
2888
  }
2886
- async testUpload(url, context, signal) {
2889
+ async testUpload(url, context) {
2887
2890
  context.writer.writeln();
2888
2891
  context.writer.writeInfo(
2889
2892
  `Running upload test (${formatBytes(UPLOAD_SIZE)})...`
@@ -2902,16 +2905,15 @@ var speedTest = (() => {
2902
2905
  let uploaded = 0;
2903
2906
  const startTime = performance.now();
2904
2907
  for (let i = 0; i < totalChunks; i++) {
2905
- if (signal.aborted) {
2908
+ if (context.signal?.aborted) {
2906
2909
  throw new DOMException("Upload aborted", "AbortError");
2907
2910
  }
2908
2911
  const start = i * CHUNK;
2909
2912
  const end = Math.min(start + CHUNK, UPLOAD_SIZE);
2910
2913
  const chunk = payload.slice(start, end);
2911
- await fetch(url, {
2914
+ await context.http.fetch(url, {
2912
2915
  method: "POST",
2913
- body: chunk,
2914
- signal
2916
+ body: chunk
2915
2917
  });
2916
2918
  uploaded += chunk.length;
2917
2919
  const elapsed = (performance.now() - startTime) / 1e3;
@@ -2921,6 +2923,7 @@ var speedTest = (() => {
2921
2923
  progressBar.setText(
2922
2924
  `${formatSpeed(speed)} | ${formatBytes(uploaded)} / ${formatBytes(UPLOAD_SIZE)}`
2923
2925
  );
2926
+ context.notifier.info(`Upload: ${Math.round(pct)}% ${formatSpeed(speed)}`);
2924
2927
  }
2925
2928
  const totalSec = (performance.now() - startTime) / 1e3;
2926
2929
  const avgSpeed = UPLOAD_SIZE / totalSec;