ng-speed-test
Version:
Angular library for testing internet connection speed
1 lines • 34.8 kB
Source Map (JSON)
{"version":3,"file":"ng-speed-test.mjs","sources":["../../../src/models/speed-test-file.model.ts","../../../src/models/speed-test-results.model.ts","../../../src/models/speed-test-settings.model.ts","../../../src/providers/speed-test-config.ts","../../../src/services/speed-test.service.ts","../../../src/speed-test.module.ts","../../../src/ng-speed-test.ts"],"sourcesContent":["export interface SpeedTestFile {\r\n path: string;\r\n shouldBustCache: boolean;\r\n size: number;\r\n}\r\n\r\nexport class SpeedTestFileModel implements SpeedTestFile {\r\n public path: string = 'https://raw.githubusercontent.com/jrquick17/ng-speed-test/02c59e4afde67c35a5ba74014b91d44b33c0b3fe/demo/src/assets/5mb.jpg';\r\n public shouldBustCache: boolean = true;\r\n public size: number = 4952221;\r\n\r\n constructor(file?: Partial<SpeedTestFile>) {\r\n if (file) {\r\n if (file.path !== undefined) {\r\n this.path = file.path;\r\n }\r\n if (file.size !== undefined) {\r\n this.size = file.size;\r\n }\r\n if (file.shouldBustCache !== undefined) {\r\n this.shouldBustCache = file.shouldBustCache;\r\n }\r\n }\r\n }\r\n}\r\n","export interface SpeedTestResults {\r\n duration: number;\r\n hasEnded: boolean;\r\n startTime: number | null;\r\n endTime: number | null;\r\n speedBps: number;\r\n speedKbps: number;\r\n speedMbps: number;\r\n}\r\n\r\nexport class SpeedTestResultsModel implements SpeedTestResults {\r\n public duration: number = 0;\r\n public hasEnded: boolean = false;\r\n public startTime: number | null = null;\r\n public endTime: number | null = null;\r\n public speedBps: number = 0;\r\n\r\n constructor(private fileSize: number) {}\r\n\r\n get speedKbps(): number {\r\n return this.speedBps / 1024;\r\n }\r\n\r\n get speedMbps(): number {\r\n return this.speedKbps / 1024;\r\n }\r\n\r\n private _update(): void {\r\n if (this.endTime !== null && this.startTime !== null) {\r\n const milliseconds = this.endTime - this.startTime;\r\n this.duration = milliseconds / 1000;\r\n const bitsLoaded = this.fileSize * 8;\r\n // Guard against a zero (or negative, e.g. clock anomalies) duration: dividing by it\r\n // would otherwise produce Infinity/NaN, which would incorrectly pass the\r\n // `speedBps > 0` validity filter in SpeedTestService.downloadTest(). Falling back to\r\n // 0 here keeps the result finite so it is correctly discarded like any other failure.\r\n this.speedBps = this.duration > 0 ? bitsLoaded / this.duration : 0;\r\n }\r\n }\r\n\r\n end(): void {\r\n if (!this.hasEnded) {\r\n this.hasEnded = true;\r\n this.endTime = performance.now();\r\n this._update();\r\n }\r\n }\r\n\r\n error(): void {\r\n if (!this.hasEnded) {\r\n this.hasEnded = true;\r\n this.endTime = null;\r\n this._update();\r\n }\r\n }\r\n\r\n start(): void {\r\n this.startTime = performance.now();\r\n }\r\n}\r\n","import {SpeedTestFile, SpeedTestFileModel} from './speed-test-file.model';\r\n\r\nexport interface SpeedTestSettings {\r\n iterations?: number;\r\n file?: SpeedTestFile;\r\n retryDelay?: number;\r\n}\r\n\r\nexport class SpeedTestSettingsModel implements SpeedTestSettings {\r\n public iterations?: number = 3;\r\n public file?: SpeedTestFileModel = new SpeedTestFileModel();\r\n public retryDelay?: number = 500;\r\n\r\n constructor(settings?: Partial<SpeedTestSettings>) {\r\n if (settings) {\r\n if (settings.iterations !== undefined) {\r\n this.iterations = settings.iterations;\r\n }\r\n if (settings.retryDelay !== undefined) {\r\n this.retryDelay = settings.retryDelay;\r\n }\r\n if (settings.file) {\r\n this.file = new SpeedTestFileModel();\r\n if (settings.file.path !== undefined) {\r\n this.file.path = settings.file.path;\r\n }\r\n if (settings.file.size !== undefined) {\r\n this.file.size = settings.file.size;\r\n }\r\n if (settings.file.shouldBustCache !== undefined) {\r\n this.file.shouldBustCache = settings.file.shouldBustCache;\r\n }\r\n }\r\n }\r\n }\r\n}\r\n","import { EnvironmentProviders, InjectionToken, makeEnvironmentProviders } from '@angular/core';\n\nimport { SpeedTestFile } from '../models/speed-test-file.model';\n\nexport interface SpeedTestConfig {\n /**\n * URL used for the pre-flight connectivity check that runs before every speed test and every\n * isOnline() / getNetworkStatus() emission. When not provided, no third-party host is\n * contacted for connectivity verification - the library relies on navigator.onLine and on the\n * actual file fetch failing (with a real, attributable error) if the network is unreachable.\n */\n connectivityCheckUrl?: string;\n /** Timeout in ms for the connectivity check, if connectivityCheckUrl is set. Defaults to 3000. */\n connectivityCheckTimeout?: number;\n /** Overrides the built-in default test file for calls that do not supply their own. */\n file?: Partial<SpeedTestFile>;\n /** Timeout in ms for the file download of a single iteration. Defaults to 15000. */\n timeout?: number;\n}\n\nexport const SPEED_TEST_CONFIG = new InjectionToken<SpeedTestConfig>('SPEED_TEST_CONFIG');\n\n/**\n * Configures SpeedTestService. Optional - every field has a working default and this call is\n * not required to use the library. Pass connectivityCheckUrl only if you want an extra,\n * network-verified connectivity check in addition to navigator.onLine; ng-speed-test does not\n * contact any third-party host on its own unless you configure one here.\n */\nexport function provideSpeedTest(config: SpeedTestConfig = {}): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: SPEED_TEST_CONFIG, useValue: config }\n ]);\n}\n","import { inject, Injectable } from '@angular/core';\r\nimport { fromEvent, merge, Observable, of, Subscription, throwError } from 'rxjs';\r\nimport { map, mergeMap, catchError, timeout, switchMap, startWith } from 'rxjs/operators';\r\n\r\nimport { SpeedTestFileModel } from '../models/speed-test-file.model';\r\nimport { SpeedTestSettingsModel } from '../models/speed-test-settings.model';\r\nimport { SpeedTestResultsModel } from '../models/speed-test-results.model';\r\nimport { SPEED_TEST_CONFIG } from '../providers/speed-test-config';\r\n\r\nexport interface SpeedTestResult {\r\n bps: number;\r\n kbps: number;\r\n mbps: number;\r\n duration: number;\r\n}\r\n\r\n@Injectable({\r\n providedIn: 'root'\r\n})\r\nexport class SpeedTestService {\r\n private readonly config = inject(SPEED_TEST_CONFIG, { optional: true }) ?? {};\r\n private readonly DEFAULT_TIMEOUT = this.config.timeout ?? 15000; // Reduced from 30s to 15s\r\n private readonly OFFLINE_CHECK_TIMEOUT = this.config.connectivityCheckTimeout ?? 3000; // Quick offline check\r\n\r\n constructor() {}\r\n\r\n private applyCacheBuster(path: string): string {\r\n const separator = path.includes('?') ? '&' : '?';\r\n return `${path}${separator}cache_bust=${Date.now()}_${Math.random()}`;\r\n }\r\n\r\n /**\r\n * Quick connectivity check before running speed test.\r\n *\r\n * Only contacts a third-party host if the consumer opted in via\r\n * provideSpeedTest({ connectivityCheckUrl }) - otherwise this trusts navigator.onLine, and an\r\n * unreachable network still surfaces as a real, attributable error from the file fetch itself.\r\n */\r\n private checkConnectivity(): Observable<boolean> {\r\n // First check navigator.onLine\r\n if (!navigator.onLine) {\r\n return of(false);\r\n }\r\n\r\n if (!this.config.connectivityCheckUrl) {\r\n return of(true);\r\n }\r\n\r\n // Then do a quick network request to verify actual connectivity\r\n return new Observable<boolean>(observer => {\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => {\r\n controller.abort();\r\n observer.next(false);\r\n observer.complete();\r\n }, this.OFFLINE_CHECK_TIMEOUT);\r\n\r\n fetch(this.config.connectivityCheckUrl!, {\r\n method: 'HEAD',\r\n mode: 'no-cors',\r\n signal: controller.signal,\r\n cache: 'no-cache'\r\n })\r\n .then(() => {\r\n clearTimeout(timeoutId);\r\n observer.next(true);\r\n observer.complete();\r\n })\r\n .catch(() => {\r\n clearTimeout(timeoutId);\r\n observer.next(false);\r\n observer.complete();\r\n });\r\n\r\n return () => {\r\n clearTimeout(timeoutId);\r\n controller.abort();\r\n };\r\n });\r\n }\r\n\r\n private downloadTest(settings: SpeedTestSettingsModel, allResults: SpeedTestResultsModel[] = []): Observable<number> {\r\n // Quick connectivity check first\r\n return this.checkConnectivity().pipe(\r\n switchMap(isConnected => {\r\n if (!isConnected) {\r\n return throwError(() => new Error('No internet connection available'));\r\n }\r\n\r\n return new Observable<SpeedTestResultsModel>(observer => {\r\n const testResult = new SpeedTestResultsModel(settings.file!.size);\r\n const abortController = new AbortController();\r\n\r\n let filePath = settings.file!.path;\r\n if (settings.file!.shouldBustCache) {\r\n filePath = this.applyCacheBuster(filePath);\r\n }\r\n\r\n testResult.start();\r\n\r\n // Set a more aggressive timeout for the fetch request\r\n const fetchTimeout = setTimeout(() => {\r\n abortController.abort();\r\n testResult.error();\r\n observer.next(testResult);\r\n observer.complete();\r\n }, this.DEFAULT_TIMEOUT);\r\n\r\n fetch(filePath, {\r\n method: 'GET',\r\n signal: abortController.signal,\r\n cache: 'no-cache'\r\n })\r\n .then(response => {\r\n clearTimeout(fetchTimeout);\r\n if (!response.ok) {\r\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\r\n }\r\n return response.blob();\r\n })\r\n .then(() => {\r\n testResult.end();\r\n observer.next(testResult);\r\n observer.complete();\r\n })\r\n .catch(() => {\r\n clearTimeout(fetchTimeout);\r\n // Surfaced via the existing error channel: testResult.error() marks\r\n // this iteration as failed (speedBps stays 0), so it is discarded by\r\n // the `speedBps > 0` filter below rather than logged to the console.\r\n // If every iteration fails, the mergeMap below throws a descriptive\r\n // error that propagates out through the returned Observable.\r\n testResult.error();\r\n\r\n const delay = settings.iterations !== 1 ? settings.retryDelay! : 0;\r\n\r\n setTimeout(() => {\r\n observer.next(testResult);\r\n observer.complete();\r\n }, delay);\r\n });\r\n\r\n // Cleanup function\r\n return () => {\r\n clearTimeout(fetchTimeout);\r\n abortController.abort();\r\n };\r\n });\r\n }),\r\n mergeMap((testResult: SpeedTestResultsModel) => {\r\n allResults.push(testResult);\r\n\r\n if (settings.iterations! <= 1) {\r\n // Calculate average speed from all valid results\r\n const validResults = allResults.filter(result => result.speedBps > 0);\r\n\r\n if (validResults.length === 0) {\r\n return throwError(() => new Error('All speed test iterations failed - no internet connection or server unreachable'));\r\n }\r\n\r\n const totalSpeed = validResults.reduce((sum, result) => sum + result.speedBps, 0);\r\n const averageSpeed = totalSpeed / validResults.length;\r\n\r\n return of(averageSpeed);\r\n } else {\r\n settings.iterations!--;\r\n return this.downloadTest(settings, allResults);\r\n }\r\n })\r\n );\r\n }\r\n\r\n private validateSettings(settings: SpeedTestSettingsModel): void {\r\n if (!settings.file?.path) {\r\n throw new Error('ng-speed-test: File path is required');\r\n }\r\n\r\n if (!settings.file?.size || settings.file.size <= 0) {\r\n throw new Error('ng-speed-test: Valid file size is required');\r\n }\r\n\r\n if (settings.iterations !== undefined && settings.iterations < 1) {\r\n throw new Error('ng-speed-test: Iterations must be at least 1');\r\n }\r\n }\r\n\r\n /**\r\n * Get internet speed in bits per second (bps)\r\n * Fails fast if no internet connection is available\r\n */\r\n getBps(customSettings?: Partial<SpeedTestSettingsModel>): Observable<number> {\r\n return new Observable(observer => {\r\n // Check connectivity immediately\r\n if (!navigator.onLine) {\r\n observer.error(new Error('No internet connection - browser reports offline'));\r\n return;\r\n }\r\n\r\n let downloadSubscription: Subscription | undefined;\r\n\r\n // Small delay to ensure proper initialization\r\n const initTimeoutId = setTimeout(() => {\r\n // Create settings with proper merging\r\n const defaultSettings = new SpeedTestSettingsModel();\r\n if (this.config.file) {\r\n defaultSettings.file = new SpeedTestFileModel(this.config.file);\r\n }\r\n const settings = this.mergeSettings(defaultSettings, customSettings);\r\n\r\n try {\r\n this.validateSettings(settings);\r\n\r\n downloadSubscription = this.downloadTest(settings).subscribe({\r\n next: (speedBps) => {\r\n observer.next(speedBps);\r\n observer.complete();\r\n },\r\n error: (error) => {\r\n observer.error(error);\r\n }\r\n });\r\n } catch (error) {\r\n observer.error(error);\r\n }\r\n }, 1);\r\n\r\n // Teardown: runs on unsubscribe (including via takeUntil/timeout upstream, or normal\r\n // completion/error). Clears the pending init setTimeout so it can't fire after\r\n // teardown, and unsubscribes the inner downloadTest() subscription, which propagates\r\n // down through its switchMap/mergeMap chain to the per-iteration fetch Observable and\r\n // triggers its own teardown (clearTimeout(fetchTimeout) + abortController.abort()),\r\n // actually cancelling the in-flight fetch instead of letting it run to completion.\r\n return () => {\r\n clearTimeout(initTimeoutId);\r\n downloadSubscription?.unsubscribe();\r\n };\r\n });\r\n }\r\n\r\n /**\r\n * Properly merge custom settings with defaults\r\n */\r\n private mergeSettings(defaultSettings: SpeedTestSettingsModel, customSettings?: Partial<SpeedTestSettingsModel>): SpeedTestSettingsModel {\r\n if (!customSettings) {\r\n return defaultSettings;\r\n }\r\n\r\n const mergedSettings = new SpeedTestSettingsModel();\r\n\r\n // Merge iterations\r\n mergedSettings.iterations = customSettings.iterations !== undefined\r\n ? customSettings.iterations\r\n : defaultSettings.iterations;\r\n\r\n // Merge retryDelay\r\n mergedSettings.retryDelay = customSettings.retryDelay !== undefined\r\n ? customSettings.retryDelay\r\n : defaultSettings.retryDelay;\r\n\r\n // Merge file settings\r\n if (customSettings.file) {\r\n mergedSettings.file = new SpeedTestFileModel();\r\n\r\n // Merge file path\r\n mergedSettings.file.path = customSettings.file.path !== undefined\r\n ? customSettings.file.path\r\n : defaultSettings.file!.path;\r\n\r\n // Merge file size\r\n mergedSettings.file.size = customSettings.file.size !== undefined\r\n ? customSettings.file.size\r\n : defaultSettings.file!.size;\r\n\r\n // Merge shouldBustCache\r\n mergedSettings.file.shouldBustCache = customSettings.file.shouldBustCache !== undefined\r\n ? customSettings.file.shouldBustCache\r\n : defaultSettings.file!.shouldBustCache;\r\n } else {\r\n mergedSettings.file = defaultSettings.file;\r\n }\r\n\r\n return mergedSettings;\r\n }\r\n\r\n /**\r\n * Get internet speed in kilobits per second (Kbps)\r\n */\r\n getKbps(settings?: Partial<SpeedTestSettingsModel>): Observable<number> {\r\n return this.getBps(settings).pipe(\r\n map(bps => bps / 1024)\r\n );\r\n }\r\n\r\n /**\r\n * Get internet speed in megabits per second (Mbps)\r\n */\r\n getMbps(settings?: Partial<SpeedTestSettingsModel>): Observable<number> {\r\n return this.getKbps(settings).pipe(\r\n map(kbps => kbps / 1024)\r\n );\r\n }\r\n\r\n /**\r\n * Get comprehensive speed test results with fast failure for offline scenarios\r\n */\r\n getSpeedTestResult(settings?: Partial<SpeedTestSettingsModel>): Observable<SpeedTestResult> {\r\n const startTime = Date.now();\r\n\r\n return this.getBps(settings).pipe(\r\n map(bps => ({\r\n bps,\r\n kbps: bps / 1024,\r\n mbps: bps / (1024 * 1024),\r\n duration: (Date.now() - startTime) / 1000\r\n })),\r\n timeout(this.DEFAULT_TIMEOUT + 5000), // Overall timeout slightly longer than individual request timeout\r\n catchError(error => {\r\n if (error.name === 'TimeoutError') {\r\n return throwError(() => new Error('Speed test timed out - please check your internet connection'));\r\n }\r\n return throwError(() => error);\r\n })\r\n );\r\n }\r\n\r\n /**\r\n * Check if the browser is online with enhanced detection\r\n */\r\n isOnline(): Observable<boolean> {\r\n return merge(\r\n fromEvent(window, 'offline').pipe(map(() => false)),\r\n fromEvent(window, 'online').pipe(map(() => true)),\r\n of(navigator.onLine)\r\n ).pipe(\r\n startWith(navigator.onLine),\r\n // Verify actual connectivity for online state\r\n switchMap(browserOnline => {\r\n if (!browserOnline) {\r\n return of(false);\r\n }\r\n // Quick connectivity verification\r\n return this.checkConnectivity();\r\n })\r\n );\r\n }\r\n\r\n /**\r\n * Monitor network connection status with enhanced detection\r\n */\r\n getNetworkStatus(): Observable<{ isOnline: boolean; effectiveType?: string; downlink?: number }> {\r\n const getConnectionInfo = () => {\r\n const connection = (navigator as any).connection ||\r\n (navigator as any).mozConnection ||\r\n (navigator as any).webkitConnection;\r\n\r\n return {\r\n isOnline: navigator.onLine,\r\n effectiveType: connection?.effectiveType,\r\n downlink: connection?.downlink\r\n };\r\n };\r\n\r\n return merge(\r\n fromEvent(window, 'offline').pipe(\r\n map(() => ({ ...getConnectionInfo(), isOnline: false }))\r\n ),\r\n fromEvent(window, 'online').pipe(\r\n map(() => getConnectionInfo()),\r\n // Verify actual connectivity when browser reports online\r\n switchMap(info =>\r\n this.checkConnectivity().pipe(\r\n map(actuallyOnline => ({ ...info, isOnline: actuallyOnline }))\r\n )\r\n )\r\n ),\r\n of(getConnectionInfo()).pipe(\r\n switchMap(info =>\r\n info.isOnline\r\n ? this.checkConnectivity().pipe(\r\n map(actuallyOnline => ({ ...info, isOnline: actuallyOnline }))\r\n )\r\n : of(info)\r\n )\r\n )\r\n );\r\n }\r\n}\r\n","import { NgModule } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { FormsModule } from '@angular/forms';\r\nimport { SpeedTestService } from './services/speed-test.service';\r\n\r\n@NgModule({\r\n imports: [CommonModule, FormsModule],\r\n providers: [SpeedTestService]\r\n})\r\nexport class SpeedTestModule {}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}