Skip to content

Commit

Permalink
Move browser source code to TypeScript
Browse files Browse the repository at this point in the history
  • Loading branch information
Acconut committed May 13, 2024
1 parent b85b7e1 commit de3473f
Show file tree
Hide file tree
Showing 16 changed files with 290 additions and 213 deletions.
40 changes: 28 additions & 12 deletions lib/browser/fileReader.js → lib/browser/fileReader.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,48 @@
import isReactNative from './isReactNative.js'
import uriToBlob from './uriToBlob.js'

import FileSource from './sources/FileSource.js'
import BlobFileSource from './sources/FileSource.js'
import StreamSource from './sources/StreamSource.js'
import { FileReader, FileSource } from '../upload.js'
import { FileSliceTypes, FileTypes, ReactNativeFile } from './index.js'

export default class FileReader {
async openFile(input, chunkSize) {
function isReactNativeFile(
input: FileTypes,
): input is ReactNativeFile {
return 'uri' in input && typeof input.uri === 'string'
}

// TODO: Make sure that we support ArrayBuffers, TypedArrays, DataViews and Blobs
export default class BrowserFileReader
implements FileReader<FileTypes, FileSliceTypes>
{
async openFile(
input: FileTypes,
chunkSize: number,
): Promise<FileSource<FileSliceTypes>> {
// In React Native, when user selects a file, instead of a File or Blob,
// you usually get a file object {} with a uri property that contains
// a local path to the file. We use XMLHttpRequest to fetch
// the file blob, before uploading with tus.
if (isReactNative() && input && typeof input.uri !== 'undefined') {
if (isReactNativeFile(input)) {
if (!isReactNative()) {
// TODO
throw new Error(``)
}

try {
const blob = await uriToBlob(input.uri)
return new FileSource(blob)
return new BlobFileSource(blob)
} catch (err) {
throw new Error(
`tus: cannot fetch \`file.uri\` as Blob, make sure the uri is correct and accessible. ${err}`,
)
}
}

// Since we emulate the Blob type in our tests (not all target browsers
// support it), we cannot use `instanceof` for testing whether the input value
// can be handled. Instead, we simply check is the slice() function and the
// size property are available.
if (typeof input.slice === 'function' && typeof input.size !== 'undefined') {
return Promise.resolve(new FileSource(input))
// File is a subtype of Blob, so we can check for Blob here.
if (input instanceof Blob) {
return Promise.resolve(new BlobFileSource(input))
}

if (typeof input.read === 'function') {
Expand All @@ -39,7 +55,7 @@ export default class FileReader {
)
}

return Promise.resolve(new StreamSource(input, chunkSize))
return Promise.resolve(new StreamSource(input))
}

return Promise.reject(
Expand Down
10 changes: 4 additions & 6 deletions lib/browser/fileSignature.js → lib/browser/fileSignature.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { UploadOptions } from '../upload.js'
import { ReactNativeFile } from './index.js'
import isReactNative from './isReactNative.js'

// TODO: Differenciate between input types

/**
* Generate a fingerprint for a file which will be used the store the endpoint
*
* @param {File} file
* @param {Object} options
* @param {Function} callback
*/
export default function fingerprint(file, options) {
if (isReactNative()) {
Expand All @@ -19,14 +17,14 @@ export default function fingerprint(file, options) {
)
}

function reactNativeFingerprint(file, options) {
function reactNativeFingerprint<F, S>(file: ReactNativeFile, options: UploadOptions<F, S>): string {
const exifHash = file.exif ? hashCode(JSON.stringify(file.exif)) : 'noexif'
return ['tus-rn', file.name || 'noname', file.size || 'nosize', exifHash, options.endpoint].join(
'/',
)
}

function hashCode(str) {
function hashCode(str: string): number {
/* eslint-disable no-bitwise */
// from https://stackoverflow.com/a/8831937/151666
let hash = 0
Expand Down
98 changes: 0 additions & 98 deletions lib/browser/httpStack.js

This file was deleted.

106 changes: 106 additions & 0 deletions lib/browser/httpStack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { HttpProgressHandler, HttpRequest, HttpResponse, HttpStack } from '../upload'

// TODO: Can we remove this eslint-disable line
/* eslint-disable max-classes-per-file */
export default class XHRHttpStack implements HttpStack<Blob> {
createRequest(method, url) {
return new XHRRequest(method, url)
}

getName() {
return 'XHRHttpStack'
}
}

class XHRRequest implements HttpRequest<Blob> {
_xhr = new XMLHttpRequest()
_method: string
_url: string
_headers: Record<string, string> = {}

constructor(method: string, url: string) {
this._xhr.open(method, url, true)

this._method = method
this._url = url
}

getMethod(): string {
return this._method
}

getURL(): string {
return this._url
}

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

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

setProgressHandler(progressHandler: HttpProgressHandler): void {
// Test support for progress events before attaching an event listener
if (!('upload' in this._xhr)) {
return
}

this._xhr.upload.onprogress = (e) => {
if (!e.lengthComputable) {
return
}

progressHandler(e.loaded)
}
}

send(body?: Blob): Promise<XHRResponse> {
return new Promise((resolve, reject) => {
this._xhr.onload = () => {
resolve(new XHRResponse(this._xhr))
}

this._xhr.onerror = (err) => {
reject(err)
}

this._xhr.send(body)
})
}

abort(): Promise<void> {
this._xhr.abort()
return Promise.resolve()
}

getUnderlyingObject(): XMLHttpRequest {
return this._xhr
}
}

class XHRResponse implements HttpResponse {
_xhr: XMLHttpRequest

constructor(xhr: XMLHttpRequest) {
this._xhr = xhr
}

getStatus(): number {
return this._xhr.status
}

getHeader(header: string): string | undefined {
return this._xhr.getResponseHeader(header) || undefined
}

getBody(): string {
return this._xhr.responseText
}

getUnderlyingObject(): XMLHttpRequest {
return this._xhr
}
}
45 changes: 0 additions & 45 deletions lib/browser/index.js

This file was deleted.

0 comments on commit de3473f

Please sign in to comment.