diff --git a/.eslintignore b/.eslintignore index e55628a5..e876629e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,4 @@ +coverage/ +dist/ node_modules/ fixture/ diff --git a/.eslintrc.js b/.eslintrc.js index f9fe1521..76899e37 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,6 +5,16 @@ module.exports = { ecmaVersion: 11, requireConfigFile: false, }, + settings: { + 'import/resolver': { + node: { + extensions: ['.js', '.ts'], + }, + }, + }, + rules: { + 'import/extensions': 'off', + }, overrides: [ { files: 'test/**', diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c7d2bedc..9d242237 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: - run: corepack yarn - run: corepack yarn prettier --check . - tsd: + typescript: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -49,7 +49,7 @@ jobs: with: node-version: 22 - run: corepack yarn - - run: corepack yarn tsd + - run: corepack yarn tsc --build vitest: runs-on: ubuntu-latest @@ -73,7 +73,7 @@ jobs: - eslint - pack - prettier - - tsd + - typescript - vitest if: startsWith(github.ref, 'refs/tags/') permissions: diff --git a/.gitignore b/.gitignore index 33be1724..d6fc5fac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ +dist/ node_modules cloudflared* credentials.js sample.js +*.tsbuildinfo npm-debug.log env.sh /coverage diff --git a/.prettierignore b/.prettierignore index d1e15844..272265b5 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,4 @@ coverage/ +dist/ fixture/ node_modules/ diff --git a/README.md b/README.md index e1b66bc6..739329b9 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ npm install --save transloadit The following code will upload an image and resize it to a thumbnail: ```javascript -const Transloadit = require('transloadit') +const { Transloadit } = require('transloadit') const transloadit = new Transloadit({ authKey: 'YOUR_TRANSLOADIT_KEY', @@ -113,7 +113,7 @@ For more example use cases and information about the available robots and their ## API -These are the public methods on the `Transloadit` object and their descriptions. The methods are based on the [Transloadit API](https://transloadit.com/docs/api/). See also [TypeScript definitions](types/index.d.ts). +These are the public methods on the `Transloadit` object and their descriptions. The methods are based on the [Transloadit API](https://transloadit.com/docs/api/). Table of contents: @@ -391,7 +391,7 @@ This function returns an object with the key `signature` (containing the calcula ### Errors -Errors from Node.js will be passed on and we use [GOT](https://github.com/sindresorhus/got) for HTTP requests and errors from there will also be passed on. When the HTTP response code is not 200, the error will be a `Transloadit.HTTPError`, which is a [got.HTTPError](https://github.com/sindresorhus/got#errors)) with some additional properties: +Errors from Node.js will be passed on and we use [GOT](https://github.com/sindresorhus/got) for HTTP requests and errors from there will also be passed on. When the HTTP response code is not 200, the error will be an `HTTPError`, which is a [got.HTTPError](https://github.com/sindresorhus/got#errors)) with some additional properties: - `HTTPError.response?.body` the JSON object returned by the server along with the error response (**note**: `HTTPError.response` will be `undefined` for non-server errors) - `HTTPError.transloaditErrorCode` alias for `HTTPError.response.body.error` ([View all error codes](https://transloadit.com/docs/api/response-codes/#error-codes)) @@ -401,7 +401,7 @@ To identify errors you can either check its props or use `instanceof`, e.g.: ```js catch (err) { - if (err instanceof Transloadit.TimeoutError) { + if (err instanceof TimeoutError) { return console.error('The request timed out', err) } if (err.code === 'ENOENT') { diff --git a/index.js b/index.js deleted file mode 100644 index 3338e8c9..00000000 --- a/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./src/Transloadit') diff --git a/package.json b/package.json index dba68ed0..2d69bdf4 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "@babel/core": "^7.12.3", "@babel/eslint-parser": "^7.15.8", "@babel/eslint-plugin": "^7.13.10", + "@types/debug": "^4.1.12", + "@types/temp": "^0.9.4", "@vitest/coverage-v8": "^2.0.5", "badge-maker": "^3.3.0", "eslint": "^7.18.0", @@ -46,7 +48,7 @@ "p-retry": "^4.2.0", "prettier": "^2.8.6", "temp": "^0.9.1", - "tsd": "^0.25.0", + "typescript": "^5.5.4", "vitest": "^2.0.5" }, "repository": { @@ -62,20 +64,22 @@ "lint": "npm-run-all --parallel 'lint:*'", "fix": "npm-run-all --serial 'fix:*'", "next:update": "next-update --keep true --tldr", + "prepack": "tsc --build", "test-unit": "vitest run --coverage ./test/unit", "test-integration": "vitest run ./test/integration", - "tsd": "tsd", "test-all": "npm run tsd && vitest run --coverage", "test": "npm run tsd && npm run test-unit", "fix:formatting": "prettier --write .", "lint:formatting": "prettier --check ." }, "license": "MIT", - "main": "./index", - "types": "types/index.d.ts", + "main": "./dist/Transloadit.js", + "exports": { + ".": "./dist/Transloadit.js", + "./package.json": "./package.json" + }, "files": [ - "index.js", - "src", - "types/index.d.ts" + "dist", + "src" ] } diff --git a/src/InconsistentResponseError.js b/src/InconsistentResponseError.js deleted file mode 100644 index b9dea30c..00000000 --- a/src/InconsistentResponseError.js +++ /dev/null @@ -1,5 +0,0 @@ -class InconsistentResponseError extends Error { - name = 'InconsistentResponseError' -} - -module.exports = InconsistentResponseError diff --git a/src/InconsistentResponseError.ts b/src/InconsistentResponseError.ts new file mode 100644 index 00000000..fbc5fe95 --- /dev/null +++ b/src/InconsistentResponseError.ts @@ -0,0 +1,3 @@ +export class InconsistentResponseError extends Error { + override name = 'InconsistentResponseError' +} diff --git a/src/PaginationStream.js b/src/PaginationStream.ts similarity index 55% rename from src/PaginationStream.js rename to src/PaginationStream.ts index b614e61c..9dad02f9 100644 --- a/src/PaginationStream.js +++ b/src/PaginationStream.ts @@ -1,15 +1,21 @@ -const stream = require('stream') +import { Readable } from 'stream' +import { PaginationList } from './Transloadit' -class PaginationStream extends stream.Readable { - constructor(_fetchPage) { +type FetchPage = (pageno: number) => PaginationList | PromiseLike> + +export class PaginationStream extends Readable { + private _fetchPage: FetchPage + private _nitems?: number + private _pageno = 0 + private _items: T[] = [] + private _itemsRead = 0 + + constructor(fetchPage: FetchPage) { super({ objectMode: true }) - this._fetchPage = _fetchPage - this._pageno = 0 - this._items = [] - this._itemsRead = 0 + this._fetchPage = fetchPage } - async _read() { + override async _read() { if (this._items.length > 0) { this._itemsRead++ process.nextTick(() => this.push(this._items.pop())) @@ -29,11 +35,8 @@ class PaginationStream extends stream.Readable { this._items.reverse() this._read() - return } catch (err) { this.emit('error', err) } } } - -module.exports = PaginationStream diff --git a/src/PollingTimeoutError.js b/src/PollingTimeoutError.js deleted file mode 100644 index 1a497fff..00000000 --- a/src/PollingTimeoutError.js +++ /dev/null @@ -1,7 +0,0 @@ -class PollingTimeoutError extends Error { - name = 'PollingTimeoutError' - - code = 'POLLING_TIMED_OUT' -} - -module.exports = PollingTimeoutError diff --git a/src/PollingTimeoutError.ts b/src/PollingTimeoutError.ts new file mode 100644 index 00000000..016a9b2a --- /dev/null +++ b/src/PollingTimeoutError.ts @@ -0,0 +1,4 @@ +export class PollingTimeoutError extends Error { + override name = 'PollingTimeoutError' + code = 'POLLING_TIMED_OUT' +} diff --git a/src/Transloadit.js b/src/Transloadit.ts similarity index 53% rename from src/Transloadit.js rename to src/Transloadit.ts index 84a7ec2a..1afd45b9 100644 --- a/src/Transloadit.js +++ b/src/Transloadit.ts @@ -1,25 +1,53 @@ -const crypto = require('crypto') -const got = require('got') -const FormData = require('form-data') -const fs = require('fs') -const fsPromises = require('fs/promises') -const debug = require('debug') -const intoStream = require('into-stream') -const isStream = require('is-stream') -const assert = require('assert') -const pMap = require('p-map') - -const InconsistentResponseError = require('./InconsistentResponseError') -const PaginationStream = require('./PaginationStream') -const PollingTimeoutError = require('./PollingTimeoutError') -const TransloaditError = require('./TransloaditError') -const pkg = require('../package.json') -const tus = require('./tus') +import { createHmac, randomUUID } from 'crypto' +import got, { RequiredRetryOptions, Headers, OptionsOfJSONResponseBody } from 'got' +import FormData from 'form-data' +import { constants, createReadStream } from 'fs' +import { access } from 'fs/promises' +import debug from 'debug' +import intoStream from 'into-stream' +import isStream from 'is-stream' +import * as assert from 'assert' +import pMap from 'p-map' +import { InconsistentResponseError } from './InconsistentResponseError' +import { PaginationStream } from './PaginationStream' +import { PollingTimeoutError } from './PollingTimeoutError' +import { TransloaditError } from './TransloaditError' +import { version } from '../package.json' +import { sendTusRequest, Stream } from './tus' + +import type { Readable } from 'stream' + +// See https://github.com/sindresorhus/got#errors +// Expose relevant errors +export { + RequestError, + ReadError, + ParseError, + UploadError, + HTTPError, + MaxRedirectsError, + TimeoutError, +} from 'got' +export { InconsistentResponseError } const log = debug('transloadit') const logWarn = debug('transloadit:warn') -function decorateHttpError(err, body) { +interface RequestOptions { + urlSuffix?: string + url?: string + timeout?: number + method?: 'delete' | 'get' | 'post' | 'put' + params?: KeyVal + fields?: Record + headers?: Headers +} + +interface CreateAssemblyPromise extends Promise { + assemblyId: string +} + +function decorateHttpError(err: TransloaditError, body: any): TransloaditError { if (!body) return err let newMessage = err.message @@ -39,7 +67,7 @@ function decorateHttpError(err, body) { /* eslint-disable no-param-reassign */ err.message = newMessage - err.stack = newStack + if (newStack != null) err.stack = newStack if (body.assembly_id) err.assemblyId = body.assembly_id if (body.error) err.transloaditErrorCode = body.error /* eslint-enable no-param-reassign */ @@ -48,45 +76,51 @@ function decorateHttpError(err, body) { } // Not sure if this is still a problem with the API, but throw a special error type so the user can retry if needed -function checkAssemblyUrls(result) { +function checkAssemblyUrls(result: Assembly) { if (result.assembly_url == null || result.assembly_ssl_url == null) { throw new InconsistentResponseError('Server returned an incomplete assembly response (no URL)') } } -function getHrTimeMs() { +function getHrTimeMs(): number { return Number(process.hrtime.bigint() / 1000000n) } -function checkResult(result) { +function checkResult(result: T | { error: string }): asserts result is T { // In case server returned a successful HTTP status code, but an `error` in the JSON object // This happens sometimes when createAssembly with an invalid file (IMPORT_FILE_ERROR) - if (typeof result === 'object' && result !== null && typeof result.error === 'string') { + if ( + typeof result === 'object' && + result !== null && + 'error' in result && + typeof result.error === 'string' + ) { throw decorateHttpError(new TransloaditError('Error in response', result), result) } } -class TransloaditClient { - // See https://github.com/sindresorhus/got#errors - // Expose relevant errors - static RequestError = got.RequestError - - static ReadError = got.ReadError - - static ParseError = got.ParseError - - static UploadError = got.UploadError - - static HTTPError = got.HTTPError - - static MaxRedirectsError = got.MaxRedirectsError - - static TimeoutError = got.TimeoutError - - static InconsistentResponseError = InconsistentResponseError +export declare namespace Transloadit { + interface Options { + authKey: string + authSecret: string + endpoint?: string + maxRetries?: number + timeout?: number + gotRetry?: RequiredRetryOptions + } +} - constructor(opts = {}) { - if (opts.authKey == null) { +export class Transloadit { + private _authKey: string + private _authSecret: string + private _endpoint: string + private _maxRetries: number + private _defaultTimeout: number + private _gotRetry: RequiredRetryOptions | number + private _lastUsedAssemblyUrl = '' + + constructor(opts: Transloadit.Options) { + if (opts?.authKey == null) { throw new Error('Please provide an authKey') } @@ -106,25 +140,22 @@ class TransloaditClient { // Passed on to got https://github.com/sindresorhus/got/blob/main/documentation/7-retry.md this._gotRetry = opts.gotRetry != null ? opts.gotRetry : 0 - - this._lastUsedAssemblyUrl = '' } - getLastUsedAssemblyUrl() { + getLastUsedAssemblyUrl(): string { return this._lastUsedAssemblyUrl } - setDefaultTimeout(timeout) { + setDefaultTimeout(timeout: number): void { this._defaultTimeout = timeout } /** * Create an Assembly * - * @param {object} opts assembly options - * @returns {Promise} + * @param opts assembly options */ - createAssembly(opts = {}, arg2) { + createAssembly(opts: CreateAssemblyOptions = {}, arg2?: void): CreateAssemblyPromise { // Warn users of old callback API if (typeof arg2 === 'function') { throw new TypeError( @@ -163,7 +194,7 @@ class TransloaditClient { if (assemblyId != null) { effectiveAssemblyId = assemblyId } else { - effectiveAssemblyId = crypto.randomUUID().replace(/-/g, '') + effectiveAssemblyId = randomUUID().replace(/-/g, '') } const urlSuffix = `/assemblies/${effectiveAssemblyId}` @@ -174,7 +205,7 @@ class TransloaditClient { await pMap( Object.entries(files), // eslint-disable-next-line no-bitwise - async ([, path]) => fsPromises.access(path, fs.constants.F_OK | fs.constants.R_OK), + async ([, path]) => access(path, constants.F_OK | constants.R_OK), { concurrency: 5 } ) @@ -191,14 +222,14 @@ class TransloaditClient { }) ) - // Wrap in object structure (so we can know if it's a pathless stream or not) - const allStreamsMap = Object.fromEntries( + // Wrap in object structure (so we can store whether it's a pathless stream or not) + const allStreamsMap = Object.fromEntries( Object.entries(streamsMap).map(([label, stream]) => [label, { stream }]) ) // Create streams from files too for (const [label, path] of Object.entries(files)) { - const stream = fs.createReadStream(path) + const stream = createReadStream(path) allStreamsMap[label] = { stream, path } // File streams have path } @@ -208,12 +239,12 @@ class TransloaditClient { allStreams.forEach(({ stream }) => stream.pause()) // If any stream emits error, we want to handle this and exit with error - const streamErrorPromise = new Promise((resolve, reject) => { + const streamErrorPromise = new Promise((resolve, reject) => { allStreams.forEach(({ stream }) => stream.on('error', reject)) }) const createAssemblyAndUpload = async () => { - const requestOpts = { + const requestOpts: RequestOptions = { urlSuffix, method: 'post', timeout, @@ -227,13 +258,17 @@ class TransloaditClient { } // upload as form multipart or tus? - const formUploadStreamsMap = isResumable ? {} : allStreamsMap + const formUploadStreamsMap: Record = isResumable ? {} : allStreamsMap - const result = await this._remoteJson(requestOpts, formUploadStreamsMap, onUploadProgress) + const result = await this._remoteJson( + requestOpts, + formUploadStreamsMap, + onUploadProgress + ) checkResult(result) if (isResumable && Object.keys(allStreamsMap).length > 0) { - await tus.sendTusRequest({ + await sendTusRequest({ streamsMap: allStreamsMap, assembly: result, onProgress: onUploadProgress, @@ -256,21 +291,29 @@ class TransloaditClient { })() // This allows the user to use or log the assemblyId even before it has been created for easier debugging - promise.assemblyId = effectiveAssemblyId - return promise + return Object.assign(promise, { assemblyId: effectiveAssemblyId }) } async awaitAssemblyCompletion( - assemblyId, - { onAssemblyProgress = () => {}, timeout, startTimeMs = getHrTimeMs(), interval = 1000 } = {} - ) { - assert(assemblyId) + assemblyId: string, + { + onAssemblyProgress = () => {}, + timeout, + startTimeMs = getHrTimeMs(), + interval = 1000, + }: AwaitAssemblyCompletionOptions = {} + ): Promise { + assert.ok(assemblyId) // eslint-disable-next-line no-constant-condition while (true) { const result = await this.getAssembly(assemblyId) - if (!['ASSEMBLY_UPLOADING', 'ASSEMBLY_EXECUTING', 'ASSEMBLY_REPLAYING'].includes(result.ok)) { + if ( + result.ok !== 'ASSEMBLY_UPLOADING' && + result.ok !== 'ASSEMBLY_EXECUTING' && + result.ok !== 'ASSEMBLY_REPLAYING' + ) { return result // Done! } @@ -291,16 +334,16 @@ class TransloaditClient { /** * Cancel the assembly * - * @param {string} assemblyId assembly ID - * @returns {Promise} after the assembly is deleted + * @param assemblyId assembly ID + * @returns after the assembly is deleted */ - async cancelAssembly(assemblyId) { + async cancelAssembly(assemblyId: string): Promise { // You may wonder why do we need to call getAssembly first: // If we use the default base URL (instead of the one returned in assembly_url_ssl), // the delete call will hang in certain cases // See test "should stop the assembly from reaching completion" const { assembly_ssl_url: url } = await this.getAssembly(assemblyId) - const opts = { + const opts: RequestOptions = { url, // urlSuffix: `/assemblies/${assemblyId}`, // Cannot simply do this, see above method: 'delete', @@ -312,17 +355,17 @@ class TransloaditClient { /** * Replay an Assembly * - * @param {string} assemblyId of the assembly to replay - * @param {object} optional params - * @returns {Promise} after the replay is started + * @param assemblyId of the assembly to replay + * @param optional params + * @returns after the replay is started */ - async replayAssembly(assemblyId, params = {}) { - const requestOpts = { + async replayAssembly(assemblyId: string, params: KeyVal = {}): Promise { + const requestOpts: RequestOptions = { urlSuffix: `/assemblies/${assemblyId}/replay`, method: 'post', } if (Object.keys(params).length > 0) requestOpts.params = params - const result = await this._remoteJson(requestOpts) + const result = await this._remoteJson(requestOpts) checkResult(result) return result } @@ -330,12 +373,15 @@ class TransloaditClient { /** * Replay an Assembly notification * - * @param {string} assemblyId of the assembly whose notification to replay - * @param {object} optional params - * @returns {Promise} after the replay is started + * @param assemblyId of the assembly whose notification to replay + * @param optional params + * @returns after the replay is started */ - async replayAssemblyNotification(assemblyId, params = {}) { - const requestOpts = { + async replayAssemblyNotification( + assemblyId: string, + params: KeyVal = {} + ): Promise<{ ok: string; success: boolean }> { + const requestOpts: RequestOptions = { urlSuffix: `/assembly_notifications/${assemblyId}/replay`, method: 'post', } @@ -346,11 +392,11 @@ class TransloaditClient { /** * List all assembly notifications * - * @param {object} params optional request options - * @returns {Promise} the list of Assembly notifications + * @param params optional request options + * @returns the list of Assembly notifications */ - async listAssemblyNotifications(params) { - const requestOpts = { + async listAssemblyNotifications(params: object): Promise> { + const requestOpts: RequestOptions = { urlSuffix: '/assembly_notifications', method: 'get', params: params || {}, @@ -359,18 +405,18 @@ class TransloaditClient { return this._remoteJson(requestOpts) } - streamAssemblyNotifications(params) { + streamAssemblyNotifications(params: object): PaginationStream { return new PaginationStream(async (page) => this.listAssemblyNotifications({ ...params, page })) } /** * List all assemblies * - * @param {object} params optional request options - * @returns {Promise} list of Assemblies + * @param params optional request options + * @returns list of Assemblies */ - async listAssemblies(params) { - const requestOpts = { + async listAssemblies(params?: KeyVal): Promise> { + const requestOpts: RequestOptions = { urlSuffix: '/assemblies', method: 'get', params: params || {}, @@ -379,18 +425,20 @@ class TransloaditClient { return this._remoteJson(requestOpts) } - streamAssemblies(params) { + streamAssemblies(params: KeyVal): Readable { return new PaginationStream(async (page) => this.listAssemblies({ ...params, page })) } /** * Get an Assembly * - * @param {string} assemblyId the Assembly Id - * @returns {Promise} the retrieved Assembly + * @param assemblyId the Assembly Id + * @returns the retrieved Assembly */ - async getAssembly(assemblyId) { - const result = await this._remoteJson({ urlSuffix: `/assemblies/${assemblyId}` }) + async getAssembly(assemblyId: string): Promise { + const result = await this._remoteJson({ + urlSuffix: `/assemblies/${assemblyId}`, + }) checkAssemblyUrls(result) return result } @@ -398,11 +446,11 @@ class TransloaditClient { /** * Create a Credential * - * @param {object} params optional request options - * @returns {Promise} when the Credential is created + * @param params optional request options + * @returns when the Credential is created */ - async createTemplateCredential(params) { - const requestOpts = { + async createTemplateCredential(params: object): Promise { + const requestOpts: RequestOptions = { urlSuffix: '/template_credentials', method: 'post', params: params || {}, @@ -414,12 +462,12 @@ class TransloaditClient { /** * Edit a Credential * - * @param {string} credentialId the Credential ID - * @param {object} params optional request options - * @returns {Promise} when the Credential is edited + * @param credentialId the Credential ID + * @param params optional request options + * @returns when the Credential is edited */ - async editTemplateCredential(credentialId, params) { - const requestOpts = { + async editTemplateCredential(credentialId: string, params: object): Promise { + const requestOpts: RequestOptions = { urlSuffix: `/template_credentials/${credentialId}`, method: 'put', params: params || {}, @@ -431,11 +479,11 @@ class TransloaditClient { /** * Delete a Credential * - * @param {string} credentialId the Credential ID - * @returns {Promise} when the Credential is deleted + * @param credentialId the Credential ID + * @returns when the Credential is deleted */ - async deleteTemplateCredential(credentialId) { - const requestOpts = { + async deleteTemplateCredential(credentialId: string): Promise { + const requestOpts: RequestOptions = { urlSuffix: `/template_credentials/${credentialId}`, method: 'delete', } @@ -446,11 +494,11 @@ class TransloaditClient { /** * Get a Credential * - * @param {string} credentialId the Credential ID - * @returns {Promise} when the Credential is retrieved + * @param credentialId the Credential ID + * @returns when the Credential is retrieved */ - async getTemplateCredential(credentialId) { - const requestOpts = { + async getTemplateCredential(credentialId: string): Promise { + const requestOpts: RequestOptions = { urlSuffix: `/template_credentials/${credentialId}`, method: 'get', } @@ -461,11 +509,11 @@ class TransloaditClient { /** * List all TemplateCredentials * - * @param {object} params optional request options - * @returns {Promise} the list of templates + * @param params optional request options + * @returns the list of templates */ - async listTemplateCredentials(params) { - const requestOpts = { + async listTemplateCredentials(params?: object): Promise { + const requestOpts: RequestOptions = { urlSuffix: '/template_credentials', method: 'get', params: params || {}, @@ -474,18 +522,18 @@ class TransloaditClient { return this._remoteJson(requestOpts) } - streamTemplateCredentials(params) { + streamTemplateCredentials(params: object): PaginationStream { return new PaginationStream(async (page) => this.listTemplateCredentials({ ...params, page })) } /** * Create an Assembly Template * - * @param {object} params optional request options - * @returns {Promise} when the template is created + * @param params optional request options + * @returns when the template is created */ - async createTemplate(params) { - const requestOpts = { + async createTemplate(params: KeyVal = {}): Promise { + const requestOpts: RequestOptions = { urlSuffix: '/templates', method: 'post', params: params || {}, @@ -497,12 +545,12 @@ class TransloaditClient { /** * Edit an Assembly Template * - * @param {string} templateId the template ID - * @param {object} params optional request options - * @returns {Promise} when the template is edited + * @param templateId the template ID + * @param params optional request options + * @returns when the template is edited */ - async editTemplate(templateId, params) { - const requestOpts = { + async editTemplate(templateId: string, params: KeyVal): Promise { + const requestOpts: RequestOptions = { urlSuffix: `/templates/${templateId}`, method: 'put', params: params || {}, @@ -514,11 +562,11 @@ class TransloaditClient { /** * Delete an Assembly Template * - * @param {string} templateId the template ID - * @returns {Promise} when the template is deleted + * @param templateId the template ID + * @returns when the template is deleted */ - async deleteTemplate(templateId) { - const requestOpts = { + async deleteTemplate(templateId: string): Promise<{ ok: string; message: string }> { + const requestOpts: RequestOptions = { urlSuffix: `/templates/${templateId}`, method: 'delete', } @@ -529,11 +577,11 @@ class TransloaditClient { /** * Get an Assembly Template * - * @param {string} templateId the template ID - * @returns {Promise} when the template is retrieved + * @param templateId the template ID + * @returns when the template is retrieved */ - async getTemplate(templateId) { - const requestOpts = { + async getTemplate(templateId: string): Promise { + const requestOpts: RequestOptions = { urlSuffix: `/templates/${templateId}`, method: 'get', } @@ -544,11 +592,11 @@ class TransloaditClient { /** * List all Assembly Templates * - * @param {object} params optional request options - * @returns {Promise} the list of templates + * @param params optional request options + * @returns the list of templates */ - async listTemplates(params) { - const requestOpts = { + async listTemplates(params?: KeyVal): Promise> { + const requestOpts: RequestOptions = { urlSuffix: '/templates', method: 'get', params: params || {}, @@ -557,19 +605,20 @@ class TransloaditClient { return this._remoteJson(requestOpts) } - streamTemplates(params) { + streamTemplates(params?: KeyVal): PaginationStream { return new PaginationStream(async (page) => this.listTemplates({ ...params, page })) } /** * Get account Billing details for a specific month * - * @param {string} month the date for the required billing in the format yyyy-mm - * @returns {Promise} with billing data + * @param month the date for the required billing in the format yyyy-mm + * @returns with billing data + * @see https://transloadit.com/docs/api/bill-date-get/ */ - async getBill(month) { - assert(month, 'month is required') - const requestOpts = { + async getBill(month: string): Promise { + assert.ok(month, 'month is required') + const requestOpts: RequestOptions = { urlSuffix: `/bill/${month}`, method: 'get', } @@ -577,23 +626,27 @@ class TransloaditClient { return this._remoteJson(requestOpts) } - calcSignature(params) { + calcSignature(params: KeyVal): { signature: string; params: string } { const jsonParams = this._prepareParams(params) const signature = this._calcSignature(jsonParams) return { signature, params: jsonParams } } - _calcSignature(toSign, algorithm = 'sha384') { - return `${algorithm}:${crypto - .createHmac(algorithm, this._authSecret) + private _calcSignature(toSign: string, algorithm = 'sha384'): string { + return `${algorithm}:${createHmac(algorithm, this._authSecret) .update(Buffer.from(toSign, 'utf-8')) .digest('hex')}` } // Sets the multipart/form-data for POST, PUT and DELETE requests, including // the streams, the signed params, and any additional fields. - _appendForm(form, params, streamsMap, fields) { + private _appendForm( + form: FormData, + params: KeyVal, + streamsMap?: Record, + fields?: Record + ): void { const sigData = this.calcSignature(params) const jsonParams = sigData.params const { signature } = sigData @@ -618,7 +671,7 @@ class TransloaditClient { // Implements HTTP GET query params, handling the case where the url already // has params. - _appendParamsToUrl(url, params) { + private _appendParamsToUrl(url: string, params: KeyVal): string { const { signature, params: jsonParams } = this.calcSignature(params) const prefix = url.indexOf('?') === -1 ? '?' : '&' @@ -627,19 +680,19 @@ class TransloaditClient { } // Responsible for including auth parameters in all requests - _prepareParams(paramsIn) { + private _prepareParams(paramsIn: KeyVal): string { let params = paramsIn if (params == null) { params = {} } - if (params.auth == null) { - params.auth = {} + if (params['auth'] == null) { + params['auth'] = {} } - if (params.auth.key == null) { - params.auth.key = this._authKey + if (params['auth'].key == null) { + params['auth'].key = this._authKey } - if (params.auth.expires == null) { - params.auth.expires = this._getExpiresDate() + if (params['auth'].expires == null) { + params['auth'].expires = this._getExpiresDate() } return JSON.stringify(params) @@ -647,7 +700,7 @@ class TransloaditClient { // We want to mock this method // eslint-disable-next-line class-methods-use-this - _getExpiresDate() { + private _getExpiresDate(): string { const expiresDate = new Date() expiresDate.setDate(expiresDate.getDate() + 1) return expiresDate.toISOString() @@ -656,7 +709,11 @@ class TransloaditClient { // Responsible for making API calls. Automatically sends streams with any POST, // PUT or DELETE requests. Automatically adds signature parameters to all // requests. Also automatically parses the JSON response. - async _remoteJson(opts, streamsMap, onProgress = () => {}) { + private async _remoteJson( + opts: RequestOptions, + streamsMap?: Record, + onProgress: CreateAssemblyOptions['onUploadProgress'] = () => {} + ): Promise { const { urlSuffix, url: urlInput, @@ -689,12 +746,12 @@ class TransloaditClient { const isUploadingStreams = streamsMap && Object.keys(streamsMap).length > 0 - const requestOpts = { + const requestOpts: OptionsOfJSONResponseBody = { retry: this._gotRetry, - body: form, + body: form as FormData, timeout, headers: { - 'Transloadit-Client': `node-sdk:${pkg.version}`, + 'Transloadit-Client': `node-sdk:${version}`, 'User-Agent': undefined, // Remove got's user-agent ...headers, }, @@ -704,10 +761,10 @@ class TransloaditClient { // For non-file streams transfer encoding does not get set, and the uploaded files will not get accepted // https://github.com/transloadit/node-sdk/issues/86 // https://github.com/form-data/form-data/issues/394#issuecomment-573595015 - if (isUploadingStreams) requestOpts.headers['transfer-encoding'] = 'chunked' + if (isUploadingStreams) requestOpts.headers!['transfer-encoding'] = 'chunked' try { - const request = got[method](url, requestOpts) + const request = got[method](url, requestOpts) if (isUploadingStreams) { request.on('uploadProgress', ({ transferred, total }) => onProgress({ uploadedBytes: transferred, totalBytes: total }) @@ -721,15 +778,26 @@ class TransloaditClient { const { statusCode, body } = err.response logWarn('HTTP error', statusCode, body) - const shouldRetry = - statusCode === 413 && - body.error === 'RATE_LIMIT_REACHED' && - body.info && - body.info.retryIn && - retryCount < this._maxRetries - + // check whether we should retry // https://transloadit.com/blog/2012/04/introducing-rate-limiting/ - if (!shouldRetry) throw decorateHttpError(err, body) + if ( + !( + statusCode === 413 && + typeof body === 'object' && + body != null && + 'error' in body && + body.error === 'RATE_LIMIT_REACHED' && + 'info' in body && + typeof body.info === 'object' && + body.info != null && + 'retryIn' in body.info && + typeof body.info.retryIn === 'number' && + Boolean(body.info.retryIn) && + retryCount < this._maxRetries + ) + ) { + throw decorateHttpError(err, body) + } const { retryIn: retryInSec } = body.info logWarn(`Rate limit reached, retrying request in approximately ${retryInSec} seconds.`) @@ -741,4 +809,161 @@ class TransloaditClient { } } -module.exports = TransloaditClient +export interface CreateAssemblyOptions { + params?: CreateAssemblyParams + files?: { + [name: string]: string + } + uploads?: { + [name: string]: Readable | intoStream.Input + } + waitForCompletion?: boolean + isResumable?: boolean + chunkSize?: number + uploadConcurrency?: number + timeout?: number + onUploadProgress?: (uploadProgress: UploadProgress) => void + onAssemblyProgress?: AssemblyProgress + assemblyId?: string +} + +export type AssemblyProgress = (assembly: Assembly) => void + +export interface CreateAssemblyParams { + /** See https://transloadit.com/docs/topics/assembly-instructions/ */ + steps?: KeyVal + template_id?: string + notify_url?: string + fields?: KeyVal + allow_steps_override?: boolean +} + +// TODO +/** Object with properties. See https://transloadit.com/docs/api/ */ +export interface KeyVal { + [key: string]: any +} + +export interface UploadProgress { + uploadedBytes?: number | undefined + totalBytes?: number | undefined +} + +/** https://transloadit.com/docs/api/assembly-status-response/#explanation-of-fields */ +export interface Assembly { + ok?: string + message?: string + assembly_id: string + parent_id?: string + account_id: string + template_id?: string + instance: string + assembly_url: string + assembly_ssl_url: string + uppyserver_url: string + companion_url: string + websocket_url: string + tus_url: string + bytes_received: number + bytes_expected: number + upload_duration: number + client_agent?: string + client_ip?: string + client_referer?: string + transloadit_client: string + start_date: string + upload_meta_data_extracted: boolean + warnings: any[] + is_infinite: boolean + has_dupe_jobs: boolean + execution_start: string + execution_duration: number + queue_duration: number + jobs_queue_duration: number + notify_start?: any + notify_url?: string + notify_status?: any + notify_response_code?: any + notify_duration?: any + last_job_completed?: string + fields: KeyVal + running_jobs: any[] + bytes_usage: number + executing_jobs: any[] + started_jobs: string[] + parent_assembly_status: any + params: string + template?: any + merged_params: string + uploads: any[] + results: any + build_id: string + error?: string + stderr?: string + stdout?: string + reason?: string +} + +/** See https://transloadit.com/docs/api/assemblies-assembly-id-get/ */ +export interface ListedAssembly { + id?: string + parent_id?: string + account_id: string + template_id?: string + instance: string + notify_url?: string + redirect_url?: string + files: string + warning_count: number + execution_duration: number + execution_start: string + ok?: string + error?: string + created: string +} + +export interface ReplayedAssembly { + ok?: string + message?: string + success: boolean + assembly_id: string + assembly_url: string + assembly_ssl_url: string + notify_url?: string +} + +export interface ListedTemplate { + id: string + name: string + encryption_version: number + require_signature_auth: number + last_used?: string + created: string + modified: string + content: TemplateContent +} + +export interface TemplateResponse { + ok: string + message: string + id: string + content: TemplateContent + name: string + require_signature_auth: number +} + +export interface TemplateContent { + steps: KeyVal +} + +export interface AwaitAssemblyCompletionOptions { + onAssemblyProgress?: AssemblyProgress + timeout?: number + interval?: number + startTimeMs?: number +} + +export interface PaginationList { + count: number + items: T[] +} diff --git a/src/TransloaditError.js b/src/TransloaditError.js deleted file mode 100644 index d2b40974..00000000 --- a/src/TransloaditError.js +++ /dev/null @@ -1,10 +0,0 @@ -class TransloaditError extends Error { - name = 'TransloaditError' - - constructor(message, body) { - super(message) - this.response = { body } - } -} - -module.exports = TransloaditError diff --git a/src/TransloaditError.ts b/src/TransloaditError.ts new file mode 100644 index 00000000..a67897a1 --- /dev/null +++ b/src/TransloaditError.ts @@ -0,0 +1,11 @@ +export class TransloaditError extends Error { + override name = 'TransloaditError' + response: { body: unknown } + assemblyId?: string + transloaditErrorCode?: string + + constructor(message: string, body: unknown) { + super(message) + this.response = { body } + } +} diff --git a/src/tus.js b/src/tus.ts similarity index 61% rename from src/tus.js rename to src/tus.ts index 1a18ee1f..98eea8d9 100644 --- a/src/tus.js +++ b/src/tus.ts @@ -1,35 +1,45 @@ -const debug = require('debug') -const nodePath = require('path') -const tus = require('tus-js-client') -const fsPromises = require('fs/promises') -const pMap = require('p-map') +import debug from 'debug' +import { basename } from 'path' +import { Upload, UploadOptions } from 'tus-js-client' +import { stat } from 'fs/promises' +import pMap from 'p-map' +import type { Readable } from 'stream' +import type { Assembly, UploadProgress } from './Transloadit' const log = debug('transloadit') -async function sendTusRequest({ +interface SendTusRequestOptions { + streamsMap: Record + assembly: Assembly + requestedChunkSize: number + uploadConcurrency: number + onProgress: (options: UploadProgress) => void +} + +export async function sendTusRequest({ streamsMap, assembly, requestedChunkSize, uploadConcurrency, onProgress, -}) { +}: SendTusRequestOptions) { const streamLabels = Object.keys(streamsMap) let totalBytes = 0 let lastEmittedProgress = 0 - const sizes = {} + const sizes: Record = {} - const haveUnknownLengthStreams = streamLabels.some((label) => !streamsMap[label].path) + const haveUnknownLengthStreams = streamLabels.some((label) => !streamsMap[label]!.path) // Initialize size data await pMap( streamLabels, async (label) => { - const { path } = streamsMap[label] + const { path } = streamsMap[label]! if (path) { - const { size } = await fsPromises.stat(path) + const { size } = await stat(path) sizes[label] = size totalBytes += size } @@ -37,16 +47,16 @@ async function sendTusRequest({ { concurrency: 5 } ) - const uploadProgresses = {} + const uploadProgresses: Record = {} - async function uploadSingleStream(label) { + async function uploadSingleStream(label: string) { uploadProgresses[label] = 0 - const { stream, path } = streamsMap[label] + const { stream, path } = streamsMap[label]! const size = sizes[label] let chunkSize = requestedChunkSize - let uploadLengthDeferred + let uploadLengthDeferred: boolean const isStreamLengthKnown = !!path if (!isStreamLengthKnown) { // tus-js-client requires these options to be set for unknown size streams @@ -55,13 +65,13 @@ async function sendTusRequest({ if (chunkSize === Infinity) chunkSize = 50e6 } - const onTusProgress = (bytesUploaded) => { + const onTusProgress = (bytesUploaded: number): void => { uploadProgresses[label] = bytesUploaded // get all uploaded bytes for all files let uploadedBytes = 0 for (const l of streamLabels) { - uploadedBytes += uploadProgresses[l] + uploadedBytes += uploadProgresses[l] ?? 0 } // don't send redundant progress @@ -76,26 +86,26 @@ async function sendTusRequest({ } } - const filename = path ? nodePath.basename(path) : label + const filename = path ? basename(path) : label - await new Promise((resolve, reject) => { - const tusOptions = { + await new Promise((resolve, reject) => { + const tusOptions: UploadOptions = { endpoint: assembly.tus_url, metadata: { assembly_url: assembly.assembly_ssl_url, fieldname: label, filename, }, - uploadSize: size, onError: reject, onProgress: onTusProgress, onSuccess: resolve, } // tus-js-client doesn't like undefined/null + if (size != null) tusOptions.uploadSize = size if (chunkSize) tusOptions.chunkSize = chunkSize if (uploadLengthDeferred) tusOptions.uploadLengthDeferred = uploadLengthDeferred - const tusUpload = new tus.Upload(stream, tusOptions) + const tusUpload = new Upload(stream, tusOptions) tusUpload.start() }) @@ -106,6 +116,7 @@ async function sendTusRequest({ await pMap(streamLabels, uploadSingleStream, { concurrency: uploadConcurrency }) } -module.exports = { - sendTusRequest, +export interface Stream { + path?: string + stream: Readable } diff --git a/test/integration/live-api.test.js b/test/integration/live-api.test.ts similarity index 78% rename from test/integration/live-api.test.js rename to test/integration/live-api.test.ts index 90c84b98..d7f20ebb 100644 --- a/test/integration/live-api.test.js +++ b/test/integration/live-api.test.ts @@ -1,22 +1,22 @@ -const crypto = require('crypto') -const querystring = require('querystring') -const temp = require('temp') -const fs = require('fs') -const nodePath = require('path') -const nodeStream = require('stream/promises') -const got = require('got') -const intoStream = require('into-stream') -const debug = require('debug') +import { randomUUID } from 'crypto' +import { parse } from 'querystring' +import * as temp from 'temp' +import { createWriteStream } from 'fs' +import { IncomingMessage, RequestListener } from 'http' +import { join } from 'path' +import { pipeline } from 'stream/promises' +import got, { RequiredRetryOptions } from 'got' +import intoStream = require('into-stream') +import debug = require('debug') + +import { CreateAssemblyOptions, Transloadit, UploadProgress } from '../../src/Transloadit' +import { createTestServer, TestServer } from '../testserver' const log = debug('transloadit:live-api') -const Transloadit = require('../../src/Transloadit') - -const { createTestServer } = require('../testserver') - -async function downloadTmpFile(url) { +async function downloadTmpFile(url: string) { const { path } = await temp.open('transloadit') - await nodeStream.pipeline(got.stream(url), fs.createWriteStream(path)) + await pipeline(got.stream(url), createWriteStream(path)) return path } @@ -28,7 +28,7 @@ function createClient(opts = {}) { } // https://github.com/sindresorhus/got/blob/main/documentation/7-retry.md#retry - const gotRetry = { + const gotRetry: RequiredRetryOptions = { limit: 2, methods: [ 'GET', @@ -39,6 +39,7 @@ function createClient(opts = {}) { 'TRACE', 'POST', // Normally we shouldn't retry on POST, as it is not idempotent, but for tests we can do it ], + calculateDelay: () => 0, statusCodes: [], errorCodes: [ 'ETIMEDOUT', @@ -55,7 +56,7 @@ function createClient(opts = {}) { return new Transloadit({ authKey, authSecret, gotRetry, ...opts }) } -function createAssembly(client, params) { +function createAssembly(client: Transloadit, params: CreateAssemblyOptions) { const promise = client.createAssembly(params) const { assemblyId } = promise console.log(expect.getState().currentTestName, 'createAssembly', assemblyId) // For easier debugging @@ -100,19 +101,19 @@ const genericOptions = { const handlers = new Map() -let testServer +let testServer: TestServer beforeAll(async () => { // cloudflared tunnels are a bit unstable, so we share one cloudflared tunnel between all tests // we do this by prefixing each "virtual" server under a uuid subpath testServer = await createTestServer((req, res) => { const regex = /^\/([^/]+)/ - const match = req.url.match(regex) + const match = req.url?.match(regex) if (match) { const [, id] = match const handler = handlers.get(id) if (handler) { - req.url = req.url.replace(regex, '') + req.url = req.url?.replace(regex, '') if (req.url === '') req.url = '/' handler(req, res) } else { @@ -128,8 +129,13 @@ afterAll(async () => { await testServer?.close() }) -async function createVirtualTestServer(handler) { - const id = crypto.randomUUID() +interface VirtualTestServer { + close: () => void + url: string +} + +async function createVirtualTestServer(handler: RequestListener): Promise { + const id = randomUUID() log('Adding virtual server handler', id) const url = `${testServer.url}/${id}` handlers.set(id, handler) @@ -144,13 +150,13 @@ async function createVirtualTestServer(handler) { } } -describe('API integration', { timeout: 30000 }, () => { +describe('API integration', { timeout: 60000 }, () => { describe('assembly creation', () => { it('should create a retrievable assembly on the server', async () => { const client = createClient() let uploadProgressCalled - const options = { + const options: CreateAssemblyOptions = { ...genericOptions, onUploadProgress: (uploadProgress) => { uploadProgressCalled = uploadProgress @@ -258,7 +264,7 @@ describe('API integration', { timeout: 30000 }, () => { const result = await createAssembly(client, params) // console.log(result) - const getMatchObject = ({ name }) => ({ + const getMatchObject = ({ name }: { name: string }) => ({ name, basename: name, ext: 'svg', @@ -273,30 +279,32 @@ describe('API integration', { timeout: 30000 }, () => { original_md5hash: '1b199e02dd833b2278ce2a0e75480b14', }) // Because order is not same as input - const uploadsKeyed = Object.fromEntries(result.uploads.map((upload) => [upload.name, upload])) - expect(uploadsKeyed.file1).toMatchObject(getMatchObject({ name: 'file1' })) - expect(uploadsKeyed.file2).toMatchObject(getMatchObject({ name: 'file2' })) - expect(uploadsKeyed.file3).toMatchObject(getMatchObject({ name: 'file3' })) - expect(uploadsKeyed.file4).toMatchObject({ - name: 'file4', - basename: 'file4', - ext: 'jpg', - size: 133788, - mime: 'image/jpeg', - type: 'image', - field: 'file4', - md5hash: '42f29c0d9d5f3ea807ef3c327f8c5890', - original_basename: 'file4', - original_name: 'file4', - original_path: '/', - original_md5hash: '42f29c0d9d5f3ea807ef3c327f8c5890', + const uploadsMap = Object.fromEntries(result.uploads.map((upload) => [upload.name, upload])) + expect(uploadsMap).toEqual({ + file1: expect.objectContaining(getMatchObject({ name: 'file1' })), + file2: expect.objectContaining(getMatchObject({ name: 'file2' })), + file3: expect.objectContaining(getMatchObject({ name: 'file3' })), + file4: expect.objectContaining({ + name: 'file4', + basename: 'file4', + ext: 'jpg', + size: 133788, + mime: 'image/jpeg', + type: 'image', + field: 'file4', + md5hash: '42f29c0d9d5f3ea807ef3c327f8c5890', + original_basename: 'file4', + original_name: 'file4', + original_path: '/', + original_md5hash: '42f29c0d9d5f3ea807ef3c327f8c5890', + }), }) }) it('should allow setting an explicit assemblyId on createAssembly', async () => { const client = createClient() - const assemblyId = crypto.randomUUID().replace(/-/g, '') + const assemblyId = randomUUID().replace(/-/g, '') const params = { assemblyId, waitForCompletion: true, @@ -328,11 +336,11 @@ describe('API integration', { timeout: 30000 }, () => { expect(result.assembly_id).toMatch(promise.assemblyId) }) - async function testUploadProgress(isResumable) { + async function testUploadProgress(isResumable: boolean) { const client = createClient() let progressCalled = false - function onUploadProgress({ uploadedBytes, totalBytes }) { + function onUploadProgress({ uploadedBytes, totalBytes }: UploadProgress) { // console.log(uploadedBytes) expect(uploadedBytes).toBeDefined() if (isResumable) { @@ -342,7 +350,7 @@ describe('API integration', { timeout: 30000 }, () => { progressCalled = true } - const params = { + const params: CreateAssemblyOptions = { isResumable, params: { steps: { @@ -388,7 +396,7 @@ describe('API integration', { timeout: 30000 }, () => { }, }, files: { - file: nodePath.join(__dirname, './fixtures/zerobytes.jpg'), + file: join(__dirname, './fixtures/zerobytes.jpg'), }, waitForCompletion: true, } @@ -414,13 +422,13 @@ describe('API integration', { timeout: 30000 }, () => { // request // Async book-keeping for delaying the response - let sendServerResponse + let sendServerResponse: () => void - const promise = new Promise((resolve) => { + const promise = new Promise((resolve) => { sendServerResponse = resolve }) - const handleRequest = async (req, res) => { + const handleRequest: RequestListener = async (req, res) => { // console.log('handler', req.url) expect(req.url).toBe('/') @@ -475,7 +483,7 @@ describe('API integration', { timeout: 30000 }, () => { // console.log('canceled', id) // Allow the upload to finish - sendServerResponse() + sendServerResponse!() // Successful cancel requests get ASSEMBLY_CANCELED even when it // completed, so we now request the assembly status to check the @@ -486,7 +494,7 @@ describe('API integration', { timeout: 30000 }, () => { // Check that awaitAssemblyCompletion gave the correct response too const awaitCompletionResponse = await awaitCompletionPromise - expect(awaitCompletionResponse.ok).toBe('ASSEMBLY_CANCELED') + expect(awaitCompletionResponse?.ok).toBe('ASSEMBLY_CANCELED') } finally { server.close() } @@ -526,7 +534,7 @@ describe('API integration', { timeout: 30000 }, () => { let n = 0 let isDone = false - await new Promise((resolve) => { + await new Promise((resolve) => { assemblies.on('readable', () => { const assembly = assemblies.read() @@ -551,29 +559,38 @@ describe('API integration', { timeout: 30000 }, () => { }) describe('assembly notification', () => { - let server + type OnNotification = (params: { + path?: string + client: Transloadit + assemblyId: string + }) => void + + let server: VirtualTestServer afterEach(() => { server?.close() }) // helper function - const streamToString = (stream) => - new Promise((resolve, reject) => { - const chunks = [] + const streamToString = (stream: IncomingMessage) => + new Promise((resolve, reject) => { + const chunks: string[] = [] stream.on('data', (chunk) => chunks.push(chunk)) stream.on('error', (err) => reject(err)) stream.on('end', () => resolve(chunks.join(''))) }) - const runNotificationTest = async (onNotification, onError) => { + const runNotificationTest = async ( + onNotification: OnNotification, + onError: (error: unknown) => void + ) => { const client = createClient() // listens for notifications - const onNotificationRequest = async (req, res) => { + const onNotificationRequest: RequestListener = async (req, res) => { try { expect(req.method).toBe('POST') const body = await streamToString(req) - const result = JSON.parse(querystring.parse(body).transloadit) + const result = JSON.parse((parse(body) as { transloadit: string }).transloadit) expect(result).toHaveProperty('ok') if (result.ok !== 'ASSEMBLY_COMPLETED') { onError(new Error(`result.ok was ${result.ok}`)) @@ -598,8 +615,8 @@ describe('API integration', { timeout: 30000 }, () => { } it('should send a notification upon assembly completion', async () => { - await new Promise((resolve, reject) => { - const onNotification = async ({ path }) => { + await new Promise((resolve, reject) => { + const onNotification: OnNotification = async ({ path }) => { try { expect(path).toBe('/') resolve() @@ -611,52 +628,54 @@ describe('API integration', { timeout: 30000 }, () => { }) }) - it('should replay the notification when requested', (done) => { + it('should replay the notification when requested', async () => { let secondNotification = false - const onNotification = async ({ path, client, assemblyId }) => { - const newPath = '/newPath' - const newUrl = `${server.url}${newPath}` + await new Promise((resolve, reject) => { + const onNotification: OnNotification = async ({ path, client, assemblyId }) => { + const newPath = '/newPath' + const newUrl = `${server.url}${newPath}` - // I think there are some eventual consistency issues here - await new Promise((resolve) => setTimeout(resolve, 1000)) + // I think there are some eventual consistency issues here + await new Promise((resolve) => setTimeout(resolve, 1000)) - const result = await client.getAssembly(assemblyId) + const result = await client.getAssembly(assemblyId) - expect(['successful', 'processing']).toContain(result.notify_status) - expect(result.notify_response_code).toBe(200) + expect(['successful', 'processing']).toContain(result.notify_status) + expect(result.notify_response_code).toBe(200) - if (secondNotification) { - expect(path).toBe(newPath) + if (secondNotification) { + expect(path).toBe(newPath) - // notify_url will not get updated to new URL - expect(result.notify_url).toBe(server.url) + // notify_url will not get updated to new URL + expect(result.notify_url).toBe(server.url) - try { - // If we quit immediately, things will not get cleaned up and jest will hang - await new Promise((resolve) => setTimeout(resolve, 2000)) - done() - } catch (err) { - done(err) - } + try { + // If we quit immediately, things will not get cleaned up and jest will hang + await new Promise((resolve) => setTimeout(resolve, 2000)) + resolve() + } catch (err) { + reject(err) + } - return - } + return + } - secondNotification = true + secondNotification = true - try { - expect(path).toBe('/') - expect(result.notify_url).toBe(server.url) + try { + expect(path).toBe('/') + expect(result.notify_url).toBe(server.url) - await new Promise((resolve) => setTimeout(resolve, 2000)) - await client.replayAssemblyNotification(assemblyId, { notify_url: newUrl }) - } catch (err) { - done(err) + await new Promise((resolve) => setTimeout(resolve, 2000)) + await client.replayAssemblyNotification(assemblyId, { notify_url: newUrl }) + } catch (err) { + reject(err) + } } - } - runNotificationTest(onNotification, (err) => done(err)) + runNotificationTest(onNotification, reject) + }) }) }) @@ -666,7 +685,7 @@ describe('API integration', { timeout: 30000 }, () => { .toISOString() .toLocaleLowerCase('en-US') .replace(/[^0-9a-z-]/g, '-')}` - let templId = null + let templId: string | null = null const client = createClient() it('should allow listing templates', async () => { @@ -682,7 +701,7 @@ describe('API integration', { timeout: 30000 }, () => { it("should be able to fetch a template's definition", async () => { expect(templId).toBeDefined() - const template = await client.getTemplate(templId) + const template = await client.getTemplate(templId!) const { name, content } = template expect(name).toBe(templName) expect(content).toEqual(genericParams) @@ -696,7 +715,7 @@ describe('API integration', { timeout: 30000 }, () => { } const editedName = `${templName}-edited` - const editResult = await client.editTemplate(templId, { + const editResult = await client.editTemplate(templId!, { name: editedName, template: editedTemplate, }) @@ -709,10 +728,10 @@ describe('API integration', { timeout: 30000 }, () => { it('should delete the template successfully', async () => { expect(templId).toBeDefined() - const template = await client.deleteTemplate(templId) + const template = await client.deleteTemplate(templId!) const { ok } = template expect(ok).toBe('TEMPLATE_DELETED') - await expect(client.getTemplate(templId)).rejects.toThrow( + await expect(client.getTemplate(templId!)).rejects.toThrow( expect.objectContaining({ transloaditErrorCode: 'TEMPLATE_NOT_FOUND' }) ) }) @@ -724,7 +743,7 @@ describe('API integration', { timeout: 30000 }, () => { .toISOString() .toLocaleLowerCase('en-US') .replace(/[^0-9a-z-]/g, '-')}` - let credId = null + let credId: string | null = null const client = createClient() it('should allow listing credentials', async () => { @@ -750,7 +769,7 @@ describe('API integration', { timeout: 30000 }, () => { it("should be able to fetch a credential's definition", async () => { expect(credId).toBeDefined() - const readResult = await client.getTemplateCredential(credId) + const readResult = await client.getTemplateCredential(credId!) const { name, content } = readResult.credential expect(name).toBe(credName) expect(content.bucket).toEqual('mybucket.example.com') @@ -759,7 +778,7 @@ describe('API integration', { timeout: 30000 }, () => { it('should allow editing a credential', async () => { expect(credId).toBeDefined() const editedName = `${credName}-edited` - const editResult = await client.editTemplateCredential(credId, { + const editResult = await client.editTemplateCredential(credId!, { name: editedName, type: 's3', content: { @@ -778,10 +797,10 @@ describe('API integration', { timeout: 30000 }, () => { it('should delete the credential successfully', async () => { expect(credId).toBeDefined() - const credential = await client.deleteTemplateCredential(credId) + const credential = await client.deleteTemplateCredential(credId!) const { ok } = credential expect(ok).toBe('TEMPLATE_CREDENTIALS_DELETED') - await expect(client.getTemplateCredential(credId)).rejects.toThrow( + await expect(client.getTemplateCredential(credId!)).rejects.toThrow( expect.objectContaining({ transloaditErrorCode: 'TEMPLATE_CREDENTIALS_NOT_READ' }) ) }) diff --git a/test/testserver.js b/test/testserver.ts similarity index 74% rename from test/testserver.js rename to test/testserver.ts index 04cab10f..dca05c18 100644 --- a/test/testserver.js +++ b/test/testserver.ts @@ -1,14 +1,19 @@ -const http = require('http') -const got = require('got') -const debug = require('debug') +import { createServer, RequestListener, Server } from 'http' +import got from 'got' +import debug from 'debug' const log = debug('transloadit:testserver') -const createTunnel = require('./tunnel') +import { createTunnel, CreateTunnelResult } from './tunnel' -async function createHttpServer(handler) { +interface HttpServer { + server: Server + port: number +} + +async function createHttpServer(handler: RequestListener): Promise { return new Promise((resolve, reject) => { - const server = http.createServer(handler) + const server = createServer(handler) let port = 8000 @@ -20,7 +25,7 @@ async function createHttpServer(handler) { }) } server.on('error', (err) => { - if (err.code === 'EADDRINUSE') { + if ((err as NodeJS.ErrnoException).code === 'EADDRINUSE') { if (++port >= 65535) { server.close() reject(new Error('Failed to find any free port to listen on')) @@ -36,17 +41,17 @@ async function createHttpServer(handler) { }) } -async function createTestServer(onRequest) { +export async function createTestServer(onRequest: RequestListener) { if (!process.env.CLOUDFLARED_PATH) { throw new Error('CLOUDFLARED_PATH environment variable not set') } - let expectedPath + let expectedPath: string let initialized = false - let onTunnelOperational - let tunnel + let onTunnelOperational: () => void + let tunnel: CreateTunnelResult - const handleHttpRequest = (req, res) => { + const handleHttpRequest: RequestListener = (req, res) => { log('HTTP request handler', req.method, req.url) if (!initialized) { @@ -63,7 +68,7 @@ async function createTestServer(onRequest) { async function close() { if (tunnel) await tunnel.close() - await new Promise((resolve) => server.close(() => resolve())) + await new Promise((resolve) => server.close(() => resolve())) log('closed tunnel') } @@ -96,7 +101,7 @@ async function createTestServer(onRequest) { } await Promise.all([ - new Promise((resolve) => { + new Promise((resolve) => { onTunnelOperational = resolve }), sendTunnelRequest(), @@ -115,6 +120,4 @@ async function createTestServer(onRequest) { } } -module.exports = { - createTestServer, -} +export type TestServer = Awaited> diff --git a/test/tunnel.js b/test/tunnel.ts similarity index 70% rename from test/tunnel.js rename to test/tunnel.ts index d20c464d..e378abf7 100644 --- a/test/tunnel.js +++ b/test/tunnel.ts @@ -1,12 +1,26 @@ -const execa = require('execa') -const readline = require('readline') -const dns = require('dns/promises') -const debug = require('debug') -const pRetry = require('p-retry') +import execa, { ExecaChildProcess } from 'execa' +import { createInterface } from 'readline' +import { Resolver } from 'dns/promises' +import debug from 'debug' +import pRetry from 'p-retry' +import * as timers from 'timers/promises' const log = debug('transloadit:cloudflared-tunnel') -async function startTunnel({ cloudFlaredPath, port }) { +interface CreateTunnelParams { + cloudFlaredPath: string + port: number +} + +interface StartTunnelResult { + url: string + process: ExecaChildProcess +} + +async function startTunnel({ + cloudFlaredPath, + port, +}: CreateTunnelParams): Promise { const process = execa( cloudFlaredPath, ['tunnel', '--url', `http://localhost:${port}`, '--no-autoupdate'], @@ -17,7 +31,7 @@ async function startTunnel({ cloudFlaredPath, port }) { return await new Promise((resolve, reject) => { const timeout = setTimeout(() => reject(new Error('Timed out trying to start tunnel')), 30000) - const rl = readline.createInterface({ input: process.stderr }) + const rl = createInterface({ input: process.stderr as NodeJS.ReadStream }) process.on('error', (err) => { console.error(err) @@ -25,7 +39,7 @@ async function startTunnel({ cloudFlaredPath, port }) { }) let fullStderr = '' - let foundUrl + let foundUrl: string rl.on('error', (err) => { reject( @@ -73,8 +87,11 @@ async function startTunnel({ cloudFlaredPath, port }) { } } -function createTunnel({ cloudFlaredPath = 'cloudflared', port }) { - let process +export function createTunnel({ + cloudFlaredPath = 'cloudflared', + port, +}: CreateTunnelParams): CreateTunnelResult { + let process: execa.ExecaChildProcess | undefined const urlPromise = (async () => { const tunnel = await pRetry(async () => startTunnel({ cloudFlaredPath, port }), { retries: 1 }) @@ -83,10 +100,12 @@ function createTunnel({ cloudFlaredPath = 'cloudflared', port }) { log('Found url', url) + await timers.setTimeout(3000) // seems to help to prevent timeouts (I think tunnel is not actually ready when cloudflared reports it to be) + // We need to wait for DNS to be resolvable. // If we don't, the operating system's dns cache will be poisoned by the not yet valid resolved entry // and it will forever fail for that subdomain name... - const resolver = new dns.Resolver() + const resolver = new Resolver() resolver.setServers(['1.1.1.1']) // use cloudflare's dns server. if we don't explicitly specify DNS server, it will also poison our OS' dns cache for (let i = 0; i < 10; i += 1) { @@ -96,7 +115,7 @@ function createTunnel({ cloudFlaredPath = 'cloudflared', port }) { await resolver.resolve4(host) return url } catch (err) { - log('dns err', err.message) + log('dns err', (err as Error).message) await new Promise((resolve) => setTimeout(resolve, 3000)) } } @@ -106,7 +125,7 @@ function createTunnel({ cloudFlaredPath = 'cloudflared', port }) { async function close() { if (!process) return - const promise = new Promise((resolve) => process.on('close', resolve)) + const promise = new Promise((resolve) => process!.on('close', resolve)) process.kill() await promise } @@ -118,4 +137,8 @@ function createTunnel({ cloudFlaredPath = 'cloudflared', port }) { } } -module.exports = createTunnel +export interface CreateTunnelResult { + process?: execa.ExecaChildProcess + urlPromise: Promise + close: () => Promise +} diff --git a/test/unit/mock-http.test.js b/test/unit/mock-http.test.ts similarity index 87% rename from test/unit/mock-http.test.js rename to test/unit/mock-http.test.ts index d78ea69c..4946d4ab 100644 --- a/test/unit/mock-http.test.js +++ b/test/unit/mock-http.test.ts @@ -1,8 +1,13 @@ -const nock = require('nock') +import nock from 'nock' -const Transloadit = require('../../src/Transloadit') +import { + HTTPError, + InconsistentResponseError, + TimeoutError, + Transloadit, +} from '../../src/Transloadit' -const getLocalClient = (opts) => +const getLocalClient = (opts?: Omit) => new Transloadit({ authKey: '', authSecret: '', endpoint: 'http://localhost', ...opts }) const createAssemblyRegex = /\/assemblies\/[0-9a-f]{32}/ @@ -14,11 +19,15 @@ describe('Mocked API tests', () => { }) it('should time out createAssembly with a custom timeout', async () => { - const client = new Transloadit({ authKey: '', authSecret: '', endpoint: 'http://localhost' }) + const client = new Transloadit({ + authKey: '', + authSecret: '', + endpoint: 'http://localhost', + }) nock('http://localhost').post(createAssemblyRegex).delay(100).reply(200) - await expect(client.createAssembly({ timeout: 10 })).rejects.toThrow(Transloadit.TimeoutError) + await expect(client.createAssembly({ timeout: 10 })).rejects.toThrow(TimeoutError) }) it('should time out other requests with a custom timeout', async () => { @@ -26,7 +35,7 @@ describe('Mocked API tests', () => { nock('http://localhost').post('/templates').delay(100).reply(200) - await expect(client.createTemplate()).rejects.toThrow(Transloadit.TimeoutError) + await expect(client.createTemplate()).rejects.toThrow(TimeoutError) }) it('should time out awaitAssemblyCompletion polling', async () => { @@ -38,7 +47,7 @@ describe('Mocked API tests', () => { .delay(100) .reply(200, { ok: 'ASSEMBLY_EXECUTING', assembly_url: '', assembly_ssl_url: '' }) - await expect(client.awaitAssemblyCompletion(1, { timeout: 1, interval: 1 })).rejects.toThrow( + await expect(client.awaitAssemblyCompletion('1', { timeout: 1, interval: 1 })).rejects.toThrow( expect.objectContaining({ code: 'POLLING_TIMED_OUT', message: 'Polling timed out' }) ) scope.done() @@ -56,7 +65,7 @@ describe('Mocked API tests', () => { await client.createAssembly() - const result = await client.awaitAssemblyCompletion(1) + const result = await client.awaitAssemblyCompletion('1') expect(result.ok).toBe('REQUEST_ABORTED') scope.done() }) @@ -73,7 +82,7 @@ describe('Mocked API tests', () => { .reply(200, { ok: 'ASSEMBLY_COMPLETED', assembly_url: '', assembly_ssl_url: '' }) await expect( - client.awaitAssemblyCompletion(1, { timeout: 100, interval: 1 }) + client.awaitAssemblyCompletion('1', { timeout: 100, interval: 1 }) ).resolves.toMatchObject({ ok: 'ASSEMBLY_COMPLETED' }) scope.done() }) @@ -115,8 +124,7 @@ describe('Mocked API tests', () => { }) it('should retry correctly on RATE_LIMIT_REACHED', async () => { - const client = getLocalClient() - client._maxRetries = 1 + const client = getLocalClient({ maxRetries: 1 }) // https://transloadit.com/blog/2012/04/introducing-rate-limiting/ @@ -158,7 +166,7 @@ describe('Mocked API tests', () => { .query(() => true) .reply(500) - const promise = client.getAssembly(1) + const promise = client.getAssembly('1') await expect(promise).rejects.toThrow( expect.not.objectContaining({ code: 'ERR_NOCK_NO_MATCH' }) ) // Make sure that it was called only once @@ -183,11 +191,11 @@ describe('Mocked API tests', () => { .reply(200, {}) // Success - await client.getAssembly(1) + await client.getAssembly('1') // Failure - const promise = client.getAssembly(1) - await expect(promise).rejects.toThrow(Transloadit.InconsistentResponseError) + const promise = client.getAssembly('1') + await expect(promise).rejects.toThrow(InconsistentResponseError) await expect(promise).rejects.toThrow( expect.objectContaining({ message: 'Server returned an incomplete assembly response (no URL)', @@ -207,10 +215,10 @@ describe('Mocked API tests', () => { .query(() => true) .reply(200, { error: 'IMPORT_FILE_ERROR', assembly_url: '', assembly_ssl_url: '' }) - const assembly = await client.getAssembly(1) + const assembly = await client.getAssembly('1') expect(assembly).toMatchObject({ error: 'IMPORT_FILE_ERROR' }) - const assembly2 = await client.awaitAssemblyCompletion(1) + const assembly2 = await client.awaitAssemblyCompletion('1') expect(assembly2).toMatchObject({ error: 'IMPORT_FILE_ERROR' }) scope.done() @@ -239,7 +247,7 @@ describe('Mocked API tests', () => { .post('/assemblies/1/replay') .reply(200, { error: 'IMPORT_FILE_ERROR' }) - await expect(client.replayAssembly(1)).rejects.toThrow( + await expect(client.replayAssembly('1')).rejects.toThrow( expect.objectContaining({ transloaditErrorCode: 'IMPORT_FILE_ERROR' }) ) scope.done() @@ -267,7 +275,7 @@ describe('Mocked API tests', () => { .query(() => true) .reply(404, { error: 'SERVER_404', message: 'unknown method / url' }) - await expect(client.getAssembly('invalid')).rejects.toThrow(Transloadit.HTTPError) + await expect(client.getAssembly('invalid')).rejects.toThrow(HTTPError) scope.done() }) }) diff --git a/test/unit/test-pagination-stream.test.js b/test/unit/test-pagination-stream.test.ts similarity index 56% rename from test/unit/test-pagination-stream.test.js rename to test/unit/test-pagination-stream.test.ts index 72975ddd..857fc58f 100644 --- a/test/unit/test-pagination-stream.test.js +++ b/test/unit/test-pagination-stream.test.ts @@ -1,15 +1,21 @@ -const { Writable } = require('stream') +import { Writable } from 'stream' -const PaginationStream = require('../../src/PaginationStream') +import { PaginationStream } from '../../src/PaginationStream' -const toArray = (callback) => { - const stream = new Writable({ objectMode: true }) - const list = [] - stream.write = (chunk) => list.push(chunk) +const toArray = (callback: (list: number[]) => void) => { + const writable = new Writable({ objectMode: true }) + const list: number[] = [] + writable.write = (chunk) => { + list.push(chunk) + return true + } - stream.end = () => callback(list) + writable.end = () => { + callback(list) + return writable + } - return stream + return writable } describe('PaginationStream', () => { @@ -21,9 +27,9 @@ describe('PaginationStream', () => { { count, items: [7, 8, 9] }, ] - const stream = new PaginationStream(async (pageno) => pages[pageno - 1]) + const stream = new PaginationStream(async (pageno) => pages[pageno - 1]) - await new Promise((resolve) => { + await new Promise((resolve) => { stream.pipe( toArray((array) => { const expected = pages.flatMap(({ items }) => items) @@ -45,11 +51,14 @@ describe('PaginationStream', () => { { count, items: [7, 8, 9] }, ] - const stream = new PaginationStream( - async (pageno) => new Promise((resolve) => process.nextTick(() => resolve(pages[pageno - 1]))) + const stream = new PaginationStream( + async (pageno) => + new Promise((resolve) => { + process.nextTick(() => resolve(pages[pageno - 1])) + }) ) - await new Promise((resolve) => { + await new Promise((resolve) => { stream.pipe( toArray((array) => { const expected = pages.flatMap(({ items }) => items) diff --git a/test/unit/test-transloadit-client.test.js b/test/unit/test-transloadit-client.test.ts similarity index 67% rename from test/unit/test-transloadit-client.test.js rename to test/unit/test-transloadit-client.test.ts index fa0edb3b..23b9307c 100644 --- a/test/unit/test-transloadit-client.test.js +++ b/test/unit/test-transloadit-client.test.ts @@ -1,21 +1,25 @@ -const stream = require('stream') -const FormData = require('form-data') -const got = require('got') +import { Readable } from 'stream' +import FormData from 'form-data' +import got, { CancelableRequest } from 'got' -const tus = require('../../src/tus') -const Transloadit = require('../../src/Transloadit') -const pkg = require('../../package.json') +import * as tus from '../../src/tus' +import { Transloadit } from '../../src/Transloadit' +import { version } from 'transloadit/package.json' const mockedExpiresDate = '2021-01-06T21:11:07.883Z' -const mockGetExpiresDate = (client) => +const mockGetExpiresDate = (client: Transloadit) => + // @ts-expect-error This mocks private internals vi.spyOn(client, '_getExpiresDate').mockReturnValue(mockedExpiresDate) -const mockGot = (method) => +const mockGot = (method: 'get') => vi.spyOn(got, method).mockImplementation(() => { - const mockPromise = Promise.resolve({ body: '' }) - mockPromise.on = vi.fn(() => {}) + const mockPromise = Promise.resolve({ + body: '', + }) as CancelableRequest + ;(mockPromise as any).on = vi.fn(() => {}) return mockPromise }) -const mockRemoteJson = (client) => +const mockRemoteJson = (client: Transloadit) => + // @ts-expect-error This mocks private internals vi.spyOn(client, '_remoteJson').mockImplementation(() => ({ body: {} })) describe('Transloadit', () => { @@ -23,7 +27,7 @@ describe('Transloadit', () => { const client = new Transloadit({ authKey: 'foo_key', authSecret: 'foo_secret' }) // mimic Stream object returned from `request` (which is not a stream v3) - const req = { pipe: () => {} } + const req = { pipe: () => {} } as Partial as Readable const promise = client.createAssembly({ uploads: { file: req } }) await expect(promise).rejects.toThrow( @@ -39,14 +43,32 @@ describe('Transloadit', () => { maxRetries: 0, } const client = new Transloadit(opts) - expect(client._authKey).toBe('foo_key') - expect(client._authSecret).toBe('foo_secret') - expect(client._endpoint).toBe('https://api2.transloadit.com') - expect(client._maxRetries).toBe(0) - expect(client._defaultTimeout).toBe(60000) + expect( + // @ts-expect-error This tests private internals + client._authKey + ).toBe('foo_key') + expect( + // @ts-expect-error This tests private internals + client._authSecret + ).toBe('foo_secret') + expect( + // @ts-expect-error This tests private internals + client._endpoint + ).toBe('https://api2.transloadit.com') + expect( + // @ts-expect-error This tests private internals + client._maxRetries + ).toBe(0) + expect( + // @ts-expect-error This tests private internals + client._defaultTimeout + ).toBe(60000) client.setDefaultTimeout(10000) - expect(client._defaultTimeout).toBe(10000) + expect( + // @ts-expect-error This tests private internals + client._defaultTimeout + ).toBe(10000) }) it('should throw when sending a trailing slash in endpoint', () => { @@ -59,11 +81,23 @@ describe('Transloadit', () => { }) it('should give error when no authSecret', () => { - expect(() => new Transloadit({ authSecret: '' })).toThrow() + expect( + () => + new Transloadit( + // @ts-expect-error This tests invalid types + { authSecret: '' } + ) + ).toThrow() }) it('should give error when no authKey', () => { - expect(() => new Transloadit({ authKey: '' })).toThrow() + expect( + () => + new Transloadit( + // @ts-expect-error This tests invalid types + { authKey: '' } + ) + ).toThrow() }) it('should allow overwriting some properties', () => { @@ -74,27 +108,36 @@ describe('Transloadit', () => { } const client = new Transloadit(opts) - expect(client._authKey).toBe('foo_key') - expect(client._authSecret).toBe('foo_secret') - expect(client._endpoint).toBe('http://foo') + expect( + // @ts-expect-error This tests private internals + client._authKey + ).toBe('foo_key') + expect( + // @ts-expect-error This tests private internals + client._authSecret + ).toBe('foo_secret') + expect( + // @ts-expect-error This tests private internals + client._endpoint + ).toBe('http://foo') }) }) describe('add stream', () => { it('should pause streams', async () => { - vi.spyOn(tus, 'sendTusRequest').mockImplementation(() => {}) + vi.spyOn(tus, 'sendTusRequest').mockImplementation(() => Promise.resolve()) const client = new Transloadit({ authKey: 'foo_key', authSecret: 'foo_secret' }) const name = 'foo_name' - const pause = vi.fn(() => {}) + const pause = vi.fn(() => mockStream) const mockStream = { pause, - pipe: () => {}, - _read: () => {}, + pipe: () => undefined, + _read: () => undefined, _readableState: {}, - on: () => {}, + on: () => mockStream, readable: true, - } + } as Partial as Readable mockRemoteJson(client) @@ -108,8 +151,8 @@ describe('Transloadit', () => { it('should append all required fields to the request form', () => { const client = new Transloadit({ authKey: 'foo_key', authSecret: 'foo_secret' }) - const stream1 = new stream.Readable() - const stream2 = new stream.Readable() + const stream1 = new Readable() + const stream2 = new Readable() const streamsMap = { stream1: { stream: stream1 }, @@ -126,6 +169,7 @@ describe('Transloadit', () => { const calcSignatureSpy = vi.spyOn(client, 'calcSignature') const formAppendSpy = vi.spyOn(form, 'append') + // @ts-expect-error This tests private internals client._appendForm(form, params, streamsMap, fields) expect(calcSignatureSpy).toHaveBeenCalledWith(params) @@ -157,6 +201,7 @@ describe('Transloadit', () => { mockGetExpiresDate(client) + // @ts-expect-error This tests private internals const fullUrl = client._appendParamsToUrl(url, params) const expected = `${url}&signature=${signature}¶ms=${encodeURIComponent(jsonParams)}` @@ -168,6 +213,7 @@ describe('Transloadit', () => { it('should add the auth key, secret and expires parameters', () => { let client = new Transloadit({ authKey: 'foo_key', authSecret: 'foo_secret' }) + // @ts-expect-error This tests private internals let r = JSON.parse(client._prepareParams()) expect(r.auth.key).toBe('foo_key') expect(r.auth.expires).not.toBeNull() @@ -178,6 +224,7 @@ describe('Transloadit', () => { } client = new Transloadit(opts) + // @ts-expect-error This tests private internals r = JSON.parse(client._prepareParams()) expect(r.auth.key).toBe('foo') return expect(r.auth.expires).not.toBeNull() @@ -193,6 +240,7 @@ describe('Transloadit', () => { }, } + // @ts-expect-error This tests private internals const r = JSON.parse(client._prepareParams(PARAMS)) expect(r.auth.key).toBe('foo_key') return expect(r.auth.expires).toBe('foo_expires') @@ -203,12 +251,14 @@ describe('Transloadit', () => { it('should calc _prepareParams and _calcSignature', () => { const client = new Transloadit({ authKey: 'foo_key', authSecret: 'foo_secret' }) + // @ts-expect-error This tests private internals client._authSecret = '13123123123' const params = { foo: 'bar' } mockGetExpiresDate(client) + // @ts-expect-error This tests private internals const prepareParamsSpy = vi.spyOn(client, '_prepareParams') const r = client.calcSignature(params) @@ -240,32 +290,52 @@ describe('Transloadit', () => { it('should crash if attempt to use callback', async () => { const client = new Transloadit({ authKey: 'foo_key', authSecret: 'foo_secret' }) const cb = () => {} - expect(() => client.createAssembly({}, cb)).toThrow(TypeError) + expect(() => + client.createAssembly( + {}, + // @ts-expect-error This tests bad input + cb + ) + ).toThrow(TypeError) }) describe('_calcSignature', () => { it('should calculate the signature properly', () => { const client = new Transloadit({ authKey: 'foo_key', authSecret: 'foo_secret' }) + // @ts-expect-error This tests private internals client._authSecret = '13123123123' let expected = 'sha384:8b90663d4b7d14ac7d647c74cb53c529198dee4689d0f8faae44f0df1c2a157acce5cb8c55a375218bc331897cf92e9d' - expect(client._calcSignature('foo')).toBe(expected) + expect( + // @ts-expect-error This tests private internals + client._calcSignature('foo') + ).toBe(expected) expected = 'sha384:3595c177fc09c9cc46672cef90685257838a0a4295056dcfd45b5d5c255e8f987e1c1ca8800b9c21ee03e4ada7485e9d' - expect(client._calcSignature('akjdkadskjads')).toBe(expected) + expect( + // @ts-expect-error This tests private internals + client._calcSignature('akjdkadskjads') + ).toBe(expected) + // @ts-expect-error This tests private internals client._authSecret = '90191902390123' expected = 'sha384:b6f967f8bd659652c6c2093bc52045becbd6e8fbd96d8ef419e07bbc9fb411c56316e75f03dfc2a6613dbe896bbad20f' - expect(client._calcSignature('foo')).toBe(expected) + expect( + // @ts-expect-error This tests private internals + client._calcSignature('foo') + ).toBe(expected) expected = 'sha384:fc75f6a4bbb06340653c0f7efff013e94eb8e402e0e45cf40ad4bc95f45a3ae3263032000727359c595a433364a84f96' - return expect(client._calcSignature('akjdkadskjads')).toBe(expected) + return expect( + // @ts-expect-error This tests private internals + client._calcSignature('akjdkadskjads') + ).toBe(expected) }) }) @@ -276,11 +346,12 @@ describe('Transloadit', () => { const get = mockGot('get') const url = '/some-url' + // @ts-expect-error This tests private internals await client._remoteJson({ url, method: 'get' }) expect(get).toHaveBeenCalledWith( expect.any(String), - expect.objectContaining({ headers: { 'Transloadit-Client': `node-sdk:${pkg.version}` } }) + expect.objectContaining({ headers: { 'Transloadit-Client': `node-sdk:${version}` } }) ) }) }) diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 00000000..6e1531c6 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,16 @@ +{ + "include": ["src"], + "compilerOptions": { + "composite": true, + "declaration": true, + "declarationMap": true, + "isolatedModules": true, + "module": "node16", + "noImplicitOverride": true, + "outDir": "dist", + "resolveJsonModule": true, + "rootDir": "src", + "sourceMap": true, + "strict": true + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..c6a629dd --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "exclude": ["src"], + "references": [{ "path": "./tsconfig.build.json" }], + "compilerOptions": { + "isolatedModules": true, + "module": "node16", + "noImplicitOverride": true, + "noEmit": true, + "resolveJsonModule": true, + "strict": true, + "types": ["vitest/globals"] + } +} diff --git a/types/index.d.ts b/types/index.d.ts deleted file mode 100644 index b3e8030b..00000000 --- a/types/index.d.ts +++ /dev/null @@ -1,223 +0,0 @@ -// Type definitions for transloadit - -import { Readable } from 'stream' -import * as intoStream from 'into-stream' - -import { - RequestError, - ReadError, - ParseError, - UploadError, - HTTPError, - MaxRedirectsError, - TimeoutError, - RequiredRetryOptions, -} from 'got' - -export interface CreateAssemblyOptions { - params?: CreateAssemblyParams - files?: { - [name: string]: string - } - uploads?: { - [name: string]: Readable | intoStream.Input - } - waitForCompletion?: boolean - isResumable?: boolean - chunkSize?: number - uploadConcurrency?: number - timeout?: number - onUploadProgress?: (uploadProgress: UploadProgress) => void - onAssemblyProgress?: AssemblyProgress - assemblyId?: string -} - -export default class Transloadit { - constructor(options: { - authKey: string - authSecret: string - endpoint?: string - maxRetries?: number - timeout?: number - gotRetry?: RequiredRetryOptions - }) - - setDefaultTimeout(timeout: number): void - - createAssembly(options: CreateAssemblyOptions): Promise - - replayAssembly(assemblyId: string, params?: KeyVal): Promise - cancelAssembly(assemblyId: string): Promise - listAssemblies(params?: KeyVal): Promise<{ count: number; items: ListedAssembly[] }> - getAssembly(assemblyId: string): Promise - streamAssemblies(params?: KeyVal): Readable - - awaitAssemblyCompletion( - assemblyId: string, - options: { - onAssemblyProgress?: AssemblyProgress - timeout?: number - interval?: number - } - ): Promise - - replayAssemblyNotification( - assemblyId: string, - params?: KeyVal - ): Promise<{ ok: string; success: boolean }> - - getLastUsedAssemblyUrl(): string - - createTemplate(params: KeyVal): Promise - editTemplate(templateId: string, params: KeyVal): Promise - deleteTemplate(templateId: string): Promise<{ ok: string; message: string }> - listTemplates(params: KeyVal): Promise<{ count: number; items: ListedTemplate[] }> - getTemplate(templateId: string): Promise - streamTemplates(params?: KeyVal): Readable - - /** https://transloadit.com/docs/api/bill-date-get/ */ - getBill(month: string): Promise - - calcSignature(params: KeyVal): { signature: string; params: string } -} - -export type AssemblyProgress = (assembly: Assembly) => void - -export interface CreateAssemblyParams { - /** See https://transloadit.com/docs/topics/assembly-instructions/ */ - steps?: KeyVal - template_id?: string - notify_url?: string - fields?: KeyVal - allow_steps_override?: boolean -} - -// TODO -/** Object with properties. See https://transloadit.com/docs/api/ */ -export interface KeyVal { - [key: string]: any -} - -export interface UploadProgress { - uploadedBytes?: number - totalBytes?: number -} - -/** https://transloadit.com/docs/api/assembly-status-response/#explanation-of-fields */ -export interface Assembly { - ok?: string - message?: string - assembly_id: string - parent_id?: string - account_id: string - template_id?: string - instance: string - assembly_url: string - assembly_ssl_url: string - uppyserver_url: string - companion_url: string - websocket_url: string - tus_url: string - bytes_received: number - bytes_expected: number - upload_duration: number - client_agent?: string - client_ip?: string - client_referer?: string - transloadit_client: string - start_date: string - upload_meta_data_extracted: boolean - warnings: any[] - is_infinite: boolean - has_dupe_jobs: boolean - execution_start: string - execution_duration: number - queue_duration: number - jobs_queue_duration: number - notify_start?: any - notify_url?: string - notify_status?: any - notify_response_code?: any - notify_duration?: any - last_job_completed?: string - fields: KeyVal - running_jobs: any[] - bytes_usage: number - executing_jobs: any[] - started_jobs: string[] - parent_assembly_status: any - params: string - template?: any - merged_params: string - uploads: any[] - results: any - build_id: string - error?: string - stderr?: string - stdout?: string - reason?: string -} - -/** See https://transloadit.com/docs/api/assemblies-assembly-id-get/ */ -export interface ListedAssembly { - id?: string - parent_id?: string - account_id: string - template_id?: string - instance: string - notify_url?: string - redirect_url?: string - files: string - warning_count: number - execution_duration: number - execution_start: string - ok?: string - error?: string - created: string -} - -export interface ReplayedAssembly { - ok?: string - message?: string - success: boolean - assembly_id: string - assembly_url: string - assembly_ssl_url: string - notify_url?: string -} - -export interface ListedTemplate { - id: string - name: string - encryption_version: number - require_signature_auth: number - last_used?: string - created: string - modified: string - content: TemplateContent -} - -export interface TemplateResponse { - ok: string - message: string - id: string - content: TemplateContent - name: string - require_signature_auth: number -} - -export interface TemplateContent { - steps: KeyVal -} - -export class InconsistentResponseError extends Error {} - -export { - RequestError, - ReadError, - ParseError, - UploadError, - HTTPError, - MaxRedirectsError, - TimeoutError, -} diff --git a/types/index.test-d.ts b/types/index.test-d.ts deleted file mode 100644 index 0fc5835b..00000000 --- a/types/index.test-d.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { expectType } from 'tsd' - -import intoStream from 'into-stream' -import { Readable } from 'stream' - -import Transloadit, { - Assembly, - ListedAssembly, - ReplayedAssembly, - TemplateResponse, - ListedTemplate, - KeyVal, -} from '../' - -const transloadit = new Transloadit({ - authKey: '123', - authSecret: '456', - endpoint: 'http://localhost', - maxRetries: 1, - gotRetry: { - limit: 2, - methods: ['GET'], - statusCodes: [404], - errorCodes: ['ERROR'], - calculateDelay: () => 1, - maxRetryAfter: 1, - }, -}) - -expectType(transloadit.getLastUsedAssemblyUrl()) -expectType(transloadit.setDefaultTimeout(1)) - -expectType>( - transloadit.createAssembly({ - params: { - steps: { foo: 'bar' }, - template_id: 'template', - notify_url: 'url', - fields: { a: 'b', c: 1 }, - allow_steps_override: false, - }, - files: { - file1: '/path/to/file', - }, - uploads: { - file2: 'string', - file3: intoStream('string'), - file4: Buffer.from('string'), - }, - isResumable: true, - chunkSize: Infinity, - uploadConcurrency: 5, - timeout: 1, - waitForCompletion: true, - onAssemblyProgress: (assembly) => { - expectType(assembly) - }, - onUploadProgress: ({ uploadedBytes, totalBytes }) => { - expectType(uploadedBytes) - expectType(totalBytes) - }, - assemblyId: '123', - }) -) - -expectType>( - transloadit.awaitAssemblyCompletion('1', { - onAssemblyProgress: (assembly) => { - expectType(assembly) - }, - timeout: 1, - interval: 1, - }) -) - -expectType>(transloadit.cancelAssembly('1')) -expectType>(transloadit.replayAssembly('1', { param1: { a: 1 } })) -expectType>( - transloadit.replayAssemblyNotification('1', { param1: { a: 1 } }) -) -expectType>( - transloadit.listAssemblies({ param1: { a: 1 } }) -) -expectType(transloadit.streamAssemblies({ param1: { a: 1 } })) -expectType>(transloadit.getAssembly('1')) - -expectType>(transloadit.createTemplate({ param1: { a: 1 } })) -expectType>(transloadit.editTemplate('1', { param1: { a: 1 } })) -expectType>(transloadit.deleteTemplate('1')) -expectType>(transloadit.getTemplate('1')) -expectType>( - transloadit.listTemplates({ param1: { a: 1 } }) -) -expectType(transloadit.streamTemplates({ param1: { a: 1 } })) - -expectType>(transloadit.getBill('2020-01')) - -expectType<{ signature: string; params: string }>(transloadit.calcSignature({ param1: { a: 1 } })) diff --git a/yarn.lock b/yarn.lock index 5137a860..31d09f1a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -41,7 +41,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.22.13": +"@babel/code-frame@npm:^7.22.13": version: 7.22.13 resolution: "@babel/code-frame@npm:7.22.13" dependencies: @@ -634,114 +634,114 @@ __metadata: languageName: node linkType: hard -"@rollup/rollup-android-arm-eabi@npm:4.22.4": - version: 4.22.4 - resolution: "@rollup/rollup-android-arm-eabi@npm:4.22.4" +"@rollup/rollup-android-arm-eabi@npm:4.21.0": + version: 4.21.0 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.21.0" conditions: os=android & cpu=arm languageName: node linkType: hard -"@rollup/rollup-android-arm64@npm:4.22.4": - version: 4.22.4 - resolution: "@rollup/rollup-android-arm64@npm:4.22.4" +"@rollup/rollup-android-arm64@npm:4.21.0": + version: 4.21.0 + resolution: "@rollup/rollup-android-arm64@npm:4.21.0" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-arm64@npm:4.22.4": - version: 4.22.4 - resolution: "@rollup/rollup-darwin-arm64@npm:4.22.4" +"@rollup/rollup-darwin-arm64@npm:4.21.0": + version: 4.21.0 + resolution: "@rollup/rollup-darwin-arm64@npm:4.21.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-x64@npm:4.22.4": - version: 4.22.4 - resolution: "@rollup/rollup-darwin-x64@npm:4.22.4" +"@rollup/rollup-darwin-x64@npm:4.21.0": + version: 4.21.0 + resolution: "@rollup/rollup-darwin-x64@npm:4.21.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-linux-arm-gnueabihf@npm:4.22.4": - version: 4.22.4 - resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.22.4" +"@rollup/rollup-linux-arm-gnueabihf@npm:4.21.0": + version: 4.21.0 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.21.0" conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm-musleabihf@npm:4.22.4": - version: 4.22.4 - resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.22.4" +"@rollup/rollup-linux-arm-musleabihf@npm:4.21.0": + version: 4.21.0 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.21.0" conditions: os=linux & cpu=arm & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-arm64-gnu@npm:4.22.4": - version: 4.22.4 - resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.22.4" +"@rollup/rollup-linux-arm64-gnu@npm:4.21.0": + version: 4.21.0 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.21.0" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm64-musl@npm:4.22.4": - version: 4.22.4 - resolution: "@rollup/rollup-linux-arm64-musl@npm:4.22.4" +"@rollup/rollup-linux-arm64-musl@npm:4.21.0": + version: 4.21.0 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.21.0" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-powerpc64le-gnu@npm:4.22.4": - version: 4.22.4 - resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.22.4" +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.21.0": + version: 4.21.0 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.21.0" conditions: os=linux & cpu=ppc64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-riscv64-gnu@npm:4.22.4": - version: 4.22.4 - resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.22.4" +"@rollup/rollup-linux-riscv64-gnu@npm:4.21.0": + version: 4.21.0 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.21.0" conditions: os=linux & cpu=riscv64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-s390x-gnu@npm:4.22.4": - version: 4.22.4 - resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.22.4" +"@rollup/rollup-linux-s390x-gnu@npm:4.21.0": + version: 4.21.0 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.21.0" conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-gnu@npm:4.22.4": - version: 4.22.4 - resolution: "@rollup/rollup-linux-x64-gnu@npm:4.22.4" +"@rollup/rollup-linux-x64-gnu@npm:4.21.0": + version: 4.21.0 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.21.0" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-musl@npm:4.22.4": - version: 4.22.4 - resolution: "@rollup/rollup-linux-x64-musl@npm:4.22.4" +"@rollup/rollup-linux-x64-musl@npm:4.21.0": + version: 4.21.0 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.21.0" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-win32-arm64-msvc@npm:4.22.4": - version: 4.22.4 - resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.22.4" +"@rollup/rollup-win32-arm64-msvc@npm:4.21.0": + version: 4.21.0 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.21.0" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-win32-ia32-msvc@npm:4.22.4": - version: 4.22.4 - resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.22.4" +"@rollup/rollup-win32-ia32-msvc@npm:4.21.0": + version: 4.21.0 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.21.0" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rollup/rollup-win32-x64-msvc@npm:4.22.4": - version: 4.22.4 - resolution: "@rollup/rollup-win32-x64-msvc@npm:4.22.4" +"@rollup/rollup-win32-x64-msvc@npm:4.21.0": + version: 4.21.0 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.21.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -762,13 +762,6 @@ __metadata: languageName: node linkType: hard -"@tsd/typescript@npm:~4.9.3": - version: 4.9.5 - resolution: "@tsd/typescript@npm:4.9.5" - checksum: ec32c2a990c75646f3b8bc50e8a6b9b2fbd43752882a3b8d302a54d7e8eccaefc269114920681bf6f2509c2db3f48629b85ac93170fe2557bcb64169976f510d - languageName: node - linkType: hard - "@types/cacheable-request@npm:^6.0.1": version: 6.0.3 resolution: "@types/cacheable-request@npm:6.0.3" @@ -781,17 +774,16 @@ __metadata: languageName: node linkType: hard -"@types/eslint@npm:^7.2.13": - version: 7.29.0 - resolution: "@types/eslint@npm:7.29.0" +"@types/debug@npm:^4.1.12": + version: 4.1.12 + resolution: "@types/debug@npm:4.1.12" dependencies: - "@types/estree": "npm:*" - "@types/json-schema": "npm:*" - checksum: 780ea3f4abba77a577a9ca5c4b66f74acc0f5ff5162b9a361ca931763ed65bca062389fc26027b416ed0a54d390e2206412db6c682f565e523d2b82159e6c46f + "@types/ms": "npm:*" + checksum: 5dcd465edbb5a7f226e9a5efd1f399c6172407ef5840686b73e3608ce135eeca54ae8037dcd9f16bdb2768ac74925b820a8b9ecc588a58ca09eca6acabe33e2f languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:1.0.5, @types/estree@npm:^1.0.0": +"@types/estree@npm:1.0.5, @types/estree@npm:^1.0.0": version: 1.0.5 resolution: "@types/estree@npm:1.0.5" checksum: b3b0e334288ddb407c7b3357ca67dbee75ee22db242ca7c56fe27db4e1a31989cb8af48a84dd401deb787fe10cc6b2ab1ee82dc4783be87ededbe3d53c79c70d @@ -805,7 +797,7 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.7": +"@types/json-schema@npm:^7.0.7": version: 7.0.14 resolution: "@types/json-schema@npm:7.0.14" checksum: da68689ccd44cb93ca4c9a4af3b25c6091ecf45fb370d1ed0d0ac5b780e235bf0b9bdc1f7e28f19e6713b22567c3db11fefcbcc6d48ac6b356d035a8f9f4ea30 @@ -828,10 +820,10 @@ __metadata: languageName: node linkType: hard -"@types/minimist@npm:^1.2.0": - version: 1.2.4 - resolution: "@types/minimist@npm:1.2.4" - checksum: 01403652c09de17b8c6d7d9959cb7a244deccf31e9e7a1a7011fba73fa2724c14fe935718e0fdc48dcd30403fd76a916cb991d4c0ddf229748ccc6c4920c3371 +"@types/ms@npm:*": + version: 0.7.34 + resolution: "@types/ms@npm:0.7.34" + checksum: ac80bd90012116ceb2d188fde62d96830ca847823e8ca71255616bc73991aa7d9f057b8bfab79e8ee44ffefb031ddd1bcce63ea82f9e66f7c31ec02d2d823ccc languageName: node linkType: hard @@ -844,13 +836,6 @@ __metadata: languageName: node linkType: hard -"@types/normalize-package-data@npm:^2.4.0": - version: 2.4.3 - resolution: "@types/normalize-package-data@npm:2.4.3" - checksum: 9ad94568b53f65d0c7fffed61c74e4a7b8625b1ebbc549f1de25287c2d20e6bca9d9cdc5826e508c9d95e02a48ac69d0282121c300667071661f37090224416b - languageName: node - linkType: hard - "@types/responselike@npm:^1.0.0": version: 1.0.2 resolution: "@types/responselike@npm:1.0.2" @@ -867,6 +852,15 @@ __metadata: languageName: node linkType: hard +"@types/temp@npm:^0.9.4": + version: 0.9.4 + resolution: "@types/temp@npm:0.9.4" + dependencies: + "@types/node": "npm:*" + checksum: 901fc8e7815b4bcdc47d1ed4273f13f2e3353ef57b33a81ee325e975989fbb32264f398a190b337360192b27b130eeceedfebd06a4eb195965affd3ff45698b7 + languageName: node + linkType: hard + "@typescript-eslint/experimental-utils@npm:^4.0.1": version: 4.33.0 resolution: "@typescript-eslint/experimental-utils@npm:4.33.0" @@ -1097,15 +1091,6 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^4.2.1": - version: 4.3.2 - resolution: "ansi-escapes@npm:4.3.2" - dependencies: - type-fest: "npm:^0.21.3" - checksum: da917be01871525a3dfcf925ae2977bc59e8c513d4423368645634bf5d4ceba5401574eb705c1e92b79f7292af5a656f78c5725a4b0e1cec97c4b413705c1d50 - languageName: node - linkType: hard - "ansi-regex@npm:^5.0.1": version: 5.0.1 resolution: "ansi-regex@npm:5.0.1" @@ -1258,13 +1243,6 @@ __metadata: languageName: node linkType: hard -"arrify@npm:^1.0.1": - version: 1.0.1 - resolution: "arrify@npm:1.0.1" - checksum: c35c8d1a81bcd5474c0c57fe3f4bad1a4d46a5fa353cedcff7a54da315df60db71829e69104b859dff96c5d68af46bd2be259fe5e50dc6aa9df3b36bea0383ab - languageName: node - linkType: hard - "assertion-error@npm:^2.0.1": version: 2.0.1 resolution: "assertion-error@npm:2.0.1" @@ -1467,24 +1445,6 @@ __metadata: languageName: node linkType: hard -"camelcase-keys@npm:^6.2.2": - version: 6.2.2 - resolution: "camelcase-keys@npm:6.2.2" - dependencies: - camelcase: "npm:^5.3.1" - map-obj: "npm:^4.0.0" - quick-lru: "npm:^4.0.1" - checksum: bf1a28348c0f285c6c6f68fb98a9d088d3c0269fed0cdff3ea680d5a42df8a067b4de374e7a33e619eb9d5266a448fe66c2dd1f8e0c9209ebc348632882a3526 - languageName: node - linkType: hard - -"camelcase@npm:^5.3.1": - version: 5.3.1 - resolution: "camelcase@npm:5.3.1" - checksum: 92ff9b443bfe8abb15f2b1513ca182d16126359ad4f955ebc83dc4ddcc4ef3fdd2c078bc223f2673dc223488e75c99b16cc4d056624374b799e6a1555cf61b23 - languageName: node - linkType: hard - "caniuse-lite@npm:^1.0.30001541": version: 1.0.30001559 resolution: "caniuse-lite@npm:1.0.30001559" @@ -1516,7 +1476,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0, chalk@npm:^4.1.0": +"chalk@npm:^4.0.0": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -1728,23 +1688,6 @@ __metadata: languageName: node linkType: hard -"decamelize-keys@npm:^1.1.0": - version: 1.1.1 - resolution: "decamelize-keys@npm:1.1.1" - dependencies: - decamelize: "npm:^1.1.0" - map-obj: "npm:^1.0.0" - checksum: 4ca385933127437658338c65fb9aead5f21b28d3dd3ccd7956eb29aab0953b5d3c047fbc207111672220c71ecf7a4d34f36c92851b7bbde6fca1a02c541bdd7d - languageName: node - linkType: hard - -"decamelize@npm:^1.1.0, decamelize@npm:^1.2.0": - version: 1.2.0 - resolution: "decamelize@npm:1.2.0" - checksum: 85c39fe8fbf0482d4a1e224ef0119db5c1897f8503bcef8b826adff7a1b11414972f6fef2d7dec2ee0b4be3863cf64ac1439137ae9e6af23a3d8dcbe26a5b4b2 - languageName: node - linkType: hard - "decompress-response@npm:^6.0.0": version: 6.0.0 resolution: "decompress-response@npm:6.0.0" @@ -2181,22 +2124,6 @@ __metadata: languageName: node linkType: hard -"eslint-formatter-pretty@npm:^4.1.0": - version: 4.1.0 - resolution: "eslint-formatter-pretty@npm:4.1.0" - dependencies: - "@types/eslint": "npm:^7.2.13" - ansi-escapes: "npm:^4.2.1" - chalk: "npm:^4.1.0" - eslint-rule-docs: "npm:^1.1.5" - log-symbols: "npm:^4.0.0" - plur: "npm:^4.0.0" - string-width: "npm:^4.2.0" - supports-hyperlinks: "npm:^2.0.0" - checksum: 7cc55b873d3e9a5049cf0db65cef873abfc299639e7527bed52dea61f0742661b68e48018cf05de4c9cd8fb9362badc20f22c50fd5f36d745a346fa17bac8b60 - languageName: node - linkType: hard - "eslint-import-resolver-node@npm:^0.3.9": version: 0.3.9 resolution: "eslint-import-resolver-node@npm:0.3.9" @@ -2367,13 +2294,6 @@ __metadata: languageName: node linkType: hard -"eslint-rule-docs@npm:^1.1.5": - version: 1.1.235 - resolution: "eslint-rule-docs@npm:1.1.235" - checksum: 76a735c1e13a511ddff1017d5913b2526643827c8fdc86a23467f680b8dcbdfd07806cb092c82dd8d0e99789f23c8a38b9d2b838cd1cd62cc1932612ed606b8e - languageName: node - linkType: hard - "eslint-scope@npm:5.1.1, eslint-scope@npm:^5.1.1": version: 5.1.1 resolution: "eslint-scope@npm:5.1.1" @@ -2639,16 +2559,6 @@ __metadata: languageName: node linkType: hard -"find-up@npm:^4.1.0": - version: 4.1.0 - resolution: "find-up@npm:4.1.0" - dependencies: - locate-path: "npm:^5.0.0" - path-exists: "npm:^4.0.0" - checksum: 0406ee89ebeefa2d507feb07ec366bebd8a6167ae74aa4e34fb4c4abd06cf782a3ce26ae4194d70706f72182841733f00551c209fe575cb00bd92104056e78c1 - languageName: node - linkType: hard - "flat-cache@npm:^3.0.4": version: 3.1.1 resolution: "flat-cache@npm:3.1.1" @@ -2922,7 +2832,7 @@ __metadata: languageName: node linkType: hard -"globby@npm:^11.0.1, globby@npm:^11.0.3": +"globby@npm:^11.0.3": version: 11.1.0 resolution: "globby@npm:11.1.0" dependencies: @@ -2971,13 +2881,6 @@ __metadata: languageName: node linkType: hard -"hard-rejection@npm:^2.1.0": - version: 2.1.0 - resolution: "hard-rejection@npm:2.1.0" - checksum: febc3343a1ad575aedcc112580835b44a89a89e01f400b4eda6e8110869edfdab0b00cd1bd4c3bfec9475a57e79e0b355aecd5be46454b6a62b9a359af60e564 - languageName: node - linkType: hard - "has-bigints@npm:^1.0.1, has-bigints@npm:^1.0.2": version: 1.0.2 resolution: "has-bigints@npm:1.0.2" @@ -3047,15 +2950,6 @@ __metadata: languageName: node linkType: hard -"hosted-git-info@npm:^4.0.1": - version: 4.1.0 - resolution: "hosted-git-info@npm:4.1.0" - dependencies: - lru-cache: "npm:^6.0.0" - checksum: 150fbcb001600336d17fdbae803264abed013548eea7946c2264c49ebe2ebd8c4441ba71dd23dd8e18c65de79d637f98b22d4760ba5fb2e0b15d62543d0fff07 - languageName: node - linkType: hard - "html-escaper@npm:^2.0.0": version: 2.0.2 resolution: "html-escaper@npm:2.0.2" @@ -3206,13 +3100,6 @@ __metadata: languageName: node linkType: hard -"irregular-plurals@npm:^3.2.0": - version: 3.5.0 - resolution: "irregular-plurals@npm:3.5.0" - checksum: 7c033bbe7325e5a6e0a26949cc6863b6ce273403d4cd5b93bd99b33fecb6605b0884097c4259c23ed0c52c2133bf7d1cdcdd7a0630e8c325161fe269b3447918 - languageName: node - linkType: hard - "is-array-buffer@npm:^3.0.1, is-array-buffer@npm:^3.0.2": version: 3.0.2 resolution: "is-array-buffer@npm:3.0.2" @@ -3266,7 +3153,7 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1, is-core-module@npm:^2.5.0": +"is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1": version: 2.13.1 resolution: "is-core-module@npm:2.13.1" dependencies: @@ -3362,13 +3249,6 @@ __metadata: languageName: node linkType: hard -"is-plain-obj@npm:^1.1.0": - version: 1.1.0 - resolution: "is-plain-obj@npm:1.1.0" - checksum: daaee1805add26f781b413fdf192fc91d52409583be30ace35c82607d440da63cc4cac0ac55136716688d6c0a2c6ef3edb2254fecbd1fe06056d6bd15975ee8c - languageName: node - linkType: hard - "is-regex@npm:^1.1.4": version: 1.1.4 resolution: "is-regex@npm:1.1.4" @@ -3436,13 +3316,6 @@ __metadata: languageName: node linkType: hard -"is-unicode-supported@npm:^0.1.0": - version: 0.1.0 - resolution: "is-unicode-supported@npm:0.1.0" - checksum: 00cbe3455c3756be68d2542c416cab888aebd5012781d6819749fefb15162ff23e38501fe681b3d751c73e8ff561ac09a5293eba6f58fdf0178462ce6dcb3453 - languageName: node - linkType: hard - "is-weakmap@npm:^2.0.1": version: 2.0.1 resolution: "is-weakmap@npm:2.0.1" @@ -3631,13 +3504,6 @@ __metadata: languageName: node linkType: hard -"json-parse-even-better-errors@npm:^2.3.0": - version: 2.3.1 - resolution: "json-parse-even-better-errors@npm:2.3.1" - checksum: 140932564c8f0b88455432e0f33c4cb4086b8868e37524e07e723f4eaedb9425bdc2bafd71bd1d9765bd15fd1e2d126972bc83990f55c467168c228c24d665f3 - languageName: node - linkType: hard - "json-schema-traverse@npm:^0.4.1": version: 0.4.1 resolution: "json-schema-traverse@npm:0.4.1" @@ -3707,13 +3573,6 @@ __metadata: languageName: node linkType: hard -"kind-of@npm:^6.0.3": - version: 6.0.3 - resolution: "kind-of@npm:6.0.3" - checksum: 61cdff9623dabf3568b6445e93e31376bee1cdb93f8ba7033d86022c2a9b1791a1d9510e026e6465ebd701a6dd2f7b0808483ad8838341ac52f003f512e0b4c4 - languageName: node - linkType: hard - "language-subtag-registry@npm:^0.3.20": version: 0.3.22 resolution: "language-subtag-registry@npm:0.3.22" @@ -3740,13 +3599,6 @@ __metadata: languageName: node linkType: hard -"lines-and-columns@npm:^1.1.6": - version: 1.2.4 - resolution: "lines-and-columns@npm:1.2.4" - checksum: 3da6ee62d4cd9f03f5dc90b4df2540fb85b352081bee77fe4bbcd12c9000ead7f35e0a38b8d09a9bb99b13223446dd8689ff3c4959807620726d788701a83d2d - languageName: node - linkType: hard - "load-json-file@npm:^4.0.0": version: 4.0.0 resolution: "load-json-file@npm:4.0.0" @@ -3759,15 +3611,6 @@ __metadata: languageName: node linkType: hard -"locate-path@npm:^5.0.0": - version: 5.0.0 - resolution: "locate-path@npm:5.0.0" - dependencies: - p-locate: "npm:^4.1.0" - checksum: 33a1c5247e87e022f9713e6213a744557a3e9ec32c5d0b5efb10aa3a38177615bf90221a5592674857039c1a0fd2063b82f285702d37b792d973e9e72ace6c59 - languageName: node - linkType: hard - "lodash._baseiteratee@npm:~4.7.0": version: 4.7.0 resolution: "lodash._baseiteratee@npm:4.7.0" @@ -3848,16 +3691,6 @@ __metadata: languageName: node linkType: hard -"log-symbols@npm:^4.0.0": - version: 4.1.0 - resolution: "log-symbols@npm:4.1.0" - dependencies: - chalk: "npm:^4.1.0" - is-unicode-supported: "npm:^0.1.0" - checksum: 67f445a9ffa76db1989d0fa98586e5bc2fd5247260dafb8ad93d9f0ccd5896d53fb830b0e54dade5ad838b9de2006c826831a3c528913093af20dff8bd24aca6 - languageName: node - linkType: hard - "loose-envify@npm:^1.4.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" @@ -3958,20 +3791,6 @@ __metadata: languageName: node linkType: hard -"map-obj@npm:^1.0.0": - version: 1.0.1 - resolution: "map-obj@npm:1.0.1" - checksum: ccca88395e7d38671ed9f5652ecf471ecd546924be2fb900836b9da35e068a96687d96a5f93dcdfa94d9a27d649d2f10a84595590f89a347fb4dda47629dcc52 - languageName: node - linkType: hard - -"map-obj@npm:^4.0.0": - version: 4.3.0 - resolution: "map-obj@npm:4.3.0" - checksum: 1c19e1c88513c8abdab25c316367154c6a0a6a0f77e3e8c391bb7c0e093aefed293f539d026dc013d86219e5e4c25f23b0003ea588be2101ccd757bacc12d43b - languageName: node - linkType: hard - "memorystream@npm:^0.3.1": version: 0.3.1 resolution: "memorystream@npm:0.3.1" @@ -3979,26 +3798,6 @@ __metadata: languageName: node linkType: hard -"meow@npm:^9.0.0": - version: 9.0.0 - resolution: "meow@npm:9.0.0" - dependencies: - "@types/minimist": "npm:^1.2.0" - camelcase-keys: "npm:^6.2.2" - decamelize: "npm:^1.2.0" - decamelize-keys: "npm:^1.1.0" - hard-rejection: "npm:^2.1.0" - minimist-options: "npm:4.1.0" - normalize-package-data: "npm:^3.0.0" - read-pkg-up: "npm:^7.0.1" - redent: "npm:^3.0.0" - trim-newlines: "npm:^3.0.0" - type-fest: "npm:^0.18.0" - yargs-parser: "npm:^20.2.3" - checksum: 998955ecff999dc3f3867ef3b51999218212497f27d75b9cbe10bdb73aac4ee308d484f7801fd1b3cfa4172819065f65f076ca018c1412fab19d0ea486648722 - languageName: node - linkType: hard - "merge-stream@npm:^2.0.0": version: 2.0.0 resolution: "merge-stream@npm:2.0.0" @@ -4067,13 +3866,6 @@ __metadata: languageName: node linkType: hard -"min-indent@npm:^1.0.0": - version: 1.0.1 - resolution: "min-indent@npm:1.0.1" - checksum: 7e207bd5c20401b292de291f02913230cb1163abca162044f7db1d951fa245b174dc00869d40dd9a9f32a885ad6a5f3e767ee104cf278f399cb4e92d3f582d5c - languageName: node - linkType: hard - "minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -4092,17 +3884,6 @@ __metadata: languageName: node linkType: hard -"minimist-options@npm:4.1.0": - version: 4.1.0 - resolution: "minimist-options@npm:4.1.0" - dependencies: - arrify: "npm:^1.0.1" - is-plain-obj: "npm:^1.1.0" - kind-of: "npm:^6.0.3" - checksum: 7871f9cdd15d1e7374e5b013e2ceda3d327a06a8c7b38ae16d9ef941e07d985e952c589e57213f7aa90a8744c60aed9524c0d85e501f5478382d9181f2763f54 - languageName: node - linkType: hard - "minimist@npm:^1.2.0, minimist@npm:^1.2.6": version: 1.2.8 resolution: "minimist@npm:1.2.8" @@ -4314,7 +4095,7 @@ __metadata: languageName: node linkType: hard -"normalize-package-data@npm:^2.3.2, normalize-package-data@npm:^2.5.0": +"normalize-package-data@npm:^2.3.2": version: 2.5.0 resolution: "normalize-package-data@npm:2.5.0" dependencies: @@ -4326,18 +4107,6 @@ __metadata: languageName: node linkType: hard -"normalize-package-data@npm:^3.0.0": - version: 3.0.3 - resolution: "normalize-package-data@npm:3.0.3" - dependencies: - hosted-git-info: "npm:^4.0.1" - is-core-module: "npm:^2.5.0" - semver: "npm:^7.3.4" - validate-npm-package-license: "npm:^3.0.1" - checksum: e5d0f739ba2c465d41f77c9d950e291ea4af78f8816ddb91c5da62257c40b76d8c83278b0d08ffbcd0f187636ebddad20e181e924873916d03e6e5ea2ef026be - languageName: node - linkType: hard - "normalize-url@npm:^6.0.1": version: 6.1.0 resolution: "normalize-url@npm:6.1.0" @@ -4527,24 +4296,6 @@ __metadata: languageName: node linkType: hard -"p-limit@npm:^2.2.0": - version: 2.3.0 - resolution: "p-limit@npm:2.3.0" - dependencies: - p-try: "npm:^2.0.0" - checksum: 8da01ac53efe6a627080fafc127c873da40c18d87b3f5d5492d465bb85ec7207e153948df6b9cbaeb130be70152f874229b8242ee2be84c0794082510af97f12 - languageName: node - linkType: hard - -"p-locate@npm:^4.1.0": - version: 4.1.0 - resolution: "p-locate@npm:4.1.0" - dependencies: - p-limit: "npm:^2.2.0" - checksum: 1b476ad69ad7f6059744f343b26d51ce091508935c1dbb80c4e0a2f397ffce0ca3a1f9f5cd3c7ce19d7929a09719d5c65fe70d8ee289c3f267cd36f2881813e9 - languageName: node - linkType: hard - "p-map@npm:^4.0.0": version: 4.0.0 resolution: "p-map@npm:4.0.0" @@ -4564,13 +4315,6 @@ __metadata: languageName: node linkType: hard -"p-try@npm:^2.0.0": - version: 2.2.0 - resolution: "p-try@npm:2.2.0" - checksum: c36c19907734c904b16994e6535b02c36c2224d433e01a2f1ab777237f4d86e6289fd5fd464850491e940379d4606ed850c03e0f9ab600b0ebddb511312e177f - languageName: node - linkType: hard - "package-json-from-dist@npm:^1.0.0": version: 1.0.0 resolution: "package-json-from-dist@npm:1.0.0" @@ -4597,25 +4341,6 @@ __metadata: languageName: node linkType: hard -"parse-json@npm:^5.0.0": - version: 5.2.0 - resolution: "parse-json@npm:5.2.0" - dependencies: - "@babel/code-frame": "npm:^7.0.0" - error-ex: "npm:^1.3.1" - json-parse-even-better-errors: "npm:^2.3.0" - lines-and-columns: "npm:^1.1.6" - checksum: 77947f2253005be7a12d858aedbafa09c9ae39eb4863adf330f7b416ca4f4a08132e453e08de2db46459256fb66afaac5ee758b44fe6541b7cdaf9d252e59585 - languageName: node - linkType: hard - -"path-exists@npm:^4.0.0": - version: 4.0.0 - resolution: "path-exists@npm:4.0.0" - checksum: 8c0bd3f5238188197dc78dced15207a4716c51cc4e3624c44fc97acf69558f5ebb9a2afff486fe1b4ee148e0c133e96c5e11a9aa5c48a3006e3467da070e5e1b - languageName: node - linkType: hard - "path-is-absolute@npm:^1.0.0": version: 1.0.1 resolution: "path-is-absolute@npm:1.0.1" @@ -4691,20 +4416,13 @@ __metadata: languageName: node linkType: hard -"picocolors@npm:^1.0.0": +"picocolors@npm:^1.0.0, picocolors@npm:^1.0.1": version: 1.0.1 resolution: "picocolors@npm:1.0.1" checksum: c63cdad2bf812ef0d66c8db29583802355d4ca67b9285d846f390cc15c2f6ccb94e8cb7eb6a6e97fc5990a6d3ad4ae42d86c84d3146e667c739a4234ed50d400 languageName: node linkType: hard -"picocolors@npm:^1.1.0": - version: 1.1.0 - resolution: "picocolors@npm:1.1.0" - checksum: 86946f6032148801ef09c051c6fb13b5cf942eaf147e30ea79edb91dd32d700934edebe782a1078ff859fb2b816792e97ef4dab03d7f0b804f6b01a0df35e023 - languageName: node - linkType: hard - "picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" @@ -4728,23 +4446,14 @@ __metadata: languageName: node linkType: hard -"plur@npm:^4.0.0": - version: 4.0.0 - resolution: "plur@npm:4.0.0" - dependencies: - irregular-plurals: "npm:^3.2.0" - checksum: 4d3010843dac60b980c9b83d4cdecc2c6bb4bf0a1d7620ba7229b5badcbc3c3767fddfdb61746f6253c3bd33ada3523375983cbe0b3f94868f4a475b9b6bd7d7 - languageName: node - linkType: hard - -"postcss@npm:^8.4.43": - version: 8.4.47 - resolution: "postcss@npm:8.4.47" +"postcss@npm:^8.4.41": + version: 8.4.41 + resolution: "postcss@npm:8.4.41" dependencies: nanoid: "npm:^3.3.7" - picocolors: "npm:^1.1.0" - source-map-js: "npm:^1.2.1" - checksum: 929f68b5081b7202709456532cee2a145c1843d391508c5a09de2517e8c4791638f71dd63b1898dba6712f8839d7a6da046c72a5e44c162e908f5911f57b5f44 + picocolors: "npm:^1.0.1" + source-map-js: "npm:^1.2.0" + checksum: c1828fc59e7ec1a3bf52b3a42f615dba53c67960ed82a81df6441b485fe43c20aba7f4e7c55425762fd99c594ecabbaaba8cf5b30fd79dfec5b52a9f63a2d690 languageName: node linkType: hard @@ -4855,13 +4564,6 @@ __metadata: languageName: node linkType: hard -"quick-lru@npm:^4.0.1": - version: 4.0.1 - resolution: "quick-lru@npm:4.0.1" - checksum: f9b1596fa7595a35c2f9d913ac312fede13d37dc8a747a51557ab36e11ce113bbe88ef4c0154968845559a7709cb6a7e7cbe75f7972182451cd45e7f057a334d - languageName: node - linkType: hard - "quick-lru@npm:^5.1.1": version: 5.1.1 resolution: "quick-lru@npm:5.1.1" @@ -4876,17 +4578,6 @@ __metadata: languageName: node linkType: hard -"read-pkg-up@npm:^7.0.0, read-pkg-up@npm:^7.0.1": - version: 7.0.1 - resolution: "read-pkg-up@npm:7.0.1" - dependencies: - find-up: "npm:^4.1.0" - read-pkg: "npm:^5.2.0" - type-fest: "npm:^0.8.1" - checksum: 82b3ac9fd7c6ca1bdc1d7253eb1091a98ff3d195ee0a45386582ce3e69f90266163c34121e6a0a02f1630073a6c0585f7880b3865efcae9c452fa667f02ca385 - languageName: node - linkType: hard - "read-pkg@npm:^3.0.0": version: 3.0.0 resolution: "read-pkg@npm:3.0.0" @@ -4898,18 +4589,6 @@ __metadata: languageName: node linkType: hard -"read-pkg@npm:^5.2.0": - version: 5.2.0 - resolution: "read-pkg@npm:5.2.0" - dependencies: - "@types/normalize-package-data": "npm:^2.4.0" - normalize-package-data: "npm:^2.5.0" - parse-json: "npm:^5.0.0" - type-fest: "npm:^0.6.0" - checksum: b51a17d4b51418e777029e3a7694c9bd6c578a5ab99db544764a0b0f2c7c0f58f8a6bc101f86a6fceb8ba6d237d67c89acf6170f6b98695d0420ddc86cf109fb - languageName: node - linkType: hard - "readable-stream@npm:^2.0.0": version: 2.3.8 resolution: "readable-stream@npm:2.3.8" @@ -4925,16 +4604,6 @@ __metadata: languageName: node linkType: hard -"redent@npm:^3.0.0": - version: 3.0.0 - resolution: "redent@npm:3.0.0" - dependencies: - indent-string: "npm:^4.0.0" - strip-indent: "npm:^3.0.0" - checksum: d64a6b5c0b50eb3ddce3ab770f866658a2b9998c678f797919ceb1b586bab9259b311407280bd80b804e2a7c7539b19238ae6a2a20c843f1a7fcff21d48c2eae - languageName: node - linkType: hard - "reflect.getprototypeof@npm:^1.0.4": version: 1.0.4 resolution: "reflect.getprototypeof@npm:1.0.4" @@ -5106,26 +4775,26 @@ __metadata: languageName: node linkType: hard -"rollup@npm:^4.20.0": - version: 4.22.4 - resolution: "rollup@npm:4.22.4" - dependencies: - "@rollup/rollup-android-arm-eabi": "npm:4.22.4" - "@rollup/rollup-android-arm64": "npm:4.22.4" - "@rollup/rollup-darwin-arm64": "npm:4.22.4" - "@rollup/rollup-darwin-x64": "npm:4.22.4" - "@rollup/rollup-linux-arm-gnueabihf": "npm:4.22.4" - "@rollup/rollup-linux-arm-musleabihf": "npm:4.22.4" - "@rollup/rollup-linux-arm64-gnu": "npm:4.22.4" - "@rollup/rollup-linux-arm64-musl": "npm:4.22.4" - "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.22.4" - "@rollup/rollup-linux-riscv64-gnu": "npm:4.22.4" - "@rollup/rollup-linux-s390x-gnu": "npm:4.22.4" - "@rollup/rollup-linux-x64-gnu": "npm:4.22.4" - "@rollup/rollup-linux-x64-musl": "npm:4.22.4" - "@rollup/rollup-win32-arm64-msvc": "npm:4.22.4" - "@rollup/rollup-win32-ia32-msvc": "npm:4.22.4" - "@rollup/rollup-win32-x64-msvc": "npm:4.22.4" +"rollup@npm:^4.13.0": + version: 4.21.0 + resolution: "rollup@npm:4.21.0" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.21.0" + "@rollup/rollup-android-arm64": "npm:4.21.0" + "@rollup/rollup-darwin-arm64": "npm:4.21.0" + "@rollup/rollup-darwin-x64": "npm:4.21.0" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.21.0" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.21.0" + "@rollup/rollup-linux-arm64-gnu": "npm:4.21.0" + "@rollup/rollup-linux-arm64-musl": "npm:4.21.0" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.21.0" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.21.0" + "@rollup/rollup-linux-s390x-gnu": "npm:4.21.0" + "@rollup/rollup-linux-x64-gnu": "npm:4.21.0" + "@rollup/rollup-linux-x64-musl": "npm:4.21.0" + "@rollup/rollup-win32-arm64-msvc": "npm:4.21.0" + "@rollup/rollup-win32-ia32-msvc": "npm:4.21.0" + "@rollup/rollup-win32-x64-msvc": "npm:4.21.0" "@types/estree": "npm:1.0.5" fsevents: "npm:~2.3.2" dependenciesMeta: @@ -5165,7 +4834,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: 4c96b6e2e0c5dbe73b4ba899cea894a05115ab8c65ccff631fbbb944e2b3a9f2eb3b99c2dce3dd91b179647df1892ffc44ecee29381ccf155ba8000b22712a32 + checksum: 984beb858da245c5e3a9027d6d87e67ad6443f1b46eab07685b861d9e49da5856693265c62a6f8262c36d11c9092713a96a9124f43e6de6698eb84d77118496a languageName: node linkType: hard @@ -5233,7 +4902,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.2.1, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.5.3": +"semver@npm:^7.2.1, semver@npm:^7.3.5, semver@npm:^7.5.3": version: 7.5.4 resolution: "semver@npm:7.5.4" dependencies: @@ -5391,13 +5060,6 @@ __metadata: languageName: node linkType: hard -"source-map-js@npm:^1.2.1": - version: 1.2.1 - resolution: "source-map-js@npm:1.2.1" - checksum: 7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf - languageName: node - linkType: hard - "spdx-correct@npm:^3.0.0": version: 3.2.0 resolution: "spdx-correct@npm:3.2.0" @@ -5462,7 +5124,7 @@ __metadata: languageName: node linkType: hard -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -5593,15 +5255,6 @@ __metadata: languageName: node linkType: hard -"strip-indent@npm:^3.0.0": - version: 3.0.0 - resolution: "strip-indent@npm:3.0.0" - dependencies: - min-indent: "npm:^1.0.0" - checksum: ae0deaf41c8d1001c5d4fbe16cb553865c1863da4fae036683b474fa926af9fc121e155cb3fc57a68262b2ae7d5b8420aa752c97a6428c315d00efe2a3875679 - languageName: node - linkType: hard - "strip-json-comments@npm:^3.1.0, strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" @@ -5618,7 +5271,7 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^7.0.0, supports-color@npm:^7.1.0": +"supports-color@npm:^7.1.0": version: 7.2.0 resolution: "supports-color@npm:7.2.0" dependencies: @@ -5627,16 +5280,6 @@ __metadata: languageName: node linkType: hard -"supports-hyperlinks@npm:^2.0.0": - version: 2.3.0 - resolution: "supports-hyperlinks@npm:2.3.0" - dependencies: - has-flag: "npm:^4.0.0" - supports-color: "npm:^7.0.0" - checksum: 4057f0d86afb056cd799602f72d575b8fdd79001c5894bcb691176f14e870a687e7981e50bc1484980e8b688c6d5bcd4931e1609816abb5a7dc1486b7babf6a1 - languageName: node - linkType: hard - "supports-preserve-symlinks-flag@npm:^1.0.0": version: 1.0.0 resolution: "supports-preserve-symlinks-flag@npm:1.0.0" @@ -5750,6 +5393,8 @@ __metadata: "@babel/core": "npm:^7.12.3" "@babel/eslint-parser": "npm:^7.15.8" "@babel/eslint-plugin": "npm:^7.13.10" + "@types/debug": "npm:^4.1.12" + "@types/temp": "npm:^0.9.4" "@vitest/coverage-v8": "npm:^2.0.5" badge-maker: "npm:^3.3.0" debug: "npm:^4.3.1" @@ -5774,19 +5419,12 @@ __metadata: p-retry: "npm:^4.2.0" prettier: "npm:^2.8.6" temp: "npm:^0.9.1" - tsd: "npm:^0.25.0" tus-js-client: "npm:^3.0.1" + typescript: "npm:^5.5.4" vitest: "npm:^2.0.5" languageName: unknown linkType: soft -"trim-newlines@npm:^3.0.0": - version: 3.0.1 - resolution: "trim-newlines@npm:3.0.1" - checksum: 03cfefde6c59ff57138412b8c6be922ecc5aec30694d784f2a65ef8dcbd47faef580b7de0c949345abdc56ec4b4abf64dd1e5aea619b200316e471a3dd5bf1f6 - languageName: node - linkType: hard - "tsconfig-paths@npm:^3.14.2": version: 3.14.2 resolution: "tsconfig-paths@npm:3.14.2" @@ -5799,22 +5437,6 @@ __metadata: languageName: node linkType: hard -"tsd@npm:^0.25.0": - version: 0.25.0 - resolution: "tsd@npm:0.25.0" - dependencies: - "@tsd/typescript": "npm:~4.9.3" - eslint-formatter-pretty: "npm:^4.1.0" - globby: "npm:^11.0.1" - meow: "npm:^9.0.0" - path-exists: "npm:^4.0.0" - read-pkg-up: "npm:^7.0.0" - bin: - tsd: dist/cli.js - checksum: 760dcbeff20f057ac515264124db3224e7deeb8fc8a9dcf393e87abab1fa3e9b6e9a2850bbed118e7c6353440e2e27ad5b7392621d100132d76b94c2a30169e5 - languageName: node - linkType: hard - "tslib@npm:^1.8.1": version: 1.14.1 resolution: "tslib@npm:1.14.1" @@ -5857,13 +5479,6 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^0.18.0": - version: 0.18.1 - resolution: "type-fest@npm:0.18.1" - checksum: 303f5ecf40d03e1d5b635ce7660de3b33c18ed8ebc65d64920c02974d9e684c72483c23f9084587e9dd6466a2ece1da42ddc95b412a461794dd30baca95e2bac - languageName: node - linkType: hard - "type-fest@npm:^0.20.2": version: 0.20.2 resolution: "type-fest@npm:0.20.2" @@ -5871,27 +5486,6 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^0.21.3": - version: 0.21.3 - resolution: "type-fest@npm:0.21.3" - checksum: 902bd57bfa30d51d4779b641c2bc403cdf1371fb9c91d3c058b0133694fcfdb817aef07a47f40faf79039eecbaa39ee9d3c532deff244f3a19ce68cea71a61e8 - languageName: node - linkType: hard - -"type-fest@npm:^0.6.0": - version: 0.6.0 - resolution: "type-fest@npm:0.6.0" - checksum: 0c585c26416fce9ecb5691873a1301b5aff54673c7999b6f925691ed01f5b9232db408cdbb0bd003d19f5ae284322523f44092d1f81ca0a48f11f7cf0be8cd38 - languageName: node - linkType: hard - -"type-fest@npm:^0.8.1": - version: 0.8.1 - resolution: "type-fest@npm:0.8.1" - checksum: dffbb99329da2aa840f506d376c863bd55f5636f4741ad6e65e82f5ce47e6914108f44f340a0b74009b0cb5d09d6752ae83203e53e98b1192cf80ecee5651636 - languageName: node - linkType: hard - "typed-array-buffer@npm:^1.0.0": version: 1.0.0 resolution: "typed-array-buffer@npm:1.0.0" @@ -5939,6 +5533,26 @@ __metadata: languageName: node linkType: hard +"typescript@npm:^5.5.4": + version: 5.5.4 + resolution: "typescript@npm:5.5.4" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 422be60f89e661eab29ac488c974b6cc0a660fb2228003b297c3d10c32c90f3bcffc1009b43876a082515a3c376b1eefcce823d6e78982e6878408b9a923199c + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A^5.5.4#optional!builtin": + version: 5.5.4 + resolution: "typescript@patch:typescript@npm%3A5.5.4#optional!builtin::version=5.5.4&hash=29ae49" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10dd9881baba22763de859e8050d6cb6e2db854197495c6f1929b08d1eb2b2b00d0b5d9b0bcee8472f1c3f4a7ef6a5d7ebe0cfd703f853aa5ae465b8404bc1ba + languageName: node + linkType: hard + "unbox-primitive@npm:^1.0.2": version: 1.0.2 resolution: "unbox-primitive@npm:1.0.2" @@ -6049,13 +5663,13 @@ __metadata: linkType: hard "vite@npm:^5.0.0": - version: 5.4.7 - resolution: "vite@npm:5.4.7" + version: 5.4.1 + resolution: "vite@npm:5.4.1" dependencies: esbuild: "npm:^0.21.3" fsevents: "npm:~2.3.3" - postcss: "npm:^8.4.43" - rollup: "npm:^4.20.0" + postcss: "npm:^8.4.41" + rollup: "npm:^4.13.0" peerDependencies: "@types/node": ^18.0.0 || >=20.0.0 less: "*" @@ -6087,7 +5701,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 0ca7ca60f71c61f3855bbabf7e33909bec32933b35914d4d281813c728183e78e7ce5be05735a7671df3a994613d3881f520a32a80715faa92effb28deee9320 + checksum: b9ea824f1a946aa494f756e6d9dd88869baa62ae5ba3071b32b6a20958fd622cb624c860bdd7daee201c83ca029feaf8bbe2d2a6e172a5d49308772f8899d86d languageName: node linkType: hard @@ -6285,10 +5899,3 @@ __metadata: checksum: 2286b5e8dbfe22204ab66e2ef5cc9bbb1e55dfc873bbe0d568aa943eb255d131890dfd5bf243637273d31119b870f49c18fcde2c6ffbb7a7a092b870dc90625a languageName: node linkType: hard - -"yargs-parser@npm:^20.2.3": - version: 20.2.9 - resolution: "yargs-parser@npm:20.2.9" - checksum: 0685a8e58bbfb57fab6aefe03c6da904a59769bd803a722bb098bd5b0f29d274a1357762c7258fb487512811b8063fb5d2824a3415a0a4540598335b3b086c72 - languageName: node - linkType: hard