Skip to content

Commit

Permalink
Activate TypeScript's noImplicitAny rule (#752)
Browse files Browse the repository at this point in the history
  • Loading branch information
Acconut authored Jan 22, 2025
1 parent f508389 commit 02cbd47
Showing 20 changed files with 395 additions and 397 deletions.
2 changes: 2 additions & 0 deletions lib/browser/BrowserFileReader.ts
Original file line number Diff line number Diff line change
@@ -32,6 +32,8 @@ export class BrowserFileReader implements FileReader {
}

// File is a subtype of Blob, so we can check for Blob here.
// TODO: Consider turning Blobs, Buffers, and Uint8Arrays into a single type.
// Potentially handling it in the same way as in Node.js
if (input instanceof Blob) {
return Promise.resolve(new BlobFileSource(input))
}
6 changes: 3 additions & 3 deletions lib/browser/XHRHttpStack.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { HttpProgressHandler, HttpRequest, HttpResponse, HttpStack } from '../options.js'

export class XHRHttpStack implements HttpStack {
createRequest(method, url) {
createRequest(method: string, url: string): HttpRequest {
return new XHRRequest(method, url)
}

@@ -39,7 +39,7 @@ class XHRRequest implements HttpRequest {
this._headers[header] = value
}

getHeader(header: string) {
getHeader(header: string): string {
return this._headers[header]
}

@@ -59,7 +59,7 @@ class XHRRequest implements HttpRequest {
}

// TODO: Validate the type of body
send(body?: Blob): Promise<XHRResponse> {
send(body?: Blob): Promise<HttpResponse> {
return new Promise((resolve, reject) => {
this._xhr.onload = () => {
resolve(new XHRResponse(this._xhr))
15 changes: 9 additions & 6 deletions lib/browser/sources/BlobFileSource.ts
Original file line number Diff line number Diff line change
@@ -13,19 +13,22 @@ export class BlobFileSource implements FileSource {
}

async slice(start: number, end: number): Promise<SliceResult> {
let value: any
// In Apache Cordova applications, a File must be resolved using
// FileReader instances, see
// https://cordova.apache.org/docs/en/8.x/reference/cordova-plugin-file/index.html#read-a-file
if (isCordova()) {
value = await readAsByteArray(this._file.slice(start, end))
value.size = value.length
} else {
value = this._file.slice(start, end)
const value = await readAsByteArray(this._file.slice(start, end))
const size = value.length
const done = end >= this.size

return Promise.resolve({ value, size, done })
}

const value = this._file.slice(start, end)
const size = value.size
const done = end >= this.size
return Promise.resolve({ value, done })

return Promise.resolve({ value, size, done })
}

close() {
22 changes: 12 additions & 10 deletions lib/browser/sources/StreamFileSource.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { FileSource } from '../../options.js'
import type { FileSource, SliceResult } from '../../options.js'

function len(blobOrArray: StreamFileSource['_buffer']): number {
if (blobOrArray === undefined) return 0
@@ -11,9 +11,6 @@ function len(blobOrArray: StreamFileSource['_buffer']): number {
This function helps StreamSource accumulate data to reach chunkSize.
*/
function concat<T extends StreamFileSource['_buffer']>(a: T, b: T): T {
if (Array.isArray(a) && Array.isArray(b)) {
return a.concat(b) as T
}
if (a instanceof Blob && b instanceof Blob) {
return new Blob([a, b], { type: a.type }) as T
}
@@ -29,7 +26,7 @@ function concat<T extends StreamFileSource['_buffer']>(a: T, b: T): T {
export class StreamFileSource implements FileSource {
private _reader: Pick<ReadableStreamDefaultReader<StreamFileSource['_buffer']>, 'read'>

private _buffer: Blob | Uint8Array | number[] | undefined
private _buffer: Blob | Uint8Array | undefined

// _bufferOffset defines at which position the content of _buffer (if it is set)
// is located in the view of the entire stream. It does not mean at which offset
@@ -47,20 +44,25 @@ export class StreamFileSource implements FileSource {
this._reader = reader
}

slice(start: number, end: number) {
slice(start: number, end: number): Promise<SliceResult> {
if (start < this._bufferOffset) {
return Promise.reject(new Error("Requested data is before the reader's current offset"))
}

return this._readUntilEnoughDataOrDone(start, end)
}

private _readUntilEnoughDataOrDone(start: number, end: number) {
private _readUntilEnoughDataOrDone(start: number, end: number): Promise<SliceResult> {
const hasEnoughData = end <= this._bufferOffset + len(this._buffer)
if (this._done || hasEnoughData) {
const value = this._getDataFromBuffer(start, end)
const done = value == null ? this._done : false
return Promise.resolve({ value, done })
if (value === null) {
return Promise.resolve({ value: null, size: null, done: true })
}

const size = value instanceof Blob ? value.size : value.length
const done = this._done
return Promise.resolve({ value, size, done })
}

return this._reader.read().then(({ value, done }) => {
@@ -76,7 +78,7 @@ export class StreamFileSource implements FileSource {
})
}

private _getDataFromBuffer(start, end) {
private _getDataFromBuffer(start: number, end: number) {
if (this._buffer === undefined) {
throw new Error('cannot _getDataFromBuffer because _buffer is unset')
}
179 changes: 60 additions & 119 deletions lib/node/FileUrlStorage.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { readFile, writeFile } from 'fs'
import combineErrors from 'combine-errors'
import * as lockfile from 'proper-lockfile'
import { readFile, writeFile } from 'fs/promises'
import { lock } from 'proper-lockfile'
import type { PreviousUpload, UrlStorage } from '../options.js'

export const canStoreURLs = true
@@ -12,98 +11,62 @@ export class FileUrlStorage implements UrlStorage {
this.path = filePath
}

findAllUploads() {
return new Promise<PreviousUpload[]>((resolve, reject) => {
this._getItems('tus::', (err, results) => {
if (err) reject(err)
else resolve(results)
})
})
async findAllUploads(): Promise<PreviousUpload[]> {
return await this._getItems('tus::')
}

findUploadsByFingerprint(fingerprint) {
return new Promise<PreviousUpload[]>((resolve, reject) => {
this._getItems(`tus::${fingerprint}`, (err, results) => {
if (err) reject(err)
else resolve(results)
})
})
async findUploadsByFingerprint(fingerprint: string): Promise<PreviousUpload[]> {
return await this._getItems(`tus::${fingerprint}`)
}

removeUpload(urlStorageKey) {
return new Promise<void>((resolve, reject) => {
this._removeItem(urlStorageKey, (err) => {
if (err) reject(err)
else resolve()
})
})
async removeUpload(urlStorageKey: string): Promise<void> {
await this._removeItem(urlStorageKey)
}

addUpload(fingerprint, upload) {
async addUpload(fingerprint: string, upload: PreviousUpload): Promise<string> {
const id = Math.round(Math.random() * 1e12)
const key = `tus::${fingerprint}::${id}`

return new Promise<string>((resolve, reject) => {
this._setItem(key, upload, (err) => {
if (err) reject(err)
else resolve(key)
})
})
await this._setItem(key, upload)
return key
}

private _setItem(key, value, cb) {
lockfile
.lock(this.path, this._lockfileOptions())
.then((release) => {
cb = this._releaseAndCb(release, cb)
this._getData((err, data) => {
if (err) {
cb(err)
return
}

data[key] = value
this._writeData(data, (err2) => cb(err2))
})
})
.catch(cb)
}
private async _setItem(key: string, value: unknown) {
const release = await lock(this.path, this._lockfileOptions())

private _getItems(prefix, cb) {
this._getData((err, data) => {
if (err) {
cb(err)
return
}

const results = Object.keys(data)
.filter((key) => key.startsWith(prefix))
.map((key) => {
const obj = data[key]
obj.urlStorageKey = key
return obj
})

cb(null, results)
})
try {
const data = await this._getData()
data[key] = value
await this._writeData(data)
} finally {
await release()
}
}

private _removeItem(key, cb) {
lockfile
.lock(this.path, this._lockfileOptions())
.then((release) => {
cb = this._releaseAndCb(release, cb)
this._getData((err, data) => {
if (err) {
cb(err)
return
}

delete data[key]
this._writeData(data, (err2) => cb(err2))
})
private async _getItems(prefix: string) {
const data = await this._getData()

const results = Object.keys(data)
.filter((key) => key.startsWith(prefix))
.map((key) => {
const obj = data[key]
obj.urlStorageKey = key
return obj
})
.catch(cb)

return results
}

private async _removeItem(key: string) {
const release = await lock(this.path, this._lockfileOptions())

try {
const data = await this._getData()
delete data[key]
await this._writeData(data)
} finally {
await release()
}
}

private _lockfileOptions() {
@@ -116,48 +79,26 @@ export class FileUrlStorage implements UrlStorage {
}
}

private _releaseAndCb(release, cb) {
return (err) => {
if (err) {
release()
.then(() => cb(err))
.catch((releaseErr) => cb(combineErrors([err, releaseErr])))
return
}
private async _writeData(data: unknown): Promise<void> {
await writeFile(this.path, JSON.stringify(data), {
encoding: 'utf8',
mode: 0o660,
flag: 'w',
})
}

release().then(cb).catch(cb)
private async _getData() {
let data = ''
try {
data = await readFile(this.path, 'utf8')
} catch (err) {
// return empty data if file does not exist
if (err != null && typeof err === 'object' && 'code' in err && err.code === 'ENOENT')
return {}
}
}

private _writeData(data, cb) {
writeFile(
this.path,
JSON.stringify(data),
{
encoding: 'utf8',
mode: 0o660,
flag: 'w',
},
(err) => cb(err),
)
}
data = data.trim()

private _getData(cb) {
readFile(this.path, 'utf8', (err, data) => {
if (err) {
// return empty data if file does not exist
if (err.code === 'ENOENT') cb(null, {})
else cb(err)
return
}

try {
data = !data.trim().length ? {} : JSON.parse(data)
} catch (error) {
cb(error)
return
}
cb(null, data)
})
return data.length === 0 ? {} : JSON.parse(data)
}
}
8 changes: 6 additions & 2 deletions lib/node/NodeHttpStack.ts
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
// but it is still included in Node.js
import * as http from 'http'
import * as https from 'https'
import { Readable, Transform } from 'stream'
import { Readable, Transform, type Writable } from 'stream'
import { parse } from 'url'
import throttle from 'lodash.throttle'
import type { HttpProgressHandler, HttpRequest, HttpResponse, HttpStack } from '../options.js'
@@ -204,7 +204,11 @@ class ProgressEmitter extends Transform {
// buffer is empty, as indicated by the emitted `drain` event.
// See https://nodejs.org/docs/latest/api/stream.html#buffering for more details
// on the buffering behavior of streams.
const writeBufferToStreamWithProgress = (stream, source, onprogress) => {
function writeBufferToStreamWithProgress(
stream: Writable,
source: Uint8Array,
onprogress: HttpProgressHandler,
) {
onprogress = throttle(onprogress, 100, {
leading: true,
trailing: false,
6 changes: 3 additions & 3 deletions lib/node/sources/BufferFileSource.ts
Original file line number Diff line number Diff line change
@@ -11,10 +11,10 @@ export class BufferFileSource implements FileSource {
}

slice(start: number, end: number) {
const value: Buffer & { size?: number } = this._buffer.slice(start, end)
value.size = value.length
const value: Buffer = this._buffer.slice(start, end)
const size = value.length
const done = end >= this.size
return Promise.resolve({ value, done })
return Promise.resolve({ value, size, done })
}

close() {}
6 changes: 3 additions & 3 deletions lib/node/sources/NodeFileSource.ts
Original file line number Diff line number Diff line change
@@ -56,9 +56,9 @@ export class NodeFileSource implements FileSource {
end: offset + end - 1,
autoClose: true,
})
stream.size = Math.min(end - start, this.size)
const done = stream.size >= this.size
return Promise.resolve({ value: stream, done })
const size = Math.min(end - start, this.size)
const done = size >= this.size
return Promise.resolve({ value: stream, size, done })
}

close() {
Loading

0 comments on commit 02cbd47

Please sign in to comment.