tus-js-client-stall-detection
Version:
A pure JavaScript client for the tus resumable upload protocol (fork with stall detection)
95 lines (80 loc) • 2.49 kB
text/typescript
import { log } from './logger.js'
import type { StallDetectionOptions } from './options.js'
export class StallDetector {
private options: StallDetectionOptions
private onStallDetected: (reason: string) => void
private intervalId: ReturnType<typeof setInterval> | null = null
private lastProgressTime = 0
private lastProgressValue = 0
private isActive = false
constructor(options: StallDetectionOptions, onStallDetected: (reason: string) => void) {
this.options = options
this.onStallDetected = onStallDetected
}
/**
* Start monitoring for stalls
* @param initialProgress Optional initial progress value to start with
*/
start(initialProgress?: number) {
if (this.intervalId) {
return // Already started
}
this.lastProgressTime = Date.now()
this.lastProgressValue = initialProgress ?? 0
this.isActive = true
log(
`tus: starting stall detection with checkInterval: ${this.options.checkInterval}ms, stallTimeout: ${this.options.stallTimeout}ms`,
)
// Setup periodic check
this.intervalId = setInterval(() => {
if (!this.isActive) {
return
}
const now = Date.now()
if (this._isProgressStalled(now)) {
this._handleStall('no progress')
}
}, this.options.checkInterval)
}
/**
* Stop monitoring for stalls
*/
stop(): void {
this.isActive = false
if (this.intervalId) {
clearInterval(this.intervalId)
this.intervalId = null
}
}
/**
* Update progress information
* @param progressValue The current progress value (bytes uploaded)
*/
updateProgress(progressValue: number): void {
// Only update progress time if the value has actually changed
if (progressValue !== this.lastProgressValue) {
this.lastProgressTime = Date.now()
this.lastProgressValue = progressValue
}
}
/**
* Check if upload has stalled based on progress events
*/
private _isProgressStalled(now: number): boolean {
const timeSinceProgress = now - this.lastProgressTime
const stallTimeout = this.options.stallTimeout
const isStalled = timeSinceProgress > stallTimeout
if (isStalled) {
log(`tus: no progress for ${timeSinceProgress}ms (limit: ${stallTimeout}ms)`)
}
return isStalled
}
/**
* Handle a detected stall
*/
private _handleStall(reason: string): void {
log(`tus: upload stalled: ${reason}`)
this.stop()
this.onStallDetected(reason)
}
}