All files / lib/storage index.ts

41.66% Statements 20/48
41.17% Branches 14/34
53.33% Functions 8/15
48.57% Lines 17/35

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129                                    19x     2x 2x                     1x 1x             2x 2x     1x 1x                                 2x   4x               2x   2x   2x                         1x   1x     1x                                                                  
import type {
	BinaryStorageBackend,
	BinaryStorageContent,
	BinaryStorageLocator,
	BinaryStorageName,
} from './types.js';
 
import { Capacitor } from '@capacitor/core';
import { dichotomid } from 'dichotomid';
 
import { splitFilenameOnExtension } from '$lib/utils.js';
 
import { CapacitorFilesystemBackend } from './capacitor.js';
import { OPFSBackend } from './opfs.js';
import { locatorToPath } from './utils.js';
 
let currentBackend: undefined | BinaryStorageBackend<BinaryStorageName>;
 
export const binaryStorage: BinaryStorage = {
	name: currentBackend?.name ?? 'uninitialized',
	async exists(...args) {
		if (!currentBackend) await initializeBinaryStorage();
		return currentBackend!.exists(...args);
	},
	async delete(...args) {
		if (!currentBackend) await initializeBinaryStorage();
		return currentBackend!.delete(...args);
	},
	async read(...args) {
		if (!currentBackend) await initializeBinaryStorage();
		return currentBackend!.read(...args);
	},
	async bytes(...args) {
		Iif (!currentBackend) await initializeBinaryStorage();
		return currentBackend!.bytes(...args);
	},
	async text(...args) {
		if (!currentBackend) await initializeBinaryStorage();
		return currentBackend!.text(...args);
	},
	async write(...args) {
		Iif (!currentBackend) await initializeBinaryStorage();
		return currentBackend!.write(...args);
	},
	async size(...args) {
		Iif (!currentBackend) await initializeBinaryStorage();
		return currentBackend!.size(...args);
	},
	async clear(...args) {
		if (!currentBackend) await initializeBinaryStorage();
		return currentBackend!.clear(...args);
	},
	async *list(...args) {
		if (!currentBackend) await initializeBinaryStorage();
		for await (const item of currentBackend!.list(...args)) {
			yield item;
		}
	},
	async count(...args) {
		if (!currentBackend) await initializeBinaryStorage();
		return currentBackend!.count(...args);
	},
	async create(locator, content, { fixedFilenameCounter = 0 } = {}) {
		const [stem, ext] = splitFilenameOnExtension(locator.name);
 
		const name = (i: number) => ({
			...locator,
			name:
				i >= 2 || fixedFilenameCounter
					? `${stem}_${i.toString().padStart(fixedFilenameCounter, '0')}.${ext}`
					: `${stem}.${ext}`,
		});
 
		const chosen = name(await dichotomid(async (i) => !(await this.exists(name(i)))));
 
		await this.write(chosen, content);
 
		return chosen;
	},
	async overwrite(locator, content) {
		const exists = await this.exists(locator);
 
		if (!exists)
			throw new Error(`File ${locatorToPath(locator)} does not exist in ${this.name}`);
 
		return this.write(locator, content);
	},
};
 
async function initializeBinaryStorage() {
	Iif (currentBackend) return;
 
	Iif (Capacitor.isNativePlatform()) {
		currentBackend = CapacitorFilesystemBackend();
	} else {
		currentBackend = await OPFSBackend();
	}
}
 
interface BinaryStorage<
	Name extends BinaryStorageName = BinaryStorageName,
> extends BinaryStorageBackend<Name> {
	/**
	 * If the file exists, add a _n at the end (with n chosen so that it doesn't exist)
	 * before writing to avoid duplicates.
	 * @returns the actual name used to write the file (last segment of the locator)
	 */
	create(
		locator: BinaryStorageLocator,
		content: BinaryStorageContent,
		options?: {
			/**
			 * Always add a trailing _N before the extension part of the filename,
			 * even if there's no need for it duplicates avoidance.
			 * Ensures that N has at least the provided number of digits.
			 *
			 * This is useful to ensure a consistent file naming scheme (in PendingStorage, for example)
			 *
			 * Defaults to 0 (disabled).
			 */
			fixedFilenameCounter?: number;
		}
	): Promise<BinaryStorageLocator>;
	/**
	 * Only write if the file already exists. Otherwise, error out.
	 */
	overwrite(locator: BinaryStorageLocator, content: BinaryStorageContent): Promise<void>;
}