Util APIs

APIs for various utilities available around the whole API surface.

Progress

interface Progress {
    (progress?: ProgressData | null): void;
    (completed?: number | null, total?: number | null, indeterminate?: boolean): void;
    data: ProgressData;
    completed: number | undefined;
    indeterminate: boolean | undefined;
    total: number | undefined;
    destroy: () => void;
    toJSON: () => ProgressData;
}

type ProgressData = {completed?: number; total?: number; indeterminate?: boolean};

Utility for reporting progress. It supports multiple methods to set current progress.

The total value is always optional, omitting it creates an indeterminate progress bar.

indeterminate parameter forces progress bar into indeterminate mode (animated stripes).

The way how progress value is displayed to the user can be customized with progressFormatter processor config.

progress(data)

Pass a progress data object with completed, total, and indeterminate values. All values are optional. Pass anything false, or an empty object to reset the progress to undefined.

This method updates both values, omitting one resets it to undefined.

progress({completed: 0.2, total: 1});
progress({completed: 1024});
progress(null); // reset

progress(completed, total?, indeterminate?)

Pass completed and total as function parameters. Both values are optional.

This method updates both values, omitting one resets it to undefined.

progress(3, 10); // normal progress
progress(3, 10, true); // forced indeterminate progress (animated stripes)
progress(3); // implicit indeterminate progress
progress(null); // reset both to null

completed / total

Set or read one of the progress values individually:

progress.completed = completed;
progress.total = total;

data

Read, or set progress data object by assigning it to progress.data:

progress.data = {completed, total};
progress.data = {completed};
progress.data = {};
progress.data = null;

This is the same as calling progress(data), with the difference that you can also read the current value.

OutputEmitters

interface OutputEmitters {
    file: (path: string, meta?: OutputMeta) => void;
    directory: (path: string, meta?: OutputMeta) => void;
    url: (url: string, meta?: OutputMeta) => void;
    string: (contents: string, meta?: {type?: string} & OutputMeta) => void;
    error: (error: Error | string, meta?: OutputMeta) => void;
}

interface OutputMeta {
    flair?: Flair;
    badge?: Badge;
}

export interface Flair {
    variant?: Variant;
    title: string;
    description?: string;
}

export interface Badge {
    variant?: Variant;
    title: string;
    icon: string;
}

Utilities for emitting output items. These show up in profile and global Outputs drawers, where they can be comfortably consumed, opened, navigated to, or copied.

Every output emitter accepts an optional OutputMeta config. Currently it is used for adding flairs and or badges to output items. These are displayed next to the output payload in the UI. For example, encode & optimize plugins use flair to display the percentage of file size savings.

The badge icon property can be a name of one of the icons built into the app, or a full path to an svg file. In case you are using your own svg file, ensure it's using currentColor to color all if its elements, otherwise it's going to look bad, and down right unreadable in one of the themes.

Currently, these icon names are available: article, bell, binary, book, bug, calendar, changelog, check, check-all, circle, circle-check, circle-plus, circle-x, clear-all, clock, cog, copy, dependency, drop, edit, error, export, file, file-add, file-missing, file-open, files, folder, folder-add, folder-missing, folder-open, github, globe, hdd, help, history, home, import, info, input, install, link, logo, logs, loupe, npm, open-external, operation, output, payload, plugins, plus, power, processor, puzzle, refresh, search, selector, star, tag, text, time, trash, undo, update, update-check, visibility, visibility-off, warning, x.


file

Type: (path: string, meta?: OutputMeta) => void

Emits a file. Path has to be a path to a file created or modified by processor.


directory

Type: (path: string, meta?: OutputMeta) => void

Emits a directory. Path has to be a path to a directory created or modified by processor.


url

Type: (url: string, meta?: OutputMeta) => void

Emits a URL.


string

Type: (contents: string, meta?: {type?: string} & OutputMeta) => void

Emits a string. This is intended for stuff like tokens, keys, or any other string values.

The OutputMeta for string accepts an additional type property which defines a string mime type, which is text/plain by default. You can change it to whatever you like. This currently serves no purpose, but in future when processor outputs can be piped into other processors, string types might be used for filtering.

