Skip to content

Commit

Permalink
Move all node-related source files to TypeScript
Browse files Browse the repository at this point in the history
  • Loading branch information
Acconut committed Apr 16, 2024
1 parent b887d7e commit b85b7e1
Show file tree
Hide file tree
Showing 13 changed files with 287 additions and 204 deletions.
4 changes: 2 additions & 2 deletions lib/browser/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import BaseUpload, { defaultOptions as baseDefaultOptions } from '../upload'
import BaseUpload, { defaultOptions as baseDefaultOptions, terminate } from '../upload'
import NoopUrlStorage from '../noopUrlStorage.js'
import { enableDebugLog } from '../logger'
import DetailedError from '../error'
Expand All @@ -24,7 +24,7 @@ class Upload extends BaseUpload {

static terminate(url, options = {}) {
options = { ...defaultOptions, ...options }
return BaseUpload.terminate(url, options)
return terminate(url, options)
}
}

Expand Down
14 changes: 9 additions & 5 deletions lib/node/fileReader.js → lib/node/fileReader.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { ReadStream } from 'fs'
import isStream from 'is-stream'

import BufferSource from './sources/BufferSource.js'
import getFileSource from './sources/FileSource.js'
import StreamSource from './sources/StreamSource.js'
import { FileReader as IFileReader } from '../upload'
import BufferSource from './sources/BufferSource'
import getFileSource from './sources/FileSource'
import StreamSource from './sources/StreamSource'
import { FileSliceTypes, FileTypes } from './index'

