@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
TypeScript
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