If you want to just inform the user about something related to the operation progress, more appropriate would be to use console.* methods that show in operation logs.

Outputs are items that could potentially be consumed by other processors. Messages such as "All done!" do not belong here.


warning

Type: (message: string, meta?: OutputMeta) => void

Emits a warning.


error

Type: (error: Error | string, meta?: OutputMeta) => void

Emits an error. Tha value can be an error object, or a string with a message.

Note: Calling console.error('str') both logs the error in operation log and emits it as an error output item. It's essentially the same as doing:

console.log('str');
output.error('str');

PreparatorUtils

interface PreparatorUtils {
    action: 'drop' | 'paste';
    modifiers: string;
    dependencies: {[key: string]: unknown};
    title(value: string | undefined | null): void;

    // CommonModals
    alert(data: ModalData): Promise<void>;
    confirm(data: ModalData): Promise<ModalResult<boolean>>;
    prompt(data: ModalData, stringOptions?: OptionString): Promise<ModalResult<string>>;
    promptOptions<T extends OptionsData | undefined = undefined>(
        data: ModalData,
        schema: OptionsSchema<T>
    ): Promise<ModalResult<T>>;
    showOpenDialog(options: OpenDialogOptions): Promise<OpenDialogReturnValue>;
    showSaveDialog(options: SaveDialogOptions): Promise<SaveDialogReturnValue>;
    openModalWindow<T = unknown>(options: string | OpenWindowOptions, payload: unknown): Promise<T>;
}

Utilities available in operation preparator. CommonModals are documented below.


action

Type: 'drop' | 'paste'

Name of the action that added items into the payload. This might grow in the future.

Note: paste action always comes with an empty modifiers id


modifiers

Type: string

A modifiers string ID of the event that added items to the operation.

For example, if user holds ctrl and shift while dropping items into the profile, this will be ctrl+shift.

Available modifiers: alt, ctrl, meta, shift

Notes:


title

Type: (value: string | undefined | null) => void

Sets or deletes operation title. By default, operation titles are constructed from its payload (file paths, domains, etc), but if you have something more descriptive or relevant, you can use this to overwrite it.


dependencies

Type: {[key: string]: unknown}

Payloads of dependencies the processor depends on.


modals

PreparatorUtils have also access to CommonModals, which are documented below. Example:

await utils.alert({title: 'Foo', message: `Foo happened because of bar.`});

Cleanup

function cleanup(directoryPath: string): Promise<void>;

Deletes everything inside directoryPath, but keeps the directory. Useful for cleaning dataPath when re-installing dependency.

If directory doesn't exist, it'll be created recursively.


directoryPath

Type: string required

Path to the directory to clean up. Throws when not a directory.

Download

function download(url: string | URL, destination: string, options?: DownloadOptions): Promise<string>;

interface DownloadOptions {
    onProgress?: (progress: {completed: number; total?: number}) => void;
    filename?: string;
    timeout?: number;
    signal?: AbortSignal;
}

A simple utility to download a file from the internet. Follows redirects, extracts the source filename, and resolves with the final downloaded filename (not path, just filename).


url

Type: string | URL required

Source url to the file to be downloaded. Can be a string with URL, or a new URL() object.


destination

Type: string required

Path to the destination directory.


options

Type: DownloadOptions optional

Object with download options.

onProgress

Type: (progress: {completed: number, total?: number}) => void

An onProgress handler. Designed so you can just plug in Progress into it:

utils.download(url, destination, {opProgress: utils.progress});

filename

Type: string

By default, download() retrieves filename from response headers or a passed url, but you can make it to ignore those, and use this filename instead.

timeout

Type: number

Socket timeout in milliseconds.

signal

Type: AbortSignal

See AbortSignal on MDN.

Return

Returns filename extracted from source headers, or a URL when not in headers.

It'll return the extracted filename even when destination filename is forced by options.filename.

Extract

function extract(archivePath: string, options?: ExtractOptions): Promise<string[] | ExtractListDetailItem[]>;
function extract(
    archivePath: string,
    destinationPath: string,
    options?: ExtractOptions
): Promise<string[] | ExtractListDetailItem[]>;

