import { encode, decode } from '@borderless/base64'; import { registerPlugin } from '@capacitor/core'; import { AbstractLevel } from 'abstract-level'; import type { AbstractDatabaseOptions } from 'abstract-level'; import type { MobileLevelPluginT } from './definitions'; const MobileLevelPlugin = registerPlugin<MobileLevelPluginT>('MobileLevel', {}); export * from './definitions'; interface DatabaseOptions<K, V> extends AbstractDatabaseOptions<K, V> { /** * Prefix for the `IDBDatabase` name. Can be set to an empty string. * * @defaultValue `'level-js-'` */ prefix?: string; /** * The version to open the `IDBDatabase` with. * * @defaultValue `1` */ version?: number | string; /** * An {@link AbstractLevel} option that has no effect on {@link BrowserLevel}. */ createIfMissing?: boolean; /** * An {@link AbstractLevel} option that has no effect on {@link BrowserLevel}. */ errorIfExists?: boolean; } const textEncoder = new TextEncoder(); class DummyIterator { public readonly results: [Uint8Array, Uint8Array][]; constructor(results: [Uint8Array, Uint8Array][]) { this.results = results; } all(): [Uint8Array, Uint8Array][] { return this.results; } } export class MobileLevel extends AbstractLevel< Uint8Array, Uint8Array, Uint8Array > { public readonly name: string; constructor( name: string, options: AbstractDatabaseOptions<Uint8Array, Uint8Array> | undefined, ) { super({}, options); this.name = name; if (typeof location !== 'string') { throw new Error('constructor requires a location string argument'); } } async _open(options: unknown): Promise<void> { if (options) throw new Error('options not supported, feel free to submit a PR'); await MobileLevelPlugin.open({ path: this.name }); } async _get(key: Uint8Array): Promise<Uint8Array | null> { const encodedKey = encode(key); const { value } = await MobileLevelPlugin.get({ key: encodedKey }); if (value === null) return null; return decode(value); } async _put(key: Uint8Array, value: Uint8Array): Promise<void> { const encodedKey = encode(key); const encodedValue = encode(value); await MobileLevelPlugin.put({ key: encodedKey, value: encodedValue }); } async _delete(key: Uint8Array): Promise<void> { const encodedKey = encode(key); await MobileLevelPlugin.delete({ key: encodedKey }); } async _close(): Promise<void> { await MobileLevelPlugin.close(); } async _batch( operations: { type: 'put' | 'delete'; key: Uint8Array; value?: Uint8Array; }[], ): Promise<void> { const encodedOperations = operations.map(({ type, key, value }) => ({ type, key: encode(key), value: value ? encode(value) : undefined, })); await MobileLevelPlugin.batch({ operations: encodedOperations }); } async _iterator(options: { gt?: string; gte?: string; lt?: string; lte?: string; limit?: number; reverse?: boolean; }): Promise<{ results: [key: Uint8Array, value: Uint8Array][] }> { const { results } = await MobileLevelPlugin.iterator(options); return { results: results.map(([key, value]) => [decode(key), decode(value)]), }; } async _clear(): Promise<void> { await MobileLevelPlugin.clear(); } }