npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@ws-serenity/use-data-transfer

v0.2.6

Published

Hook for handling remote and local files by unified API

Downloads

32

Readme

UseDataTransfer

Хук для предоставления единого api для удаленных и локальных файлов.

Валидация: поддерживает api для валидации файлов.
Гибкость: поддерживает любые представления для описаний файлов, ошибок валидации, и конфигураций для скачивания файлов.
Кэширование: кэширует файлы, полученные для предпросмотра, до момента размонтирования компонента.
Состояние загрузки: предоставляет переменную, отражающую текущий процент загрузки каждого из файлов на сервер.
Отложенная синхронизация: имеет возможность отложить синхронизацию состояния с сервером, до вызова соответствующего метода.
Асинхронность: методы хука не блокируют поток.


Быстрый страт

Пример

Обобщения

В начале, нужно определить типы данных, используемые хуком.

TDataImage

TDataImage (image от en - образ, а не от en - изображение) определяет представление, в котором хранится информация о файле (но не сам файл).
Тип обязан иметь поле id: string, его можно или указать вручную (допускается ⚠️), или расширить имеющийся тип DataImage (оптимально ✅). Рекомендуется разбивать тип на type Additions + type CustomData = DataImage + Additions, тогда, можно будет использовать типизированный Object.assign<DataImage, Additions>(...), для создания TDataImage.

import { UseDataTransfer } from "@ws-serenity/use-data-transfer"; 

// Пусть каждый образ хранит название вложения и его размер
export type CustomDataImageAdditions = {
    name: string
    size: string
}

export type CustomDataImage = CustomDataImageAdditions & UseDataTransfer.DataImage

Хук расширяет интерфейс TDataImage до HookDrivenDataImage<TDataImage>.

const {add, images} = useDataTransfer<CustomDataImage /*..*/>(/*..*/);

add(file);

const hookDrivenImage = images[0];

console.log(hookDrivenImage.state); // Состояние данных, может принимать значения local | remote | uploading
const blob = hookDrivenImage.preview().blob; // Метод для скачивания данных в ОЗУ
hookDrivenImage.download(); // Метод для скачивания данных в ПЗУ
hookDrivenImage.remove(); // Метод для удаления данных

Таким образом, TDataImage не должен содержать следующие зарезервированные под расширение поля или методы: state, remove, preview, download.

TError

TError определяет представление ошибок, используемых при валидации локальных файлов. Может быть представлен любым значением типа Record<any, any>. TError, аналогично, будет расширен до HookDrivenValidationError<TError>.

const {add, images, errors} = useDataTransfer<CustomDataImage, CustomError /*..*/>(/*..*/);

add(file);

const hookDrivenError = errors[0];
console.log(hookDrivenError.id); // Уникальный id ошибка
hookDrivenError.resolve(); // Уадлить ошибку из коллекции

TError не должен содержать поля или методы: id, resolve.

Работа с Id
Хук создает уникальный идентификатор для каждого из добавленных образов. Образы, полученные удаленно, должны иметь свой id, как часть контракта, на котором построена работа с сервером. Когда локальный файл будет успешно отправлен на сервер, его id будет подменен на id присвоенный сервером.
Ошибки валидации, в отличие от образов, не могут прийти с сервера, их id на протяжении всего жизненного цикла контролируется хуком. Поэтому для TDataImage id это необходимое поле, а для TError расширяемое хуком.

TPreviewConfig и TDownloadConfig

TPreviewConfig и TDownloadConfig определяют конфиги, с которыми могут быть запрошены удаленные файлы. Могут быть представлены чем угодно, в том числе и отсутствовать, те, иметь значение, установленное по умолчанию, undefined.

Например:

export type CustomPreviewConfig = 'original' | 'thumbnail'

export type CustomDownloadConfig = {
    width: number
    height: number
}

// Также, может использоваться один тип
export type AnotherDownloadConfig = CustomPreviewConfig

Вспомогательные обработчики

Для того чтобы хук мог работать с TDataImage, ему необходимо предоставить маппер с сигнатурой (file: File, image: DataImage) => TDataImage, называемый genericDataImageBuilder.

// Для определенного выше `CustomDataImage`, может быть представлен так
const customDataImageBuilder = (file: File, image: DataImage) => {
    return Object.assign<DataImage, CustomDataImageAdditions>(image, {
        name: file.name,
        size: file.size
    })
}

Хук использует кэширование для каждого hookDrivenImage.preview(previewConfig) запроса. Кэшируется не только файл, но и конфиг запроса. Так как хук ничего не знает об используемом TPreviewConfig, то ему нужен вспомогательный обработчик cacheWorker: CacheWorker.

