UNPKG

@uppy/core

Version:

Core module for the extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:

395 lines 16 kB
import { type Store } from '@uppy/store-default'; import type { Body, CompanionClientProvider, CompanionClientSearchProvider, CompanionFile, FileProgressStarted, I18n, Locale, Meta, MinimalRequiredUppyFile, OptionalPluralizeLocale, UppyFile } from '@uppy/utils'; import { Translator } from '@uppy/utils'; import type { h } from 'preact'; import type BasePlugin from './BasePlugin.js'; import { debugLogger } from './loggers.js'; import type { Restrictions, ValidateableFile } from './Restricter.js'; import { RestrictionError } from './Restricter.js'; type Processor = (fileIDs: string[], uploadID: string) => Promise<unknown> | void; type LogLevel = 'info' | 'warning' | 'error' | 'success'; export type UnknownPlugin<M extends Meta, B extends Body, PluginState extends Record<string, unknown> = Record<string, unknown>> = BasePlugin<any, M, B, PluginState>; /** * ids are always `string`s, except the root folder's id can be `null` */ export type PartialTreeId = string | null; export type PartialTreeStatusFile = 'checked' | 'unchecked'; export type PartialTreeStatus = PartialTreeStatusFile | 'partial'; export type PartialTreeFile = { type: 'file'; id: string; /** * There exist two types of restrictions: * - individual restrictions (`allowedFileTypes`, `minFileSize`, `maxFileSize`), and * - aggregate restrictions (`maxNumberOfFiles`, `maxTotalFileSize`). * * `.restrictionError` reports whether this file passes individual restrictions. * */ restrictionError: string | null; status: PartialTreeStatusFile; parentId: PartialTreeId; data: CompanionFile; }; export type PartialTreeFolderNode = { type: 'folder'; id: string; /** * Consider `(.nextPagePath, .cached)` a composite key that can represent 4 states: * - `{ cached: true, nextPagePath: null }` - we fetched all pages in this folder * - `{ cached: true, nextPagePath: 'smth' }` - we fetched 1st page, and there are still pages left to fetch in this folder * - `{ cached: false, nextPagePath: null }` - we didn't fetch the 1st page in this folder * - `{ cached: false, nextPagePath: 'someString' }` - ❌ CAN'T HAPPEN ❌ */ cached: boolean; nextPagePath: PartialTreeId; status: PartialTreeStatus; parentId: PartialTreeId; data: CompanionFile; }; export type PartialTreeFolderRoot = { type: 'root'; id: PartialTreeId; cached: boolean; nextPagePath: PartialTreeId; }; export type PartialTreeFolder = PartialTreeFolderNode | PartialTreeFolderRoot; /** * PartialTree has the following structure. * * FolderRoot * ┌─────┴─────┐ * FolderNode File * ┌─────┴────┐ * File File * * Root folder is called `PartialTreeFolderRoot`, * all other folders are called `PartialTreeFolderNode`, because they are "internal nodes". * * It's possible for `PartialTreeFolderNode` to be a leaf node if it doesn't contain any files. */ export type PartialTree = (PartialTreeFile | PartialTreeFolder)[]; export type UnknownProviderPluginState = { authenticated: boolean | undefined; didFirstRender: boolean; searchString: string; loading: boolean | string; partialTree: PartialTree; currentFolderId: PartialTreeId; username: string | null; }; export interface AsyncStore { getItem: (key: string) => Promise<string | null>; setItem: (key: string, value: string) => Promise<void>; removeItem: (key: string) => Promise<void>; } /** * This is a base for a provider that does not necessarily use the Companion-assisted OAuth2 flow */ export interface BaseProviderPlugin { title: string; icon: () => h.JSX.Element; storage: AsyncStore; } export type UnknownProviderPlugin<M extends Meta, B extends Body> = UnknownPlugin<M, B, UnknownProviderPluginState> & BaseProviderPlugin & { rootFolderId: string | null; files: UppyFile<M, B>[]; provider: CompanionClientProvider; view: any; }; export type UnknownSearchProviderPluginState = { isInputMode: boolean; } & Pick<UnknownProviderPluginState, 'loading' | 'searchString' | 'partialTree' | 'currentFolderId'>; export type UnknownSearchProviderPlugin<M extends Meta, B extends Body> = UnknownPlugin<M, B, UnknownSearchProviderPluginState> & BaseProviderPlugin & { provider: CompanionClientSearchProvider; }; export interface UploadResult<M extends Meta, B extends Body> { successful?: UppyFile<M, B>[]; failed?: UppyFile<M, B>[]; uploadID?: string; [key: string]: unknown; } interface CurrentUpload<M extends Meta, B extends Body> { fileIDs: string[]; step: number; result: UploadResult<M, B>; } interface Plugins extends Record<string, Record<string, unknown> | undefined> { } export interface State<M extends Meta, B extends Body> extends Record<string, unknown> { meta: M; capabilities: { uploadProgress: boolean; individualCancellation: boolean; resumableUploads: boolean; isMobileDevice?: boolean; darkMode?: boolean; }; currentUploads: Record<string, CurrentUpload<M, B>>; allowNewUpload: boolean; recoveredState: null | Required<Pick<State<M, B>, 'files' | 'currentUploads'>>; error: string | null; files: { [key: string]: UppyFile<M, B>; }; info: Array<{ isHidden?: boolean; type: LogLevel; message: string; details?: string | Record<string, string> | null; }>; plugins: Plugins; totalProgress: number; companion?: Record<string, string>; } export interface UppyOptions<M extends Meta, B extends Body> { id?: string; autoProceed?: boolean; /** * @deprecated Use allowMultipleUploadBatches */ allowMultipleUploads?: boolean; allowMultipleUploadBatches?: boolean; logger?: typeof debugLogger; debug?: boolean; restrictions: Restrictions; meta?: M; onBeforeFileAdded?: (currentFile: UppyFile<M, B>, files: { [key: string]: UppyFile<M, B>; }) => UppyFile<M, B> | boolean | undefined; onBeforeUpload?: (files: { [key: string]: UppyFile<M, B>; }) => { [key: string]: UppyFile<M, B>; } | boolean; locale?: Locale; store?: Store<State<M, B>>; infoTimeout?: number; } export interface UppyOptionsWithOptionalRestrictions<M extends Meta, B extends Body> extends Omit<UppyOptions<M, B>, 'restrictions'> { restrictions?: Partial<Restrictions>; } type MinimalRequiredOptions<M extends Meta, B extends Body> = Partial<Omit<UppyOptions<M, B>, 'locale' | 'meta' | 'restrictions'> & { locale: OptionalPluralizeLocale; meta: Partial<M>; restrictions: Partial<Restrictions>; }>; export type NonNullableUppyOptions<M extends Meta, B extends Body> = Required<UppyOptions<M, B>>; export interface _UppyEventMap<M extends Meta, B extends Body> { 'back-online': () => void; 'cancel-all': () => void; complete: (result: UploadResult<M, B>) => void; error: (error: { name: string; message: string; details?: string; }, file?: UppyFile<M, B>, response?: UppyFile<M, B>['response']) => void; 'file-added': (file: UppyFile<M, B>) => void; 'file-removed': (file: UppyFile<M, B>) => void; 'files-added': (files: UppyFile<M, B>[]) => void; 'info-hidden': () => void; 'info-visible': () => void; 'is-offline': () => void; 'is-online': () => void; 'pause-all': () => void; 'plugin-added': (plugin: UnknownPlugin<any, any>) => void; 'plugin-remove': (plugin: UnknownPlugin<any, any>) => void; 'postprocess-complete': (file: UppyFile<M, B> | undefined, progress?: NonNullable<FileProgressStarted['preprocess']>) => void; 'postprocess-progress': (file: UppyFile<M, B> | undefined, progress: NonNullable<FileProgressStarted['postprocess']>) => void; 'preprocess-complete': (file: UppyFile<M, B> | undefined, progress?: NonNullable<FileProgressStarted['preprocess']>) => void; 'preprocess-progress': (file: UppyFile<M, B> | undefined, progress: NonNullable<FileProgressStarted['preprocess']>) => void; progress: (progress: number) => void; restored: (pluginData: any) => void; 'restore-confirmed': () => void; 'restore-canceled': () => void; 'restriction-failed': (file: UppyFile<M, B> | undefined, error: Error) => void; 'resume-all': () => void; 'retry-all': (files: UppyFile<M, B>[]) => void; 'state-update': (prevState: State<M, B>, nextState: State<M, B>, patch?: Partial<State<M, B>>) => void; upload: (uploadID: string, files: UppyFile<M, B>[]) => void; 'upload-error': (file: UppyFile<M, B> | undefined, error: { name: string; message: string; details?: string; }, response?: Omit<NonNullable<UppyFile<M, B>['response']>, 'uploadURL'> | undefined) => void; 'upload-pause': (file: UppyFile<M, B> | undefined, isPaused: boolean) => void; 'upload-progress': (file: UppyFile<M, B> | undefined, progress: FileProgressStarted) => void; 'upload-retry': (file: UppyFile<M, B>) => void; 'upload-stalled': (error: { message: string; details?: string; }, files: UppyFile<M, B>[]) => void; 'upload-success': (file: UppyFile<M, B> | undefined, response: NonNullable<UppyFile<M, B>['response']>) => void; } export interface UppyEventMap<M extends Meta, B extends Body> extends _UppyEventMap<M, B> { 'upload-start': (files: UppyFile<M, B>[]) => void; } /** `OmitFirstArg<typeof someArray>` is the type of the returned value of `someArray.slice(1)`. */ type OmitFirstArg<T> = T extends [any, ...infer U] ? U : never; /** * Uppy Core module. * Manages plugins, state updates, acts as an event bus, * adds/removes files and metadata. */ export declare class Uppy<M extends Meta = Meta, B extends Body = Record<string, never>> { #private; static VERSION: string; defaultLocale: OptionalPluralizeLocale; locale: Locale; opts: NonNullableUppyOptions<M, B>; store: NonNullableUppyOptions<M, B>['store']; i18n: I18n; i18nArray: Translator['translateArray']; scheduledAutoProceed: ReturnType<typeof setTimeout> | null; wasOffline: boolean; /** * Instantiate Uppy */ constructor(opts?: UppyOptionsWithOptionalRestrictions<M, B>); emit<T extends keyof UppyEventMap<M, B>>(event: T, ...args: Parameters<UppyEventMap<M, B>[T]>): void; on<K extends keyof UppyEventMap<M, B>>(event: K, callback: UppyEventMap<M, B>[K]): this; once<K extends keyof UppyEventMap<M, B>>(event: K, callback: UppyEventMap<M, B>[K]): this; off<K extends keyof UppyEventMap<M, B>>(event: K, callback: UppyEventMap<M, B>[K]): this; /** * Iterate on all plugins and run `update` on them. * Called each time state changes. * */ updateAll(state: Partial<State<M, B>>): void; /** * Updates state with a patch */ setState(patch?: Partial<State<M, B>>): void; /** * Returns current state. */ getState(): State<M, B>; patchFilesState(filesWithNewState: { [id: string]: Partial<UppyFile<M, B>>; }): void; /** * Shorthand to set state for a specific file. */ setFileState(fileID: string, state: Partial<UppyFile<M, B>>): void; i18nInit(): void; setOptions(newOpts: MinimalRequiredOptions<M, B>): void; resetProgress(): void; clear(): void; addPreProcessor(fn: Processor): void; removePreProcessor(fn: Processor): boolean; addPostProcessor(fn: Processor): void; removePostProcessor(fn: Processor): boolean; addUploader(fn: Processor): void; removeUploader(fn: Processor): boolean; setMeta(data: Partial<M>): void; setFileMeta(fileID: string, data: State<M, B>['meta']): void; /** * Get a file object. */ getFile(fileID: string): UppyFile<M, B>; /** * Get all files in an array. */ getFiles(): UppyFile<M, B>[]; getFilesByIds(ids: string[]): UppyFile<M, B>[]; getObjectOfFilesPerState(): { newFiles: UppyFile<M, B>[]; startedFiles: UppyFile<M, B>[]; uploadStartedFiles: UppyFile<M, B>[]; pausedFiles: UppyFile<M, B>[]; completeFiles: UppyFile<M, B>[]; erroredFiles: UppyFile<M, B>[]; inProgressFiles: UppyFile<M, B>[]; inProgressNotPausedFiles: UppyFile<M, B>[]; processingFiles: UppyFile<M, B>[]; isUploadStarted: boolean; isAllComplete: boolean; isAllErrored: boolean; isAllPaused: boolean; isUploadInProgress: boolean; isSomeGhost: boolean; }; validateRestrictions(file: ValidateableFile<M, B>, files?: ValidateableFile<M, B>[]): RestrictionError<M, B> | null; validateSingleFile(file: ValidateableFile<M, B>): string | null; validateAggregateRestrictions(files: ValidateableFile<M, B>[]): string | null; checkIfFileAlreadyExists(fileID: string): boolean; /** * Add a new file to `state.files`. This will run `onBeforeFileAdded`, * try to guess file type in a clever way, check file against restrictions, * and start an upload if `autoProceed === true`. */ addFile(file: File | MinimalRequiredUppyFile<M, B>): UppyFile<M, B>['id']; /** * Add multiple files to `state.files`. See the `addFile()` documentation. * * If an error occurs while adding a file, it is logged and the user is notified. * This is good for UI plugins, but not for programmatic use. * Programmatic users should usually still use `addFile()` on individual files. */ addFiles(fileDescriptors: MinimalRequiredUppyFile<M, B>[]): void; removeFiles(fileIDs: string[]): void; removeFile(fileID: string): void; pauseResume(fileID: string): boolean | undefined; pauseAll(): void; resumeAll(): void; retryAll(): Promise<UploadResult<M, B> | undefined>; cancelAll(): void; retryUpload(fileID: string): Promise<UploadResult<M, B> | undefined>; logout(): void; updateOnlineStatus(): void; getID(): string; /** * Registers a plugin with Core. */ use<T extends typeof BasePlugin<any, M, B>>(Plugin: T, ...args: OmitFirstArg<ConstructorParameters<T>>): this; /** * Find one Plugin by name. */ getPlugin<T extends UnknownPlugin<M, B> = UnknownPlugin<M, B>>(id: string): T | undefined; /** * Iterate through all `use`d plugins. * */ iteratePlugins(method: (plugin: UnknownPlugin<M, B>) => void): void; /** * Uninstall and remove a plugin. * * @param {object} instance The plugin instance to remove. */ removePlugin(instance: UnknownPlugin<M, B>): void; /** * Uninstall all plugins and close down this Uppy instance. */ destroy(): void; hideInfo(): void; /** * Set info message in `state.info`, so that UI plugins like `Informer` * can display the message. */ info(message: string | { message: string; details?: string | Record<string, string>; }, type?: LogLevel, duration?: number): void; /** * Passes messages to a function, provided in `opts.logger`. * If `opts.logger: Uppy.debugLogger` or `opts.debug: true`, logs to the browser console. */ log(message: unknown, type?: 'error' | 'warning'): void; registerRequestClient(id: string, client: unknown): void; /** @protected */ getRequestClientForFile<Client>(file: UppyFile<M, B>): Client; /** * Restore an upload by its ID. */ restore(uploadID: string): Promise<UploadResult<M, B> | undefined>; /** * Add data to an upload's result object. */ addResultData(uploadID: string, data: CurrentUpload<M, B>['result']): void; /** * Start an upload for all the files that are not currently being uploaded. */ upload(): Promise<NonNullable<UploadResult<M, B>> | undefined>; } export default Uppy; //# sourceMappingURL=Uppy.d.ts.map