interface ExtractOptions {
    overwrite?: boolean;
    listDetails?: boolean;
    onProgress?: (progress: ProgressData) => void;
    onLog?: (message: string) => void;
}

interface ExtractListDetailItem {
    name: string; // File name
    path: string; // Full path to the file
    size: number;
    isDirectory: boolean;
    isFile: boolean;
}

A utility to extract files from an archive. Automatically decompresses .tar.gz, .tar.xz, ....

Returns a list of root files and folders extracted from the archive.

Quick example
const files = await extract('archive.zip'); // extracts into same directory as archive
const files = await extract('archive.zip', 'out'); // specify destination directory
console.log(file); // list of root files from extracted archive

archivePath

Type: string required

Path to the archive to extract.


destinationPath

Type: string optional

Path to the destination directory. By default, the directory of the archive will be used.


options

Type: ExtractOptions optional

Extract options object.

overwrite

Type: boolean optional
Default: false

Passing true will make extraction overwrite any existing files.

listDetails

Type: boolean optional
Default: false

Wether the returned list should contain file details (path, size, isDirectory, isFile). By default, only filename strings are returned.

onProgress

Type: (progress: ProgressData) => void optional

Progress reporter designed to accepts Progress util. Example:

const files = await extract('archive.zip', {onProgress: util.progress});

onLog

Type: (message: string) => void optional

Reports extraction logs.

Example

Disabled:

console.log(await extract('archive.zip', {listDetails: false}));
[ "file.ext" ]

Enabled:

console.log(await extract('archive.zip', {listDetails: true}));
[
    {
        path: "file.ext",
        size: 1024,
        isDirectory: false,
        isFile: true,
    }
]

Return

Returns either list of root or all filenames or file details in the extracted file.

CommonModals

interface CommonModals {
    alert(data: ModalData): Promise<void>;
    confirm(data: ModalData): Promise<ModalResult<boolean>>;
    prompt(data: ModalData, stringOptions?: OptionString): Promise<ModalResult<string>>;
    promptOptions<T extends OptionsData | undefined = undefined>(
        data: ModalData,
        schema: OptionsSchema<T>
    ): Promise<ModalResult<T>>;
    showOpenDialog(options: OpenDialogOptions): Promise<OpenDialogReturnValue>;
    showSaveDialog(options: SaveDialogOptions): Promise<SaveDialogReturnValue>;
    openModalWindow<T = unknown>(options: string | OpenWindowOptions, payload?: unknown): Promise<ModalResult<T>>;
}

interface ModalData {
    variant?: 'success' | 'info' | 'warning' | 'danger';
    title?: string;
    message?: string;
    details?: string;
}

interface ModalResult<T = unknown> {
    canceled: boolean;
    payload: T;
    modifiers: string;
}

interface OpenWindowOptions {
    path: string;
    title?: string;
    width?: number; // Suggested width
    height?: number; // Suggested height
    minWidth?: number;
    minHeight?: number;
}

Modal interfaces available in various places around the API.


alert

Type: (data: ModalData) => Promise<void>

Displays an alert modal window to the user with an OK button to close it. Returns a promise that resolves once the modal has been closed.

data

interface ModalData {
    variant?: 'success' | 'info' | 'warning' | 'danger';
    title?: string;
    message?: string;
    details?: string;
}
Example
await alert({
    variant: 'danger',
    title: 'Error',
    message: 'Some error message.',
});

confirm

Type: (data: ModalData) => Promise<ModalResult<boolean>>

Displays a confirmation dialogue with OK and Cancel buttons. Returns a promise that resolves with ModalResult<boolean> interface, where payload is either true when user confirmed, or false when they closed the modal.

data

interface ModalData {
    variant?: 'success' | 'info' | 'warning' | 'danger';
    title?: string;
    message?: string;
    details?: string;
}

Return

Returns ModalResult<boolean>, which looks like this:

{
    canceled: boolean;
    payload: boolean;
    modifiers: string; // example: 'alt+ctrl+shift'
}
Example
const result = await confirm({
    title: 'Question',
    message: 'Do you agree?',
});
if (result.payload) {
    // user agreed
}

prompt

Type: (data: ModalData, stringOptions?: OptionString) => Promise<ModalResult<string>>

Prompts user for a string input. Resolves with ModalResult<string> interface.

data

interface ModalData {
    variant?: 'success' | 'info' | 'warning' | 'danger';
    title?: string;
    message?: string;
    details?: string;
}

stringOptions

Validating & formatting options of string option schema, that is:

{
    default?: string;
    rows?: number;
    cols?: number; // set above 1 to create a textarea
    min?: number;
    max?: number;
    validator?: (value: string) => boolean;
    asyncValidator?: (value: string) => Promise<boolean>;
}

Return

Returns ModalResult<string>, which looks like this:

{
    canceled: boolean;
    payload: string;
    modifiers: string; // example: 'alt+ctrl+shift'
}
Example
const result = await prompt({
    title: 'Credentials',
    message: 'Enter key:',
});
if (!result.canceled) {
    console.log(result.payload); // users key
}

promptOptions

Type: <T = unknown>(data: ModalData, optionsSchema: OptionsSchema[]) => Promise<ModalResult<T>>

Prompts user with options constructed from passed options schema, similar to defining processor options. Resolves with ModalResult<T> interface

data

interface ModalData {
    variant?: 'success' | 'info' | 'warning' | 'danger';
    title?: string;
    message?: string;
    details?: string;
}

optionsSchema

OptionsSchema to construct options UI for user to fill. Same as defining processor options.

Return

Returns ModalResult<T>, where T is data the optionsSchema should produce. Looks like this:

{
    canceled: boolean;
    payload: T;
    modifiers: string; // example: 'alt+ctrl+shift'
}
Example
const result = await promptOptions({title: 'Form', message: 'Fill this:'}, [
    {name: 'foo', type: 'boolean', default: false},
]);
if (!result.canceled) {
    console.log(result.payload.foo); // boolean
}

showOpenDialog

Type: (options: OpenDialogOptions) => Promise<OpenDialogReturnValue>

Creates a dialog to retrieve file or directory path(s) to open.

This is just a proxy to Electron's dialog.showOpenDialog without the browserWindow parameter.

Example
const result = await utils.showOpenDialog({properties: ['openFile']});
if (!result.canceled) {
    console.log(result.filePaths[0]);
}

showSaveDialog

Type: (options: SaveDialogOptions) => Promise<SaveDialogReturnValue>

Creates a dialog to retrieve a destination file path.

This is just a proxy to Electron's dialog.showSaveDialog without the browserWindow parameter.

Example
const result = await utils.showSaveDialog({properties: ['createDirectory']});
if (!result.canceled) {
    console.log(result.filePath);
}

openModalWindow

Type: <T = unknown>(options: string | OpenWindowOptions, payload?: any): Promise<T>

Allows preparator to open a window with its own UI to do something to the payload.

The first argument is either path to the html that renders the UI (nodeIntegration enabled), or an object with options.

path to the html is relative from the root of the plugin.

interface OpenWindowOptions {
    path: string;
    width?: number; // Suggested width
    height?: number; // Suggested height
    minWidth?: number;
    minHeight?: number;
}

Inside the window, you can use the @drovp/utils for modal-window to communicate with the preparator and resolve with the new or modified payload.

Example

Main plugin file:

plugin.registerProcessor({
    // ... other options
    operationPreparator: async (payload, utils) => {
        const {canceled, payload} =  await utils.openModalWindow('window.html', payload);
        return canceled ? null : payload;
    },
});

Window HTML file:

<DOCTYPE! html>
    <html lang="en">
        <head>
            <meta charset="utf-8" />
            <title>Plugin window</title>
        </head>
        <body>
            <div id="app"></div>
            <script src="/window.js"></script>
        </body></html
></DOCTYPE!>

Window JS file:

const {getPayload, resolve, openContextMenu} = require('@drovp/utils/modal-window');

getPayload().then((payload) => {
    console.log(payload); // Payload passed via openModalWindow(..., payload);
    const newPayload = await renderSomeUIAndDoSomething(payload);
    resolve(newPayload); // This also closes the window
    // If you want to close the window without resolving, just do:
    window.close();
});