export default class FileReader {
openFile(input, chunkSize) {
// TODO: Consider renaming this NodeFileReader
export default class FileReader implements IFileReader<FileTypes, FileSliceTypes> {
// TODO: Use async here and less Promise.resolve
openFile(input: FileTypes, chunkSize: number) {
if (Buffer.isBuffer(input)) {
return Promise.resolve(new BufferSource(input))
}
Expand Down
11 changes: 9 additions & 2 deletions lib/node/fileSignature.js → lib/node/fileSignature.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import * as fs from 'fs'
import * as path from 'path'
import { createHash } from 'crypto'
import { UploadOptions } from '../upload'
import { FileTypes, FileSliceTypes } from './index'

/**
* Generate a fingerprint for a file which will be used the store the endpoint
*
* @param {File} file
* @param {Object} options
*/
export default function fingerprint(file, options) {
export default function fingerprint(
file: FileTypes,
options: UploadOptions<FileTypes, FileSliceTypes>,
): Promise<string | null> {
if (Buffer.isBuffer(file)) {
// create MD5 hash for buffer type
const blockSize = 64 * 1024 // 64kb
Expand All @@ -20,7 +25,9 @@ export default function fingerprint(file, options) {

if (file instanceof fs.ReadStream && file.path != null) {
return new Promise((resolve, reject) => {
const name = path.resolve(file.path)
const name = path.resolve(
Buffer.isBuffer(file.path) ? file.path.toString('utf-8') : file.path,
)
fs.stat(file.path, (err, info) => {
if (err) {
reject(err)
Expand Down
66 changes: 44 additions & 22 deletions lib/node/httpStack.js → lib/node/httpStack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import * as https from 'https'
import { parse } from 'url'
import { Readable, Transform } from 'stream'
import throttle from 'lodash.throttle'
import { HttpProgressHandler, HttpRequest, HttpResponse, HttpStack } from '../upload'
import { FileSliceTypes } from './index'

export default class NodeHttpStack {
constructor(requestOptions = {}) {
export default class NodeHttpStack implements HttpStack<FileSliceTypes> {
private _requestOptions: http.RequestOptions

constructor(requestOptions: http.RequestOptions = {}) {
this._requestOptions = requestOptions
}

createRequest(method, url) {
createRequest(method: string, url: string) {
return new Request(method, url, this._requestOptions)
}

Expand All @@ -21,14 +25,18 @@ export default class NodeHttpStack {
}
}

class Request {
constructor(method, url, options) {
class Request implements HttpRequest<FileSliceTypes> {
_method: string
_url: string
_headers: Record<string, string> = {}
_request: http.ClientRequest | null = null
_progressHandler: HttpProgressHandler = () => {}
_requestOptions: http.RequestOptions

constructor(method: string, url: string, options: http.RequestOptions) {
this._method = method
this._url = url
this._headers = {}
this._request = null
this._progressHandler = () => {}
this._requestOptions = options || {}
this._requestOptions = options
}

getMethod() {
Expand All @@ -39,19 +47,19 @@ class Request {
return this._url
}

setHeader(header, value) {
setHeader(header: string, value: string) {
this._headers[header] = value
}

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

setProgressHandler(progressHandler) {
setProgressHandler(progressHandler: HttpProgressHandler) {
this._progressHandler = progressHandler
}

send(body = null) {
send(body?: FileSliceTypes): Promise<HttpResponse> {
return new Promise((resolve, reject) => {
const options = {
...parse(this._url),
Expand All @@ -64,16 +72,18 @@ class Request {
},
}

//@ts-expect-error
if (body && body.size) {
//@ts-expect-error
options.headers['Content-Length'] = body.size
}

const httpModule = options.protocol === 'https:' ? https : http
this._request = httpModule.request(options)
const req = this._request
req.on('response', (res) => {
const resChunks = []
res.on('data', (data) => {
const resChunks: Buffer[] = []
res.on('data', (data: Buffer) => {
resChunks.push(data)
})

Expand Down Expand Up @@ -114,18 +124,28 @@ class Request {
}
}

class Response {
constructor(res, body) {
class Response implements HttpResponse {
_response: http.IncomingMessage
_body: string

constructor(res: http.IncomingMessage, body: string) {
this._response = res
this._body = body
}

getStatus() {
if (this._response.statusCode == undefined) {
throw new Error('no status code available yet')
}
return this._response.statusCode
}

getHeader(header) {
return this._response.headers[header.toLowerCase()]
getHeader(header: string) {
const values = this._response.headers[header.toLowerCase()]
if (Array.isArray(values)) {
return values.join(', ')
}
return values
}

getBody() {
Expand All @@ -141,7 +161,10 @@ class Response {
// track of the number of bytes which have been piped through it and will
// invoke the `onprogress` function whenever new number are available.
class ProgressEmitter extends Transform {
constructor(onprogress) {
_onprogress: HttpProgressHandler
_position = 0

constructor(onprogress: HttpProgressHandler) {
super()

// The _onprogress property will be invoked, whenever a chunk is piped
Expand All @@ -153,10 +176,9 @@ class ProgressEmitter extends Transform {
leading: true,
trailing: false,
})
this._position = 0
}

_transform(chunk, encoding, callback) {
_transform(chunk: Buffer, encoding: string, callback: (err: Error | null, data: Buffer) => void) {
this._position += chunk.length
this._onprogress(this._position)
callback(null, chunk)
Expand Down
50 changes: 0 additions & 50 deletions lib/node/index.js

This file was deleted.

60 changes: 60 additions & 0 deletions lib/node/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import BaseUpload, {
UploadOptions,
defaultOptions as baseDefaultOptions,
terminate,
} from '../upload'
import NoopUrlStorage from '../noopUrlStorage'
import { enableDebugLog } from '../logger'
import DetailedError from '../error'

import { FileUrlStorage, canStoreURLs } from './urlStorage'
import DefaultHttpStack from './httpStack'
import FileReader from './fileReader'
import fingerprint from './fileSignature'
import StreamSource from './sources/StreamSource'

import { ReadStream } from 'fs'
import { Readable } from 'stream'

const defaultOptions = {
...baseDefaultOptions,
httpStack: new DefaultHttpStack(),
fileReader: new FileReader(),
urlStorage: new NoopUrlStorage(),
fingerprint,
}

export type FileTypes = Buffer | Readable | ReadStream
export type FileSliceTypes = Buffer | ReadStream

class Upload extends BaseUpload<FileTypes, FileSliceTypes> {
constructor(file: FileTypes, options: Partial<UploadOptions<FileTypes, FileSliceTypes>> = {}) {
const allOpts = { ...defaultOptions, ...options }
super(file, allOpts)
}

static terminate(url: string, options: Partial<UploadOptions<FileTypes, FileSliceTypes>> = {}) {
const allOpts = { ...defaultOptions, ...options }
return terminate(url, allOpts)
}
}

// The Node.js environment does not have restrictions which may cause
// tus-js-client not to function.
const isSupported = true

// The usage of the commonjs exporting syntax instead of the new ECMAScript
// one is actually inteded and prevents weird behaviour if we are trying to
// import this module in another module using Babel.
export {
Upload,
defaultOptions,
isSupported,
// Make FileUrlStorage module available as it will not be set by default.
FileUrlStorage,
canStoreURLs,
enableDebugLog,
DefaultHttpStack,
DetailedError,
StreamSource,
}
15 changes: 0 additions & 15 deletions lib/node/sources/BufferSource.js

This file was deleted.

20 changes: 20 additions & 0 deletions lib/node/sources/BufferSource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { FileSource } from '../../upload'

export default class BufferSource implements FileSource<Buffer> {
size: number
private _buffer: Buffer

constructor(buffer: Buffer) {
this._buffer = buffer
this.size = buffer.length
}

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

close() {}
}

0 comments on commit b85b7e1

Please sign in to comment.