mirror of
https://github.com/transloadit/uppy.git
synced 2026-07-17 16:50:22 +00:00
Merge "main" into "restore/pr-6284 and resolve conflicts
This commit is contained in:
commit
e51c1ad99b
409 changed files with 1193 additions and 2873 deletions
|
|
@ -3,13 +3,10 @@
|
|||
"@uppy/google-photos-picker": patch
|
||||
"@uppy/google-drive-picker": patch
|
||||
"@uppy/thumbnail-generator": patch
|
||||
"@uppy/companion-client": patch
|
||||
"@uppy/golden-retriever": patch
|
||||
"@uppy/image-generator": patch
|
||||
"@uppy/provider-views": patch
|
||||
"@uppy/remote-sources": patch
|
||||
"@uppy/screen-capture": patch
|
||||
"@uppy/store-default": patch
|
||||
"@uppy/google-drive": patch
|
||||
"@uppy/image-editor": patch
|
||||
"@uppy/transloadit": patch
|
||||
|
|
@ -31,7 +28,6 @@
|
|||
"@uppy/webdav": patch
|
||||
"@uppy/audio": patch
|
||||
"@uppy/react": patch
|
||||
"@uppy/utils": patch
|
||||
"@uppy/core": patch
|
||||
"@uppy/form": patch
|
||||
"@uppy/zoom": patch
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
"@uppy/companion-client": patch
|
||||
"@uppy/core": patch
|
||||
---
|
||||
|
||||
uploadRemoteFile() now queues token request and websocket request as a single job in the request queue.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
"@uppy/companion-client": major
|
||||
"@uppy/core": major
|
||||
"@uppy/companion": major
|
||||
---
|
||||
|
||||
Send token using websocket instead of window.opener.
|
||||
Breaking in `@uppy/companion-client` because it needs newest version of Companion in order to work.
|
||||
Breaking in `@uppy/core` because it needs newest version of Companion in order to work.
|
||||
Breaking in `@uppy/companion` because `companion.socket()` now requires `companionOptions` to be passed as the second argument.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
/** @jsx h */
|
||||
|
||||
import { getAllowedHosts, Provider, tokenStorage } from '@uppy/companion-client'
|
||||
import { UIPlugin } from '@uppy/core'
|
||||
import { ProviderViews } from '@uppy/provider-views'
|
||||
import {
|
||||
getAllowedHosts,
|
||||
Provider,
|
||||
tokenStorage,
|
||||
} from '@uppy/core/companion-client'
|
||||
import { ProviderViews } from '@uppy/core/provider-views'
|
||||
|
||||
const defaultOptions = {}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,9 @@
|
|||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@uppy/companion-client": "workspace:*",
|
||||
"@uppy/core": "workspace:*",
|
||||
"@uppy/dashboard": "workspace:*",
|
||||
"@uppy/google-drive": "workspace:*",
|
||||
"@uppy/provider-views": "workspace:*",
|
||||
"@uppy/tus": "workspace:*",
|
||||
"preact": "^10.29.2"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,11 +2,7 @@
|
|||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": [
|
||||
"@uppy/core#build",
|
||||
"@uppy/dashboard#build",
|
||||
"@uppy/utils#build"
|
||||
],
|
||||
"dependsOn": ["@uppy/core#build", "@uppy/dashboard#build"],
|
||||
"inputs": [
|
||||
"projects/uppy/angular/src/**/*.{js,ts,jsx,tsx}",
|
||||
"package.json",
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@
|
|||
"./package.json": "./package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@uppy/utils": "workspace:^",
|
||||
"preact": "^10.29.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import type {
|
|||
} from '@uppy/core'
|
||||
|
||||
import { UIPlugin } from '@uppy/core'
|
||||
import type { LocaleStrings } from '@uppy/utils'
|
||||
import { getFileTypeExtension } from '@uppy/utils'
|
||||
import type { LocaleStrings } from '@uppy/core/utils'
|
||||
import { getFileTypeExtension } from '@uppy/core/utils'
|
||||
import packageJson from '../package.json' with { type: 'json' }
|
||||
import locale from './locale.js'
|
||||
import PermissionsScreen from './PermissionsScreen.js'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { I18n } from '@uppy/utils'
|
||||
import type { I18n } from '@uppy/core/utils'
|
||||
|
||||
interface DiscardButtonProps {
|
||||
onDiscard: () => void
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { I18n } from '@uppy/utils'
|
||||
import type { I18n } from '@uppy/core/utils'
|
||||
import type { h } from 'preact'
|
||||
|
||||
interface PermissionsScreenProps {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { I18n } from '@uppy/utils'
|
||||
import type { I18n } from '@uppy/core/utils'
|
||||
|
||||
interface RecordButtonProps {
|
||||
recording: boolean
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { I18n } from '@uppy/utils'
|
||||
import type { I18n } from '@uppy/core/utils'
|
||||
import { useEffect, useRef } from 'preact/hooks'
|
||||
import AudioSourceSelect, {
|
||||
type AudioSourceSelectProps,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { I18n } from '@uppy/utils'
|
||||
import type { I18n } from '@uppy/core/utils'
|
||||
|
||||
interface SubmitButtonProps {
|
||||
onSubmit: () => void
|
||||
|
|
|
|||
|
|
@ -7,9 +7,6 @@
|
|||
"include": ["./src/**/*.*"],
|
||||
"exclude": ["./src/**/*.test.ts"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../utils/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../core/tsconfig.build.json"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@
|
|||
},
|
||||
"include": ["./package.json", "./src/**/*.*"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../utils/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../core/tsconfig.build.json"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,10 +37,6 @@
|
|||
".": "./lib/index.js",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@uppy/companion-client": "workspace:^",
|
||||
"@uppy/utils": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-sdk/client-s3": "^3.362.0",
|
||||
"@aws-sdk/client-sts": "^3.362.0",
|
||||
|
|
|
|||
444
packages/@uppy/aws-s3/src/HTTPCommunicationQueue.ts
Normal file
444
packages/@uppy/aws-s3/src/HTTPCommunicationQueue.ts
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
import type { Body, Meta, UppyFile } from '@uppy/core'
|
||||
import type {
|
||||
RateLimitedQueue,
|
||||
WrapPromiseFunctionType,
|
||||
} from '@uppy/core/utils'
|
||||
import type AwsS3Multipart from './index.js'
|
||||
import type {
|
||||
AwsS3MultipartOptions,
|
||||
AwsS3UploadParameters,
|
||||
uploadPartBytes,
|
||||
} from './index.js'
|
||||
import { type Chunk, pausingUploadReason } from './MultipartUploader.js'
|
||||
import type { UploadPartBytesResult, UploadResult } from './utils.js'
|
||||
import { throwIfAborted } from './utils.js'
|
||||
|
||||
function removeMetadataFromURL(urlString: string) {
|
||||
const urlObject = new URL(urlString)
|
||||
urlObject.search = ''
|
||||
urlObject.hash = ''
|
||||
return urlObject.href
|
||||
}
|
||||
|
||||
export class HTTPCommunicationQueue<M extends Meta, B extends Body> {
|
||||
#abortMultipartUpload!: WrapPromiseFunctionType<
|
||||
AwsS3Multipart<M, B>['abortMultipartUpload']
|
||||
>
|
||||
|
||||
#cache = new WeakMap()
|
||||
|
||||
#createMultipartUpload!: WrapPromiseFunctionType<
|
||||
AwsS3Multipart<M, B>['createMultipartUpload']
|
||||
>
|
||||
|
||||
#fetchSignature!: WrapPromiseFunctionType<AwsS3Multipart<M, B>['signPart']>
|
||||
|
||||
#getUploadParameters!: WrapPromiseFunctionType<
|
||||
AwsS3Multipart<M, B>['getUploadParameters']
|
||||
>
|
||||
|
||||
#listParts!: WrapPromiseFunctionType<AwsS3Multipart<M, B>['listParts']>
|
||||
|
||||
#previousRetryDelay!: number
|
||||
|
||||
#requests
|
||||
|
||||
#retryDelays!: { values: () => Iterator<number> }
|
||||
|
||||
#sendCompletionRequest!: WrapPromiseFunctionType<
|
||||
AwsS3Multipart<M, B>['completeMultipartUpload']
|
||||
>
|
||||
|
||||
#setS3MultipartState
|
||||
|
||||
#uploadPartBytes!: WrapPromiseFunctionType<uploadPartBytes>
|
||||
|
||||
#getFile
|
||||
|
||||
constructor(
|
||||
requests: RateLimitedQueue,
|
||||
options: AwsS3MultipartOptions<M, B>,
|
||||
setS3MultipartState: (file: UppyFile<M, B>, result: UploadResult) => void,
|
||||
getFile: (file: UppyFile<M, B>) => UppyFile<M, B>,
|
||||
) {
|
||||
this.#requests = requests
|
||||
this.#setS3MultipartState = setS3MultipartState
|
||||
this.#getFile = getFile
|
||||
this.setOptions(options)
|
||||
}
|
||||
|
||||
setOptions(options: Partial<AwsS3MultipartOptions<M, B>>): void {
|
||||
const requests = this.#requests
|
||||
|
||||
if ('abortMultipartUpload' in options) {
|
||||
this.#abortMultipartUpload = requests.wrapPromiseFunction(
|
||||
options.abortMultipartUpload as any,
|
||||
{ priority: 1 },
|
||||
)
|
||||
}
|
||||
if ('createMultipartUpload' in options) {
|
||||
this.#createMultipartUpload = requests.wrapPromiseFunction(
|
||||
options.createMultipartUpload as any,
|
||||
{ priority: -1 },
|
||||
)
|
||||
}
|
||||
if ('signPart' in options) {
|
||||
this.#fetchSignature = requests.wrapPromiseFunction(
|
||||
options.signPart as any,
|
||||
)
|
||||
}
|
||||
if ('listParts' in options) {
|
||||
this.#listParts = requests.wrapPromiseFunction(options.listParts as any)
|
||||
}
|
||||
if ('completeMultipartUpload' in options) {
|
||||
this.#sendCompletionRequest = requests.wrapPromiseFunction(
|
||||
options.completeMultipartUpload as any,
|
||||
{ priority: 1 },
|
||||
)
|
||||
}
|
||||
if ('retryDelays' in options) {
|
||||
this.#retryDelays = options.retryDelays ?? []
|
||||
}
|
||||
if ('uploadPartBytes' in options) {
|
||||
this.#uploadPartBytes = requests.wrapPromiseFunction(
|
||||
options.uploadPartBytes as any,
|
||||
{ priority: Infinity },
|
||||
)
|
||||
}
|
||||
if ('getUploadParameters' in options) {
|
||||
this.#getUploadParameters = requests.wrapPromiseFunction(
|
||||
options.getUploadParameters as any,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async #shouldRetry(err: any, retryDelayIterator: Iterator<number>) {
|
||||
const requests = this.#requests
|
||||
const status = err?.source?.status
|
||||
|
||||
// TODO: this retry logic is taken out of Tus. We should have a centralized place for retrying,
|
||||
// perhaps the rate limited queue, and dedupe all plugins with that.
|
||||
if (status == null) {
|
||||
return false
|
||||
}
|
||||
if (status === 403 && err.message === 'Request has expired') {
|
||||
if (!requests.isPaused) {
|
||||
// We don't want to exhaust the retryDelayIterator as long as there are
|
||||
// more than one request in parallel, to give slower connection a chance
|
||||
// to catch up with the expiry set in Companion.
|
||||
if (requests.limit === 1 || this.#previousRetryDelay == null) {
|
||||
const next = retryDelayIterator.next()
|
||||
if (next == null || next.done) {
|
||||
return false
|
||||
}
|
||||
// If there are more than 1 request done in parallel, the RLQ limit is
|
||||
// decreased and the failed request is requeued after waiting for a bit.
|
||||
// If there is only one request in parallel, the limit can't be
|
||||
// decreased, so we iterate over `retryDelayIterator` as we do for
|
||||
// other failures.
|
||||
// `#previousRetryDelay` caches the value so we can re-use it next time.
|
||||
this.#previousRetryDelay = next.value
|
||||
}
|
||||
// No need to stop the other requests, we just want to lower the limit.
|
||||
requests.rateLimit(0)
|
||||
await new Promise((resolve) =>
|
||||
setTimeout(resolve, this.#previousRetryDelay),
|
||||
)
|
||||
}
|
||||
} else if (status === 429) {
|
||||
// HTTP 429 Too Many Requests => to avoid the whole download to fail, pause all requests.
|
||||
if (!requests.isPaused) {
|
||||
const next = retryDelayIterator.next()
|
||||
if (next == null || next.done) {
|
||||
return false
|
||||
}
|
||||
requests.rateLimit(next.value)
|
||||
}
|
||||
} else if (status > 400 && status < 500 && status !== 409) {
|
||||
// HTTP 4xx, the server won't send anything, it's doesn't make sense to retry
|
||||
return false
|
||||
} else if (typeof navigator !== 'undefined' && navigator.onLine === false) {
|
||||
// The navigator is offline, let's wait for it to come back online.
|
||||
if (!requests.isPaused) {
|
||||
requests.pause()
|
||||
window.addEventListener(
|
||||
'online',
|
||||
() => {
|
||||
requests.resume()
|
||||
},
|
||||
{ once: true },
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// Other error code means the request can be retried later.
|
||||
const next = retryDelayIterator.next()
|
||||
if (next == null || next.done) {
|
||||
return false
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, next.value))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async getUploadId(
|
||||
file: UppyFile<M, B>,
|
||||
signal: AbortSignal,
|
||||
): Promise<UploadResult> {
|
||||
let cachedResult: Promise<UploadResult> | UploadResult | undefined
|
||||
// As the cache is updated asynchronously, there could be a race condition
|
||||
// where we just miss a new result so we loop here until we get nothing back,
|
||||
// at which point it's out turn to create a new cache entry.
|
||||
for (;;) {
|
||||
if (file.data == null) throw new Error('File data is empty')
|
||||
cachedResult = this.#cache.get(file.data)
|
||||
if (cachedResult == null) break
|
||||
try {
|
||||
return await cachedResult
|
||||
} catch {
|
||||
// In case of failure, we want to ignore the cached error.
|
||||
// At this point, either there's a new cached value, or we'll exit the loop a create a new one.
|
||||
}
|
||||
}
|
||||
|
||||
const promise = this.#createMultipartUpload(this.#getFile(file), signal)
|
||||
|
||||
const abortPromise = () => {
|
||||
if (file.data == null) throw new Error('File data is empty')
|
||||
promise.abort(signal.reason)
|
||||
this.#cache.delete(file.data)
|
||||
}
|
||||
signal.addEventListener('abort', abortPromise, { once: true })
|
||||
this.#cache.set(file.data, promise)
|
||||
promise.then(
|
||||
async (result) => {
|
||||
signal.removeEventListener('abort', abortPromise)
|
||||
this.#setS3MultipartState(file, result)
|
||||
if (file.data == null) throw new Error('File data is empty')
|
||||
this.#cache.set(file.data, result)
|
||||
},
|
||||
() => {
|
||||
signal.removeEventListener('abort', abortPromise)
|
||||
if (file.data == null) throw new Error('File data is empty')
|
||||
this.#cache.delete(file.data)
|
||||
},
|
||||
)
|
||||
|
||||
return promise
|
||||
}
|
||||
|
||||
async abortFileUpload(file: UppyFile<M, B>): Promise<void> {
|
||||
if (file.data == null) throw new Error('File data is empty')
|
||||
const result = this.#cache.get(file.data)
|
||||
if (result == null) {
|
||||
// If the createMultipartUpload request never was made, we don't
|
||||
// need to send the abortMultipartUpload request.
|
||||
return
|
||||
}
|
||||
// Remove the cache entry right away for follow-up requests do not try to
|
||||
// use the soon-to-be aborted cached values.
|
||||
this.#cache.delete(file.data)
|
||||
this.#setS3MultipartState(file, Object.create(null))
|
||||
let awaitedResult: UploadResult
|
||||
try {
|
||||
awaitedResult = await result
|
||||
} catch {
|
||||
// If the cached result rejects, there's nothing to abort.
|
||||
return
|
||||
}
|
||||
await this.#abortMultipartUpload(this.#getFile(file), awaitedResult)
|
||||
}
|
||||
|
||||
async #nonMultipartUpload(
|
||||
file: UppyFile<M, B>,
|
||||
chunk: Chunk,
|
||||
signal?: AbortSignal,
|
||||
) {
|
||||
const {
|
||||
method = 'POST',
|
||||
url,
|
||||
fields,
|
||||
headers,
|
||||
} = await this.#getUploadParameters(this.#getFile(file), {
|
||||
signal,
|
||||
}).abortOn(signal)
|
||||
|
||||
let body: FormData | Blob
|
||||
const data = chunk.getData()
|
||||
if (method.toUpperCase() === 'POST') {
|
||||
const formData = new FormData()
|
||||
Object.entries(fields!).forEach(([key, value]) => {
|
||||
formData.set(key, value)
|
||||
})
|
||||
formData.set('file', data)
|
||||
body = formData
|
||||
} else {
|
||||
body = data
|
||||
}
|
||||
|
||||
const { onProgress, onComplete } = chunk
|
||||
|
||||
const result = (await this.#uploadPartBytes({
|
||||
signature: { url, headers, method } as any,
|
||||
body,
|
||||
size: data.size,
|
||||
onProgress,
|
||||
onComplete,
|
||||
signal,
|
||||
}).abortOn(signal)) as unknown as B // todo this doesn't make sense
|
||||
|
||||
// Note: `fields.key` is not returned by old Companion versions.
|
||||
// See https://github.com/transloadit/uppy/pull/5602
|
||||
const key = fields?.key
|
||||
this.#setS3MultipartState(file, { key: key! })
|
||||
|
||||
return {
|
||||
...result,
|
||||
location:
|
||||
(result.location as string | undefined) ?? removeMetadataFromURL(url),
|
||||
bucket: fields?.bucket,
|
||||
key,
|
||||
}
|
||||
}
|
||||
|
||||
async uploadFile(
|
||||
file: UppyFile<M, B>,
|
||||
chunks: Chunk[],
|
||||
signal: AbortSignal,
|
||||
): Promise<B & Partial<UploadPartBytesResult>> {
|
||||
throwIfAborted(signal)
|
||||
if (chunks.length === 1 && !chunks[0].shouldUseMultipart) {
|
||||
return this.#nonMultipartUpload(file, chunks[0], signal)
|
||||
}
|
||||
const { uploadId, key } = await this.getUploadId(file, signal)
|
||||
throwIfAborted(signal)
|
||||
try {
|
||||
const parts = await Promise.all(
|
||||
chunks.map((chunk, i) => this.uploadChunk(file, i + 1, chunk, signal)),
|
||||
)
|
||||
throwIfAborted(signal)
|
||||
return await this.#sendCompletionRequest(
|
||||
this.#getFile(file),
|
||||
{ key, uploadId, parts, signal },
|
||||
signal,
|
||||
).abortOn(signal)
|
||||
} catch (err) {
|
||||
if (err?.cause !== pausingUploadReason && err?.name !== 'AbortError') {
|
||||
// We purposefully don't wait for the promise and ignore its status,
|
||||
// because we want the error `err` to bubble up ASAP to report it to the
|
||||
// user. A failure to abort is not that big of a deal anyway.
|
||||
this.abortFileUpload(file)
|
||||
}
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
restoreUploadFile(file: UppyFile<M, B>, uploadIdAndKey: UploadResult): void {
|
||||
if (file.data == null) throw new Error('File data is empty')
|
||||
this.#cache.set(file.data, uploadIdAndKey)
|
||||
}
|
||||
|
||||
async resumeUploadFile(
|
||||
file: UppyFile<M, B>,
|
||||
chunks: Array<Chunk | null>,
|
||||
signal: AbortSignal,
|
||||
): Promise<B> {
|
||||
throwIfAborted(signal)
|
||||
if (
|
||||
chunks.length === 1 &&
|
||||
chunks[0] != null &&
|
||||
!chunks[0].shouldUseMultipart
|
||||
) {
|
||||
return this.#nonMultipartUpload(file, chunks[0], signal)
|
||||
}
|
||||
const { uploadId, key } = await this.getUploadId(file, signal)
|
||||
throwIfAborted(signal)
|
||||
const alreadyUploadedParts = await this.#listParts(
|
||||
this.#getFile(file),
|
||||
{ uploadId, key, signal },
|
||||
signal,
|
||||
).abortOn(signal)
|
||||
throwIfAborted(signal)
|
||||
const parts = await Promise.all(
|
||||
chunks.map((chunk, i) => {
|
||||
const partNumber = i + 1
|
||||
const alreadyUploadedInfo = alreadyUploadedParts.find(
|
||||
({ PartNumber }) => PartNumber === partNumber,
|
||||
)
|
||||
if (alreadyUploadedInfo == null) {
|
||||
return this.uploadChunk(file, partNumber, chunk!, signal)
|
||||
}
|
||||
// Already uploaded chunks are set to null. If we are restoring the upload, we need to mark it as already uploaded.
|
||||
chunk?.setAsUploaded?.()
|
||||
return { PartNumber: partNumber, ETag: alreadyUploadedInfo.ETag }
|
||||
}),
|
||||
)
|
||||
throwIfAborted(signal)
|
||||
return this.#sendCompletionRequest(
|
||||
this.#getFile(file),
|
||||
{ key, uploadId, parts, signal },
|
||||
signal,
|
||||
).abortOn(signal)
|
||||
}
|
||||
|
||||
async uploadChunk(
|
||||
file: UppyFile<M, B>,
|
||||
partNumber: number,
|
||||
chunk: Chunk,
|
||||
signal: AbortSignal,
|
||||
): Promise<UploadPartBytesResult & { PartNumber: number }> {
|
||||
throwIfAborted(signal)
|
||||
const { uploadId, key } = await this.getUploadId(file, signal)
|
||||
|
||||
const signatureRetryIterator = this.#retryDelays.values()
|
||||
const chunkRetryIterator = this.#retryDelays.values()
|
||||
const shouldRetrySignature = () => {
|
||||
const next = signatureRetryIterator.next()
|
||||
if (next == null || next.done) {
|
||||
return null
|
||||
}
|
||||
return next.value
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
throwIfAborted(signal)
|
||||
const chunkData = chunk.getData()
|
||||
const { onProgress, onComplete } = chunk
|
||||
let signature: AwsS3UploadParameters
|
||||
|
||||
try {
|
||||
signature = await this.#fetchSignature(this.#getFile(file), {
|
||||
// Always defined for multipart uploads
|
||||
uploadId: uploadId!,
|
||||
key,
|
||||
partNumber,
|
||||
body: chunkData,
|
||||
signal,
|
||||
}).abortOn(signal)
|
||||
} catch (err) {
|
||||
const timeout = shouldRetrySignature()
|
||||
if (timeout == null || signal.aborted) {
|
||||
throw err
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, timeout))
|
||||
continue
|
||||
}
|
||||
|
||||
throwIfAborted(signal)
|
||||
try {
|
||||
return {
|
||||
PartNumber: partNumber,
|
||||
...(await this.#uploadPartBytes({
|
||||
signature,
|
||||
body: chunkData,
|
||||
size: chunkData.size,
|
||||
onProgress,
|
||||
onComplete,
|
||||
signal,
|
||||
}).abortOn(signal)),
|
||||
}
|
||||
} catch (err) {
|
||||
if (!(await this.#shouldRetry(err, chunkRetryIterator))) throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
265
packages/@uppy/aws-s3/src/MultipartUploader.ts
Normal file
265
packages/@uppy/aws-s3/src/MultipartUploader.ts
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
import type { Uppy } from '@uppy/core'
|
||||
import type { Body, Meta, UppyFile } from '@uppy/core/utils'
|
||||
import { AbortController } from '@uppy/core/utils'
|
||||
import type { HTTPCommunicationQueue } from './HTTPCommunicationQueue.js'
|
||||
|
||||
const MB = 1024 * 1024
|
||||
|
||||
interface MultipartUploaderOptions<M extends Meta, B extends Body> {
|
||||
getChunkSize?: (file: { size: number }) => number
|
||||
onProgress?: (bytesUploaded: number, bytesTotal: number) => void
|
||||
onPartComplete?: (part: { PartNumber: number; ETag: string }) => void
|
||||
shouldUseMultipart?: boolean | ((file: UppyFile<M, B>) => boolean)
|
||||
onSuccess?: (result: B) => void
|
||||
onError?: (err: unknown) => void
|
||||
companionComm: HTTPCommunicationQueue<M, B>
|
||||
file: UppyFile<M, B>
|
||||
log: Uppy<M, B>['log']
|
||||
|
||||
uploadId?: string
|
||||
key: string
|
||||
}
|
||||
|
||||
const defaultOptions = {
|
||||
getChunkSize(file: { size: number }) {
|
||||
return Math.ceil(file.size / 10000)
|
||||
},
|
||||
onProgress() {},
|
||||
onPartComplete() {},
|
||||
onSuccess() {},
|
||||
onError(err: unknown) {
|
||||
throw err
|
||||
},
|
||||
} satisfies Partial<MultipartUploaderOptions<any, any>>
|
||||
|
||||
export interface Chunk {
|
||||
getData: () => Blob
|
||||
onProgress: (ev: ProgressEvent) => void
|
||||
onComplete: (etag: string) => void
|
||||
shouldUseMultipart: boolean
|
||||
setAsUploaded?: () => void
|
||||
}
|
||||
|
||||
function ensureInt<T>(value: T): T extends number | string ? number : never {
|
||||
if (typeof value === 'string') {
|
||||
// @ts-expect-error TS is not able to recognize it's fine.
|
||||
return parseInt(value, 10)
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
// @ts-expect-error TS is not able to recognize it's fine.
|
||||
return value
|
||||
}
|
||||
throw new TypeError('Expected a number')
|
||||
}
|
||||
|
||||
export const pausingUploadReason = Symbol('pausing upload, not an actual error')
|
||||
|
||||
/**
|
||||
* A MultipartUploader instance is used per file upload to determine whether a
|
||||
* upload should be done as multipart or as a regular S3 upload
|
||||
* (based on the user-provided `shouldUseMultipart` option value) and to manage
|
||||
* the chunk splitting.
|
||||
*/
|
||||
class MultipartUploader<M extends Meta, B extends Body> {
|
||||
options: MultipartUploaderOptions<M, B> &
|
||||
Required<Pick<MultipartUploaderOptions<M, B>, keyof typeof defaultOptions>>
|
||||
|
||||
#abortController = new AbortController()
|
||||
|
||||
#chunks: Array<Chunk | null> = []
|
||||
|
||||
#chunkState: { uploaded: number; etag?: string; done?: boolean }[] = []
|
||||
|
||||
/**
|
||||
* The (un-chunked) data to upload.
|
||||
*/
|
||||
#data: Blob
|
||||
|
||||
#file: UppyFile<M, B>
|
||||
|
||||
#uploadHasStarted = false
|
||||
|
||||
#onError: (err: unknown) => void
|
||||
|
||||
#onSuccess: (result: B) => void
|
||||
|
||||
#shouldUseMultipart: MultipartUploaderOptions<M, B>['shouldUseMultipart']
|
||||
|
||||
#isRestoring: boolean
|
||||
|
||||
#onReject = (err: unknown) =>
|
||||
(err as any)?.cause === pausingUploadReason ? null : this.#onError(err)
|
||||
|
||||
#maxMultipartParts = 10_000
|
||||
|
||||
#minPartSize = 5 * MB
|
||||
|
||||
constructor(data: Blob, options: MultipartUploaderOptions<M, B>) {
|
||||
this.options = {
|
||||
...defaultOptions,
|
||||
...options,
|
||||
}
|
||||
// Use default `getChunkSize` if it was null or something
|
||||
this.options.getChunkSize ??= defaultOptions.getChunkSize
|
||||
|
||||
this.#data = data
|
||||
this.#file = options.file
|
||||
this.#onSuccess = this.options.onSuccess
|
||||
this.#onError = this.options.onError
|
||||
this.#shouldUseMultipart = this.options.shouldUseMultipart
|
||||
|
||||
// When we are restoring an upload, we already have an UploadId and a Key. Otherwise
|
||||
// we need to call `createMultipartUpload` to get an `uploadId` and a `key`.
|
||||
// Non-multipart uploads are not restorable.
|
||||
this.#isRestoring = (options.uploadId && options.key) as any as boolean
|
||||
|
||||
this.#initChunks()
|
||||
}
|
||||
|
||||
// initChunks checks the user preference for using multipart uploads (opts.shouldUseMultipart)
|
||||
// and calculates the optimal part size. When using multipart part uploads every part except for the last has
|
||||
// to be at least 5 MB and there can be no more than 10K parts.
|
||||
// This means we sometimes need to change the preferred part size from the user in order to meet these requirements.
|
||||
#initChunks() {
|
||||
const fileSize = this.#data.size
|
||||
const shouldUseMultipart =
|
||||
typeof this.#shouldUseMultipart === 'function'
|
||||
? this.#shouldUseMultipart(this.#file)
|
||||
: Boolean(this.#shouldUseMultipart)
|
||||
|
||||
if (shouldUseMultipart && fileSize > this.#minPartSize) {
|
||||
// At least 5MB per request:
|
||||
let chunkSize = Math.max(
|
||||
this.options.getChunkSize(this.#data) as number, // Math.max can take undefined but TS does not think so
|
||||
this.#minPartSize,
|
||||
)
|
||||
let arraySize = Math.floor(fileSize / chunkSize)
|
||||
|
||||
// At most 10k requests per file:
|
||||
if (arraySize > this.#maxMultipartParts) {
|
||||
arraySize = this.#maxMultipartParts
|
||||
chunkSize = fileSize / this.#maxMultipartParts
|
||||
}
|
||||
this.#chunks = Array(arraySize)
|
||||
|
||||
for (let offset = 0, j = 0; offset < fileSize; offset += chunkSize, j++) {
|
||||
const end = Math.min(fileSize, offset + chunkSize)
|
||||
|
||||
// Defer data fetching/slicing until we actually need the data, because it's slow if we have a lot of files
|
||||
const getData = () => {
|
||||
const i2 = offset
|
||||
return this.#data.slice(i2, end)
|
||||
}
|
||||
|
||||
this.#chunks[j] = {
|
||||
getData,
|
||||
onProgress: this.#onPartProgress(j),
|
||||
onComplete: this.#onPartComplete(j),
|
||||
shouldUseMultipart,
|
||||
}
|
||||
if (this.#isRestoring) {
|
||||
const size =
|
||||
offset + chunkSize > fileSize ? fileSize - offset : chunkSize
|
||||
// setAsUploaded is called by listPart, to keep up-to-date the
|
||||
// quantity of data that is left to actually upload.
|
||||
this.#chunks[j]!.setAsUploaded = () => {
|
||||
this.#chunks[j] = null
|
||||
this.#chunkState[j].uploaded = size
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.#chunks = [
|
||||
{
|
||||
getData: () => this.#data,
|
||||
onProgress: this.#onPartProgress(0),
|
||||
onComplete: this.#onPartComplete(0),
|
||||
shouldUseMultipart,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
this.#chunkState = this.#chunks.map(() => ({ uploaded: 0 }))
|
||||
}
|
||||
|
||||
#createUpload() {
|
||||
this.options.companionComm
|
||||
.uploadFile(
|
||||
this.#file,
|
||||
this.#chunks as Chunk[],
|
||||
this.#abortController.signal,
|
||||
)
|
||||
.then(this.#onSuccess, this.#onReject)
|
||||
this.#uploadHasStarted = true
|
||||
}
|
||||
|
||||
#resumeUpload() {
|
||||
this.options.companionComm
|
||||
.resumeUploadFile(this.#file, this.#chunks, this.#abortController.signal)
|
||||
.then(this.#onSuccess, this.#onReject)
|
||||
}
|
||||
|
||||
#onPartProgress = (index: number) => (ev: ProgressEvent) => {
|
||||
if (!ev.lengthComputable) return
|
||||
|
||||
this.#chunkState[index].uploaded = ensureInt(ev.loaded)
|
||||
|
||||
const totalUploaded = this.#chunkState.reduce((n, c) => n + c.uploaded, 0)
|
||||
this.options.onProgress(totalUploaded, this.#data.size)
|
||||
}
|
||||
|
||||
#onPartComplete = (index: number) => (etag: string) => {
|
||||
// This avoids the net::ERR_OUT_OF_MEMORY in Chromium Browsers.
|
||||
this.#chunks[index] = null
|
||||
this.#chunkState[index].etag = etag
|
||||
this.#chunkState[index].done = true
|
||||
|
||||
const part = {
|
||||
PartNumber: index + 1,
|
||||
ETag: etag,
|
||||
}
|
||||
this.options.onPartComplete(part)
|
||||
}
|
||||
|
||||
#abortUpload() {
|
||||
this.#abortController.abort()
|
||||
this.options.companionComm
|
||||
.abortFileUpload(this.#file)
|
||||
.catch((err: unknown) => this.options.log(err as Error))
|
||||
}
|
||||
|
||||
start(): void {
|
||||
if (this.#uploadHasStarted) {
|
||||
if (!this.#abortController.signal.aborted)
|
||||
this.#abortController.abort(pausingUploadReason)
|
||||
this.#abortController = new AbortController()
|
||||
this.#resumeUpload()
|
||||
} else if (this.#isRestoring) {
|
||||
this.options.companionComm.restoreUploadFile(this.#file, {
|
||||
uploadId: this.options.uploadId,
|
||||
key: this.options.key,
|
||||
})
|
||||
this.#resumeUpload()
|
||||
} else {
|
||||
this.#createUpload()
|
||||
}
|
||||
}
|
||||
|
||||
pause(): void {
|
||||
this.#abortController.abort(pausingUploadReason)
|
||||
// Swap it out for a new controller, because this instance may be resumed later.
|
||||
this.#abortController = new AbortController()
|
||||
}
|
||||
|
||||
abort(opts?: { really?: boolean }): void {
|
||||
if (opts?.really) this.#abortUpload()
|
||||
else this.pause()
|
||||
}
|
||||
|
||||
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: accessed via Symbol in tests
|
||||
private [Symbol.for('uppy test: getChunkState')]() {
|
||||
return this.#chunkState
|
||||
}
|
||||
}
|
||||
|
||||
export default MultipartUploader
|
||||
|
|
@ -7,12 +7,6 @@
|
|||
"include": ["./src/**/*.*"],
|
||||
"exclude": ["./src/**/*.test.ts"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../companion-client/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../utils/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../core/tsconfig.build.json"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,12 +7,6 @@
|
|||
},
|
||||
"include": ["./package.json", "./src/**/*.*"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../companion-client/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../utils/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../core/tsconfig.build.json"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,9 +34,6 @@
|
|||
"./package.json": "./package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@uppy/companion-client": "workspace:^",
|
||||
"@uppy/provider-views": "workspace:^",
|
||||
"@uppy/utils": "workspace:^",
|
||||
"preact": "^10.29.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
import {
|
||||
type CompanionPluginOptions,
|
||||
getAllowedHosts,
|
||||
Provider,
|
||||
tokenStorage,
|
||||
} from '@uppy/companion-client'
|
||||
import type {
|
||||
AsyncStore,
|
||||
Body,
|
||||
|
|
@ -13,9 +7,14 @@ import type {
|
|||
UppyFile,
|
||||
} from '@uppy/core'
|
||||
import { UIPlugin, type Uppy } from '@uppy/core'
|
||||
import { ProviderViews } from '@uppy/provider-views'
|
||||
|
||||
import type { LocaleStrings } from '@uppy/utils'
|
||||
import {
|
||||
type CompanionPluginOptions,
|
||||
getAllowedHosts,
|
||||
Provider,
|
||||
tokenStorage,
|
||||
} from '@uppy/core/companion-client'
|
||||
import { ProviderViews } from '@uppy/core/provider-views'
|
||||
import type { LocaleStrings } from '@uppy/core/utils'
|
||||
// biome-ignore lint/style/useImportType: h is not a type
|
||||
import { type ComponentChild, h } from 'preact'
|
||||
import packageJson from '../package.json' with { type: 'json' }
|
||||
|
|
|
|||
|
|
@ -7,15 +7,6 @@
|
|||
"include": ["./src/**/*.*"],
|
||||
"exclude": ["./src/**/*.test.ts"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../companion-client/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../provider-views/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../utils/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../core/tsconfig.build.json"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,15 +6,6 @@
|
|||
},
|
||||
"include": ["./package.json", "./src/**/*.*"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../companion-client/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../provider-views/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../utils/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../core/tsconfig.build.json"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
tsconfig.*
|
||||
|
|
@ -1,301 +0,0 @@
|
|||
# @uppy/companion-client
|
||||
|
||||
## 5.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 0c16fe4: - Split UppyFile into two intefaces distinguished by the `isRemote` boolean:
|
||||
- LocalUppyFile
|
||||
- RemoteUppyFile
|
||||
- Updated dependencies [0c16fe4]
|
||||
- @uppy/core@5.1.1
|
||||
- @uppy/utils@7.1.1
|
||||
|
||||
## 5.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 5ba2c1c: Introduce the concept of server-side search and add support for it for the Dropbox provider. Previously, only client-side filtering in the currently viewed folder was possible, which was limiting. Now users using Companion with Dropbox can perform a search across their entire account.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5ba2c1c]
|
||||
- @uppy/utils@7.1.0
|
||||
- @uppy/core@5.1.0
|
||||
|
||||
## 5.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 975317d: Removed "main" from package.json, since export maps serve as the contract for the public API.
|
||||
- Updated dependencies [4b6a76c]
|
||||
- Updated dependencies [975317d]
|
||||
- Updated dependencies [9bac4c8]
|
||||
- @uppy/core@5.0.2
|
||||
- @uppy/utils@7.0.2
|
||||
|
||||
## 5.0.0
|
||||
|
||||
### Major Changes
|
||||
|
||||
- c5b51f6: ### Export maps for all packages
|
||||
|
||||
All packages now have export maps. This is a breaking change in two cases:
|
||||
|
||||
1. The css imports have changed from `@uppy[package]/dist/styles.min.css` to `@uppy[package]/css/styles.min.css`
|
||||
2. You were importing something that wasn't exported from the root, for instance `@uppy/core/lib/foo.js`. You can now only import things we explicitly exported.
|
||||
|
||||
#### Changed imports for `@uppy/react`, `@uppy/vue`, and `@uppy/svelte`
|
||||
|
||||
Some components, like Dashboard, require a peer dependency to work but since all components were exported from a single file you were forced to install all peer dependencies. Even if you never imported, for instance, the status bar component.
|
||||
|
||||
Every component that requires a peer dependency has now been moved to a subpath, such as `@uppy/react/dashboard`, so you only need to install the peer dependencies you need.
|
||||
|
||||
**Example for `@uppy/react`:**
|
||||
|
||||
**Before:**
|
||||
|
||||
```javascript
|
||||
import { Dashboard, StatusBar } from "@uppy/react";
|
||||
```
|
||||
|
||||
**Now:**
|
||||
|
||||
```javascript
|
||||
import Dashboard from "@uppy/react/dashboard";
|
||||
import StatusBar from "@uppy/react/status-bar";
|
||||
```
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d301c01]
|
||||
- Updated dependencies [c5b51f6]
|
||||
- @uppy/utils@7.0.0
|
||||
- @uppy/core@5.0.0
|
||||
|
||||
## 4.5.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1b1a9e3: Define "files" in package.json
|
||||
- Updated dependencies [1b1a9e3]
|
||||
- @uppy/utils@6.2.2
|
||||
- @uppy/core@4.5.2
|
||||
|
||||
## 4.5.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 0c24c5a: Use TypeScript compiler instead of Babel
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0c24c5a]
|
||||
- Updated dependencies [0c24c5a]
|
||||
- @uppy/core@4.5.0
|
||||
- @uppy/utils@6.2.0
|
||||
|
||||
## 4.4.2
|
||||
|
||||
Released: 2025-05-18
|
||||
Included in: Uppy v4.16.0
|
||||
|
||||
- @uppy/companion-client: don't reject on incorrect origin (Mikael Finstad / #5736)
|
||||
|
||||
## 4.4.0
|
||||
|
||||
Released: 2025-01-06
|
||||
Included in: Uppy v4.11.0
|
||||
|
||||
- @uppy/angular,@uppy/audio,@uppy/aws-s3,@uppy/box,@uppy/companion-client,@uppy/compressor,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/drop-target,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/form,@uppy/golden-retriever,@uppy/google-drive-picker,@uppy/google-drive,@uppy/google-photos-picker,@uppy/google-photos,@uppy/image-editor,@uppy/informer,@uppy/instagram,@uppy/locales,@uppy/onedrive,@uppy/progress-bar,@uppy/provider-views,@uppy/react,@uppy/remote-sources,@uppy/screen-capture,@uppy/status-bar,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/tus,@uppy/unsplash,@uppy/url,@uppy/vue,@uppy/webcam,@uppy/webdav,@uppy/xhr-upload,@uppy/zoom: Remove "paths" from all tsconfig's (Merlijn Vos / #5572)
|
||||
|
||||
## 4.2.0
|
||||
|
||||
Released: 2024-12-05
|
||||
Included in: Uppy v4.8.0
|
||||
|
||||
- @uppy/companion-client: Fix allowed origins (Mikael Finstad / #5536)
|
||||
- @uppy/audio,@uppy/aws-s3,@uppy/box,@uppy/companion-client,@uppy/compressor,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/drop-target,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/form,@uppy/golden-retriever,@uppy/google-drive,@uppy/google-photos,@uppy/image-editor,@uppy/informer,@uppy/instagram,@uppy/locales,@uppy/onedrive,@uppy/progress-bar,@uppy/provider-views,@uppy/react,@uppy/remote-sources,@uppy/screen-capture,@uppy/status-bar,@uppy/store-default,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/tus,@uppy/unsplash,@uppy/url,@uppy/utils,@uppy/vue,@uppy/webcam,@uppy/xhr-upload,@uppy/zoom: cleanup tsconfig (Mikael Finstad / #5520)
|
||||
|
||||
## 4.1.1
|
||||
|
||||
Released: 2024-10-31
|
||||
Included in: Uppy v4.6.0
|
||||
|
||||
- @uppy/aws-s3,@uppy/box,@uppy/companion-client,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/form,@uppy/golden-retriever,@uppy/google-drive,@uppy/google-photos,@uppy/image-editor,@uppy/informer,@uppy/instagram,@uppy/locales,@uppy/onedrive,@uppy/progress-bar,@uppy/provider-views,@uppy/react-native,@uppy/react,@uppy/redux-dev-tools,@uppy/screen-capture,@uppy/status-bar,@uppy/store-default,@uppy/store-redux,@uppy/svelte,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/tus,@uppy/unsplash,@uppy/url,@uppy/utils,@uppy/vue,@uppy/webcam,@uppy/xhr-upload,@uppy/zoom: Fix links (Anthony Veaudry / #5492)
|
||||
|
||||
## 4.0.0
|
||||
|
||||
Released: 2024-07-10
|
||||
Included in: Uppy v4.0.0
|
||||
|
||||
- docs,@uppy/companion-client: don't close socket when pausing (Mikael Finstad / #4821)
|
||||
|
||||
## 4.0.0-beta.7
|
||||
|
||||
Released: 2024-06-04
|
||||
Included in: Uppy v4.0.0-beta.10
|
||||
|
||||
- @uppy/companion-client: do not allow boolean `RequestOptions` (Mikael Finstad / #5198)
|
||||
- @uppy/companion-client: remove deprecated options (Mikael Finstad / #5198)
|
||||
- @uppy/companion-client: make `supportsRefreshToken` default (Mikael Finstad / #5198)
|
||||
- @uppy/companion-client: remove optional chaining (Mikael Finstad / #5198)
|
||||
- @uppy/companion-client: remove `Socket` (Mikael Finstad / #5198)
|
||||
|
||||
## 4.0.0-beta.6
|
||||
|
||||
Released: 2024-05-14
|
||||
Included in: Uppy v4.0.0-beta.7
|
||||
|
||||
- @uppy/companion-client,@uppy/dropbox,@uppy/screen-capture,@uppy/unsplash,@uppy/url,@uppy/webcam: Use `title` consistently from locales (Merlijn Vos / #5134)
|
||||
|
||||
## 4.0.0-beta.1
|
||||
|
||||
Released: 2024-03-28
|
||||
Included in: Uppy v4.0.0-beta.1
|
||||
|
||||
- @uppy/companion-client,@uppy/provider-views,@uppy/status-bar: fix type imports (Antoine du Hamel / #5038)
|
||||
- @uppy/companion-client: Replace Provider.initPlugin with composition (Merlijn Vos / #4977)
|
||||
|
||||
## 3.8.0
|
||||
|
||||
Released: 2024-03-27
|
||||
Included in: Uppy v3.24.0
|
||||
|
||||
- @uppy/box,@uppy/companion-client,@uppy/provider-views,@uppy/status-bar: fix type imports (Antoine du Hamel / #5038)
|
||||
- @uppy/companion-client: Replace Provider.initPlugin with composition (Merlijn Vos / #4977)
|
||||
|
||||
## 3.7.4
|
||||
|
||||
Released: 2024-02-28
|
||||
Included in: Uppy v3.23.0
|
||||
|
||||
- @uppy/companion-client,@uppy/utils,@uppy/xhr-upload: improvements for #4922 (Mikael Finstad / #4960)
|
||||
|
||||
## 3.7.3
|
||||
|
||||
Released: 2024-02-22
|
||||
Included in: Uppy v3.22.2
|
||||
|
||||
- @uppy/companion-client: fix body/url on upload-success (Merlijn Vos / #4922)
|
||||
- @uppy/companion-client: remove unnecessary `'use strict'` directives (Antoine du Hamel / #4943)
|
||||
- @uppy/companion-client: type changes for provider-views (Antoine du Hamel / #4938)
|
||||
- @uppy/companion-client: update types (Antoine du Hamel / #4927)
|
||||
|
||||
## 3.7.1
|
||||
|
||||
Released: 2024-02-19
|
||||
Included in: Uppy v3.22.0
|
||||
|
||||
- @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/companion-client,@uppy/tus,@uppy/xhr-upload: update `uppyfile` objects before emitting events (antoine du hamel / #4928)
|
||||
- @uppy/companion-client: fix tests and linter (antoine du hamel / #4890)
|
||||
- @uppy/companion-client: migrate to ts (merlijn vos / #4864)
|
||||
- @uppy/companion-client: fix `typeerror` (antoine du hamel)
|
||||
|
||||
## 3.7.0
|
||||
|
||||
Released: 2023-12-12
|
||||
Included in: Uppy v3.21.0
|
||||
|
||||
- @uppy/companion-client: avoid unnecessary preflight requests (Antoine du Hamel / #4462)
|
||||
|
||||
## 3.6.1
|
||||
|
||||
Released: 2023-11-24
|
||||
Included in: Uppy v3.20.0
|
||||
|
||||
- @uppy/companion-client: fix log type error (Mikael Finstad / #4766)
|
||||
- @uppy/companion-client: revert breaking change (Antoine du Hamel / #4801)
|
||||
|
||||
## 3.5.0
|
||||
|
||||
Released: 2023-10-20
|
||||
Included in: Uppy v3.18.0
|
||||
|
||||
- @uppy/companion-client: fixup! Added Companion OAuth Key type (Murderlon / #4668)
|
||||
- @uppy/companion-client: Added Companion OAuth Key type (Chris Pratt / #4668)
|
||||
|
||||
## 3.4.1
|
||||
|
||||
Released: 2023-09-29
|
||||
Included in: Uppy v3.17.0
|
||||
|
||||
- @uppy/companion-client: fix a refresh token race condition (Mikael Finstad / #4695)
|
||||
|
||||
## 3.4.0
|
||||
|
||||
Released: 2023-09-05
|
||||
Included in: Uppy v3.15.0
|
||||
|
||||
- @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/companion-client,@uppy/core,@uppy/tus,@uppy/utils,@uppy/xhr-upload: Move remote file upload logic into companion-client (Merlijn Vos / #4573)
|
||||
|
||||
## 3.3.0
|
||||
|
||||
Released: 2023-08-15
|
||||
Included in: Uppy v3.14.0
|
||||
|
||||
- @uppy/companion-client,@uppy/provider-views: make authentication optional (Dominik Schmidt / #4556)
|
||||
|
||||
## 3.1.2
|
||||
|
||||
Released: 2023-04-04
|
||||
Included in: Uppy v3.7.0
|
||||
|
||||
- @uppy/companion-client: do not open socket more than once (Artur Paikin)
|
||||
|
||||
## 3.1.1
|
||||
|
||||
Released: 2022-11-16
|
||||
Included in: Uppy v3.3.1
|
||||
|
||||
- @uppy/companion-client: treat `*` the same as missing header (Antoine du Hamel / #4221)
|
||||
|
||||
## 3.1.0
|
||||
|
||||
Released: 2022-11-10
|
||||
Included in: Uppy v3.3.0
|
||||
|
||||
- @uppy/companion-client: add support for `AbortSignal` (Antoine du Hamel / #4201)
|
||||
- @uppy/companion-client: prevent preflight race condition (Mikael Finstad / #4182)
|
||||
|
||||
## 3.0.2
|
||||
|
||||
Released: 2022-09-25
|
||||
Included in: Uppy v3.1.0
|
||||
|
||||
- @uppy/audio,@uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/box,@uppy/companion-client,@uppy/companion,@uppy/compressor,@uppy/core,@uppy/dashboard,@uppy/drag-drop,@uppy/drop-target,@uppy/dropbox,@uppy/facebook,@uppy/file-input,@uppy/form,@uppy/golden-retriever,@uppy/google-drive,@uppy/image-editor,@uppy/informer,@uppy/instagram,@uppy/locales,@uppy/onedrive,@uppy/progress-bar,@uppy/provider-views,@uppy/react,@uppy/redux-dev-tools,@uppy/remote-sources,@uppy/screen-capture,@uppy/status-bar,@uppy/store-default,@uppy/store-redux,@uppy/svelte,@uppy/thumbnail-generator,@uppy/transloadit,@uppy/tus,@uppy/unsplash,@uppy/url,@uppy/utils,@uppy/vue,@uppy/webcam,@uppy/xhr-upload,@uppy/zoom: add missing entries to changelog for individual packages (Antoine du Hamel / #4092)
|
||||
|
||||
## 3.0.0
|
||||
|
||||
Released: 2022-08-22
|
||||
Included in: Uppy v3.0.0
|
||||
|
||||
- Switch to ESM
|
||||
|
||||
## 2.2.0
|
||||
|
||||
Released: 2022-05-30
|
||||
Included in: Uppy v2.11.0
|
||||
|
||||
- @uppy/companion-client: Revert "Revert "@uppy/companion-client: refactor to ESM"" (Antoine du Hamel / #3730)
|
||||
|
||||
## 2.1.0
|
||||
|
||||
Released: 2022-05-14
|
||||
Included in: Uppy v2.10.0
|
||||
|
||||
- @uppy/companion-client: refactor to ESM (Antoine du Hamel / #3693)
|
||||
|
||||
## 2.0.6
|
||||
|
||||
Released: 2022-04-07
|
||||
Included in: Uppy v2.9.2
|
||||
|
||||
- @uppy/aws-s3,@uppy/companion-client,@uppy/transloadit,@uppy/utils: Propagate `isNetworkError` through error wrappers (Renée Kooi / #3620)
|
||||
|
||||
## 2.0.5
|
||||
|
||||
Released: 2022-02-14
|
||||
Included in: Uppy v2.5.0
|
||||
|
||||
- @uppy/companion-client,@uppy/companion,@uppy/provider-views,@uppy/robodog: Finishing touches on Companion dynamic Oauth (Renée Kooi / #2802)
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Transloadit
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
# @uppy/companion-client
|
||||
|
||||
<img src="https://uppy.io/img/logo.svg" width="120" alt="Uppy logo: a smiling puppy above a pink upwards arrow" align="right">
|
||||
|
||||
[](https://www.npmjs.com/package/@uppy/companion-client)
|
||||

|
||||

|
||||

|
||||
|
||||
Client library for communication with Companion. Intended for use in Uppy
|
||||
plugins.
|
||||
|
||||
Uppy is being developed by the folks at [Transloadit](https://transloadit.com),
|
||||
a versatile file encoding service.
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
import Uppy from '@uppy/core'
|
||||
import { Provider, RequestClient, Socket } from '@uppy/companion-client'
|
||||
|
||||
const uppy = new Uppy()
|
||||
|
||||
const client = new RequestClient(uppy, {
|
||||
companionUrl: 'https://uppy.mywebsite.com/',
|
||||
})
|
||||
client.get('/drive/list').then(() => {})
|
||||
|
||||
const provider = new Provider(uppy, {
|
||||
companionUrl: 'https://uppy.mywebsite.com/',
|
||||
provider: providerPluginInstance,
|
||||
})
|
||||
provider.checkAuth().then(() => {})
|
||||
|
||||
const socket = new Socket({ target: 'wss://uppy.mywebsite.com/' })
|
||||
socket.on('progress', () => {})
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
> Unless you are writing a custom provider plugin, you do not need to install
|
||||
> this.
|
||||
|
||||
```bash
|
||||
$ npm install @uppy/companion-client
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation for this plugin can be found on the
|
||||
[Uppy website](https://uppy.io/docs/companion).
|
||||
|
||||
## License
|
||||
|
||||
[The MIT License](./LICENSE).
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
{
|
||||
"name": "@uppy/companion-client",
|
||||
"description": "Client library for communication with Companion. Intended for use in Uppy plugins.",
|
||||
"version": "5.1.1",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"build": "tsc --build tsconfig.build.json",
|
||||
"typecheck": "tsc --build",
|
||||
"test": "vitest run --environment=jsdom --silent='passed-only'"
|
||||
},
|
||||
"keywords": [
|
||||
"file uploader",
|
||||
"uppy",
|
||||
"uppy-plugin",
|
||||
"companion",
|
||||
"provider"
|
||||
],
|
||||
"homepage": "https://uppy.io",
|
||||
"bugs": {
|
||||
"url": "https://github.com/transloadit/uppy/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/transloadit/uppy.git"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"lib",
|
||||
"dist",
|
||||
"CHANGELOG.md"
|
||||
],
|
||||
"exports": {
|
||||
".": "./lib/index.js",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@uppy/utils": "workspace:^",
|
||||
"namespace-emitter": "^2.0.1",
|
||||
"p-retry": "^6.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jsdom": "^29.1.1",
|
||||
"typescript": "^5.8.3",
|
||||
"vitest": "^4.1.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@uppy/core": "workspace:^"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.shared",
|
||||
"compilerOptions": {
|
||||
"outDir": "./lib",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["./src/**/*.*"],
|
||||
"exclude": ["./src/**/*.test.ts"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../utils/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../core/tsconfig.build.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.shared",
|
||||
"compilerOptions": {
|
||||
"emitDeclarationOnly": false,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["./package.json", "./src/**/*.*"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../utils/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../core/tsconfig.build.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build", "@uppy/core#build"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import type {
|
|||
UnknownProviderPluginState,
|
||||
UppyEventMap,
|
||||
} from '@uppy/core'
|
||||
import type { ProviderViews } from '@uppy/provider-views'
|
||||
import type { ProviderViews } from '@uppy/core/provider-views'
|
||||
import { dequal } from 'dequal/lite'
|
||||
import { Subscribers } from './utils.js'
|
||||
|
||||
|
|
|
|||
|
|
@ -18,12 +18,6 @@
|
|||
},
|
||||
{
|
||||
"path": "../image-editor/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../utils/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../core/tsconfig.build.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,6 @@
|
|||
},
|
||||
{
|
||||
"path": "../image-editor/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../utils/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../core/tsconfig.build.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@transloadit/prettier-bytes": "^1.1.0",
|
||||
"@uppy/utils": "workspace:^",
|
||||
"compressorjs": "^1.3.0",
|
||||
"preact": "^10.29.2",
|
||||
"promise-queue": "^2.2.5"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import Core from '@uppy/core'
|
||||
import { getFileNameAndExtension } from '@uppy/utils'
|
||||
import { getFileNameAndExtension } from '@uppy/core/utils'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import CompressorPlugin from './index.js'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { prettierBytes } from '@transloadit/prettier-bytes'
|
||||
import type { DefinePluginOpts, PluginOpts } from '@uppy/core'
|
||||
import { BasePlugin, type Uppy } from '@uppy/core'
|
||||
import type { Body, LocalUppyFile, Meta, UppyFile } from '@uppy/utils'
|
||||
import { getFileNameAndExtension, RateLimitedQueue } from '@uppy/utils'
|
||||
import type { Body, LocalUppyFile, Meta, UppyFile } from '@uppy/core/utils'
|
||||
import { getFileNameAndExtension, RateLimitedQueue } from '@uppy/core/utils'
|
||||
import CompressorJS from 'compressorjs'
|
||||
import locale from './locale.js'
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,6 @@
|
|||
"include": ["./src/**/*.*"],
|
||||
"exclude": ["./src/**/*.test.ts"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../utils/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../core/tsconfig.build.json"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,6 @@
|
|||
},
|
||||
"include": ["./package.json", "./src/**/*.*"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../utils/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../core/tsconfig.build.json"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
],
|
||||
"scripts": {
|
||||
"build": "tsc --build tsconfig.build.json",
|
||||
"build:css": "sass --load-path=../../ src/style.scss dist/style.css && postcss dist/style.css -u cssnano -o dist/style.min.css",
|
||||
"build:css": "sass --load-path=../../ src/style.scss dist/style.css && sass --load-path=../../ src/provider-views/style.scss dist/provider-views.css && postcss dist/style.css -u cssnano -o dist/style.min.css && postcss dist/provider-views.css -u cssnano -o dist/provider-views.min.css",
|
||||
"typecheck": "tsc --build",
|
||||
"test": "vitest run --environment=jsdom --silent='passed-only'"
|
||||
},
|
||||
|
|
@ -38,20 +38,32 @@
|
|||
"./css/style.min.css": "./dist/style.min.css",
|
||||
"./css/style.css": "./dist/style.css",
|
||||
"./css/style.scss": "./src/style.scss",
|
||||
"./store-default": "./lib/store/index.js",
|
||||
"./utils": "./lib/utils/index.js",
|
||||
"./companion-client": "./lib/companion-client/index.js",
|
||||
"./provider-views": "./lib/provider-views/index.js",
|
||||
"./provider-views/css/style.min.css": "./dist/provider-views.min.css",
|
||||
"./provider-views/css/style.css": "./dist/provider-views.css",
|
||||
"./provider-views/css/style.scss": "./src/provider-views/style.scss",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@transloadit/prettier-bytes": "^1.1.0",
|
||||
"@uppy/store-default": "workspace:^",
|
||||
"@uppy/utils": "workspace:^",
|
||||
"classnames": "^2.5.1",
|
||||
"lodash": "^4.18.1",
|
||||
"mime-match": "^1.0.2",
|
||||
"namespace-emitter": "^2.0.1",
|
||||
"nanoid": "^5.1.11",
|
||||
"p-queue": "^9.3.0",
|
||||
"p-retry": "^6.1.0",
|
||||
"preact": "^10.29.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/deep-freeze": "^0",
|
||||
"@types/gapi": "^0.0.47",
|
||||
"@types/google.accounts": "^0.0.14",
|
||||
"@types/google.picker": "^0.0.42",
|
||||
"@types/lodash": "^4.14.199",
|
||||
"@types/node": "^20.19.0",
|
||||
"cssnano": "^8.0.1",
|
||||
"deep-freeze": "^0.0.1",
|
||||
|
|
|
|||
|
|
@ -7,9 +7,14 @@
|
|||
* See `Plugin` for the extended version with Preact rendering for interfaces.
|
||||
*/
|
||||
|
||||
import type { Body, I18n, Meta, OptionalPluralizeLocale } from '@uppy/utils'
|
||||
import { Translator } from '@uppy/utils'
|
||||
import type { State, UnknownPlugin, Uppy } from './Uppy.js'
|
||||
import type {
|
||||
Body,
|
||||
I18n,
|
||||
Meta,
|
||||
OptionalPluralizeLocale,
|
||||
} from './utils/index.js'
|
||||
import { Translator } from './utils/index.js'
|
||||
|
||||
export type PluginOpts = {
|
||||
locale?: OptionalPluralizeLocale
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Body, Meta, UppyFile } from '@uppy/utils'
|
||||
import type { _UppyEventMap, Uppy, UppyEventMap } from './Uppy.js'
|
||||
import type { Body, Meta, UppyFile } from './utils/index.js'
|
||||
|
||||
/**
|
||||
* Create a wrapper around an event emitter with a `remove` method to remove
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { prettierBytes } from '@transloadit/prettier-bytes'
|
||||
import type { Body, I18n, Meta, UppyFile } from '@uppy/utils'
|
||||
// @ts-expect-error untyped
|
||||
import match from 'mime-match'
|
||||
import type { NonNullableUppyOptions, State } from './Uppy.js'
|
||||
import type { Body, I18n, Meta, UppyFile } from './utils/index.js'
|
||||
|
||||
export type Restrictions = {
|
||||
maxFileSize: number | null
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import type { Body, Meta } from '@uppy/utils'
|
||||
import { findDOMElement, getTextDirection } from '@uppy/utils'
|
||||
import { render } from 'preact'
|
||||
import type { PluginOpts } from './BasePlugin.js'
|
||||
import BasePlugin from './BasePlugin.js'
|
||||
import type { State } from './Uppy.js'
|
||||
import type { Body, Meta } from './utils/index.js'
|
||||
import { findDOMElement, getTextDirection } from './utils/index.js'
|
||||
|
||||
/**
|
||||
* Defer a frequent call to the microtask queue.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import fs from 'node:fs'
|
|||
import path from 'node:path'
|
||||
import { prettierBytes } from '@transloadit/prettier-bytes'
|
||||
import type { Body, Meta } from '@uppy/core'
|
||||
import type { Locale } from '@uppy/utils'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import BasePlugin, {
|
||||
type DefinePluginOpts,
|
||||
|
|
@ -21,6 +20,7 @@ import InvalidPluginWithoutType from './mocks/invalidPluginWithoutType.js'
|
|||
import { RestrictionError } from './Restricter.js'
|
||||
import UIPlugin from './UIPlugin.js'
|
||||
import type { State } from './Uppy.js'
|
||||
import type { Locale } from './utils/index.js'
|
||||
|
||||
const sampleImage = fs.readFileSync(
|
||||
path.join(__dirname, '../../compressor/fixtures/image.jpg'),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,28 @@
|
|||
/* global AggregateError */
|
||||
|
||||
import DefaultStore, { type Store } from '@uppy/store-default'
|
||||
import throttle from 'lodash/throttle.js'
|
||||
// @ts-expect-error untyped
|
||||
import ee from 'namespace-emitter'
|
||||
import { nanoid } from 'nanoid/non-secure'
|
||||
import type { h } from 'preact'
|
||||
import packageJson from '../package.json' with { type: 'json' }
|
||||
import type BasePlugin from './BasePlugin.js'
|
||||
import type Provider from './companion-client/Provider.js'
|
||||
import type SearchProvider from './companion-client/SearchProvider.js'
|
||||
import getFileName from './getFileName.js'
|
||||
import locale from './locale.js'
|
||||
import { debugLogger, justErrorsLogger } from './loggers.js'
|
||||
import type ProviderView from './provider-views/ProviderView/ProviderView.js'
|
||||
import type { Restrictions, ValidateableFile } from './Restricter.js'
|
||||
import {
|
||||
defaultOptions as defaultRestrictionOptions,
|
||||
Restricter,
|
||||
RestrictionError,
|
||||
} from './Restricter.js'
|
||||
import DefaultStore, { type Store } from './store/index.js'
|
||||
import supportsUploadProgress from './supportsUploadProgress.js'
|
||||
import type {
|
||||
Body,
|
||||
CompanionClientProvider,
|
||||
CompanionClientSearchProvider,
|
||||
CompanionFile,
|
||||
FileProgressNotStarted,
|
||||
FileProgressStarted,
|
||||
|
|
@ -17,30 +35,13 @@ import type {
|
|||
RemoteUppyFile,
|
||||
UppyFile,
|
||||
UppyFileId,
|
||||
} from '@uppy/utils'
|
||||
} from './utils/index.js'
|
||||
import {
|
||||
getFileNameAndExtension,
|
||||
getFileType,
|
||||
getSafeFileId,
|
||||
Translator,
|
||||
} from '@uppy/utils'
|
||||
import throttle from 'lodash/throttle.js'
|
||||
// @ts-expect-error untyped
|
||||
import ee from 'namespace-emitter'
|
||||
import { nanoid } from 'nanoid/non-secure'
|
||||
import type { h } from 'preact'
|
||||
import packageJson from '../package.json' with { type: 'json' }
|
||||
import type BasePlugin from './BasePlugin.js'
|
||||
import getFileName from './getFileName.js'
|
||||
import locale from './locale.js'
|
||||
import { debugLogger, justErrorsLogger } from './loggers.js'
|
||||
import type { Restrictions, ValidateableFile } from './Restricter.js'
|
||||
import {
|
||||
defaultOptions as defaultRestrictionOptions,
|
||||
Restricter,
|
||||
RestrictionError,
|
||||
} from './Restricter.js'
|
||||
import supportsUploadProgress from './supportsUploadProgress.js'
|
||||
} from './utils/index.js'
|
||||
|
||||
type Processor = (
|
||||
fileIDs: string[],
|
||||
|
|
@ -165,8 +166,6 @@ export interface BaseProviderPlugin {
|
|||
* UnknownProviderPlugin can be any Companion plugin (such as Google Drive)
|
||||
* that uses the Companion-assisted OAuth flow.
|
||||
* As the plugins are passed around throughout Uppy we need a generic type for this.
|
||||
* It may seems like duplication, but this type safe. Changing the type of `storage`
|
||||
* will error in the `Provider` class of @uppy/companion-client and vice versa.
|
||||
*
|
||||
* Note that this is the *plugin* class, not a version of the `Provider` class.
|
||||
* `Provider` does operate on Companion plugins with `uppy.getPlugin()`.
|
||||
|
|
@ -178,16 +177,25 @@ export type UnknownProviderPlugin<
|
|||
BaseProviderPlugin & {
|
||||
rootFolderId: string | null
|
||||
files: UppyFile<M, B>[]
|
||||
provider: CompanionClientProvider
|
||||
// Can't be typed unfortunately, we can't depend on `provider-views` in `core`.
|
||||
view: any
|
||||
// Structural subset of the real `Provider` class,structural rather than the nominal class type so custom
|
||||
// providers matching the public surface aren't forced to inherit from `Provider`.
|
||||
provider: Pick<
|
||||
Provider<M, B>,
|
||||
| 'name'
|
||||
| 'provider'
|
||||
| 'login'
|
||||
| 'logout'
|
||||
| 'fetchPreAuthToken'
|
||||
| 'fileUrl'
|
||||
| 'list'
|
||||
| 'search'
|
||||
>
|
||||
view: ProviderView<M, B>
|
||||
}
|
||||
|
||||
/*
|
||||
* UnknownSearchProviderPlugin can be any search Companion plugin (such as Unsplash).
|
||||
* As the plugins are passed around throughout Uppy we need a generic type for this.
|
||||
* It may seems like duplication, but this type safe. Changing the type of `title`
|
||||
* will error in the `SearchProvider` class of @uppy/companion-client and vice versa.
|
||||
*
|
||||
* Note that this is the *plugin* class, not a version of the `SearchProvider` class.
|
||||
* `SearchProvider` does operate on Companion plugins with `uppy.getPlugin()`.
|
||||
|
|
@ -203,7 +211,11 @@ export type UnknownSearchProviderPlugin<
|
|||
B extends Body,
|
||||
> = UnknownPlugin<M, B, UnknownSearchProviderPluginState> &
|
||||
BaseProviderPlugin & {
|
||||
provider: CompanionClientSearchProvider
|
||||
// Structural subset of the real `SearchProvider` class (see note above).
|
||||
provider: Pick<
|
||||
SearchProvider<M, B>,
|
||||
'name' | 'provider' | 'fileUrl' | 'search'
|
||||
>
|
||||
}
|
||||
|
||||
// for better readability
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { AsyncStore, UIPluginOptions } from '@uppy/core'
|
||||
import type { AsyncStore, UIPluginOptions } from '../index.js'
|
||||
|
||||
export interface CompanionPluginOptions extends UIPluginOptions {
|
||||
storage?: AsyncStore
|
||||
|
|
@ -4,14 +4,13 @@ import type {
|
|||
PluginOpts,
|
||||
UnknownProviderPlugin,
|
||||
Uppy,
|
||||
} from '@uppy/core'
|
||||
import {
|
||||
type CompanionClientProvider,
|
||||
getSocketHost,
|
||||
type RequestOptions,
|
||||
} from '@uppy/utils'
|
||||
} from '../index.js'
|
||||
import { getSocketHost } from '../utils/index.js'
|
||||
import type { CompanionPluginOptions } from './index.js'
|
||||
import RequestClient, { authErrorStatusCode } from './RequestClient.js'
|
||||
import RequestClient, {
|
||||
authErrorStatusCode,
|
||||
type RequestOptions,
|
||||
} from './RequestClient.js'
|
||||
|
||||
export interface Opts extends PluginOpts, CompanionPluginOptions {
|
||||
pluginId: string
|
||||
|
|
@ -31,10 +30,10 @@ function getOrigin() {
|
|||
return location.origin
|
||||
}
|
||||
|
||||
export default class Provider<M extends Meta, B extends Body>
|
||||
extends RequestClient<M, B>
|
||||
implements CompanionClientProvider
|
||||
{
|
||||
export default class Provider<
|
||||
M extends Meta,
|
||||
B extends Body,
|
||||
> extends RequestClient<M, B> {
|
||||
#refreshingTokenPromise: Promise<void> | undefined
|
||||
|
||||
provider: string
|
||||
|
|
@ -273,11 +272,13 @@ export default class Provider<M extends Meta, B extends Body>
|
|||
}
|
||||
|
||||
async login({
|
||||
uppyVersions,
|
||||
uppyVersions = '',
|
||||
authFormData,
|
||||
signal,
|
||||
}: {
|
||||
uppyVersions: string
|
||||
// `uppyVersions` is optional because callers such as `ProviderView` do not
|
||||
// have it on hand; the OAuth/simple-auth helpers always receive a string.
|
||||
uppyVersions?: string
|
||||
authFormData: unknown
|
||||
signal: AbortSignal
|
||||
}): Promise<void> {
|
||||
|
|
@ -1,21 +1,24 @@
|
|||
import type Uppy from '@uppy/core'
|
||||
import type {
|
||||
Body,
|
||||
Meta,
|
||||
RemoteUppyFile,
|
||||
RequestOptions,
|
||||
UppyFile,
|
||||
} from '@uppy/utils'
|
||||
import pRetry, { AbortError } from 'p-retry'
|
||||
import packageJson from '../../package.json' with { type: 'json' }
|
||||
import type Uppy from '../index.js'
|
||||
import type { Body, Meta, RemoteUppyFile, UppyFile } from '../utils/index.js'
|
||||
import {
|
||||
ErrorWithCause,
|
||||
fetchWithNetworkError,
|
||||
getSocketHost,
|
||||
UserFacingApiError,
|
||||
} from '@uppy/utils'
|
||||
import pRetry, { AbortError } from 'p-retry'
|
||||
import packageJson from '../package.json' with { type: 'json' }
|
||||
} from '../utils/index.js'
|
||||
import AuthError from './AuthError.js'
|
||||
|
||||
export type RequestOptions = {
|
||||
method?: string
|
||||
data?: Record<string, unknown>
|
||||
skipPostResponse?: boolean
|
||||
signal?: AbortSignal
|
||||
authFormData?: unknown
|
||||
qs?: Record<string, string>
|
||||
}
|
||||
|
||||
type CompanionHeaders = Record<string, string> | undefined
|
||||
|
||||
export type Opts = {
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import type { Body, Meta, Uppy } from '@uppy/core'
|
||||
import type { CompanionClientSearchProvider } from '@uppy/utils'
|
||||
import type { Body, Meta, Uppy } from '../index.js'
|
||||
import RequestClient, { type Opts } from './RequestClient.js'
|
||||
|
||||
const getName = (id: string): string => {
|
||||
|
|
@ -9,10 +8,10 @@ const getName = (id: string): string => {
|
|||
.join(' ')
|
||||
}
|
||||
|
||||
export default class SearchProvider<M extends Meta, B extends Body>
|
||||
extends RequestClient<M, B>
|
||||
implements CompanionClientSearchProvider
|
||||
{
|
||||
export default class SearchProvider<
|
||||
M extends Meta,
|
||||
B extends Body,
|
||||
> extends RequestClient<M, B> {
|
||||
provider: string
|
||||
|
||||
id: string
|
||||
|
|
@ -5,6 +5,9 @@
|
|||
export type { CompanionPluginOptions } from './CompanionPluginOptions.js'
|
||||
export { default as getAllowedHosts } from './getAllowedHosts.js'
|
||||
export { default as Provider } from './Provider.js'
|
||||
export { default as RequestClient } from './RequestClient.js'
|
||||
export {
|
||||
default as RequestClient,
|
||||
type RequestOptions,
|
||||
} from './RequestClient.js'
|
||||
export { default as SearchProvider } from './SearchProvider.js'
|
||||
export * as tokenStorage from './tokenStorage.js'
|
||||
|
|
@ -1,16 +1,10 @@
|
|||
export type { Store } from '@uppy/store-default'
|
||||
export type {
|
||||
Body,
|
||||
Meta,
|
||||
MinimalRequiredUppyFile,
|
||||
UppyFile,
|
||||
} from '@uppy/utils'
|
||||
export type { DefinePluginOpts, PluginOpts } from './BasePlugin.js'
|
||||
export { default as BasePlugin } from './BasePlugin.js'
|
||||
export { default as EventManager } from './EventManager.js'
|
||||
export { debugLogger } from './loggers.js'
|
||||
export type { Restrictions, ValidateableFile } from './Restricter.js'
|
||||
export { RestrictionError } from './Restricter.js'
|
||||
export type { Store } from './store/index.js'
|
||||
export type { PluginTarget, UIPluginOptions } from './UIPlugin.js'
|
||||
export { default as UIPlugin } from './UIPlugin.js'
|
||||
export type {
|
||||
|
|
@ -34,3 +28,9 @@ export type {
|
|||
UppyOptions,
|
||||
} from './Uppy.js'
|
||||
export { default, default as Uppy } from './Uppy.js'
|
||||
export type {
|
||||
Body,
|
||||
Meta,
|
||||
MinimalRequiredUppyFile,
|
||||
UppyFile,
|
||||
} from './utils/index.js'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { getTimeStamp } from '@uppy/utils'
|
||||
import { getTimeStamp } from './utils/index.js'
|
||||
|
||||
// Swallow all logs, except errors.
|
||||
// default if logger is not set or debug: false
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { Body, Meta, PartialTreeFolder } from '@uppy/core'
|
||||
import { Fragment, type h } from 'preact'
|
||||
import type { Body, Meta, PartialTreeFolder } from '../index.js'
|
||||
import type ProviderView from './ProviderView/index.js'
|
||||
|
||||
type BreadcrumbsProps<M extends Meta, B extends Body> = {
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
import { useEffect, useState } from 'preact/hooks'
|
||||
import type {
|
||||
Body,
|
||||
Meta,
|
||||
PartialTreeFile,
|
||||
PartialTreeFolderNode,
|
||||
} from '@uppy/core'
|
||||
import type { I18n } from '@uppy/utils'
|
||||
import { VirtualList } from '@uppy/utils'
|
||||
import { useEffect, useState } from 'preact/hooks'
|
||||
} from '../index.js'
|
||||
import type { I18n } from '../utils/index.js'
|
||||
import { VirtualList } from '../utils/index.js'
|
||||
import Item from './Item/index.js'
|
||||
import type ProviderView from './ProviderView/ProviderView.js'
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import type { I18n } from '@uppy/utils'
|
||||
import type { I18n } from '../utils/index.js'
|
||||
import { useSearchForm } from './useSearchForm.js'
|
||||
|
||||
interface FilterInputProps {
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import type { Body, Meta, PartialTree } from '@uppy/core'
|
||||
import type { I18n } from '@uppy/utils'
|
||||
import classNames from 'classnames'
|
||||
import { useMemo } from 'preact/hooks'
|
||||
import type { Body, Meta, PartialTree } from '../index.js'
|
||||
import type { I18n } from '../utils/index.js'
|
||||
import type ProviderView from './ProviderView/ProviderView.js'
|
||||
import getNumberOfSelectedFiles from './utils/PartialTreeUtils/getNumberOfSelectedFiles.js'
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import type { AsyncStore, Uppy } from '@uppy/core'
|
||||
import type { I18n } from '@uppy/utils'
|
||||
import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
|
||||
import type { AsyncStore, Uppy } from '../../index.js'
|
||||
import type { I18n } from '../../utils/index.js'
|
||||
import AuthView from '../ProviderView/AuthView.js'
|
||||
import {
|
||||
authorize,
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import type { PartialTreeFile, PartialTreeFolderNode } from '@uppy/core'
|
||||
import type { h } from 'preact'
|
||||
import type { PartialTreeFile, PartialTreeFolderNode } from '../../../index.js'
|
||||
import ItemIcon from './ItemIcon.js'
|
||||
|
||||
type GridItemProps = {
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import type { h } from 'preact'
|
||||
import type {
|
||||
PartialTreeFile,
|
||||
PartialTreeFolderNode,
|
||||
PartialTreeId,
|
||||
} from '@uppy/core'
|
||||
import type { h } from 'preact'
|
||||
} from '../../../index.js'
|
||||
import ItemIcon from './ItemIcon.js'
|
||||
|
||||
// if folder:
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import type { PartialTreeFile, PartialTreeFolderNode } from '@uppy/core'
|
||||
import type { I18n } from '@uppy/utils'
|
||||
import classNames from 'classnames'
|
||||
import type { PartialTreeFile, PartialTreeFolderNode } from '../../../index.js'
|
||||
import type { I18n } from '../../../utils/index.js'
|
||||
import type ProviderView from '../../ProviderView/ProviderView.js'
|
||||
import ItemIcon from './ItemIcon.js'
|
||||
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
import classNames from 'classnames'
|
||||
import type { h } from 'preact'
|
||||
import type {
|
||||
PartialTreeFile,
|
||||
PartialTreeFolderNode,
|
||||
PartialTreeId,
|
||||
} from '@uppy/core'
|
||||
import type { I18n } from '@uppy/utils'
|
||||
import classNames from 'classnames'
|
||||
import type { h } from 'preact'
|
||||
} from '../../index.js'
|
||||
import type { I18n } from '../../utils/index.js'
|
||||
import GridItem from './components/GridItem.js'
|
||||
import ListItem from './components/ListItem.js'
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import type { Body, Meta } from '@uppy/core'
|
||||
import type { I18n } from '@uppy/utils'
|
||||
import type { h } from 'preact'
|
||||
import { useCallback } from 'preact/hooks'
|
||||
import type { Body, Meta } from '../../index.js'
|
||||
import type { I18n } from '../../utils/index.js'
|
||||
import type ProviderViews from './ProviderView.js'
|
||||
import type { Opts } from './ProviderView.js'
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import type { PartialTreeFile, PartialTreeFolderNode } from '@uppy/core'
|
||||
import type { I18n } from '@uppy/utils'
|
||||
import type { PartialTreeFile, PartialTreeFolderNode } from '../../index.js'
|
||||
import type { I18n } from '../../utils/index.js'
|
||||
import SearchResultItem from '../Item/components/SearchResultItem.js'
|
||||
import type ProviderView from './ProviderView.js'
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import type { Body, Meta, PartialTreeFolder } from '@uppy/core'
|
||||
import type { I18n } from '@uppy/utils'
|
||||
import classNames from 'classnames'
|
||||
import type { h } from 'preact'
|
||||
import type { Body, Meta, PartialTreeFolder } from '../../index.js'
|
||||
import type { I18n } from '../../utils/index.js'
|
||||
import Breadcrumbs from '../Breadcrumbs.js'
|
||||
import type ProviderView from './ProviderView.js'
|
||||
import User from './User.js'
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
import classNames from 'classnames'
|
||||
import debounce from 'lodash/debounce.js'
|
||||
import type { h } from 'preact'
|
||||
import packageJson from '../../../package.json' with { type: 'json' }
|
||||
import type {
|
||||
Body,
|
||||
Meta,
|
||||
|
|
@ -9,13 +13,9 @@ import type {
|
|||
UnknownProviderPlugin,
|
||||
UnknownProviderPluginState,
|
||||
ValidateableFile,
|
||||
} from '@uppy/core'
|
||||
import type { CompanionFile, I18n } from '@uppy/utils'
|
||||
import { remoteFileObjToLocal } from '@uppy/utils'
|
||||
import classNames from 'classnames'
|
||||
import debounce from 'lodash/debounce.js'
|
||||
import type { h } from 'preact'
|
||||
import packageJson from '../../package.json' with { type: 'json' }
|
||||
} from '../../index.js'
|
||||
import type { CompanionFile, I18n } from '../../utils/index.js'
|
||||
import { remoteFileObjToLocal } from '../../utils/index.js'
|
||||
import Browser from '../Browser.js'
|
||||
import FilterInput from '../FilterInput.js'
|
||||
import FooterActions from '../FooterActions.js'
|
||||
|
|
@ -66,6 +66,17 @@ const getDefaultState = (
|
|||
|
||||
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>
|
||||
|
||||
/**
|
||||
* Shape of the responses Companion returns from its `list` and `search`
|
||||
* endpoints. The `Provider` methods are generic, so we pass this as the
|
||||
* expected response type at the call sites.
|
||||
*/
|
||||
type ProviderListResponse = {
|
||||
username: string
|
||||
nextPagePath: string | null
|
||||
items: CompanionFile[]
|
||||
}
|
||||
|
||||
export interface Opts<M extends Meta, B extends Body> {
|
||||
provider: UnknownProviderPlugin<M, B>['provider']
|
||||
viewType: 'list' | 'grid'
|
||||
|
|
@ -248,10 +259,13 @@ export default class ProviderView<M extends Meta, B extends Body> {
|
|||
await this.#withAbort(async (signal) => {
|
||||
const scopePath =
|
||||
currentFolder.type === 'root' ? undefined : currentFolderId
|
||||
const { items } = await this.provider.search!(searchString, {
|
||||
const { items } = await this.provider.search<ProviderListResponse>(
|
||||
searchString,
|
||||
{
|
||||
signal,
|
||||
path: scopePath,
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
// For each searched file, build the entire path (from the root all the way to the leaf node)
|
||||
// This is because we need to make sure all ancestor folders are present in the partialTree before we open the folder or check the file.
|
||||
|
|
@ -389,10 +403,10 @@ export default class ProviderView<M extends Meta, B extends Body> {
|
|||
let currentPagePath = folderId
|
||||
let currentItems: CompanionFile[] = []
|
||||
do {
|
||||
const { username, nextPagePath, items } = await this.provider.list(
|
||||
currentPagePath,
|
||||
{ signal },
|
||||
)
|
||||
const { username, nextPagePath, items } =
|
||||
await this.provider.list<ProviderListResponse>(currentPagePath, {
|
||||
signal,
|
||||
})
|
||||
// It's important to set the username during one of our first fetches
|
||||
this.plugin.setPluginState({ username })
|
||||
|
||||
|
|
@ -478,7 +492,8 @@ export default class ProviderView<M extends Meta, B extends Body> {
|
|||
) {
|
||||
this.isHandlingScroll = true
|
||||
await this.#withAbort(async (signal) => {
|
||||
const { nextPagePath, items } = await this.provider.list(
|
||||
const { nextPagePath, items } =
|
||||
await this.provider.list<ProviderListResponse>(
|
||||
currentFolder.nextPagePath,
|
||||
{ signal },
|
||||
)
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
import classNames from 'classnames'
|
||||
import type { h } from 'preact'
|
||||
import packageJson from '../../../package.json' with { type: 'json' }
|
||||
import type {
|
||||
Body,
|
||||
DefinePluginOpts,
|
||||
|
|
@ -9,12 +12,9 @@ import type {
|
|||
UnknownSearchProviderPlugin,
|
||||
UnknownSearchProviderPluginState,
|
||||
ValidateableFile,
|
||||
} from '@uppy/core'
|
||||
import type { CompanionFile } from '@uppy/utils'
|
||||
import { remoteFileObjToLocal } from '@uppy/utils'
|
||||
import classNames from 'classnames'
|
||||
import type { h } from 'preact'
|
||||
import packageJson from '../../package.json' with { type: 'json' }
|
||||
} from '../../index.js'
|
||||
import type { CompanionFile } from '../../utils/index.js'
|
||||
import { remoteFileObjToLocal } from '../../utils/index.js'
|
||||
import Browser from '../Browser.js'
|
||||
import FilterInput from '../FilterInput.js'
|
||||
import FooterActions from '../FooterActions.js'
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
// p-queue does not have a `"main"` field in its `package.json`, and that makes `import/no-unresolved` freak out.
|
||||
// We can safely ignore it because bundlers will happily use the `"exports"` field instead.
|
||||
import PQueue from 'p-queue'
|
||||
import type {
|
||||
PartialTree,
|
||||
PartialTreeFile,
|
||||
PartialTreeFolderNode,
|
||||
PartialTreeId,
|
||||
} from '@uppy/core'
|
||||
import type { CompanionFile } from '@uppy/utils'
|
||||
// p-queue does not have a `"main"` field in its `package.json`, and that makes `import/no-unresolved` freak out.
|
||||
// We can safely ignore it because bundlers will happily use the `"exports"` field instead.
|
||||
import PQueue from 'p-queue'
|
||||
} from '../../../index.js'
|
||||
import type { CompanionFile } from '../../../utils/index.js'
|
||||
import shallowClone from './shallowClone.js'
|
||||
|
||||
export type ApiList = (directory: PartialTreeId) => Promise<{
|
||||
|
|
@ -3,8 +3,8 @@ import type {
|
|||
PartialTreeFile,
|
||||
PartialTreeFolder,
|
||||
PartialTreeFolderNode,
|
||||
} from '@uppy/core'
|
||||
import type { CompanionFile } from '@uppy/utils'
|
||||
} from '../../../index.js'
|
||||
import type { CompanionFile } from '../../../utils/index.js'
|
||||
|
||||
const afterOpenFolder = (
|
||||
oldPartialTree: PartialTree,
|
||||
|
|
@ -4,8 +4,8 @@ import type {
|
|||
PartialTreeFolder,
|
||||
PartialTreeFolderNode,
|
||||
PartialTreeId,
|
||||
} from '@uppy/core'
|
||||
import type { CompanionFile } from '@uppy/utils'
|
||||
} from '../../../index.js'
|
||||
import type { CompanionFile } from '../../../utils/index.js'
|
||||
|
||||
const afterScrollFolder = (
|
||||
oldPartialTree: PartialTree,
|
||||
|
|
@ -4,7 +4,7 @@ import type {
|
|||
PartialTreeFolder,
|
||||
PartialTreeFolderNode,
|
||||
PartialTreeId,
|
||||
} from '@uppy/core'
|
||||
} from '../../../index.js'
|
||||
import shallowClone from './shallowClone.js'
|
||||
|
||||
/*
|
||||
|
|
@ -3,7 +3,7 @@ import type {
|
|||
PartialTreeFolder,
|
||||
PartialTreeFolderNode,
|
||||
PartialTreeId,
|
||||
} from '@uppy/core'
|
||||
} from '../../../index.js'
|
||||
|
||||
const getBreadcrumbs = (
|
||||
partialTree: PartialTree,
|
||||
|
|
@ -3,8 +3,8 @@ import type {
|
|||
PartialTreeFile,
|
||||
PartialTreeFolderNode,
|
||||
PartialTreeId,
|
||||
} from '@uppy/core'
|
||||
import type { CompanionFile } from '@uppy/utils'
|
||||
} from '../../../index.js'
|
||||
import type { CompanionFile } from '../../../utils/index.js'
|
||||
|
||||
export interface Cache {
|
||||
[key: string]: (PartialTreeFile | PartialTreeFolderNode)[]
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import type { PartialTree } from '@uppy/core'
|
||||
import type { PartialTree } from '../../../index.js'
|
||||
|
||||
/**
|
||||
* We're interested in all 'checked' leaves of this tree,
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
import { describe, expect, it, vi } from 'vitest'
|
||||
import type {
|
||||
PartialTree,
|
||||
PartialTreeFile,
|
||||
PartialTreeFolderNode,
|
||||
PartialTreeFolderRoot,
|
||||
PartialTreeId,
|
||||
} from '@uppy/core'
|
||||
import type { CompanionFile } from '@uppy/utils'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
} from '../../../index.js'
|
||||
import type { CompanionFile } from '../../../utils/index.js'
|
||||
import afterFill from './afterFill.js'
|
||||
import afterOpenFolder from './afterOpenFolder.js'
|
||||
import afterScrollFolder from './afterScrollFolder.js'
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import type { PartialTree } from '@uppy/core'
|
||||
import type { PartialTree } from '../../../index.js'
|
||||
|
||||
/**
|
||||
* One-level copying is sufficient as mutations within our `partialTree` are limited to properties
|
||||
|
|
@ -1,19 +1,23 @@
|
|||
import type { UnknownPlugin } from '@uppy/core'
|
||||
import type {
|
||||
UnknownPlugin,
|
||||
UnknownProviderPlugin,
|
||||
UnknownSearchProviderPlugin,
|
||||
} from '../../index.js'
|
||||
import type {
|
||||
Body,
|
||||
CompanionClientProvider,
|
||||
CompanionClientSearchProvider,
|
||||
CompanionFile,
|
||||
Meta,
|
||||
UppyFileNonGhost,
|
||||
} from '@uppy/utils'
|
||||
import { getSafeFileId } from '@uppy/utils'
|
||||
} from '../../utils/index.js'
|
||||
import { getSafeFileId } from '../../utils/index.js'
|
||||
import companionFileToUppyFile from './companionFileToUppyFile.js'
|
||||
|
||||
const addFiles = <M extends Meta, B extends Body>(
|
||||
companionFiles: CompanionFile[],
|
||||
plugin: UnknownPlugin<M, B>,
|
||||
provider: CompanionClientProvider | CompanionClientSearchProvider,
|
||||
provider:
|
||||
| UnknownProviderPlugin<M, B>['provider']
|
||||
| UnknownSearchProviderPlugin<M, B>['provider'],
|
||||
): void => {
|
||||
const uppyFiles = companionFiles.map((f) =>
|
||||
companionFileToUppyFile<M, B>(f, plugin, provider),
|
||||
|
|
@ -1,17 +1,21 @@
|
|||
import type { UnknownPlugin } from '@uppy/core'
|
||||
import type {
|
||||
UnknownPlugin,
|
||||
UnknownProviderPlugin,
|
||||
UnknownSearchProviderPlugin,
|
||||
} from '../../index.js'
|
||||
import type {
|
||||
Body,
|
||||
CompanionClientProvider,
|
||||
CompanionClientSearchProvider,
|
||||
CompanionFile,
|
||||
Meta,
|
||||
RemoteUppyFile,
|
||||
} from '@uppy/utils'
|
||||
} from '../../utils/index.js'
|
||||
|
||||
const companionFileToUppyFile = <M extends Meta, B extends Body>(
|
||||
file: CompanionFile,
|
||||
plugin: UnknownPlugin<M, B>,
|
||||
provider: CompanionClientProvider | CompanionClientSearchProvider,
|
||||
provider:
|
||||
| UnknownProviderPlugin<M, B>['provider']
|
||||
| UnknownSearchProviderPlugin<M, B>['provider'],
|
||||
): RemoteUppyFile<M, B> => {
|
||||
const name = file.name || file.id
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import type { PartialTreeFile, PartialTreeFolderNode } from '@uppy/core'
|
||||
import type { PartialTreeFile, PartialTreeFolderNode } from '../../index.js'
|
||||
|
||||
// Shift-clicking selects a single consecutive list of items
|
||||
// starting at the previous click.
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue