diff --git a/lib/browser/BrowserFileReader.ts b/lib/browser/BrowserFileReader.ts index c481308a..34ae94e0 100644 --- a/lib/browser/BrowserFileReader.ts +++ b/lib/browser/BrowserFileReader.ts @@ -6,7 +6,7 @@ import { supportedTypes as supportedBaseTypes, } from '../commonFileReader.js' import type { FileReader, FileSource, UploadInput } from '../options.js' -import { BlobFileSource } from '../sources/BlobFileSource.js' +import { ReactNativeFileSource } from "../sources/ReactNativeFileSource.js"; export class BrowserFileReader implements FileReader { async openFile(input: UploadInput, chunkSize: number): Promise { @@ -20,8 +20,7 @@ export class BrowserFileReader implements FileReader { } try { - const blob = await uriToBlob(input.uri) - return new BlobFileSource(blob) + return new ReactNativeFileSource(input) } catch (err) { throw new Error( `tus: cannot fetch \`file.uri\` as Blob, make sure the uri is correct and accessible. ${err}`, diff --git a/lib/browser/fileSignature.ts b/lib/browser/fileSignature.ts deleted file mode 100644 index 276b8f5f..00000000 --- a/lib/browser/fileSignature.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { ReactNativeFile, UploadInput, UploadOptions } from '../options.js' -import { isReactNativeFile, isReactNativePlatform } from '../reactnative/isReactNative.js' - -/** - * Generate a fingerprint for a file which will be used the store the endpoint - */ -export function fingerprint(file: UploadInput, options: UploadOptions) { - if (isReactNativePlatform() && isReactNativeFile(file)) { - return Promise.resolve(reactNativeFingerprint(file, options)) - } - - if (file instanceof Blob) { - return Promise.resolve( - //@ts-expect-error TODO: We have to check the input type here - // This can be fixed by moving the fingerprint function to the FileReader class - ['tus-br', file.name, file.type, file.size, file.lastModified, options.endpoint].join('-'), - ) - } - - return Promise.resolve(null) -} - -function reactNativeFingerprint(file: ReactNativeFile, options: UploadOptions): string { - const exifHash = file.exif ? hashCode(JSON.stringify(file.exif)) : 'noexif' - return ['tus-rn', file.name || 'noname', file.size || 'nosize', exifHash, options.endpoint].join( - '/', - ) -} - -function hashCode(str: string): number { - // from https://stackoverflow.com/a/8831937/151666 - let hash = 0 - if (str.length === 0) { - return hash - } - for (let i = 0; i < str.length; i++) { - const char = str.charCodeAt(i) - hash = (hash << 5) - hash + char - hash &= hash // Convert to 32bit integer - } - return hash -} diff --git a/lib/browser/index.ts b/lib/browser/index.ts index c584040d..9fdb91bc 100644 --- a/lib/browser/index.ts +++ b/lib/browser/index.ts @@ -2,11 +2,10 @@ import { DetailedError } from '../DetailedError.js' import { NoopUrlStorage } from '../NoopUrlStorage.js' import { enableDebugLog } from '../logger.js' import type { UploadInput, UploadOptions } from '../options.js' -import { BaseUpload, defaultOptions as baseDefaultOptions, terminate } from '../upload.js' +import { BaseUpload, defaultOptions as baseDefaultOptions, noOpFingerprint, terminate } from '../upload.js' import { BrowserFileReader } from './BrowserFileReader.js' import { XHRHttpStack as DefaultHttpStack } from './XHRHttpStack.js' -import { fingerprint } from './fileSignature.js' import { WebStorageUrlStorage, canStoreURLs } from './urlStorage.js' const defaultOptions = { @@ -14,7 +13,7 @@ const defaultOptions = { httpStack: new DefaultHttpStack(), fileReader: new BrowserFileReader(), urlStorage: canStoreURLs ? new WebStorageUrlStorage() : new NoopUrlStorage(), - fingerprint, + fingerprint: noOpFingerprint } class Upload extends BaseUpload { diff --git a/lib/node/fileSignature.ts b/lib/node/fileSignature.ts deleted file mode 100644 index 8dff27de..00000000 --- a/lib/node/fileSignature.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { createHash } from 'node:crypto' -import { ReadStream } from 'node:fs' -import { stat } from 'node:fs/promises' -import * as path from 'node:path' -import type { UploadInput, UploadOptions } from '../options.js' - -export async function fingerprint( - file: UploadInput, - options: UploadOptions, -): Promise { - if (Buffer.isBuffer(file)) { - // create MD5 hash for buffer type - const blockSize = 64 * 1024 // 64kb - const content = file.slice(0, Math.min(blockSize, file.length)) - const hash = createHash('md5').update(content).digest('hex') - const ret = ['node-buffer', hash, file.length, options.endpoint].join('-') - return ret - } - - if (file instanceof ReadStream && file.path != null) { - const name = path.resolve(Buffer.isBuffer(file.path) ? file.path.toString('utf-8') : file.path) - const info = await stat(file.path) - const ret = ['node-file', name, info.size, info.mtime.getTime(), options.endpoint].join('-') - - return ret - } - - // fingerprint cannot be computed for file input type - return null -} diff --git a/lib/node/index.ts b/lib/node/index.ts index 91516141..94c133bc 100644 --- a/lib/node/index.ts +++ b/lib/node/index.ts @@ -2,19 +2,18 @@ import { DetailedError } from '../DetailedError.js' import { NoopUrlStorage } from '../NoopUrlStorage.js' import { enableDebugLog } from '../logger.js' import type { UploadInput, UploadOptions } from '../options.js' -import { BaseUpload, defaultOptions as baseDefaultOptions, terminate } from '../upload.js' +import { BaseUpload, defaultOptions as baseDefaultOptions, noOpFingerprint, terminate } from '../upload.js' import { canStoreURLs } from './FileUrlStorage.js' import { NodeFileReader } from './NodeFileReader.js' import { NodeHttpStack as DefaultHttpStack } from './NodeHttpStack.js' -import { fingerprint } from './fileSignature.js' const defaultOptions = { ...baseDefaultOptions, httpStack: new DefaultHttpStack(), fileReader: new NodeFileReader(), urlStorage: new NoopUrlStorage(), - fingerprint, + fingerprint: noOpFingerprint } class Upload extends BaseUpload { diff --git a/lib/node/sources/NodeStreamFileSource.ts b/lib/node/sources/NodeStreamFileSource.ts index 143f2f25..8d5d822a 100644 --- a/lib/node/sources/NodeStreamFileSource.ts +++ b/lib/node/sources/NodeStreamFileSource.ts @@ -1,5 +1,5 @@ -import type { Readable } from 'node:stream' -import type { FileSource } from '../../options.js' +import { Readable, Transform } from 'node:stream' +import type { FileSource, UploadOptions } from '../../options.js' /** * readChunk reads a chunk with the given size from the given @@ -74,9 +74,16 @@ export class NodeStreamFileSource implements FileSource { }) } + // TODO: Implement fingerprint calculation for NodeStreamFileSource. + // This should likely involve reading the stream and creating a hash/checksum + // while preserving the stream's content for later use. + fingerprint(options: UploadOptions): Promise { + return Promise.resolve(null); + } + async slice(start: number, end: number) { // Fail fast if the caller requests a proportion of the data which is not - // available any more. + // available anymore. if (start < this._bufPos) { throw new Error('cannot slice from position which we already seeked away') } diff --git a/lib/node/sources/PathFileSource.ts b/lib/node/sources/PathFileSource.ts index 51ef4842..b7f67037 100644 --- a/lib/node/sources/PathFileSource.ts +++ b/lib/node/sources/PathFileSource.ts @@ -1,5 +1,5 @@ -import { type ReadStream, createReadStream, promises as fsPromises } from 'node:fs' -import type { FileSource, PathReference } from '../../options.js' +import { createReadStream, promises as fsPromises, type ReadStream } from 'node:fs' +import type { FileSource, PathReference, UploadOptions } from '../../options.js' export async function getFileSourceFromPath(file: PathReference): Promise { const path = file.path.toString() @@ -34,6 +34,10 @@ export class PathFileSource implements FileSource { this.size = size } + fingerprint(options: UploadOptions): Promise { + return Promise.resolve(["tus-path-file-source", this.size, this._path, options.endpoint].join('-')) + } + slice(start: number, end: number) { // TODO: Does this create multiple file descriptors? Can we reduce this by // using a file handle instead? diff --git a/lib/options.ts b/lib/options.ts index 011de2f5..ad3ab569 100644 --- a/lib/options.ts +++ b/lib/options.ts @@ -23,7 +23,7 @@ export interface ReactNativeFile { /** * PathReference is a reference to a file on disk. Currently, it's only supported * in Node.js. It can be supplied as a normal object or as an instance of `fs.ReadStream`, - * which also statisfies this interface. + * which also satisfies this interface. * * Optionally, a start and/or end position can be defined to define a range of bytes from * the file that should be uploaded instead of the entire file. Both start and end are @@ -54,7 +54,7 @@ export interface UploadOptions { uploadUrl?: string metadata: { [key: string]: string } metadataForPartialUploads: UploadOptions['metadata'] - fingerprint: (file: UploadInput, options: UploadOptions) => Promise + fingerprint: (file: UploadInput, options: UploadOptions, sourceFingerprint: string | null) => Promise uploadSize?: number onProgress?: (bytesSent: number, bytesTotal: number | null) => void @@ -117,6 +117,7 @@ export interface FileSource { size: number | null slice(start: number, end: number): Promise close(): void + fingerprint(options: UploadOptions): Promise } // TODO: Allow Web Streams' ReadableStream as well diff --git a/lib/sources/ArrayBufferViewFileSource.ts b/lib/sources/ArrayBufferViewFileSource.ts index c9e7e0c8..b04d8ec3 100644 --- a/lib/sources/ArrayBufferViewFileSource.ts +++ b/lib/sources/ArrayBufferViewFileSource.ts @@ -1,14 +1,14 @@ -import type { FileSource, SliceResult } from '../options.js' +import type { FileSource, SliceResult, UploadOptions } from '../options.js' /** * ArrayBufferViewFileSource implements FileSource for ArrayBufferView instances - * (e.g. TypedArry or DataView). + * (e.g., TypedArray or DataView). * * Note that the underlying ArrayBuffer should not change once passed to tus-js-client * or it will lead to weird behavior. */ export class ArrayBufferViewFileSource implements FileSource { - private _view: ArrayBufferView + private readonly _view: ArrayBufferView size: number @@ -17,6 +17,13 @@ export class ArrayBufferViewFileSource implements FileSource { this.size = view.byteLength } + async fingerprint(options: UploadOptions): Promise { + const buffer = this._view.buffer.slice(this._view.byteOffset, this._view.byteOffset + this._view.byteLength); + const hashBuffer = await crypto.subtle.digest('SHA-256', buffer); + const hashContent = Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join(''); + return Promise.resolve([hashContent, options.endpoint].join('-')); + } + slice(start: number, end: number): Promise { const buffer = this._view.buffer const startInBuffer = this._view.byteOffset + start diff --git a/lib/sources/BlobFileSource.ts b/lib/sources/BlobFileSource.ts index 8a67dab4..23f6e37d 100644 --- a/lib/sources/BlobFileSource.ts +++ b/lib/sources/BlobFileSource.ts @@ -1,12 +1,12 @@ import { isCordova } from '../cordova/isCordova.js' import { readAsByteArray } from '../cordova/readAsByteArray.js' -import type { FileSource, SliceResult } from '../options.js' +import type { FileSource, SliceResult, UploadOptions } from '../options.js' /** * BlobFileSource implements FileSource for Blobs (and therefore also for File instances). */ export class BlobFileSource implements FileSource { - private _file: Blob + private readonly _file: Blob size: number @@ -15,6 +15,19 @@ export class BlobFileSource implements FileSource { this.size = file.size } + fingerprint(options: UploadOptions): Promise { + let name, lastModified; + if (this._file instanceof File) { + name = this._file.name; + lastModified = this._file.lastModified; + } else { + name = 'blob'; + lastModified = 0; + } + + return Promise.resolve(['tus-br', name, this._file.type, this._file.size, lastModified, options.endpoint].join('-')); + } + async slice(start: number, end: number): Promise { // TODO: This looks fishy. We should test how this actually works in Cordova // and consider moving this into the lib/cordova/ directory. diff --git a/lib/sources/ReactNativeFileSource.ts b/lib/sources/ReactNativeFileSource.ts new file mode 100644 index 00000000..a556b22b --- /dev/null +++ b/lib/sources/ReactNativeFileSource.ts @@ -0,0 +1,51 @@ +import { FileSource, ReactNativeFile, SliceResult, UploadOptions } from "../options.js"; + +export class ReactNativeFileSource implements FileSource { + private readonly _file: ReactNativeFile + size: number; + + constructor(file: ReactNativeFile) { + this._file = file + this.size = Number(file.size) + } + + fingerprint(options: UploadOptions): Promise { + return Promise.resolve(this.reactNativeFingerprint(this._file, options)) + } + + // TODO: Implement the slice method to read file content from start to end positions + // The implementation should: + // 1. Read the file content from the ReactNative file URI + // 2. Return the sliced content as value + // 3. Calculate proper size and done values + async slice(start: number, end: number): Promise { + let value = null, size = null, done = true; + + return { value, size, done } + } + + close(): void { + // Nothing to do here since we don't need to release any resources. + } + + private reactNativeFingerprint(file: ReactNativeFile, options: UploadOptions): string { + const exifHash = file.exif ? this.hashCode(JSON.stringify(file.exif)) : 'noexif' + return ['tus-rn', file.name || 'noname', file.size || 'nosize', exifHash, options.endpoint].join( + '/', + ) + } + + private hashCode(str: string): number { + // from https://stackoverflow.com/a/8831937/151666 + let hash = 0 + if (str.length === 0) { + return hash + } + for (let i = 0; i < str.length; i++) { + const char = str.charCodeAt(i) + hash = (hash << 5) - hash + char + hash &= hash // Convert to 32bit integer + } + return hash + } +} diff --git a/lib/sources/WebStreamFileSource.ts b/lib/sources/WebStreamFileSource.ts index 5d1f6434..429d6a53 100644 --- a/lib/sources/WebStreamFileSource.ts +++ b/lib/sources/WebStreamFileSource.ts @@ -1,4 +1,4 @@ -import type { FileSource, SliceResult } from '../options.js' +import type {FileSource, SliceResult, UploadOptions} from '../options.js' function len(blobOrArray: WebStreamFileSource['_buffer']): number { if (blobOrArray === undefined) return 0 @@ -28,7 +28,7 @@ function concat(a: T, b: T): T { */ // TODO: Can we share code with NodeStreamFileSource? export class WebStreamFileSource implements FileSource { - private _reader: ReadableStreamDefaultReader + private readonly _reader: ReadableStreamDefaultReader private _buffer: Blob | Uint8Array | undefined @@ -50,10 +50,13 @@ export class WebStreamFileSource implements FileSource { 'Readable stream is already locked to reader. tus-js-client cannot obtain a new reader.', ) } - this._reader = stream.getReader() } + fingerprint(options: UploadOptions): Promise { + return Promise.resolve(null); + } + async slice(start: number, end: number): Promise { if (start < this._bufferOffset) { throw new Error("Requested data is before the reader's current offset") diff --git a/lib/upload.ts b/lib/upload.ts index ce88ffcd..2e21ed4f 100644 --- a/lib/upload.ts +++ b/lib/upload.ts @@ -56,6 +56,8 @@ export const defaultOptions = { protocol: PROTOCOL_TUS_V1 as UploadOptions['protocol'], } +export const noOpFingerprint = async () => null + export class BaseUpload { options: UploadOptions @@ -68,10 +70,10 @@ export class BaseUpload { // The underlying request object for the current PATCH request private _req?: HttpRequest - // The fingerpinrt for the current file (set after start()) + // The fingerprint for the current file (set after start()) private _fingerprint: string | null = null - // The key that the URL storage returned when saving an URL with a fingerprint, + // The key that the URL storage returned when saving a URL with a fingerprint, private _urlStorageKey?: string // The offset used in the current PATCH request @@ -105,6 +107,10 @@ export class BaseUpload { // parts, if the parallelUploads option is used. private _parallelUploadUrls?: string[] + // True if the remote upload resource's length is deferred (either taken from + // upload options or HEAD response) + private _uploadLengthDeferred: boolean + constructor(file: UploadInput, options: UploadOptions) { // Warn about removed options from previous versions if ('resume' in options) { @@ -120,11 +126,13 @@ export class BaseUpload { // TODO: Remove this cast this.options.chunkSize = Number(this.options.chunkSize) + this._uploadLengthDeferred = this.options.uploadLengthDeferred + this.file = file } - async findPreviousUploads(): Promise { - const fingerprint = await this.options.fingerprint(this.file, this.options) + async findPreviousUploads(sourceFingerprint: string | null): Promise { + const fingerprint = await this.options?.fingerprint?.(this.file, this.options, sourceFingerprint) if (!fingerprint) { throw new Error('tus: unable to calculate fingerprint for this input file') } @@ -180,7 +188,7 @@ export class BaseUpload { return } - if (this.options.uploadLengthDeferred) { + if (this._uploadLengthDeferred) { this._emitError( new Error( 'tus: cannot use the `uploadLengthDeferred` option when parallelUploads is enabled', @@ -222,8 +230,26 @@ export class BaseUpload { }) } + async _getFingerprint(): Promise { + const sourceFingerprint = await this._getSourceFingerprint() + + if (this.options.fingerprint) { + return await this.options.fingerprint(this.file, this.options, sourceFingerprint) + } + + return sourceFingerprint + } + + private async _getSourceFingerprint() { + return this._source ? this._source.fingerprint(this.options) : null + } + private async _prepareAndStartUpload(): Promise { - this._fingerprint = await this.options.fingerprint(this.file, this.options) + if (this._source == null) { + this._source = await this.options.fileReader.openFile(this.file, this.options.chunkSize) + } + + this._fingerprint = await this._getFingerprint() if (this._fingerprint == null) { log( 'No fingerprint was calculated meaning that the upload cannot be stored in the URL storage.', @@ -232,14 +258,10 @@ export class BaseUpload { log(`Calculated fingerprint: ${this._fingerprint}`) } - if (this._source == null) { - this._source = await this.options.fileReader.openFile(this.file, this.options.chunkSize) - } - // First, we look at the uploadLengthDeferred option. // Next, we check if the caller has supplied a manual upload size. // Finally, we try to use the calculated size from the source object. - if (this.options.uploadLengthDeferred) { + if (this._uploadLengthDeferred) { this._size = null } else if (this.options.uploadSize != null) { this._size = Number(this.options.uploadSize) @@ -589,7 +611,7 @@ export class BaseUpload { const req = this._openRequest('POST', this.options.endpoint) - if (this.options.uploadLengthDeferred) { + if (this._uploadLengthDeferred) { req.setHeader('Upload-Defer-Length', '1') } else { if (this._size == null) { @@ -606,7 +628,7 @@ export class BaseUpload { let res: HttpResponse try { - if (this.options.uploadDataDuringCreation && !this.options.uploadLengthDeferred) { + if (this.options.uploadDataDuringCreation && !this._uploadLengthDeferred) { this._offset = 0 res = await this._addChunkToRequest(req) } else { @@ -728,11 +750,14 @@ export class BaseUpload { throw new DetailedError('tus: invalid Upload-Offset header', undefined, req, res) } + const deferLength = res.getHeader('Upload-Defer-Length') + this._uploadLengthDeferred = deferLength === '1' + // @ts-expect-error parseInt also handles undefined as we want it to const length = Number.parseInt(res.getHeader('Upload-Length'), 10) if ( Number.isNaN(length) && - !this.options.uploadLengthDeferred && + !this._uploadLengthDeferred && this.options.protocol === PROTOCOL_TUS_V1 ) { throw new DetailedError('tus: invalid or missing length value', undefined, req, res) @@ -842,7 +867,7 @@ export class BaseUpload { if ( // @ts-expect-error _size is set here (end === Number.POSITIVE_INFINITY || end > this._size) && - !this.options.uploadLengthDeferred + !this._uploadLengthDeferred ) { // @ts-expect-error _size is set here end = this._size @@ -856,9 +881,10 @@ export class BaseUpload { // If the upload length is deferred, the upload size was not specified during // upload creation. So, if the file reader is done reading, we know the total // upload size and can tell the tus server. - if (this.options.uploadLengthDeferred && done) { + if (this._uploadLengthDeferred && done) { this._size = this._offset + sizeOfValue req.setHeader('Upload-Length', `${this._size}`) + this._uploadLengthDeferred = false } // The specified uploadSize might not match the actual amount of data that a source @@ -867,7 +893,7 @@ export class BaseUpload { // in a loop of repeating empty PATCH requests. // See https://community.transloadit.com/t/how-to-abort-hanging-companion-uploads/16488/13 const newSize = this._offset + sizeOfValue - if (!this.options.uploadLengthDeferred && done && newSize !== this._size) { + if (!this._uploadLengthDeferred && done && newSize !== this._size) { throw new Error( `upload was configured with a size of ${this._size} bytes, but the source is done after ${newSize} bytes`, ) @@ -1218,5 +1244,6 @@ export async function terminate(url: string, options: UploadOptions): Promise=20" }, @@ -79,14 +86,22 @@ "type": "git", "url": "git+https://github.com/tus/tus-js-client.git" }, - "keywords": ["tus", "resumable", "upload", "protocol", "progress", "file", "browser"], + "keywords": [ + "tus", + "resumable", + "upload", + "protocol", + "progress", + "file", + "browser" + ], "license": "MIT", "bugs": { "url": "https://github.com/tus/tus-js-client/issues" }, "homepage": "https://github.com/tus/tus-js-client", "devDependencies": { - "@arethetypeswrong/cli": "^0.17.3", + "@arethetypeswrong/cli": "^0.18.1", "@biomejs/biome": "^1.7.3", "@rollup/plugin-commonjs": "^28.0.2", "@rollup/plugin-node-resolve": "^16.0.0", @@ -144,5 +159,6 @@ "lint-type-exports": "attw --pack .", "lint": "npm-run-all lint-*", "fix": "biome check --write ." - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/test/spec/test-common.js b/test/spec/test-common.js index 2e20d1fa..53000c23 100644 --- a/test/spec/test-common.js +++ b/test/spec/test-common.js @@ -1330,5 +1330,63 @@ describe('tus', () => { expect(options.onError).not.toHaveBeenCalled() expect(options.onSuccess).toHaveBeenCalled() }) + + it('should send upload length on the last request when length is deferred and we know the total size', async () => { + const testStack = new TestHttpStack() + const file = getBlob('hello world') + const options = { + httpStack: testStack, + endpoint: 'http://tus.io/uploads', + uploadUrl: 'http://tus.io/uploads/resuming', + chunkSize: 4, + // No `uploadLengthDeferred: true` here, but the client learns + // about the deferred length from the HEAD response. + } + + const upload = new Upload(file, options) + upload.start() + + let req = await testStack.nextRequest() + expect(req.url).toBe('http://tus.io/uploads/resuming') + expect(req.method).toBe('HEAD') + + req.respondWith({ + status: 204, + responseHeaders: { + 'Upload-Defer-Length': '1', + 'Upload-Offset': '5', + }, + }) + + req = await testStack.nextRequest() + expect(req.url).toBe('http://tus.io/uploads/resuming') + expect(req.method).toBe('PATCH') + expect(req.requestHeaders['Upload-Offset']).toBe('5') + expect(req.requestHeaders['Upload-Length']).toBe(undefined) + expect(req.body.size).toBe(4) + expect(await req.body.text()).toBe(' wor') + + req.respondWith({ + status: 204, + responseHeaders: { + 'Upload-Offset': '9', + }, + }) + + req = await testStack.nextRequest() + expect(req.url).toBe('http://tus.io/uploads/resuming') + expect(req.method).toBe('PATCH') + expect(req.requestHeaders['Upload-Offset']).toBe('9') + expect(req.requestHeaders['Upload-Length']).toBe('11') + expect(req.body.size).toBe(2) + expect(await req.body.text()).toBe('ld') + + req.respondWith({ + status: 204, + responseHeaders: { + 'Upload-Offset': '11', + }, + }) + }) }) }) diff --git a/test/spec/test-web-stream.js b/test/spec/test-web-stream.js index 12f13baa..0a4c7a8e 100644 --- a/test/spec/test-web-stream.js +++ b/test/spec/test-web-stream.js @@ -234,6 +234,7 @@ describe('tus', () => { let req = await testStack.nextRequest() expect(req.url).toBe('http://tus.io/files/') expect(req.method).toBe('POST') + expect(req.requestHeaders['Upload-Defer-Length']).toBe('1') req.respondWith({ status: 201, @@ -258,6 +259,7 @@ describe('tus', () => { status: 204, responseHeaders: { 'Upload-Offset': '0', + 'Upload-Defer-Length': '1', }, }) @@ -306,6 +308,7 @@ describe('tus', () => { let req = await testStack.nextRequest() expect(req.url).toBe('http://tus.io/files/') expect(req.method).toBe('POST') + expect(req.requestHeaders['Upload-Defer-Length']).toBe('1') req.respondWith({ status: 201, @@ -341,6 +344,7 @@ describe('tus', () => { status: 204, responseHeaders: { 'Upload-Offset': '6', + 'Upload-Defer-Length': '1', }, }) @@ -442,6 +446,7 @@ describe('tus', () => { status: 200, responseHeaders: { 'Upload-Offset': '6', + 'Upload-Defer-Length': '1', }, }) diff --git a/yarn.lock b/yarn.lock index 82c2b4ff..425c2390 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,12 +7,12 @@ resolved "https://registry.yarnpkg.com/@andrewbranch/untar.js/-/untar.js-1.0.3.tgz#ba9494f85eb83017c5c855763969caf1d0adea00" integrity sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw== -"@arethetypeswrong/cli@^0.17.3": - version "0.17.4" - resolved "https://registry.yarnpkg.com/@arethetypeswrong/cli/-/cli-0.17.4.tgz#45405f75081710c1cf0dab6edb4320fd75d215c8" - integrity sha512-AeiKxtf67XD/NdOqXgBOE5TZWH3EOCt+0GkbUpekOzngc+Q/cRZ5azjWyMxISxxfp0EItgm5NoSld9p7BAA5xQ== +"@arethetypeswrong/cli@^0.18.1": + version "0.18.1" + resolved "https://registry.yarnpkg.com/@arethetypeswrong/cli/-/cli-0.18.1.tgz#d7f50d6200d0030ae24ed61da841994e87557b6d" + integrity sha512-SS1Z5gRSvbP4tl98KlNygSUp3Yfenktt782MQKEbYm6GFPowztnnvdEUhQGm2uVDIH4YkU6av+n8Lm6OEOigqA== dependencies: - "@arethetypeswrong/core" "0.17.4" + "@arethetypeswrong/core" "0.18.1" chalk "^4.1.2" cli-table3 "^0.6.3" commander "^10.0.1" @@ -20,16 +20,16 @@ marked-terminal "^7.1.0" semver "^7.5.4" -"@arethetypeswrong/core@0.17.4": - version "0.17.4" - resolved "https://registry.yarnpkg.com/@arethetypeswrong/core/-/core-0.17.4.tgz#f3f85aa8bbcca6d215938580165e47b2244c7f4c" - integrity sha512-Izvir8iIoU+X4SKtDAa5kpb+9cpifclzsbA8x/AZY0k0gIfXYQ1fa1B6Epfe6vNA2YfDX8VtrZFgvnXB6aPEoQ== +"@arethetypeswrong/core@0.18.1": + version "0.18.1" + resolved "https://registry.yarnpkg.com/@arethetypeswrong/core/-/core-0.18.1.tgz#0dbfab6cabccfe5d4e69e926ba4a3344b496702d" + integrity sha512-uUw47cLgB6zYOpAxFp94NG/J9ev0wcOC+UOmTCFEWtbDEn4vpR0ScoPxD7LCGcPczOd7bDJSJL/gMSz3BknYcw== dependencies: "@andrewbranch/untar.js" "^1.0.3" "@loaderkit/resolve" "^1.0.2" cjs-module-lexer "^1.2.3" fflate "^0.8.2" - lru-cache "^10.4.3" + lru-cache "^11.0.1" semver "^7.5.4" typescript "5.6.1-rc" validate-npm-package-name "^5.0.0" @@ -155,16 +155,16 @@ resolved "https://registry.yarnpkg.com/@publint/pack/-/pack-0.1.2.tgz#1b9a9567423262093e4a73e77697b65bf622f8c9" integrity sha512-S+9ANAvUmjutrshV4jZjaiG8XQyuJIZ8a4utWmN/vW1sgQ9IfBnPndwkmQYw53QmouOIytT874u65HEmu6H5jw== -"@puppeteer/browsers@2.10.2": - version "2.10.2" - resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-2.10.2.tgz#c2a63cee699c6b5b971b9fcba9095098970f1648" - integrity sha512-i4Ez+s9oRWQbNjtI/3+jxr7OH508mjAKvza0ekPJem0ZtmsYHP3B5dq62+IaBHKaGCOuqJxXzvFLUhJvQ6jtsQ== +"@puppeteer/browsers@2.10.5": + version "2.10.5" + resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-2.10.5.tgz#dddb8f8716ae6364f6f2d31125e76f311dd4a49d" + integrity sha512-eifa0o+i8dERnngJwKrfp3dEq7ia5XFyoqB17S4gK8GhsQE4/P8nxOfQSE0zQHxzzLo/cmF+7+ywEQ7wK7Fb+w== dependencies: - debug "^4.4.0" + debug "^4.4.1" extract-zip "^2.0.1" progress "^2.0.3" proxy-agent "^6.5.0" - semver "^7.7.1" + semver "^7.7.2" tar-fs "^3.0.8" yargs "^17.7.2" @@ -209,105 +209,105 @@ estree-walker "^2.0.2" picomatch "^4.0.2" -"@rollup/rollup-android-arm-eabi@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.1.tgz#e1562d360bca73c7bef6feef86098de3a2f1d442" - integrity sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw== - -"@rollup/rollup-android-arm64@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.1.tgz#37ba63940211673e15dcc5f469a78e34276dbca7" - integrity sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw== - -"@rollup/rollup-darwin-arm64@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.1.tgz#58b1eb86d997d71dabc5b78903233a3c27438ca0" - integrity sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA== - -"@rollup/rollup-darwin-x64@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.1.tgz#5e22dab3232b1e575d930ce891abb18fe19c58c9" - integrity sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw== - -"@rollup/rollup-freebsd-arm64@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.1.tgz#04c892d9ff864d66e31419634726ab0bebb33707" - integrity sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw== - -"@rollup/rollup-freebsd-x64@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.1.tgz#f4b1e091f7cf5afc9e3a029d70128ad56409ecfb" - integrity sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q== - -"@rollup/rollup-linux-arm-gnueabihf@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.1.tgz#c8814bb5ce047a81b1fe4a33628dfd4ac52bd864" - integrity sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg== - -"@rollup/rollup-linux-arm-musleabihf@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.1.tgz#5b4e7bd83cbebbf5ffe958802dcfd4ee34bf73a3" - integrity sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg== - -"@rollup/rollup-linux-arm64-gnu@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.1.tgz#141c848e53cee011e82a11777b8a51f1b3e8d77c" - integrity sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg== - -"@rollup/rollup-linux-arm64-musl@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.1.tgz#22ebeaf2fa301aa4aa6c84b760e6cd1d1ac7eb1e" - integrity sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ== - -"@rollup/rollup-linux-loongarch64-gnu@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.1.tgz#20b77dc78e622f5814ff8e90c14c938ceb8043bc" - integrity sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ== - -"@rollup/rollup-linux-powerpc64le-gnu@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.1.tgz#2c90f99c987ef1198d4f8d15d754c286e1f07b13" - integrity sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg== - -"@rollup/rollup-linux-riscv64-gnu@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.1.tgz#9336fd5e47d7f4760d02aa85f76976176eef53ca" - integrity sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ== - -"@rollup/rollup-linux-riscv64-musl@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.1.tgz#d75b4d54d46439bb5c6c13762788f57e798f5670" - integrity sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA== - -"@rollup/rollup-linux-s390x-gnu@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.1.tgz#e9f09b802f1291839247399028beaef9ce034c81" - integrity sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg== - -"@rollup/rollup-linux-x64-gnu@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.1.tgz#0413169dc00470667dea8575c1129d4e7a73eb29" - integrity sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ== - -"@rollup/rollup-linux-x64-musl@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.1.tgz#c76fd593323c60ea219439a00da6c6d33ffd0ea6" - integrity sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ== - -"@rollup/rollup-win32-arm64-msvc@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.1.tgz#c7724c386eed0bda5ae7143e4081c1910cab349b" - integrity sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg== - -"@rollup/rollup-win32-ia32-msvc@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.1.tgz#7749e1b65cb64fe6d41ad1ad9e970a0ccc8ac350" - integrity sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA== - -"@rollup/rollup-win32-x64-msvc@4.40.1": - version "4.40.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.1.tgz#8078b71fe0d5825dcbf83d52a7dc858b39da165c" - integrity sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA== +"@rollup/rollup-android-arm-eabi@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz#f39f09f60d4a562de727c960d7b202a2cf797424" + integrity sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw== + +"@rollup/rollup-android-arm64@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.1.tgz#d19af7e23760717f1d879d4ca3d2cd247742dff2" + integrity sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA== + +"@rollup/rollup-darwin-arm64@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz#1c3a2fbf205d80641728e05f4a56c909e95218b7" + integrity sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w== + +"@rollup/rollup-darwin-x64@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.1.tgz#aa66d2ba1a25e609500e13bef06dc0e71cc0c0d4" + integrity sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg== + +"@rollup/rollup-freebsd-arm64@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.1.tgz#df10a7b6316a0ef1028c6ca71a081124c537e30d" + integrity sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg== + +"@rollup/rollup-freebsd-x64@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.1.tgz#a3fdce8a05e95b068cbcb46e4df5185e407d0c35" + integrity sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA== + +"@rollup/rollup-linux-arm-gnueabihf@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.1.tgz#49f766c55383bd0498014a9d76924348c2f3890c" + integrity sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg== + +"@rollup/rollup-linux-arm-musleabihf@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.1.tgz#1d4d7d32fc557e17d52e1857817381ea365e2959" + integrity sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA== + +"@rollup/rollup-linux-arm64-gnu@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.1.tgz#f4fc317268441e9589edad3be8f62b6c03009bc1" + integrity sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA== + +"@rollup/rollup-linux-arm64-musl@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.1.tgz#63a1f1b0671cb17822dabae827fef0e443aebeb7" + integrity sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg== + +"@rollup/rollup-linux-loongarch64-gnu@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.1.tgz#c659b01cc6c0730b547571fc3973e1e955369f98" + integrity sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw== + +"@rollup/rollup-linux-powerpc64le-gnu@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.1.tgz#612e746f9ad7e58480f964d65e0d6c3f4aae69a8" + integrity sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A== + +"@rollup/rollup-linux-riscv64-gnu@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.1.tgz#4610dbd1dcfbbae32fbc10c20ae7387acb31110c" + integrity sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw== + +"@rollup/rollup-linux-riscv64-musl@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.1.tgz#054911fab40dc83fafc21e470193c058108f19d8" + integrity sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw== + +"@rollup/rollup-linux-s390x-gnu@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.1.tgz#98896eca8012547c7f04bd07eaa6896825f9e1a5" + integrity sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g== + +"@rollup/rollup-linux-x64-gnu@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz#01cf56844a1e636ee80dfb364e72c2b7142ad896" + integrity sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A== + +"@rollup/rollup-linux-x64-musl@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz#e67c7531df6dff0b4c241101d4096617fbca87c3" + integrity sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ== + +"@rollup/rollup-win32-arm64-msvc@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.1.tgz#7eeada98444e580674de6989284e4baacd48ea65" + integrity sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ== + +"@rollup/rollup-win32-ia32-msvc@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.1.tgz#516c4b54f80587b4a390aaf4940b40870271d35d" + integrity sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg== + +"@rollup/rollup-win32-x64-msvc@4.41.1": + version "4.41.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.1.tgz#848f99b0d9936d92221bb6070baeff4db6947a30" + integrity sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw== "@sindresorhus/is@^4.6.0": version "4.6.0" @@ -354,9 +354,9 @@ integrity sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A== "@types/node@*", "@types/node@>=10.0.0", "@types/node@^22.13.0": - version "22.15.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.3.tgz#b7fb9396a8ec5b5dfb1345d8ac2502060e9af68b" - integrity sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw== + version "22.15.29" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.29.tgz#c75999124a8224a3f79dd8b6ccfb37d74098f678" + integrity sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ== dependencies: undici-types "~6.21.0" @@ -731,10 +731,10 @@ chokidar@^3.5.1, chokidar@^3.5.2: optionalDependencies: fsevents "~2.3.2" -chromium-bidi@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-4.1.1.tgz#e1c34154ddd94473f180fd15158a24d36049e3d5" - integrity sha512-biR7t4vF3YluE6RlMSk9IWk+b9U+WWyzHp+N2pL9vRTk+UXHYRTVp7jTK58ZNzMLBgoLMHY4QyJMbeuw3eKxqg== +chromium-bidi@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-5.1.0.tgz#8d0e47f7ac9270262df29792318dd5378e983e62" + integrity sha512-9MSRhWRVoRPDG0TgzkHrshFSJJNZzfY5UFqUMuksg7zL1yoZIZ3jLB0YAgHclbiAxPI86pBnwDX1tbzoiV8aFw== dependencies: mitt "^3.0.1" zod "^3.24.1" @@ -943,10 +943,10 @@ debug@^3.1.0: dependencies: ms "^2.1.1" -debug@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" - integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== +debug@^4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" + integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== dependencies: ms "^2.1.3" @@ -1006,10 +1006,10 @@ destroy@1.2.0: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== -devtools-protocol@0.0.1425554: - version "0.0.1425554" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1425554.tgz#51ed2fed1405f56783d24a393f7c75b6bbb58029" - integrity sha512-uRfxR6Nlzdzt0ihVIkV+sLztKgs7rgquY/Mhcv1YNCWDh5IZgl5mnn2aeEnW5stYTE0wwiF4RYVz8eMEpV1SEw== +devtools-protocol@0.0.1439962: + version "0.0.1439962" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1439962.tgz#395c5ca1cd83aa451c667056a025f9873c4598c1" + integrity sha512-jJF48UdryzKiWhJ1bLKr7BFWUQCEIT5uCNbDLqkQJBtkFxYzILJH44WN0PDKMIlGDN7Utb8vyUY85C3w4R/t2g== di@^0.0.1: version "0.0.1" @@ -1894,14 +1894,14 @@ jasmine-core@^4.1.0: integrity sha512-O236+gd0ZXS8YAjFx8xKaJ94/erqUliEkJTDedyE7iHvv4ZVqi+q+8acJxu05/WJDKm512EUNn809In37nWlAQ== jasmine-core@^5.1.1, jasmine-core@~5.7.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-5.7.0.tgz#23476f408056a33674469005d87d57d873b4c525" - integrity sha512-EnUzZBHxS1Ofq+FPWs16rs2YC9o6Hb3buKJQDlkhJBDx+Bm5wNF+J1gUS06dWuW2ozaQ3oNIA1SESX9M5LopOQ== + version "5.7.1" + resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-5.7.1.tgz#c347ede2dbf1b6b3b3c3362a5b6651414ada4745" + integrity sha512-QnurrtpKsPoixxG2R3d1xP0St/2kcX5oTZyDyQJMY+Vzi/HUlu1kGm+2V8Tz+9lV991leB1l0xcsyz40s9xOOw== jasmine@^5.1.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-5.7.0.tgz#94b947cdf6bda6da549846ab672aea3c601857d1" - integrity sha512-pifsdoSBgOlAbw1Tg1Vm4LxXEzDv6a+dTzHUaI9aYYqdP+PiMFgz2Mce/7TBfvuP9kshl0yZn7+G0/G1n+yExw== + version "5.7.1" + resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-5.7.1.tgz#56c27d7dab7663802426a2d84eeabe3768491cac" + integrity sha512-E/4fkRNy/9ALz6z3Z3/tYXFAohoznVy7In9FWutG2fqBSkILJHFzbgZtHJUw5UrL3jgUQ4sdGYOVZ5KpSXYjGw== dependencies: glob "^10.2.2" jasmine-core "~5.7.0" @@ -2101,10 +2101,10 @@ log4js@^6.4.1: rfdc "^1.3.0" streamroller "^3.1.5" -lru-cache@^10.4.3: - version "10.4.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" - integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== +lru-cache@^11.0.1: + version "11.1.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.1.0.tgz#afafb060607108132dbc1cf8ae661afb69486117" + integrity sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A== lru-cache@^7.14.1: version "7.18.3" @@ -2582,28 +2582,28 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -puppeteer-core@24.7.2: - version "24.7.2" - resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-24.7.2.tgz#734e377a5634ce1e419fa3ce20ad297a7e1a99ff" - integrity sha512-P9pZyTmJqKODFCnkZgemCpoFA4LbAa8+NumHVQKyP5X9IgdNS1ZnAnIh1sMAwhF8/xEUGf7jt+qmNLlKieFw1Q== +puppeteer-core@24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-24.9.0.tgz#fc489e83bf65db1dc72e53a78140ee567efd847e" + integrity sha512-HFdCeH/wx6QPz8EncafbCqJBqaCG1ENW75xg3cLFMRUoqZDgByT6HSueiumetT2uClZxwqj0qS4qMVZwLHRHHw== dependencies: - "@puppeteer/browsers" "2.10.2" - chromium-bidi "4.1.1" - debug "^4.4.0" - devtools-protocol "0.0.1425554" + "@puppeteer/browsers" "2.10.5" + chromium-bidi "5.1.0" + debug "^4.4.1" + devtools-protocol "0.0.1439962" typed-query-selector "^2.12.0" - ws "^8.18.1" + ws "^8.18.2" puppeteer@^24.1.1: - version "24.7.2" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-24.7.2.tgz#1798b05db1a8b99f592239b11b54a470a56cdace" - integrity sha512-ifYqoY6wGs0yZeFuFPn8BE9FhuveXkarF+eO18I2e/axdoCh4Qh1AE+qXdJBhdaeoPt6eRNTY4Dih29Jbq8wow== + version "24.9.0" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-24.9.0.tgz#1d3f805e0170ca481b637a47c71a09b815594dae" + integrity sha512-L0pOtALIx8rgDt24Y+COm8X52v78gNtBOW6EmUcEPci0TYD72SAuaXKqasRIx4JXxmg2Tkw5ySKcpPOwN8xXnQ== dependencies: - "@puppeteer/browsers" "2.10.2" - chromium-bidi "4.1.1" + "@puppeteer/browsers" "2.10.5" + chromium-bidi "5.1.0" cosmiconfig "^9.0.0" - devtools-protocol "0.0.1425554" - puppeteer-core "24.7.2" + devtools-protocol "0.0.1439962" + puppeteer-core "24.9.0" typed-query-selector "^2.12.0" q@~1.5.0: @@ -2756,32 +2756,32 @@ rimraf@~2.6.2: glob "^7.1.3" rollup@^4.30.1: - version "4.40.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.40.1.tgz#03d6c53ebb6a9c2c060ae686a61e72a2472b366f" - integrity sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw== + version "4.41.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.41.1.tgz#46ddc1b33cf1b0baa99320d3b0b4973dc2253b6a" + integrity sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw== dependencies: "@types/estree" "1.0.7" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.40.1" - "@rollup/rollup-android-arm64" "4.40.1" - "@rollup/rollup-darwin-arm64" "4.40.1" - "@rollup/rollup-darwin-x64" "4.40.1" - "@rollup/rollup-freebsd-arm64" "4.40.1" - "@rollup/rollup-freebsd-x64" "4.40.1" - "@rollup/rollup-linux-arm-gnueabihf" "4.40.1" - "@rollup/rollup-linux-arm-musleabihf" "4.40.1" - "@rollup/rollup-linux-arm64-gnu" "4.40.1" - "@rollup/rollup-linux-arm64-musl" "4.40.1" - "@rollup/rollup-linux-loongarch64-gnu" "4.40.1" - "@rollup/rollup-linux-powerpc64le-gnu" "4.40.1" - "@rollup/rollup-linux-riscv64-gnu" "4.40.1" - "@rollup/rollup-linux-riscv64-musl" "4.40.1" - "@rollup/rollup-linux-s390x-gnu" "4.40.1" - "@rollup/rollup-linux-x64-gnu" "4.40.1" - "@rollup/rollup-linux-x64-musl" "4.40.1" - "@rollup/rollup-win32-arm64-msvc" "4.40.1" - "@rollup/rollup-win32-ia32-msvc" "4.40.1" - "@rollup/rollup-win32-x64-msvc" "4.40.1" + "@rollup/rollup-android-arm-eabi" "4.41.1" + "@rollup/rollup-android-arm64" "4.41.1" + "@rollup/rollup-darwin-arm64" "4.41.1" + "@rollup/rollup-darwin-x64" "4.41.1" + "@rollup/rollup-freebsd-arm64" "4.41.1" + "@rollup/rollup-freebsd-x64" "4.41.1" + "@rollup/rollup-linux-arm-gnueabihf" "4.41.1" + "@rollup/rollup-linux-arm-musleabihf" "4.41.1" + "@rollup/rollup-linux-arm64-gnu" "4.41.1" + "@rollup/rollup-linux-arm64-musl" "4.41.1" + "@rollup/rollup-linux-loongarch64-gnu" "4.41.1" + "@rollup/rollup-linux-powerpc64le-gnu" "4.41.1" + "@rollup/rollup-linux-riscv64-gnu" "4.41.1" + "@rollup/rollup-linux-riscv64-musl" "4.41.1" + "@rollup/rollup-linux-s390x-gnu" "4.41.1" + "@rollup/rollup-linux-x64-gnu" "4.41.1" + "@rollup/rollup-linux-x64-musl" "4.41.1" + "@rollup/rollup-win32-arm64-msvc" "4.41.1" + "@rollup/rollup-win32-ia32-msvc" "4.41.1" + "@rollup/rollup-win32-x64-msvc" "4.41.1" fsevents "~2.3.2" sade@^1.8.1: @@ -2831,15 +2831,15 @@ safe-regex-test@^1.0.0: integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@^7.5.4: - version "7.7.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.0.tgz#9c6fe61d0c6f9fa9e26575162ee5a9180361b09c" - integrity sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ== - -semver@^7.7.1: version "7.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== +semver@^7.7.2: + version "7.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" + integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -3219,9 +3219,9 @@ supports-preserve-symlinks-flag@^1.0.0: integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== tar-fs@^3.0.8: - version "3.0.8" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.8.tgz#8f62012537d5ff89252d01e48690dc4ebed33ab7" - integrity sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg== + version "3.0.9" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.9.tgz#d570793c6370d7078926c41fa422891566a0b617" + integrity sha512-XF4w9Xp+ZQgifKakjZYmFdkLoSWd34VGKcsTCwlNWM7QG3ZbaxnTsaBwnjFZqHRf/rROxaR8rXnbtwdvaDI+lA== dependencies: pump "^3.0.0" tar-stream "^3.1.5" @@ -3539,10 +3539,10 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -ws@^8.18.1: - version "8.18.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.1.tgz#ea131d3784e1dfdff91adb0a4a116b127515e3cb" - integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== +ws@^8.18.2: + version "8.18.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.2.tgz#42738b2be57ced85f46154320aabb51ab003705a" + integrity sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ== ws@~8.11.0: version "8.11.0"