export type CacheWorker<TPreviewConfig> = {
    search: (cache, config?) => CachedEntity | undefined // 1
    getLocalFileQualityConfig: (file: File) => TPreviewConfig // 2
}

Для разрешения случаев, когда вместо запроса на сервер, можно предоставить кэшированный файл от запроса с другим конфигом, используется search (1) метод. Например, такое поведение может быть полезно, когда кэширован оригинал изображения, и необходима версия с меньшим разрешением.
Если search находит подходящую сущность, то он возвращает её, в ином случае - undefined.

При добавлении локального файла, необходимо знать какому TPreviewConfig он соответствует. Для этого используется метод getLocalFileQualityConfig (2).

Для общего случая TPreviewConfig = undefined, cacheWorker может быть таким:

const cacheWorker: CacheWorker<undefined> = {
    getLocalFileQualityConfig: () => undefined,
    search: (entities) => entities[0]
}

Провайдер для удаленных файлов

Для работы с сервером хуку требуется реализация абстрактного класса RemoteDataProvider.

export abstract class RemoteDataProvider<
    TDataImage extends DataImage = DataImage,
    TPreviewConfig = undefined,
    TDownloadConfig = undefined
> {
    // Отменяет выполняющуюся загрузку файла на сервер
    abstract abort(imageId: string): Promise<void>
    
    // Возвращает описания всех файлов на сервере
    abstract getAllDataImages(): Promise<TDataImage[]>
    
    // Скачивание файлов
    abstract preview(imageId: string, config: TPreviewConfig): Promise<Blob>
    abstract download(imageId: string, config: TDownloadConfig): Promise<void>
    
    // Изменение состояния на сервере
    abstract commit(
        config: {
            // Commit конфиг содержит коллекцию из id файлов, которые необходимо удалить
            toDelete?: string[],
            // и коллекцию { локальный id (присвоенный хуком) -> файл }, которую необходимо загрузить
            toUpload?: Record<string, Blob>
        },
        // Событие для отслеживания состояния загрузки
        onProgress?: (event: ProgressEvent) => void
    // Обязано вернуть коллекцию { локальный id (присвоенный хуком) -> удаленный id (присвоенный сервером) }
    ): Promise<Record<string, string>>
}

Использование хука

Определив все обобщения, подготовив вспомогательные обработчики и реализацию RemoteDataProvider, можно использовать хук.

const {
    images, // Все образы
    errors, // Все ошибки
    resolve, // Удалить ошибку по id
    remove, // Удалить файл по id
    add, // Добавить коллекцию локальных файлов
    dataTransferProgress, // Коллекция { локальный id -> доля отправленных данных }
    isLoading, // true до обработки remoteDataProvider.getAllImages()
    commit // Синхронизирует изменения с сервером
} = useDataTransfer<CustomDataImage, CustomError>(
    remoteDataProvider,
    customDataImageBuilder,
    cacheWorker
)

Конфигурация

Необязательный четвертый параметр useDataTransfer - конфигурация, дополнительно настраивающая поведение хука.

| Параметр | Тип | По умолчанию | Описание | |----------------------------------|----------------------|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | readonly | boolean | false | Устанавливает поведение "только для чтения". | | strictReadonly | boolean | true | Если true, то, при попытке изменения состояния хука, будет вызвано исключение. | | dataTransferBehaviour | DataTransferBehavior | execute-on-commit | Если execute-on-commit, то синхронизация с сервером будет произведена после вызова commit(). Если execute-on-action, то синхронизация с сервером будет происходить после каждого изменения, а вызов commit() приведет к исключению. | | dataTransferAccumulationInterval | number | 500 | Период в миллисекундах, с которым будет обновляться состояние dataTransferProgress при наличии активных загрузок. | | validators | ValidationFunction[] | | Коллекция правил для валидации файлов. | | onErrorResolved | ActionCallback | | Коллбэк для resolve(). | | onImageRemoved | ActionCallback | | Коллбэк для remove(). | | onImageAdded | ImageCallback | | Коллбэк для add(), вызывается отдельно для каждого файла. |

Валидация

Функция валидации имеет следующую сигнатуру:

export type Context = {
    images: HookDrivenDataImage[]
}

// В случае, если ошибок нет, то функция ничего не возвращает
export type ValidationFunction = (file: File, ctx: Context) => TError | void

Функции валидации будут вызваны в порядке объявления, как только валидатор вернет ошибку, проверка остановится. Например, если файл не удовлетворяет функциям валидации А и Б (объявленным в соответствующем порядке), то в errors попадет только ошибка из валидатора А.


Ограничения

Изменение параметров хука, отличных от config, в реальном времени может привести к некорректному поведению.