@signaldb/opfs 2.0.0-beta.21 → 2.0.0-beta.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +14 -15
- package/dist/index.mjs +30 -23
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,29 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Creates a
|
|
3
|
-
* Origin Private File System (OPFS).
|
|
4
|
-
*
|
|
5
|
-
* and deserialization.
|
|
2
|
+
* Creates a storage adapter for managing a SignalDB collection using the
|
|
3
|
+
* Origin Private File System (OPFS). The collection is stored as a folder: one file
|
|
4
|
+
* per document under `items/`, one file per indexed value under `index/`.
|
|
6
5
|
* @template T - The type of the items in the collection.
|
|
7
6
|
* @template I - The type of the unique identifier for the items.
|
|
8
|
-
* @param folderName - The
|
|
7
|
+
* @param folderName - The folder in OPFS this collection is stored in.
|
|
9
8
|
* @param options - Optional configuration for serialization and deserialization.
|
|
10
9
|
* @param options.serialize - A function to serialize items to a string (default: `JSON.stringify`).
|
|
11
10
|
* @param options.deserialize - A function to deserialize a string into items (default: `JSON.parse`).
|
|
12
|
-
* @returns A SignalDB
|
|
11
|
+
* @returns A SignalDB storage adapter for managing data in OPFS.
|
|
13
12
|
* @example
|
|
14
|
-
* import
|
|
15
|
-
* import
|
|
13
|
+
* import { Collection, DefaultDataAdapter } from '@signaldb/core';
|
|
14
|
+
* import createOPFSAdapter from '@signaldb/opfs';
|
|
16
15
|
*
|
|
17
|
-
* const
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* const dataAdapter = new DefaultDataAdapter({
|
|
17
|
+
* storage: name => createOPFSAdapter(name, {
|
|
18
|
+
* serialize: (items) => JSON.stringify(items, null, 2), // Pretty-print JSON
|
|
19
|
+
* deserialize: (itemsString) => JSON.parse(itemsString), // Default JSON parse
|
|
20
|
+
* }),
|
|
20
21
|
* });
|
|
21
22
|
*
|
|
22
|
-
* const collection = new Collection(
|
|
23
|
-
* persistence: adapter,
|
|
24
|
-
* });
|
|
23
|
+
* const collection = new Collection('posts', dataAdapter);
|
|
25
24
|
*
|
|
26
|
-
* // Perform operations on the collection, and changes will be reflected in
|
|
25
|
+
* // Perform operations on the collection, and changes will be reflected in OPFS.
|
|
27
26
|
*/
|
|
28
27
|
export default function createOPFSAdapter<T extends {
|
|
29
28
|
id: I;
|
package/dist/index.mjs
CHANGED
|
@@ -22,7 +22,8 @@ function i(i, a) {
|
|
|
22
22
|
fileNameForIndexKey: (e) => Promise.resolve(n(e)),
|
|
23
23
|
joinPath: (...e) => Promise.resolve(e.join("/")),
|
|
24
24
|
ensureDir: async (e) => {
|
|
25
|
-
|
|
25
|
+
let t = await navigator.storage.getDirectory();
|
|
26
|
+
await c(t, e, !0);
|
|
26
27
|
},
|
|
27
28
|
fileExists: async (e) => {
|
|
28
29
|
let t = await navigator.storage.getDirectory();
|
|
@@ -38,46 +39,52 @@ function i(i, a) {
|
|
|
38
39
|
return !0;
|
|
39
40
|
}
|
|
40
41
|
},
|
|
41
|
-
readObject: async (e) => r(e, async () =>
|
|
42
|
+
readObject: async (e) => r(e, async () => {
|
|
43
|
+
let t = await navigator.storage.getDirectory(), n = await (await (await l(t, e, !1)).getFile()).text();
|
|
44
|
+
return s(n);
|
|
45
|
+
}),
|
|
42
46
|
writeObject: async (e, t) => r(e, async () => {
|
|
43
|
-
let n = await
|
|
44
|
-
if (typeof
|
|
45
|
-
let
|
|
47
|
+
let n = await navigator.storage.getDirectory(), r = await l(n, e, !0), i = o(t);
|
|
48
|
+
if (typeof i != "string") throw TypeError("serialize() must return a string");
|
|
49
|
+
let a = await r.createWritable();
|
|
46
50
|
try {
|
|
47
|
-
let e = new TextEncoder().encode(
|
|
48
|
-
await
|
|
51
|
+
let e = new TextEncoder().encode(i);
|
|
52
|
+
await a.write({
|
|
49
53
|
type: "write",
|
|
50
54
|
position: 0,
|
|
51
55
|
data: e
|
|
52
|
-
}), await
|
|
56
|
+
}), await a.truncate(e.byteLength), await a.close();
|
|
53
57
|
} catch (e) {
|
|
54
|
-
throw await
|
|
58
|
+
throw await a.abort(), e;
|
|
55
59
|
}
|
|
56
60
|
}),
|
|
57
|
-
readIndexObject: async (e) => r(e, async () =>
|
|
61
|
+
readIndexObject: async (e) => r(e, async () => {
|
|
62
|
+
let t = await navigator.storage.getDirectory(), n = await (await (await l(t, e, !1)).getFile()).text();
|
|
63
|
+
return s(n);
|
|
64
|
+
}),
|
|
58
65
|
writeIndexObject: async (e, t) => r(e, async () => {
|
|
59
|
-
let n = await
|
|
60
|
-
if (typeof
|
|
61
|
-
let
|
|
66
|
+
let n = await navigator.storage.getDirectory(), r = await l(n, e, !0), i = o(t);
|
|
67
|
+
if (typeof i != "string") throw TypeError("serialize() must return a string");
|
|
68
|
+
let a = await r.createWritable();
|
|
62
69
|
try {
|
|
63
|
-
let e = new TextEncoder().encode(
|
|
64
|
-
await
|
|
70
|
+
let e = new TextEncoder().encode(i);
|
|
71
|
+
await a.write({
|
|
65
72
|
type: "write",
|
|
66
73
|
position: 0,
|
|
67
74
|
data: e
|
|
68
|
-
}), await
|
|
75
|
+
}), await a.truncate(e.byteLength), await a.close();
|
|
69
76
|
} catch (e) {
|
|
70
|
-
throw await
|
|
77
|
+
throw await a.abort(), e;
|
|
71
78
|
}
|
|
72
79
|
}),
|
|
73
80
|
listFilesRecursive: async (e) => {
|
|
74
|
-
let t = await
|
|
75
|
-
for await (let
|
|
76
|
-
else if (
|
|
77
|
-
let
|
|
78
|
-
|
|
81
|
+
let t = await navigator.storage.getDirectory(), n = await c(t, e, !1), r = [];
|
|
82
|
+
for await (let t of n.values()) if (t.kind === "file") r.push(t.name);
|
|
83
|
+
else if (t.kind === "directory") {
|
|
84
|
+
let n = await u.listFilesRecursive(`${e}/${t.name}`);
|
|
85
|
+
r.push(...n.map((e) => `${t.name}/${e}`));
|
|
79
86
|
}
|
|
80
|
-
return
|
|
87
|
+
return r;
|
|
81
88
|
},
|
|
82
89
|
removeEntry: async (e, t) => {
|
|
83
90
|
let n = await navigator.storage.getDirectory(), r = e.split("/").filter(Boolean), i = r.pop();
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Driver } from '@signaldb/generic-fs'\nimport createGenericFSAdapter from '@signaldb/generic-fs'\nimport { serializeValue } from '@signaldb/core'\n\n/**\n * Convert an arbitrary filename into a OPFS safe filename.\n * @param input - The input filename to sanitize.\n * @returns A safe filename.\n */\nfunction toSafeFilename(input: string): string {\n const replacement = '_'\n\n let name = input.normalize('NFC')\n\n const escapeRegex = (s: string) => s.replaceAll(/[-/\\\\^$*+?.()|[\\]{}]/g, String.raw`\\$&`)\n name = name.replaceAll('/', replacement)\n name = name.replaceAll(new RegExp(`${escapeRegex(replacement)}{2,}`, 'g'), replacement)\n\n if (!name) name = 'unnamed'\n\n return name\n}\n\n/**\n * Acquire a lock for a specific file path to ensure exclusive access during read/write operations.\n * @param path - The file path to lock.\n * @param fn - The asynchronous function to execute while holding the lock.\n * @returns The result of the function `fn`.\n */\nasync function withPathLock<T>(path: string, fn: () => Promise<T>): Promise<T> {\n const lockName = `opfs:${path}`\n // Use exclusive mode so reads cannot interleave with writes\n return navigator.locks.request(lockName, { mode: 'exclusive' }, fn)\n}\n\n/**\n * Creates a persistence adapter for managing a SignalDB collection using the\n * Origin Private File System (OPFS). This adapter allows data to be stored and managed\n * directly in the browser's file system with support for customizable serialization\n * and deserialization.\n * @template T - The type of the items in the collection.\n * @template I - The type of the unique identifier for the items.\n * @param folderName - The name of the file in OPFS where data will be stored.\n * @param options - Optional configuration for serialization and deserialization.\n * @param options.serialize - A function to serialize items to a string (default: `JSON.stringify`).\n * @param options.deserialize - A function to deserialize a string into items (default: `JSON.parse`).\n * @returns A SignalDB persistence adapter for managing data in OPFS.\n * @example\n * import createOPFSAdapter from './createOPFSAdapter';\n * import { Collection } from '@signaldb/core';\n *\n * const adapter = createOPFSAdapter('myCollection.json', {\n * serialize: (items) => JSON.stringify(items, null, 2), // Pretty-print JSON\n * deserialize: (itemsString) => JSON.parse(itemsString), // Default JSON parse\n * });\n *\n * const collection = new Collection({\n * persistence: adapter,\n * });\n *\n * // Perform operations on the collection, and changes will be reflected in the OPFS file.\n */\nexport default function createOPFSAdapter<\n T extends { id: I } & Record<string, any>,\n I,\n>(\n folderName: string,\n options?: {\n serialize?: (data: any) => string,\n deserialize?: (input: string) => any,\n },\n) {\n const {\n serialize = JSON.stringify,\n deserialize = JSON.parse,\n } = options || {}\n\n const ensureDirectoryExists = async (\n rootDirectory: FileSystemDirectoryHandle,\n directoryPath: string,\n createIfMissing: boolean,\n ) => {\n const parts = directoryPath.split('/').filter(Boolean)\n let current = rootDirectory\n for (const part of parts) {\n current = await current.getDirectoryHandle(part, { create: createIfMissing })\n }\n return current\n }\n\n const getFileHandleForPath = async (\n rootDirectory: FileSystemDirectoryHandle,\n fullPath: string,\n createIfMissing: boolean,\n ): Promise<FileSystemFileHandle> => {\n const parts = fullPath.split('/').filter(Boolean)\n const fileName = parts.pop() as string\n const directoryHandle = await ensureDirectoryExists(rootDirectory, parts.join('/'), createIfMissing)\n return directoryHandle.getFileHandle(fileName, { create: createIfMissing })\n }\n\n const driver: Driver<T, I> = {\n fileNameForId: id => Promise.resolve(toSafeFilename(serializeValue(id) as string)),\n fileNameForIndexKey: key => Promise.resolve(toSafeFilename(key)),\n joinPath: (...parts) => Promise.resolve(parts.join('/')),\n ensureDir: async (directoryPath) => {\n const rootDirectory = await navigator.storage.getDirectory()\n await ensureDirectoryExists(rootDirectory, directoryPath, true)\n },\n\n fileExists: async (path) => {\n const rootDirectory = await navigator.storage.getDirectory()\n try {\n await getFileHandleForPath(rootDirectory, path, false)\n return true\n } catch {\n const parts = path.split('/').filter(Boolean)\n let directory = rootDirectory\n for (const part of parts) {\n try {\n directory = await directory.getDirectoryHandle(part)\n } catch {\n return false\n }\n }\n return true\n }\n },\n\n readObject: async path => withPathLock(path, async () => {\n const rootDirectory = await navigator.storage.getDirectory()\n const handle = await getFileHandleForPath(rootDirectory, path, false)\n const file = await handle.getFile()\n const text = await file.text()\n return deserialize(text)\n }),\n\n writeObject: async (path, value) => withPathLock(path, async () => {\n const rootDirectory = await navigator.storage.getDirectory()\n const handle = await getFileHandleForPath(rootDirectory, path, true)\n const text = serialize(value)\n if (typeof text !== 'string') {\n throw new TypeError('serialize() must return a string')\n }\n const writableStream = await handle.createWritable()\n try {\n const encoded = new TextEncoder().encode(text)\n await writableStream.write({ type: 'write', position: 0, data: encoded })\n await writableStream.truncate(encoded.byteLength)\n await writableStream.close()\n } catch (error) {\n await writableStream.abort()\n throw error\n }\n }),\n\n readIndexObject: async path => withPathLock(path, async () => {\n const rootDirectory = await navigator.storage.getDirectory()\n const handle = await getFileHandleForPath(rootDirectory, path, false)\n const file = await handle.getFile()\n const text = await file.text()\n return deserialize(text)\n }),\n\n writeIndexObject: async (path, value) => withPathLock(path, async () => {\n const rootDirectory = await navigator.storage.getDirectory()\n const handle = await getFileHandleForPath(rootDirectory, path, true)\n const text = serialize(value)\n if (typeof text !== 'string') {\n throw new TypeError('serialize() must return a string')\n }\n const writableStream = await handle.createWritable()\n try {\n const encoded = new TextEncoder().encode(text)\n await writableStream.write({ type: 'write', position: 0, data: encoded })\n await writableStream.truncate(encoded.byteLength)\n await writableStream.close()\n } catch (error) {\n await writableStream.abort()\n throw error\n }\n }),\n\n listFilesRecursive: async (directoryPath) => {\n const rootDirectory = await navigator.storage.getDirectory()\n const directoryHandle = await ensureDirectoryExists(rootDirectory, directoryPath, false)\n\n const files: string[] = []\n // @ts-expect-error -- for-await-of on FileSystemDirectoryHandle is not in types yet\n for await (const entry of directoryHandle.values()) {\n if (entry.kind === 'file') {\n files.push(entry.name as string)\n } else if (entry.kind === 'directory') {\n const subFiles = await driver.listFilesRecursive(`${directoryPath}/${entry.name}`)\n files.push(...subFiles.map(f => `${entry.name}/${f}`))\n }\n }\n return files\n },\n\n removeEntry: async (path, removeOptions) => {\n const rootDirectory = await navigator.storage.getDirectory()\n const pathParts = path.split('/').filter(Boolean)\n const name = pathParts.pop()\n if (!name) throw new Error('Invalid path')\n const parent = pathParts.length > 0\n ? await ensureDirectoryExists(rootDirectory, pathParts.join('/'), false)\n : rootDirectory\n await parent.removeEntry(name, { recursive: Boolean(removeOptions?.recursive) })\n },\n }\n return createGenericFSAdapter<T, I>(driver, folderName)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Driver } from '@signaldb/generic-fs'\nimport createGenericFSAdapter from '@signaldb/generic-fs'\nimport { serializeValue } from '@signaldb/core'\n\n/**\n * Convert an arbitrary filename into a OPFS safe filename.\n * @param input - The input filename to sanitize.\n * @returns A safe filename.\n */\nfunction toSafeFilename(input: string): string {\n const replacement = '_'\n\n let name = input.normalize('NFC')\n\n const escapeRegex = (s: string) => s.replaceAll(/[-/\\\\^$*+?.()|[\\]{}]/g, String.raw`\\$&`)\n name = name.replaceAll('/', replacement)\n name = name.replaceAll(new RegExp(`${escapeRegex(replacement)}{2,}`, 'g'), replacement)\n\n if (!name) name = 'unnamed'\n\n return name\n}\n\n/**\n * Acquire a lock for a specific file path to ensure exclusive access during read/write operations.\n * @param path - The file path to lock.\n * @param fn - The asynchronous function to execute while holding the lock.\n * @returns The result of the function `fn`.\n */\nasync function withPathLock<T>(path: string, fn: () => Promise<T>): Promise<T> {\n const lockName = `opfs:${path}`\n // Use exclusive mode so reads cannot interleave with writes\n return navigator.locks.request(lockName, { mode: 'exclusive' }, fn)\n}\n\n/**\n * Creates a storage adapter for managing a SignalDB collection using the\n * Origin Private File System (OPFS). The collection is stored as a folder: one file\n * per document under `items/`, one file per indexed value under `index/`.\n * @template T - The type of the items in the collection.\n * @template I - The type of the unique identifier for the items.\n * @param folderName - The folder in OPFS this collection is stored in.\n * @param options - Optional configuration for serialization and deserialization.\n * @param options.serialize - A function to serialize items to a string (default: `JSON.stringify`).\n * @param options.deserialize - A function to deserialize a string into items (default: `JSON.parse`).\n * @returns A SignalDB storage adapter for managing data in OPFS.\n * @example\n * import { Collection, DefaultDataAdapter } from '@signaldb/core';\n * import createOPFSAdapter from '@signaldb/opfs';\n *\n * const dataAdapter = new DefaultDataAdapter({\n * storage: name => createOPFSAdapter(name, {\n * serialize: (items) => JSON.stringify(items, null, 2), // Pretty-print JSON\n * deserialize: (itemsString) => JSON.parse(itemsString), // Default JSON parse\n * }),\n * });\n *\n * const collection = new Collection('posts', dataAdapter);\n *\n * // Perform operations on the collection, and changes will be reflected in OPFS.\n */\nexport default function createOPFSAdapter<\n T extends { id: I } & Record<string, any>,\n I,\n>(\n folderName: string,\n options?: {\n serialize?: (data: any) => string,\n deserialize?: (input: string) => any,\n },\n) {\n const {\n serialize = JSON.stringify,\n deserialize = JSON.parse,\n } = options || {}\n\n const ensureDirectoryExists = async (\n rootDirectory: FileSystemDirectoryHandle,\n directoryPath: string,\n createIfMissing: boolean,\n ) => {\n const parts = directoryPath.split('/').filter(Boolean)\n let current = rootDirectory\n for (const part of parts) {\n current = await current.getDirectoryHandle(part, { create: createIfMissing })\n }\n return current\n }\n\n const getFileHandleForPath = async (\n rootDirectory: FileSystemDirectoryHandle,\n fullPath: string,\n createIfMissing: boolean,\n ): Promise<FileSystemFileHandle> => {\n const parts = fullPath.split('/').filter(Boolean)\n const fileName = parts.pop() as string\n const directoryHandle = await ensureDirectoryExists(rootDirectory, parts.join('/'), createIfMissing)\n return directoryHandle.getFileHandle(fileName, { create: createIfMissing })\n }\n\n const driver: Driver<T, I> = {\n fileNameForId: id => Promise.resolve(toSafeFilename(serializeValue(id) as string)),\n fileNameForIndexKey: key => Promise.resolve(toSafeFilename(key)),\n joinPath: (...parts) => Promise.resolve(parts.join('/')),\n ensureDir: async (directoryPath) => {\n const rootDirectory = await navigator.storage.getDirectory()\n await ensureDirectoryExists(rootDirectory, directoryPath, true)\n },\n\n fileExists: async (path) => {\n const rootDirectory = await navigator.storage.getDirectory()\n try {\n await getFileHandleForPath(rootDirectory, path, false)\n return true\n } catch {\n const parts = path.split('/').filter(Boolean)\n let directory = rootDirectory\n for (const part of parts) {\n try {\n directory = await directory.getDirectoryHandle(part)\n } catch {\n return false\n }\n }\n return true\n }\n },\n\n readObject: async path => withPathLock(path, async () => {\n const rootDirectory = await navigator.storage.getDirectory()\n const handle = await getFileHandleForPath(rootDirectory, path, false)\n const file = await handle.getFile()\n const text = await file.text()\n return deserialize(text)\n }),\n\n writeObject: async (path, value) => withPathLock(path, async () => {\n const rootDirectory = await navigator.storage.getDirectory()\n const handle = await getFileHandleForPath(rootDirectory, path, true)\n const text = serialize(value)\n if (typeof text !== 'string') {\n throw new TypeError('serialize() must return a string')\n }\n const writableStream = await handle.createWritable()\n try {\n const encoded = new TextEncoder().encode(text)\n await writableStream.write({ type: 'write', position: 0, data: encoded })\n await writableStream.truncate(encoded.byteLength)\n await writableStream.close()\n } catch (error) {\n await writableStream.abort()\n throw error\n }\n }),\n\n readIndexObject: async path => withPathLock(path, async () => {\n const rootDirectory = await navigator.storage.getDirectory()\n const handle = await getFileHandleForPath(rootDirectory, path, false)\n const file = await handle.getFile()\n const text = await file.text()\n return deserialize(text)\n }),\n\n writeIndexObject: async (path, value) => withPathLock(path, async () => {\n const rootDirectory = await navigator.storage.getDirectory()\n const handle = await getFileHandleForPath(rootDirectory, path, true)\n const text = serialize(value)\n if (typeof text !== 'string') {\n throw new TypeError('serialize() must return a string')\n }\n const writableStream = await handle.createWritable()\n try {\n const encoded = new TextEncoder().encode(text)\n await writableStream.write({ type: 'write', position: 0, data: encoded })\n await writableStream.truncate(encoded.byteLength)\n await writableStream.close()\n } catch (error) {\n await writableStream.abort()\n throw error\n }\n }),\n\n listFilesRecursive: async (directoryPath) => {\n const rootDirectory = await navigator.storage.getDirectory()\n const directoryHandle = await ensureDirectoryExists(rootDirectory, directoryPath, false)\n\n const files: string[] = []\n // @ts-expect-error -- for-await-of on FileSystemDirectoryHandle is not in types yet\n for await (const entry of directoryHandle.values()) {\n if (entry.kind === 'file') {\n files.push(entry.name as string)\n } else if (entry.kind === 'directory') {\n const subFiles = await driver.listFilesRecursive(`${directoryPath}/${entry.name}`)\n files.push(...subFiles.map(f => `${entry.name}/${f}`))\n }\n }\n return files\n },\n\n removeEntry: async (path, removeOptions) => {\n const rootDirectory = await navigator.storage.getDirectory()\n const pathParts = path.split('/').filter(Boolean)\n const name = pathParts.pop()\n if (!name) throw new Error('Invalid path')\n const parent = pathParts.length > 0\n ? await ensureDirectoryExists(rootDirectory, pathParts.join('/'), false)\n : rootDirectory\n await parent.removeEntry(name, { recursive: Boolean(removeOptions?.recursive) })\n },\n }\n return createGenericFSAdapter<T, I>(driver, folderName)\n}\n"],"mappings":"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"}
|
package/dist/index.umd.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(function(e,t){typeof exports==`object`&&typeof module<`u`?module.exports=t(require("@signaldb/generic-fs"),require("@signaldb/core")):typeof define==`function`&&define.amd?define([`@signaldb/generic-fs`,`@signaldb/core`],t):(e=typeof globalThis<`u`?globalThis:e||self,e.SignalDB=t(e._signaldb_generic_fs,e._signaldb_core))})(this,function(e,t){var n=Object.create,r=Object.defineProperty,i=Object.getOwnPropertyDescriptor,a=Object.getOwnPropertyNames,o=Object.getPrototypeOf,s=Object.prototype.hasOwnProperty,c=(e,t,n,o)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var c=a(t),l=0,u=c.length,d;l<u;l++)d=c[l],!s.call(e,d)&&d!==n&&r(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(o=i(t,d))||o.enumerable});return e};e=((e,t,i)=>(i=e==null?{}:n(o(e)),c(t||!e||!e.__esModule?r(i,`default`,{value:e,enumerable:!0}):i,e)))(e);function l(e){let t=e.normalize(`NFC`);return t=t.replaceAll(`/`,`_`),t=t.replaceAll(RegExp(`${(e=>e.replaceAll(/[-/\\^$*+?.()|[\]{}]/g,String.raw`\$&`))(`_`)}{2,}`,`g`),`_`),t||=`unnamed`,t}async function u(e,t){let n=`opfs:${e}`;return navigator.locks.request(n,{mode:`exclusive`},t)}function d(n,r){let{serialize:i=JSON.stringify,deserialize:a=JSON.parse}=r||{},o=async(e,t,n)=>{let r=t.split(`/`).filter(Boolean),i=e;for(let e of r)i=await i.getDirectoryHandle(e,{create:n});return i},s=async(e,t,n)=>{let r=t.split(`/`).filter(Boolean),i=r.pop();return(await o(e,r.join(`/`),n)).getFileHandle(i,{create:n})},c={fileNameForId:e=>Promise.resolve(l((0,t.serializeValue)(e))),fileNameForIndexKey:e=>Promise.resolve(l(e)),joinPath:(...e)=>Promise.resolve(e.join(`/`)),ensureDir:async e=>{
|
|
1
|
+
(function(e,t){typeof exports==`object`&&typeof module<`u`?module.exports=t(require("@signaldb/generic-fs"),require("@signaldb/core")):typeof define==`function`&&define.amd?define([`@signaldb/generic-fs`,`@signaldb/core`],t):(e=typeof globalThis<`u`?globalThis:e||self,e.SignalDB=t(e._signaldb_generic_fs,e._signaldb_core))})(this,function(e,t){var n=Object.create,r=Object.defineProperty,i=Object.getOwnPropertyDescriptor,a=Object.getOwnPropertyNames,o=Object.getPrototypeOf,s=Object.prototype.hasOwnProperty,c=(e,t,n,o)=>{if(t&&typeof t==`object`||typeof t==`function`)for(var c=a(t),l=0,u=c.length,d;l<u;l++)d=c[l],!s.call(e,d)&&d!==n&&r(e,d,{get:(e=>t[e]).bind(null,d),enumerable:!(o=i(t,d))||o.enumerable});return e};e=((e,t,i)=>(i=e==null?{}:n(o(e)),c(t||!e||!e.__esModule||!s.call(e,`default`)?r(i,`default`,{value:e,enumerable:!0}):i,e)))(e);function l(e){let t=e.normalize(`NFC`);return t=t.replaceAll(`/`,`_`),t=t.replaceAll(RegExp(`${(e=>e.replaceAll(/[-/\\^$*+?.()|[\]{}]/g,String.raw`\$&`))(`_`)}{2,}`,`g`),`_`),t||=`unnamed`,t}async function u(e,t){let n=`opfs:${e}`;return navigator.locks.request(n,{mode:`exclusive`},t)}function d(n,r){let{serialize:i=JSON.stringify,deserialize:a=JSON.parse}=r||{},o=async(e,t,n)=>{let r=t.split(`/`).filter(Boolean),i=e;for(let e of r)i=await i.getDirectoryHandle(e,{create:n});return i},s=async(e,t,n)=>{let r=t.split(`/`).filter(Boolean),i=r.pop();return(await o(e,r.join(`/`),n)).getFileHandle(i,{create:n})},c={fileNameForId:e=>Promise.resolve(l((0,t.serializeValue)(e))),fileNameForIndexKey:e=>Promise.resolve(l(e)),joinPath:(...e)=>Promise.resolve(e.join(`/`)),ensureDir:async e=>{let t=await navigator.storage.getDirectory();await o(t,e,!0)},fileExists:async e=>{let t=await navigator.storage.getDirectory();try{return await s(t,e,!1),!0}catch{let n=e.split(`/`).filter(Boolean),r=t;for(let e of n)try{r=await r.getDirectoryHandle(e)}catch{return!1}return!0}},readObject:async e=>u(e,async()=>{let t=await navigator.storage.getDirectory(),n=await(await(await s(t,e,!1)).getFile()).text();return a(n)}),writeObject:async(e,t)=>u(e,async()=>{let n=await navigator.storage.getDirectory(),r=await s(n,e,!0),a=i(t);if(typeof a!=`string`)throw TypeError(`serialize() must return a string`);let o=await r.createWritable();try{let e=new TextEncoder().encode(a);await o.write({type:`write`,position:0,data:e}),await o.truncate(e.byteLength),await o.close()}catch(e){throw await o.abort(),e}}),readIndexObject:async e=>u(e,async()=>{let t=await navigator.storage.getDirectory(),n=await(await(await s(t,e,!1)).getFile()).text();return a(n)}),writeIndexObject:async(e,t)=>u(e,async()=>{let n=await navigator.storage.getDirectory(),r=await s(n,e,!0),a=i(t);if(typeof a!=`string`)throw TypeError(`serialize() must return a string`);let o=await r.createWritable();try{let e=new TextEncoder().encode(a);await o.write({type:`write`,position:0,data:e}),await o.truncate(e.byteLength),await o.close()}catch(e){throw await o.abort(),e}}),listFilesRecursive:async e=>{let t=await navigator.storage.getDirectory(),n=await o(t,e,!1),r=[];for await(let t of n.values())if(t.kind===`file`)r.push(t.name);else if(t.kind===`directory`){let n=await c.listFilesRecursive(`${e}/${t.name}`);r.push(...n.map(e=>`${t.name}/${e}`))}return r},removeEntry:async(e,t)=>{let n=await navigator.storage.getDirectory(),r=e.split(`/`).filter(Boolean),i=r.pop();if(!i)throw Error(`Invalid path`);await(r.length>0?await o(n,r.join(`/`),!1):n).removeEntry(i,{recursive:!!t?.recursive})}};return(0,e.default)(c,n)}return d});
|
|
2
2
|
//# sourceMappingURL=index.umd.js.map
|
package/dist/index.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.umd.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Driver } from '@signaldb/generic-fs'\nimport createGenericFSAdapter from '@signaldb/generic-fs'\nimport { serializeValue } from '@signaldb/core'\n\n/**\n * Convert an arbitrary filename into a OPFS safe filename.\n * @param input - The input filename to sanitize.\n * @returns A safe filename.\n */\nfunction toSafeFilename(input: string): string {\n const replacement = '_'\n\n let name = input.normalize('NFC')\n\n const escapeRegex = (s: string) => s.replaceAll(/[-/\\\\^$*+?.()|[\\]{}]/g, String.raw`\\$&`)\n name = name.replaceAll('/', replacement)\n name = name.replaceAll(new RegExp(`${escapeRegex(replacement)}{2,}`, 'g'), replacement)\n\n if (!name) name = 'unnamed'\n\n return name\n}\n\n/**\n * Acquire a lock for a specific file path to ensure exclusive access during read/write operations.\n * @param path - The file path to lock.\n * @param fn - The asynchronous function to execute while holding the lock.\n * @returns The result of the function `fn`.\n */\nasync function withPathLock<T>(path: string, fn: () => Promise<T>): Promise<T> {\n const lockName = `opfs:${path}`\n // Use exclusive mode so reads cannot interleave with writes\n return navigator.locks.request(lockName, { mode: 'exclusive' }, fn)\n}\n\n/**\n * Creates a persistence adapter for managing a SignalDB collection using the\n * Origin Private File System (OPFS). This adapter allows data to be stored and managed\n * directly in the browser's file system with support for customizable serialization\n * and deserialization.\n * @template T - The type of the items in the collection.\n * @template I - The type of the unique identifier for the items.\n * @param folderName - The name of the file in OPFS where data will be stored.\n * @param options - Optional configuration for serialization and deserialization.\n * @param options.serialize - A function to serialize items to a string (default: `JSON.stringify`).\n * @param options.deserialize - A function to deserialize a string into items (default: `JSON.parse`).\n * @returns A SignalDB persistence adapter for managing data in OPFS.\n * @example\n * import createOPFSAdapter from './createOPFSAdapter';\n * import { Collection } from '@signaldb/core';\n *\n * const adapter = createOPFSAdapter('myCollection.json', {\n * serialize: (items) => JSON.stringify(items, null, 2), // Pretty-print JSON\n * deserialize: (itemsString) => JSON.parse(itemsString), // Default JSON parse\n * });\n *\n * const collection = new Collection({\n * persistence: adapter,\n * });\n *\n * // Perform operations on the collection, and changes will be reflected in the OPFS file.\n */\nexport default function createOPFSAdapter<\n T extends { id: I } & Record<string, any>,\n I,\n>(\n folderName: string,\n options?: {\n serialize?: (data: any) => string,\n deserialize?: (input: string) => any,\n },\n) {\n const {\n serialize = JSON.stringify,\n deserialize = JSON.parse,\n } = options || {}\n\n const ensureDirectoryExists = async (\n rootDirectory: FileSystemDirectoryHandle,\n directoryPath: string,\n createIfMissing: boolean,\n ) => {\n const parts = directoryPath.split('/').filter(Boolean)\n let current = rootDirectory\n for (const part of parts) {\n current = await current.getDirectoryHandle(part, { create: createIfMissing })\n }\n return current\n }\n\n const getFileHandleForPath = async (\n rootDirectory: FileSystemDirectoryHandle,\n fullPath: string,\n createIfMissing: boolean,\n ): Promise<FileSystemFileHandle> => {\n const parts = fullPath.split('/').filter(Boolean)\n const fileName = parts.pop() as string\n const directoryHandle = await ensureDirectoryExists(rootDirectory, parts.join('/'), createIfMissing)\n return directoryHandle.getFileHandle(fileName, { create: createIfMissing })\n }\n\n const driver: Driver<T, I> = {\n fileNameForId: id => Promise.resolve(toSafeFilename(serializeValue(id) as string)),\n fileNameForIndexKey: key => Promise.resolve(toSafeFilename(key)),\n joinPath: (...parts) => Promise.resolve(parts.join('/')),\n ensureDir: async (directoryPath) => {\n const rootDirectory = await navigator.storage.getDirectory()\n await ensureDirectoryExists(rootDirectory, directoryPath, true)\n },\n\n fileExists: async (path) => {\n const rootDirectory = await navigator.storage.getDirectory()\n try {\n await getFileHandleForPath(rootDirectory, path, false)\n return true\n } catch {\n const parts = path.split('/').filter(Boolean)\n let directory = rootDirectory\n for (const part of parts) {\n try {\n directory = await directory.getDirectoryHandle(part)\n } catch {\n return false\n }\n }\n return true\n }\n },\n\n readObject: async path => withPathLock(path, async () => {\n const rootDirectory = await navigator.storage.getDirectory()\n const handle = await getFileHandleForPath(rootDirectory, path, false)\n const file = await handle.getFile()\n const text = await file.text()\n return deserialize(text)\n }),\n\n writeObject: async (path, value) => withPathLock(path, async () => {\n const rootDirectory = await navigator.storage.getDirectory()\n const handle = await getFileHandleForPath(rootDirectory, path, true)\n const text = serialize(value)\n if (typeof text !== 'string') {\n throw new TypeError('serialize() must return a string')\n }\n const writableStream = await handle.createWritable()\n try {\n const encoded = new TextEncoder().encode(text)\n await writableStream.write({ type: 'write', position: 0, data: encoded })\n await writableStream.truncate(encoded.byteLength)\n await writableStream.close()\n } catch (error) {\n await writableStream.abort()\n throw error\n }\n }),\n\n readIndexObject: async path => withPathLock(path, async () => {\n const rootDirectory = await navigator.storage.getDirectory()\n const handle = await getFileHandleForPath(rootDirectory, path, false)\n const file = await handle.getFile()\n const text = await file.text()\n return deserialize(text)\n }),\n\n writeIndexObject: async (path, value) => withPathLock(path, async () => {\n const rootDirectory = await navigator.storage.getDirectory()\n const handle = await getFileHandleForPath(rootDirectory, path, true)\n const text = serialize(value)\n if (typeof text !== 'string') {\n throw new TypeError('serialize() must return a string')\n }\n const writableStream = await handle.createWritable()\n try {\n const encoded = new TextEncoder().encode(text)\n await writableStream.write({ type: 'write', position: 0, data: encoded })\n await writableStream.truncate(encoded.byteLength)\n await writableStream.close()\n } catch (error) {\n await writableStream.abort()\n throw error\n }\n }),\n\n listFilesRecursive: async (directoryPath) => {\n const rootDirectory = await navigator.storage.getDirectory()\n const directoryHandle = await ensureDirectoryExists(rootDirectory, directoryPath, false)\n\n const files: string[] = []\n // @ts-expect-error -- for-await-of on FileSystemDirectoryHandle is not in types yet\n for await (const entry of directoryHandle.values()) {\n if (entry.kind === 'file') {\n files.push(entry.name as string)\n } else if (entry.kind === 'directory') {\n const subFiles = await driver.listFilesRecursive(`${directoryPath}/${entry.name}`)\n files.push(...subFiles.map(f => `${entry.name}/${f}`))\n }\n }\n return files\n },\n\n removeEntry: async (path, removeOptions) => {\n const rootDirectory = await navigator.storage.getDirectory()\n const pathParts = path.split('/').filter(Boolean)\n const name = pathParts.pop()\n if (!name) throw new Error('Invalid path')\n const parent = pathParts.length > 0\n ? await ensureDirectoryExists(rootDirectory, pathParts.join('/'), false)\n : rootDirectory\n await parent.removeEntry(name, { recursive: Boolean(removeOptions?.recursive) })\n },\n }\n return createGenericFSAdapter<T, I>(driver, folderName)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.umd.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Driver } from '@signaldb/generic-fs'\nimport createGenericFSAdapter from '@signaldb/generic-fs'\nimport { serializeValue } from '@signaldb/core'\n\n/**\n * Convert an arbitrary filename into a OPFS safe filename.\n * @param input - The input filename to sanitize.\n * @returns A safe filename.\n */\nfunction toSafeFilename(input: string): string {\n const replacement = '_'\n\n let name = input.normalize('NFC')\n\n const escapeRegex = (s: string) => s.replaceAll(/[-/\\\\^$*+?.()|[\\]{}]/g, String.raw`\\$&`)\n name = name.replaceAll('/', replacement)\n name = name.replaceAll(new RegExp(`${escapeRegex(replacement)}{2,}`, 'g'), replacement)\n\n if (!name) name = 'unnamed'\n\n return name\n}\n\n/**\n * Acquire a lock for a specific file path to ensure exclusive access during read/write operations.\n * @param path - The file path to lock.\n * @param fn - The asynchronous function to execute while holding the lock.\n * @returns The result of the function `fn`.\n */\nasync function withPathLock<T>(path: string, fn: () => Promise<T>): Promise<T> {\n const lockName = `opfs:${path}`\n // Use exclusive mode so reads cannot interleave with writes\n return navigator.locks.request(lockName, { mode: 'exclusive' }, fn)\n}\n\n/**\n * Creates a storage adapter for managing a SignalDB collection using the\n * Origin Private File System (OPFS). The collection is stored as a folder: one file\n * per document under `items/`, one file per indexed value under `index/`.\n * @template T - The type of the items in the collection.\n * @template I - The type of the unique identifier for the items.\n * @param folderName - The folder in OPFS this collection is stored in.\n * @param options - Optional configuration for serialization and deserialization.\n * @param options.serialize - A function to serialize items to a string (default: `JSON.stringify`).\n * @param options.deserialize - A function to deserialize a string into items (default: `JSON.parse`).\n * @returns A SignalDB storage adapter for managing data in OPFS.\n * @example\n * import { Collection, DefaultDataAdapter } from '@signaldb/core';\n * import createOPFSAdapter from '@signaldb/opfs';\n *\n * const dataAdapter = new DefaultDataAdapter({\n * storage: name => createOPFSAdapter(name, {\n * serialize: (items) => JSON.stringify(items, null, 2), // Pretty-print JSON\n * deserialize: (itemsString) => JSON.parse(itemsString), // Default JSON parse\n * }),\n * });\n *\n * const collection = new Collection('posts', dataAdapter);\n *\n * // Perform operations on the collection, and changes will be reflected in OPFS.\n */\nexport default function createOPFSAdapter<\n T extends { id: I } & Record<string, any>,\n I,\n>(\n folderName: string,\n options?: {\n serialize?: (data: any) => string,\n deserialize?: (input: string) => any,\n },\n) {\n const {\n serialize = JSON.stringify,\n deserialize = JSON.parse,\n } = options || {}\n\n const ensureDirectoryExists = async (\n rootDirectory: FileSystemDirectoryHandle,\n directoryPath: string,\n createIfMissing: boolean,\n ) => {\n const parts = directoryPath.split('/').filter(Boolean)\n let current = rootDirectory\n for (const part of parts) {\n current = await current.getDirectoryHandle(part, { create: createIfMissing })\n }\n return current\n }\n\n const getFileHandleForPath = async (\n rootDirectory: FileSystemDirectoryHandle,\n fullPath: string,\n createIfMissing: boolean,\n ): Promise<FileSystemFileHandle> => {\n const parts = fullPath.split('/').filter(Boolean)\n const fileName = parts.pop() as string\n const directoryHandle = await ensureDirectoryExists(rootDirectory, parts.join('/'), createIfMissing)\n return directoryHandle.getFileHandle(fileName, { create: createIfMissing })\n }\n\n const driver: Driver<T, I> = {\n fileNameForId: id => Promise.resolve(toSafeFilename(serializeValue(id) as string)),\n fileNameForIndexKey: key => Promise.resolve(toSafeFilename(key)),\n joinPath: (...parts) => Promise.resolve(parts.join('/')),\n ensureDir: async (directoryPath) => {\n const rootDirectory = await navigator.storage.getDirectory()\n await ensureDirectoryExists(rootDirectory, directoryPath, true)\n },\n\n fileExists: async (path) => {\n const rootDirectory = await navigator.storage.getDirectory()\n try {\n await getFileHandleForPath(rootDirectory, path, false)\n return true\n } catch {\n const parts = path.split('/').filter(Boolean)\n let directory = rootDirectory\n for (const part of parts) {\n try {\n directory = await directory.getDirectoryHandle(part)\n } catch {\n return false\n }\n }\n return true\n }\n },\n\n readObject: async path => withPathLock(path, async () => {\n const rootDirectory = await navigator.storage.getDirectory()\n const handle = await getFileHandleForPath(rootDirectory, path, false)\n const file = await handle.getFile()\n const text = await file.text()\n return deserialize(text)\n }),\n\n writeObject: async (path, value) => withPathLock(path, async () => {\n const rootDirectory = await navigator.storage.getDirectory()\n const handle = await getFileHandleForPath(rootDirectory, path, true)\n const text = serialize(value)\n if (typeof text !== 'string') {\n throw new TypeError('serialize() must return a string')\n }\n const writableStream = await handle.createWritable()\n try {\n const encoded = new TextEncoder().encode(text)\n await writableStream.write({ type: 'write', position: 0, data: encoded })\n await writableStream.truncate(encoded.byteLength)\n await writableStream.close()\n } catch (error) {\n await writableStream.abort()\n throw error\n }\n }),\n\n readIndexObject: async path => withPathLock(path, async () => {\n const rootDirectory = await navigator.storage.getDirectory()\n const handle = await getFileHandleForPath(rootDirectory, path, false)\n const file = await handle.getFile()\n const text = await file.text()\n return deserialize(text)\n }),\n\n writeIndexObject: async (path, value) => withPathLock(path, async () => {\n const rootDirectory = await navigator.storage.getDirectory()\n const handle = await getFileHandleForPath(rootDirectory, path, true)\n const text = serialize(value)\n if (typeof text !== 'string') {\n throw new TypeError('serialize() must return a string')\n }\n const writableStream = await handle.createWritable()\n try {\n const encoded = new TextEncoder().encode(text)\n await writableStream.write({ type: 'write', position: 0, data: encoded })\n await writableStream.truncate(encoded.byteLength)\n await writableStream.close()\n } catch (error) {\n await writableStream.abort()\n throw error\n }\n }),\n\n listFilesRecursive: async (directoryPath) => {\n const rootDirectory = await navigator.storage.getDirectory()\n const directoryHandle = await ensureDirectoryExists(rootDirectory, directoryPath, false)\n\n const files: string[] = []\n // @ts-expect-error -- for-await-of on FileSystemDirectoryHandle is not in types yet\n for await (const entry of directoryHandle.values()) {\n if (entry.kind === 'file') {\n files.push(entry.name as string)\n } else if (entry.kind === 'directory') {\n const subFiles = await driver.listFilesRecursive(`${directoryPath}/${entry.name}`)\n files.push(...subFiles.map(f => `${entry.name}/${f}`))\n }\n }\n return files\n },\n\n removeEntry: async (path, removeOptions) => {\n const rootDirectory = await navigator.storage.getDirectory()\n const pathParts = path.split('/').filter(Boolean)\n const name = pathParts.pop()\n if (!name) throw new Error('Invalid path')\n const parent = pathParts.length > 0\n ? await ensureDirectoryExists(rootDirectory, pathParts.join('/'), false)\n : rootDirectory\n await parent.removeEntry(name, { recursive: Boolean(removeOptions?.recursive) })\n },\n }\n return createGenericFSAdapter<T, I>(driver, folderName)\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signaldb/opfs",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.22",
|
|
4
4
|
"description": "",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "rimraf dist && vite build",
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
"dist"
|
|
54
54
|
],
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@signaldb/core": "2.0.0-beta.
|
|
56
|
+
"@signaldb/core": "2.0.0-beta.22"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@signaldb/generic-fs": "^2.0.0-beta.
|
|
59
|
+
"@signaldb/generic-fs": "^2.0.0-beta.22"
|
|
60
60
|
}
|
|
61
61
|
}
|