ngx-uploadx
Version:
Angular Resumable Upload Module
588 lines (569 loc) • 19.8 kB
TypeScript
import * as i0 from '@angular/core';
import { InjectionToken, OnInit, EventEmitter, ModuleWithProviders, Provider, OnDestroy } from '@angular/core';
import * as ngx_uploadx from 'ngx-uploadx';
import { Observable } from 'rxjs';
/**
* Allows canceling some operation by calling cancel().
* onCancel callback can be used to execute cleanup logic when cancel is called.
* @deprecated Use AbortController instead. Will be removed in next major version.
*/
declare class Canceler {
/**
* Callback function to execute cleanup logic when cancel() is called
*/
onCancel: () => void;
/**
* Cancels the operation.
*/
cancel(): void;
}
declare enum ErrorType {
NotFound = 0,
Auth = 1,
Retryable = 2,
Fatal = 3
}
type ShouldRetryFunction = (code: number, attempts: number) => boolean;
type KeepPartialFunction = (code: number) => boolean;
interface RetryConfig {
/** Maximum number of retry attempts */
maxAttempts?: number;
/** Upload not exist status codes */
shouldRestartCodes?: number[];
/** Bad token? status codes */
authErrorCodes?: number[];
/** Retryable 4xx status codes */
shouldRetryCodes?: number[];
/** Overrides the built-in function that determines whether the operation should be repeated */
shouldRetry?: ShouldRetryFunction;
/** The minimum retry delay */
minDelay?: number;
/** The maximum retry delay */
maxDelay?: number;
/** Delay used between retries for non-error responses with missing range/offset */
onBusyDelay?: number;
/** Time interval after which hanged requests must be retried */
timeout?: number;
/** Determines whether partial chunks should be kept */
keepPartial?: boolean | KeepPartialFunction;
}
/**
* Retryable ErrorHandler
*/
declare class RetryHandler {
static readonly STALL_THRESHOLD = 3;
attempts: number;
config: Required<RetryConfig>;
private observedValue?;
private repeatCount;
cancel: () => void;
constructor(configOptions?: RetryConfig);
/**
* Determine error type based on response code
* @param code - HTTP response status code
*/
kind(code: number): ErrorType;
/**
* Wait before next retry attempt
* @param time - Delay in ms
*/
wait(time?: number): Promise<void>;
/**
* @deprecated Use checkForStall instead
*/
observe(value?: string | number): void;
/**
* Check if upload is stalled
*/
checkForStall(value?: string | number): boolean;
}
type ResponseBody = any;
type RequestHeaders = Record<string, boolean | number | string>;
type Metadata = Record<string, unknown>;
interface RequestConfig {
body?: BodyInit | null;
canceler?: Canceler;
signal?: AbortSignal;
headers: RequestHeaders;
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
onUploadProgress?: (evt: ProgressEvent) => void;
responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text';
url: string;
timeout?: number;
validateStatus?: (status: number) => boolean;
withCredentials?: boolean;
}
type RequestOptions = Partial<RequestConfig> & {
skipAuthorization?: boolean;
};
type AuthorizeRequest = (req: RequestConfig, token?: string) => RequestConfig | Promise<RequestConfig>;
type PreRequest = (req: RequestConfig) => Promise<RequestOptions> | RequestOptions | void;
type UploadStatus = 'added' | 'queue' | 'uploading' | 'complete' | 'error' | 'cancelled' | 'paused' | 'retry' | 'updated';
type UploadAction = 'upload' | 'cancel' | 'pause' | 'update';
interface UploadState {
/** Uploaded file */
readonly file: File;
/** Original file name */
readonly name: string;
/** Progress percentage */
readonly progress: number;
/** Estimated remaining time */
readonly remaining: number;
/** HTTP response body */
readonly response: ResponseBody;
/** HTTP response status code */
readonly responseStatus: number;
/** HTTP response headers */
readonly responseHeaders: Record<string, string>;
/** File size in bytes */
readonly size: number;
/** Upload speed bytes/sec */
readonly speed: number;
/** Upload status */
readonly status: UploadStatus;
/** Unique upload id */
readonly uploadId: string;
/** File url */
readonly url: string;
}
interface UploadItem {
/**
* URL to create new uploads.
* @defaultValue '/upload'
*/
endpoint?: string;
/**
* Headers to be appended to each HTTP request
*/
headers?: RequestHeaders | ((file: File) => RequestHeaders);
/**
* Custom uploads metadata
*/
metadata?: Metadata | ((file: File) => Metadata);
/**
* Authorization token as a `string` or function returning a `string` or `Promise<string>`
*/
token?: string | ((httpStatus: number) => string | Promise<string>);
}
interface UploadxControlEvent extends UploadItem {
readonly uploadId?: string;
action?: UploadAction;
}
interface UploaderOptions extends UploadItem {
retryConfig?: RetryConfig;
/**
* Set a fixed chunk size.
* If not specified, the optimal size will be automatically adjusted based on the network speed.
*/
chunkSize?: number;
/** Adaptive chunk size limit */
maxChunkSize?: number;
withCredentials?: boolean;
/**
* Set the expected server response type
*/
responseType?: 'json' | 'text' | 'document';
/**
* Function called before every request
*/
prerequest?: PreRequest;
/**
* Function used to apply authorization token
*/
authorize?: AuthorizeRequest;
}
interface AjaxRequestConfig extends RequestOptions {
[x: string]: unknown;
data?: unknown;
url: string;
}
interface AjaxResponse<T> {
data: T;
status: number;
headers: Record<string, string>;
}
interface Ajax {
request: <T = string>(config: AjaxRequestConfig) => Promise<AjaxResponse<T>>;
}
declare class UploadxAjax {
private buildXhr;
constructor(buildXhr: () => XMLHttpRequest);
request: <T = string>({ method, data, headers, url, responseType, signal, onUploadProgress, timeout, withCredentials, validateStatus }: AjaxRequestConfig) => Promise<AjaxResponse<T>>;
getResponseHeaders(xhr: XMLHttpRequest): Record<string, string>;
getResponseBody<T>(xhr: XMLHttpRequest, responseType?: string): T;
}
/**
* Injection token for the AJAX client used by Uploadx.
*
* @example
* // Using a custom AJAX client (e.g., axios)
* import axios from 'axios';
* const axiosInstance = axios.create({ });
* { provide: UPLOADX_AJAX, useFactory: () => ({ request: axiosInstance.request }) }
*/
declare const UPLOADX_AJAX: InjectionToken<Ajax>;
/**
* Adaptive chunk size
*/
declare class DynamicChunk {
/** Maximum chunk size in bytes */
static maxSize: number;
/** Minimum chunk size in bytes */
static minSize: number;
/** Initial chunk size in bytes */
static size: number;
static minChunkTime: number;
static maxChunkTime: number;
/**
* Scales the chunk size based on the throughput.
* If the elapsed time to upload a chunk is less than the min time, increase the chunk size.
* If the elapsed time is more than the max time, decrease the chunk size.
* Keeps the chunk size within the min and max limits.
* @param throughput - represents the upload rate in bytes/sec.
*/
static scale(throughput: number): number;
}
/**
* Uploader Base Class
*/
declare abstract class Uploader implements UploadState {
readonly file: File;
readonly options: Readonly<UploaderOptions>;
readonly stateChange: (uploader: Uploader) => void;
readonly ajax: Ajax;
name: string;
readonly size: number;
readonly uploadId: string;
response: ResponseBody;
responseStatus: number;
responseHeaders: Record<string, string>;
progress: number;
remaining: number;
speed: number;
/** Custom headers */
headers: RequestHeaders;
/** Metadata Object */
metadata: Metadata;
/** Upload endpoint */
endpoint: string;
/** Chunk size in bytes */
chunkSize: number;
/** Auth token/tokenGetter */
token: UploadxControlEvent['token'];
/** Byte offset within the whole file */
offset?: number;
/** Retries handler */
retry: RetryHandler;
abortController: AbortController;
/** Set HttpRequest responseType */
responseType?: 'json' | 'text' | 'document';
protected _authorize: AuthorizeRequest;
protected _prerequest: PreRequest;
protected _token: string;
private _eventsCount;
constructor(file: File, options: Readonly<UploaderOptions>, stateChange: (uploader: Uploader) => void, ajax: Ajax);
private _url;
get url(): string;
set url(value: string);
private _status;
get status(): UploadStatus;
/**
* Transition uploader to a new status.
* @sideEffects
* - Calls this.abort() when transitioning to 'paused'
* - Calls this.cleanup() for terminal states
* - Emits state change via this.stateChange()
*/
private moveTo;
set status(next: UploadStatus);
/**
* Configure uploader
*/
configure({ metadata, headers, token, endpoint, action }: UploadxControlEvent): void;
/**
* Starts uploading
*/
upload(): Promise<void>;
/**
* Performs http requests
*/
request(requestOptions: RequestOptions): Promise<void>;
/**
* Set auth token string
*/
updateToken: () => string | Promise<string>;
/**
* Get file URI
*/
protected abstract getFileUrl(): Promise<string>;
/**
* Send file content and return an offset for the next request
*/
protected abstract sendFileContent(): Promise<number | undefined>;
/**
* Get an offset for the next request
*/
protected abstract getOffset(): Promise<number | undefined>;
/**
* Updating the metadata of the upload
*/
protected update<T = {
metadata?: Metadata;
}>(_data: T): Promise<string>;
protected abort(): void;
protected cancel(): Promise<void>;
/**
* Gets the value from the response
*/
protected getValueFromResponse(key: string): string | null;
/**
* Get file chunk
* @param offset - number of bytes of the file to skip
* @param size - chunk size
*/
getChunk(offset?: number, size?: number): {
start: number;
end: number;
body: Blob;
};
private getRetryAfterFromBackend;
private cancelAndSendState;
private updateAndSendState;
private cleanup;
private onProgress;
}
interface UidService {
generateId(uploader: Uploader): Promise<string> | string;
}
declare class IdService implements UidService {
generateId(uploader: Uploader): Promise<string> | string;
static ɵfac: i0.ɵɵFactoryDeclaration<IdService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<IdService>;
}
/**
* Global Module Options
*/
interface UploadxOptions extends UploaderOptions {
/**
* Provide a user-defined class to support another upload protocol or to extend an existing one.
* @defaultValue UploadX
*/
uploaderClass?: UploaderClass;
/**
* Set the maximum parallel uploads
* @defaultValue 2
*/
concurrency?: number;
/**
* Automatically start upload when files added
* @defaultValue true
*/
autoUpload?: boolean;
/**
* File types the user can pick from the file input
*/
allowedTypes?: string;
/**
* Add 'multiple' attribute
* @defaultValue true
*/
multiple?: boolean;
/**
* Retention time for incomplete uploads
* @defaultValue 24
*/
storeIncompleteHours?: number;
}
interface UploadxFactoryOptions extends UploadxOptions {
endpoint: string;
autoUpload: boolean;
concurrency: number;
uploaderClass: UploaderClass;
authorize: AuthorizeRequest;
storeIncompleteHours: number;
}
type UploaderClass = new (file: File, options: UploaderOptions, stateChange: (uploader: Uploader) => void, ajax: Ajax) => Uploader;
declare const UPLOADX_FACTORY_OPTIONS: InjectionToken<UploadxFactoryOptions>;
declare const UPLOADX_OPTIONS: InjectionToken<UploadxOptions>;
declare class Store<T = string> {
readonly prefix: string;
private ttl;
constructor(prefix?: string);
set(key: string, value: T): void;
get(key: string): T | null;
delete(key: string): void;
clear(maxAgeHours?: number): void;
private keys;
}
declare const store: Store<string> | Map<string, string>;
declare function isLocalStorageAvailable(): boolean;
/**
* Implements tus resumable upload protocol
* {@link https://github.com/tus/tus-resumable-upload-protocol/blob/master/protocol.md Github}
*/
declare class Tus extends Uploader {
headers: {
'Tus-Resumable': string;
};
getFileUrl(): Promise<string>;
sendFileContent(): Promise<number | undefined>;
getOffset(): Promise<number | undefined>;
protected getOffsetFromResponse(): number | undefined;
}
/**
* Implements XHR/CORS Resumable Upload
* {@link https://github.com/kukhariev/node-uploadx/blob/master/proto.md Github}
* @see {@link https://developers.google.com/drive/api/v3/manage-uploads#resumable Google Drive API documentation}
*/
declare class UploaderX extends Uploader {
responseType: "json";
getFileUrl(): Promise<string>;
sendFileContent(): Promise<number | undefined>;
getOffset(): Promise<number | undefined>;
update<T>(data: T): Promise<string>;
protected getOffsetFromResponse(): number | undefined;
}
declare function getRangeEnd(range?: string): number;
declare class UploadxDirective implements OnInit {
set uploadx(value: UploadxOptions | '');
options: UploadxOptions;
set control(value: UploadxControlEvent | '');
state: EventEmitter<UploadState>;
private readonly elementRef;
private readonly renderer;
private readonly uploadService;
ngOnInit(): void;
fileChange(event: Event): void;
fileListener(files?: FileList | File[]): void;
static ɵfac: i0.ɵɵFactoryDeclaration<UploadxDirective, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<UploadxDirective, "[uploadx]", never, { "uploadx": { "alias": "uploadx"; "required": false; }; "options": { "alias": "options"; "required": false; }; "control": { "alias": "control"; "required": false; }; }, { "state": "state"; }, never, never, true, never>;
}
declare class UploadxDropDirective {
active: boolean;
fileInput?: UploadxDirective;
private readonly uploadService;
dropHandler(event: DragEvent): void;
onDragOver(event: DragEvent): void;
onDragLeave(event: DragEvent): void;
/**
* Extracts the files from a `DragEvent` object
*/
getFiles(event: DragEvent): FileList | File[];
protected _stopEvents(event: DragEvent): void;
static ɵfac: i0.ɵɵFactoryDeclaration<UploadxDropDirective, never>;
static ɵdir: i0.ɵɵDirectiveDeclaration<UploadxDropDirective, "[uploadxDrop]", never, {}, {}, ["fileInput"], never, true, never>;
}
declare class UploadxModule {
static withConfig(options: UploadxOptions): ModuleWithProviders<UploadxModule>;
static ɵfac: i0.ɵɵFactoryDeclaration<UploadxModule, never>;
static ɵmod: i0.ɵɵNgModuleDeclaration<UploadxModule, never, [typeof UploadxDirective, typeof UploadxDropDirective], [typeof UploadxDirective, typeof UploadxDropDirective]>;
static ɵinj: i0.ɵɵInjectorDeclaration<UploadxModule>;
}
/**
* Provides configuration options for standalone app.
*
* @example
* ```ts
* bootstrapApplication(AppComponent, {
* providers: [
* provideUploadx({
* endpoint: uploadUrl,
* allowedTypes: 'video/*,audio/*',
* maxChunkSize: 96 * 1024 * 1024
* })
* ]
* });
* ```
*/
declare function provideUploadx(options?: UploadxOptions): Provider[];
declare const UPLOAD_STATE_KEYS: (keyof UploadState)[];
declare class UploadxService implements OnDestroy {
/** Upload Queue */
queue: Uploader[];
readonly options: UploadxFactoryOptions;
private readonly eventsStream;
private subs;
private ngZone;
readonly ajax: ngx_uploadx.Ajax;
private idService;
constructor();
/** Upload status events */
get events(): Observable<UploadState>;
/**
* Initializes service
* @param options global module options
* @returns Observable that emits a new value on progress or status changes
*/
init(options?: UploadxOptions): Observable<UploadState>;
/**
* Initializes service
* @param options global module options
* @returns Observable that emits the current array of uploaders
*/
connect(options?: UploadxOptions): Observable<Uploader[]>;
/**
* Terminates all uploads and clears the queue
*/
disconnect(): void;
/**
* Returns current uploads state
* @example
* // restore background uploads
* this.uploads = this.uploadService.state();
*/
state(): UploadState[];
ngOnDestroy(): void;
/**
* Creates uploaders for files and adds them to the upload queue
*/
handleFiles(files: FileList | File | File[], options?: UploadxOptions): void;
/**
* Upload control
* @example
* // pause all
* this.uploadService.control({ action: 'pause' });
* // pause upload with uploadId
* this.uploadService.control({ action: 'pause', uploadId});
* // set token
* this.uploadService.control({ token: `TOKEN` });
*/
control(evt: UploadxControlEvent): void;
/**
* Number of active uploads
*/
get activeUploadsCount(): number;
/**
* Performs http requests
*/
request<T = string>(config: AjaxRequestConfig): Promise<AjaxResponse<T>>;
private stateChange;
private addUploaderInstance;
private processQueue;
static ɵfac: i0.ɵɵFactoryDeclaration<UploadxService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<UploadxService>;
}
declare function resolveUrl(url: string, base: string): string;
/**
* Unwraps a value if it is a function, otherwise returns the value directly.
* Useful for allowing values to optionally be specified as functions.
*/
declare function unfunc<T, V>(value: T | ((ref: V) => T), ref: V): T;
declare const pick: <T, K extends keyof T>(obj: T, props: K[]) => Pick<T, K>;
declare function isNumber(x?: unknown): x is number;
/**
* 32-bit FNV-1a hash function
*/
declare function createHash(str: string): number;
/**
* Utility functions for base64 encoding and decoding strings and objects.
*/
declare const b64: {
encode: (str: string) => string;
decode: (str: string) => string;
serialize: (obj: Record<string, unknown>) => string;
parse: (encoded: string) => Record<string, string>;
};
declare function isBrowser(): boolean;
declare function onLine(): boolean;
export { Canceler, DynamicChunk, ErrorType, IdService, RetryHandler, Store, Tus, UPLOADX_AJAX, UPLOADX_FACTORY_OPTIONS, UPLOADX_OPTIONS, UPLOAD_STATE_KEYS, Uploader, UploaderX, UploadxAjax, UploadxDirective, UploadxDropDirective, UploadxModule, UploadxService, b64, createHash, getRangeEnd, isBrowser, isLocalStorageAvailable, isNumber, onLine, pick, provideUploadx, resolveUrl, store, unfunc };
export type { Ajax, AjaxRequestConfig, AjaxResponse, AuthorizeRequest, KeepPartialFunction, Metadata, PreRequest, RequestConfig, RequestHeaders, RequestOptions, ResponseBody, RetryConfig, ShouldRetryFunction, UidService, UploadAction, UploadState, UploadStatus, UploaderClass, UploaderOptions, UploadxControlEvent, UploadxFactoryOptions, UploadxOptions };