Интерфейс кастомного драйвера

localForage предоставляет единый асинхронный API поверх различных механизмов хранения данных в браузере, таких как IndexedDB, WebSQL и localStorage. Одной из ключевых архитектурных особенностей библиотеки является возможность замены стандартных драйверов на пользовательские реализации. Это открывает путь к интеграции альтернативных хранилищ, специфических бэкендов или экспериментальных механизмов хранения, сохраняя при этом совместимость с единым интерфейсом.

В основе системы хранения localForage лежит абстракция драйвера. Драйвер — это объект, реализующий минимальный набор методов, необходимых для работы с ключ-значение хранилищем. Все операции библиотеки делегируются текущему активному драйверу, выбранному во время инициализации или динамически в процессе выполнения.

Каждый драйвер обязан соответствовать контракту, который включает базовые операции:

  • инициализация хранилища;
  • получение значения по ключу;
  • запись значения;
  • удаление значения;
  • очистка хранилища;
  • перечисление ключей и их количества.

Эти методы формируют универсальный слой, через который localForage взаимодействует с любым типом хранилища.

Контракт пользовательского драйвера

Кастомный драйвер в localForage представляет собой объект с обязательными методами. Его структура должна соответствовать внутреннему интерфейсу библиотеки, который можно условно представить следующим образом:

  • _driver — уникальный идентификатор драйвера;
  • _initStorage(options) — инициализация экземпляра хранилища;
  • getItem(key, callback) — получение значения;
  • setItem(key, value, callback) — сохранение значения;
  • removeItem(key, callback) — удаление значения;
  • clear(callback) — очистка всех записей;
  • length(callback) — количество элементов;
  • key(n, callback) — получение ключа по индексу;
  • iterate(iterator, callback) — итерация по всем элементам.

Каждый метод должен поддерживать асинхронную модель выполнения. В большинстве реализаций используется callback-стиль, однако localForage оборачивает его в Promise-совместимый интерфейс.

Инициализация пользовательского драйвера

Регистрация кастомного драйвера осуществляется через метод defineDriver. После регистрации драйвер становится доступным для выбора так же, как и встроенные реализации.

Процесс можно представить следующим образом:

  • определение объекта драйвера;
  • регистрация через систему localForage;
  • активация драйвера через setDriver.

При этом библиотека проверяет наличие всех обязательных методов и корректность идентификатора _driver.

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

Структура минимального драйвера

Базовая реализация кастомного драйвера может опираться на обычный объект JavaScript в памяти:

  • хранение данных в Map или объекте;
  • синхронное выполнение операций с оборачиванием в Promise;
  • отсутствие персистентности между сессиями.

Такой драйвер часто используется для тестирования или как fallback в средах без поддержки встроенных хранилищ.

Пример логики хранения:

  • внутренний контейнер Map;
  • ключи и значения сохраняются напрямую;
  • операции выполняются асинхронно через Promise.resolve.

Несмотря на простоту, такой драйвер полностью совместим с API localForage.

Реализация методов драйвера

Инициализация хранилища

Метод _initStorage вызывается при активации драйвера. Он получает конфигурацию экземпляра и должен подготовить внутренние структуры хранения. В случае in-memory реализации создаётся новый контейнер данных, изолированный от других экземпляров.

Получение данных

Метод getItem(key) возвращает значение, связанное с ключом. Если ключ отсутствует, возвращается null. Важно соблюдать единообразие поведения с встроенными драйверами, где отсутствие данных не считается ошибкой.

Сохранение данных

Метод setItem(key, value) записывает значение в хранилище. Внутри драйвера может происходить сериализация, если хранилище требует строкового формата. localForage обычно передаёт уже сериализуемые данные, но ответственность за совместимость остаётся за драйвером.

Удаление данных

Метод removeItem(key) удаляет запись из хранилища. После удаления дальнейшие обращения к ключу должны возвращать null.

Очистка хранилища

Метод clear() полностью очищает внутреннее состояние драйвера. В случае Map-реализации выполняется вызов clear() у контейнера.

Работа с ключами

Метод key(n) возвращает ключ по индексу. Это предполагает наличие упорядоченного списка ключей, который формируется из внутреннего состояния хранилища. Порядок может зависеть от реализации, но должен быть стабильным в рамках одного экземпляра.

Итерация

Метод iterate(iterator) позволяет последовательно проходить по всем элементам. Итератор получает значение, ключ и индекс. Возвращаемое значение может прервать итерацию, если возвращается ненулевое значение.

Регистрация драйвера в localForage

После реализации объекта драйвера он регистрируется через механизм расширения. Регистрация добавляет драйвер в глобальный список доступных реализаций. Далее он становится доступен через стандартный API выбора драйверов.

После регистрации возможны следующие сценарии:

  • использование драйвера как основного;
  • комбинирование с fallback-драйверами;
  • динамическое переключение между реализациями.

Важно учитывать, что порядок регистрации влияет на приоритет выбора при использовании setDriver с массивом драйверов.

Совместимость с Promise-обёрткой

localForage автоматически оборачивает callback-интерфейс драйвера в Promise API. Это означает, что даже если драйвер реализован в классическом стиле Node.js callback (err, result), библиотека преобразует его в цепочку промисов.

Для кастомных драйверов это поведение критично, поскольку позволяет не дублировать логику под разные стили асинхронности.

Однако драйвер должен строго соблюдать сигнатуры методов, иначе обёртка не сможет корректно обработать результат.

Ограничения пользовательских драйверов

Несмотря на гибкость архитектуры, кастомные драйверы имеют ряд ограничений:

  • отсутствие прямого доступа к внутреннему кэшу localForage;
  • необходимость соблюдения строгого API;
  • невозможность модификации поведения сериализации без обходных решений;
  • зависимость от корректной асинхронной модели.

Также следует учитывать, что драйверы не должны нарушать изоляцию экземпляров. Каждый вызов _initStorage должен создавать независимое пространство данных.

Пример логической модели драйвера

Условная модель кастомного драйвера включает следующие компоненты:

  • внутреннее хранилище данных;
  • таблицу ключей;
  • асинхронный слой выполнения;
  • интерфейс соответствия контракту localForage.

Эта модель обеспечивает совместимость с системой управления драйверами, позволяя библиотеке воспринимать кастомную реализацию как нативную.

Поведение при переключении драйверов

При смене драйвера через setDriver происходит повторная инициализация хранилища. Данные не переносятся автоматически между драйверами. Это означает, что переключение между реализациями может привести к смене контекста данных.

Некоторые драйверы могут реализовывать механизм миграции, однако это выходит за рамки базового контракта localForage и требует отдельной логики внутри пользовательской реализации.

Взаимодействие с экземплярами localForage

Каждый экземпляр localForage может использовать собственный набор настроек и драйверов. Это позволяет создавать изолированные хранилища с разными стратегиями хранения данных.

Кастомный драйвер в этом контексте становится инструментом для:

  • разделения логических доменов данных;
  • внедрения специфических политик хранения;
  • интеграции с нестандартными API хранения.

Такая архитектура позволяет использовать localForage не только как обёртку над браузерным storage, но и как универсальный слой абстракции данных.