diff --git a/packages/@uppy/companion-client/src/AuthError.js b/packages/@uppy/companion-client/src/AuthError.ts similarity index 87% rename from packages/@uppy/companion-client/src/AuthError.js rename to packages/@uppy/companion-client/src/AuthError.ts index 14517d3400..3bd743091f 100644 --- a/packages/@uppy/companion-client/src/AuthError.js +++ b/packages/@uppy/companion-client/src/AuthError.ts @@ -1,6 +1,8 @@ 'use strict' class AuthError extends Error { + public isAuthError: true + constructor () { super('Authorization required') this.name = 'AuthError' diff --git a/packages/@uppy/companion-client/src/Provider.js b/packages/@uppy/companion-client/src/Provider.ts similarity index 93% rename from packages/@uppy/companion-client/src/Provider.js rename to packages/@uppy/companion-client/src/Provider.ts index 28a7a38f71..8f2d10efa7 100644 --- a/packages/@uppy/companion-client/src/Provider.js +++ b/packages/@uppy/companion-client/src/Provider.ts @@ -1,18 +1,18 @@ 'use strict' -import RequestClient from './RequestClient.js' -import * as tokenStorage from './tokenStorage.js' +import RequestClient from './RequestClient.ts' +import * as tokenStorage from './tokenStorage.ts' -const getName = (id) => { +const getName = (id: string): string => { return id.split('-').map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(' ') } -function getOrigin () { +function getOrigin (): Location['origin'] { // eslint-disable-next-line no-restricted-globals return location.origin } -function getRegex (value) { +function getRegex (value: unknown): RegExp | undefined { if (typeof value === 'string') { return new RegExp(`^${value}$`) } if (value instanceof RegExp) { @@ -21,14 +21,14 @@ function getRegex (value) { return undefined } -function isOriginAllowed (origin, allowedOrigin) { +function isOriginAllowed (origin: Location['origin'], allowedOrigin: unknown): boolean { const patterns = Array.isArray(allowedOrigin) ? allowedOrigin.map(getRegex) : [getRegex(allowedOrigin)] return patterns .some((pattern) => pattern?.test(origin) || pattern?.test(`${origin}/`)) // allowing for trailing '/' } export default class Provider extends RequestClient { - #refreshingTokenPromise + #refreshingTokenPromise: Promise constructor (uppy, opts) { super(uppy, opts) @@ -41,9 +41,9 @@ export default class Provider extends RequestClient { this.preAuthToken = null } - async headers () { + async headers (): Promise> { const [headers, token] = await Promise.all([super.headers(), this.#getAuthToken()]) - const authHeaders = {} + const authHeaders: Record = { __proto__: null as unknown as string } if (token) { authHeaders['uppy-auth-token'] = token } @@ -56,7 +56,7 @@ export default class Provider extends RequestClient { return { ...headers, ...authHeaders } } - onReceiveResponse (response) { + onReceiveResponse (response: Response) { super.onReceiveResponse(response) const plugin = this.uppy.getPlugin(this.pluginId) const oldAuthenticated = plugin.getPluginState().authenticated @@ -158,7 +158,7 @@ export default class Provider extends RequestClient { async request (...args) { await this.#refreshingTokenPromise - try { + try{ // to test simulate access token expired (leading to a token token refresh), // see mockAccessTokenExpiredError in companion/drive. // If you want to test refresh token *and* access token invalid, do this for example with Google Drive: diff --git a/packages/@uppy/companion-client/src/RequestClient.test.js b/packages/@uppy/companion-client/src/RequestClient.test.ts similarity index 91% rename from packages/@uppy/companion-client/src/RequestClient.test.js rename to packages/@uppy/companion-client/src/RequestClient.test.ts index b1817827c5..c14d0d7aef 100644 --- a/packages/@uppy/companion-client/src/RequestClient.test.js +++ b/packages/@uppy/companion-client/src/RequestClient.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import RequestClient from './RequestClient.js' +import RequestClient from './RequestClient.ts' describe('RequestClient', () => { it('has a hostname without trailing slash', () => { diff --git a/packages/@uppy/companion-client/src/RequestClient.js b/packages/@uppy/companion-client/src/RequestClient.ts similarity index 91% rename from packages/@uppy/companion-client/src/RequestClient.js rename to packages/@uppy/companion-client/src/RequestClient.ts index ffaac2fd28..cdcab28c25 100644 --- a/packages/@uppy/companion-client/src/RequestClient.js +++ b/packages/@uppy/companion-client/src/RequestClient.ts @@ -8,12 +8,15 @@ import ErrorWithCause from '@uppy/utils/lib/ErrorWithCause' import emitSocketProgress from '@uppy/utils/lib/emitSocketProgress' import getSocketHost from '@uppy/utils/lib/getSocketHost' -import AuthError from './AuthError.js' - +import AuthError from './AuthError.ts' +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore We don't want TS to generate types for the package.json import packageJson from '../package.json' -// Remove the trailing slash so we can always safely append /xyz. -function stripSlash (url) { +/** + * Removes the trailing slash so we can always safely append /xyz. + */ +function stripSlash (url: string): string { return url.replace(/\/$/, '') } @@ -23,15 +26,15 @@ const socketActivityTimeoutMs = 5 * 60 * 1000 // set to a low number like 10000 const authErrorStatusCode = 401 class HttpError extends Error { - statusCode + public statusCode: number - constructor({ statusCode, message }) { + constructor({ statusCode, message }: { statusCode: number, message: string}) { super(message) this.statusCode = statusCode } } -async function handleJSONResponse (res) { +async function handleJSONResponse (res: Response): Promise> { if (res.status === authErrorStatusCode) { throw new AuthError() } @@ -61,7 +64,7 @@ const allowedHeadersCache = new Map() export default class RequestClient { static VERSION = packageJson.version - #companionHeaders + #companionHeaders: Record constructor (uppy, opts) { this.uppy = uppy @@ -70,21 +73,21 @@ export default class RequestClient { this.#companionHeaders = opts?.companionHeaders } - setCompanionHeaders (headers) { + setCompanionHeaders (headers: Record): void { this.#companionHeaders = headers } - [Symbol.for('uppy test: getCompanionHeaders')] () { + private [Symbol.for('uppy test: getCompanionHeaders')] (): Record { return this.#companionHeaders } - get hostname () { + get hostname (): string { const { companion } = this.uppy.getState() const host = this.opts.companionUrl return stripSlash(companion && companion[host] ? companion[host] : host) } - async headers () { + async headers (): Promise> { const defaultHeaders = { Accept: 'application/json', 'Content-Type': 'application/json', @@ -97,7 +100,7 @@ export default class RequestClient { } } - onReceiveResponse ({ headers }) { + onReceiveResponse ({ headers }: Response): void { const state = this.uppy.getState() const companion = state.companion || {} const host = this.opts.companionUrl @@ -110,7 +113,7 @@ export default class RequestClient { } } - #getUrl (url) { + #getUrl (url: string): string { if (/^(https?:|)\/\//.test(url)) { return url } @@ -131,7 +134,7 @@ export default class RequestClient { Subsequent requests use the cached result of the preflight. However if there is an error retrieving the allowed headers, we will try again next time */ - async preflight (path) { + async preflight (path: string): Promise{ const allowedHeadersCached = allowedHeadersCache.get(this.hostname) if (allowedHeadersCached != null) return allowedHeadersCached @@ -141,7 +144,7 @@ export default class RequestClient { 'uppy-auth-token', ] - const promise = (async () => { + const promise = (async (): Promise => { try { const response = await fetch(this.#getUrl(path), { method: 'OPTIONS' }) @@ -176,7 +179,7 @@ export default class RequestClient { return promise } - async preflightAndHeaders (path) { + async preflightAndHeaders (path:string):Record { const [allowedHeaders, headers] = await Promise.all([ this.preflight(path), this.headers(), @@ -195,8 +198,7 @@ export default class RequestClient { ) } - /** @protected */ - async request ({ path, method = 'GET', data, skipPostResponse, signal }) { +protected async request ({ path, method = 'GET', data, skipPostResponse, signal }): ReturnType { try { const headers = await this.preflightAndHeaders(path) const response = await fetchWithNetworkError(this.#getUrl(path), { @@ -219,21 +221,21 @@ export default class RequestClient { } } - async get (path, options = undefined) { + async get (path, options = undefined): ReturnType { // TODO: remove boolean support for options that was added for backward compatibility. // eslint-disable-next-line no-param-reassign if (typeof options === 'boolean') options = { skipPostResponse: options } return this.request({ ...options, path }) } - async post (path, data, options = undefined) { + async post (path, data, options = undefined): ReturnType { // TODO: remove boolean support for options that was added for backward compatibility. // eslint-disable-next-line no-param-reassign if (typeof options === 'boolean') options = { skipPostResponse: options } return this.request({ ...options, path, method: 'POST', data }) } - async delete (path, data = undefined, options) { + async delete (path, data = undefined, options): ReturnType { // TODO: remove boolean support for options that was added for backward compatibility. // eslint-disable-next-line no-param-reassign if (typeof options === 'boolean') options = { skipPostResponse: options } @@ -312,7 +314,7 @@ export default class RequestClient { } } - #requestSocketToken = async ({ file, postBody, signal }) => { + #requestSocketToken = async ({ file, postBody, signal }): Promise => { if (file.remote.url == null) { throw new Error('Cannot connect to an undefined URL') } @@ -329,10 +331,8 @@ export default class RequestClient { * This method will ensure a websocket for the specified file and returns a promise that resolves * when the file has finished downloading, or rejects if it fails. * It will retry if the websocket gets disconnected - * - * @param {{ file: UppyFile, queue: RateLimitedQueue, signal: AbortSignal }} file */ - async #awaitRemoteFileUpload ({ file, queue, signal }) { + async #awaitRemoteFileUpload ({ file, queue, signal }: { file: UppyFile, queue: RateLimitedQueue, signal?: AbortSignal }): Promise { let removeEventHandlers const { capabilities } = this.uppy.getState() diff --git a/packages/@uppy/companion-client/src/SearchProvider.js b/packages/@uppy/companion-client/src/SearchProvider.ts similarity index 72% rename from packages/@uppy/companion-client/src/SearchProvider.js rename to packages/@uppy/companion-client/src/SearchProvider.ts index 19bc051d74..0ab41adfa3 100644 --- a/packages/@uppy/companion-client/src/SearchProvider.js +++ b/packages/@uppy/companion-client/src/SearchProvider.ts @@ -1,12 +1,14 @@ 'use strict' -import RequestClient from './RequestClient.js' +import RequestClient from './RequestClient.ts' -const getName = (id) => { +const getName = (id: string) : string=> { return id.split('-').map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(' ') } export default class SearchProvider extends RequestClient { + public id: string + constructor (uppy, opts) { super(uppy, opts) this.provider = opts.provider @@ -15,11 +17,11 @@ export default class SearchProvider extends RequestClient { this.pluginId = this.opts.pluginId } - fileUrl (id) { + fileUrl (id: string): string { return `${this.hostname}/search/${this.id}/get/${id}` } - search (text, queries) { + search (text: string, queries?: string): Promise { return this.get(`search/${this.id}/list?q=${encodeURIComponent(text)}${queries ? `&${queries}` : ''}`) } } diff --git a/packages/@uppy/companion-client/src/index.js b/packages/@uppy/companion-client/src/index.js deleted file mode 100644 index 6dc7cb19e0..0000000000 --- a/packages/@uppy/companion-client/src/index.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' - -/** - * Manages communications with Companion - */ - -export { default as RequestClient } from './RequestClient.js' -export { default as Provider } from './Provider.js' -export { default as SearchProvider } from './SearchProvider.js' diff --git a/packages/@uppy/companion-client/src/index.ts b/packages/@uppy/companion-client/src/index.ts new file mode 100644 index 0000000000..c347daaf88 --- /dev/null +++ b/packages/@uppy/companion-client/src/index.ts @@ -0,0 +1,7 @@ +/** + * Manages communications with Companion + */ + +export { default as RequestClient } from './RequestClient.ts' +export { default as Provider } from './Provider.ts' +export { default as SearchProvider } from './SearchProvider.ts' diff --git a/packages/@uppy/companion-client/src/tokenStorage.js b/packages/@uppy/companion-client/src/tokenStorage.ts similarity index 50% rename from packages/@uppy/companion-client/src/tokenStorage.js rename to packages/@uppy/companion-client/src/tokenStorage.ts index 2bfc9f5873..e16816246e 100644 --- a/packages/@uppy/companion-client/src/tokenStorage.js +++ b/packages/@uppy/companion-client/src/tokenStorage.ts @@ -3,18 +3,18 @@ /** * This module serves as an Async wrapper for LocalStorage */ -export function setItem (key, value) { +export function setItem (key: Parameters[0], value:Parameters[1]): Promise { return new Promise((resolve) => { localStorage.setItem(key, value) resolve() }) } -export function getItem (key) { +export function getItem (key: Parameters[0]): Promise> { return Promise.resolve(localStorage.getItem(key)) } -export function removeItem (key) { +export function removeItem (key: Parameters[0]): Promise { return new Promise((resolve) => { localStorage.removeItem(key) resolve() diff --git a/packages/@uppy/companion-client/tsconfig.build.json b/packages/@uppy/companion-client/tsconfig.build.json new file mode 100644 index 0000000000..97515c01f2 --- /dev/null +++ b/packages/@uppy/companion-client/tsconfig.build.json @@ -0,0 +1,21 @@ +{ + "extends": "../../../tsconfig.shared", + "compilerOptions": { + "outDir": "./lib", + "rootDir": "./src", + "resolveJsonModule": false, + "noImplicitAny": false, + "skipLibCheck": true + }, + "include": [ + "./src/**/*.*" + ], + "exclude": [ + "./src/**/*.test.ts" + ], + "references": [ + { + "path": "../utils/tsconfig.build.json" + } + ] +} diff --git a/packages/@uppy/companion-client/tsconfig.json b/packages/@uppy/companion-client/tsconfig.json new file mode 100644 index 0000000000..1c179363de --- /dev/null +++ b/packages/@uppy/companion-client/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../../tsconfig.shared", + "compilerOptions": { + "emitDeclarationOnly": false, + "noEmit": true + }, + "include": [ + "./package.json", + "./src/**/*.*" + ], + "references": [ + { + "path": "../utils/tsconfig.build.json" + } + ] +}