@signaldb/opfs 1.0.1 → 2.0.0-beta.1
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/README.md +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.mjs +93 -51
- 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 +5 -2
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# @signaldb/opfs
|
|
2
2
|
|
|
3
|
-
This is the OPFS
|
|
3
|
+
This is the OPFS storage adapter for [SignalDB](https://github.com/maxnowack/signaldb). SignalDB is a local-first JavaScript database with real-time sync, enabling optimistic UI with signal-based reactivity across multiple frameworks.
|
|
4
4
|
|
|
5
5
|
See https://signaldb.js.org/reference/opfs/ for more information.
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* and deserialization.
|
|
6
6
|
* @template T - The type of the items in the collection.
|
|
7
7
|
* @template I - The type of the unique identifier for the items.
|
|
8
|
-
* @param
|
|
8
|
+
* @param folderName - The name of the file in OPFS where data will be stored.
|
|
9
9
|
* @param options - Optional configuration for serialization and deserialization.
|
|
10
10
|
* @param options.serialize - A function to serialize items to a string (default: `JSON.stringify`).
|
|
11
11
|
* @param options.deserialize - A function to deserialize a string into items (default: `JSON.parse`).
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
export default function createOPFSAdapter<T extends {
|
|
29
29
|
id: I;
|
|
30
|
-
} & Record<string, any>, I>(
|
|
31
|
-
serialize?: (
|
|
32
|
-
deserialize?: (
|
|
33
|
-
}): import("@signaldb/core").
|
|
30
|
+
} & Record<string, any>, I>(folderName: string, options?: {
|
|
31
|
+
serialize?: (data: any) => string;
|
|
32
|
+
deserialize?: (input: string) => any;
|
|
33
|
+
}): import("@signaldb/core").StorageAdapter<T, I>;
|
package/dist/index.mjs
CHANGED
|
@@ -1,59 +1,101 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
let
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
import p from "@signaldb/generic-fs";
|
|
2
|
+
import { serializeValue as m } from "@signaldb/core";
|
|
3
|
+
function u(l) {
|
|
4
|
+
let i = l.normalize("NFC");
|
|
5
|
+
const d = (s) => s.replaceAll(/[-/\\^$*+?.()|[\]{}]/g, String.raw`\$&`);
|
|
6
|
+
return i = i.replaceAll("/", "_"), i = i.replaceAll(new RegExp(`${d("_")}{2,}`, "g"), "_"), i || (i = "unnamed"), i;
|
|
7
|
+
}
|
|
8
|
+
async function g(l, y) {
|
|
9
|
+
const i = `opfs:${l}`;
|
|
10
|
+
return navigator.locks.request(i, { mode: "exclusive" }, y);
|
|
11
|
+
}
|
|
12
|
+
function D(l, y) {
|
|
13
|
+
const { serialize: i = JSON.stringify, deserialize: d = JSON.parse } = y || {}, s = async (e, a, o) => {
|
|
14
|
+
const r = a.split("/").filter(Boolean);
|
|
15
|
+
let t = e;
|
|
16
|
+
for (const n of r)
|
|
17
|
+
t = await t.getDirectoryHandle(n, { create: o });
|
|
18
|
+
return t;
|
|
19
|
+
}, w = async (e, a, o) => {
|
|
20
|
+
const r = a.split("/").filter(Boolean), t = r.pop();
|
|
21
|
+
return (await s(e, r.join("/"), o)).getFileHandle(t, { create: o });
|
|
22
|
+
}, f = {
|
|
23
|
+
fileNameForId: (e) => Promise.resolve(u(m(e))),
|
|
24
|
+
fileNameForIndexKey: (e) => Promise.resolve(u(e)),
|
|
25
|
+
joinPath: (...e) => Promise.resolve(e.join("/")),
|
|
26
|
+
ensureDir: async (e) => {
|
|
27
|
+
const a = await navigator.storage.getDirectory();
|
|
28
|
+
await s(a, e, !0);
|
|
12
29
|
},
|
|
13
|
-
async
|
|
14
|
-
|
|
30
|
+
fileExists: async (e) => {
|
|
31
|
+
const a = await navigator.storage.getDirectory();
|
|
32
|
+
try {
|
|
33
|
+
return await w(a, e, !1), !0;
|
|
34
|
+
} catch {
|
|
35
|
+
const o = e.split("/").filter(Boolean);
|
|
36
|
+
let r = a;
|
|
37
|
+
for (const t of o)
|
|
38
|
+
try {
|
|
39
|
+
r = await r.getDirectoryHandle(t);
|
|
40
|
+
} catch {
|
|
41
|
+
return !1;
|
|
42
|
+
}
|
|
43
|
+
return !0;
|
|
44
|
+
}
|
|
15
45
|
},
|
|
16
|
-
async
|
|
17
|
-
a
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
46
|
+
readObject: async (e) => g(e, async () => {
|
|
47
|
+
const a = await navigator.storage.getDirectory(), t = await (await (await w(a, e, !1)).getFile()).text();
|
|
48
|
+
return d(t);
|
|
49
|
+
}),
|
|
50
|
+
writeObject: async (e, a) => g(e, async () => {
|
|
51
|
+
const o = await navigator.storage.getDirectory(), r = await w(o, e, !0), t = i(a);
|
|
52
|
+
if (typeof t != "string")
|
|
53
|
+
throw new TypeError("serialize() must return a string");
|
|
54
|
+
const n = await r.createWritable();
|
|
55
|
+
try {
|
|
56
|
+
const c = new TextEncoder().encode(t);
|
|
57
|
+
await n.write({ type: "write", position: 0, data: c }), await n.truncate(c.byteLength), await n.close();
|
|
58
|
+
} catch (c) {
|
|
59
|
+
throw await n.abort(), c;
|
|
23
60
|
}
|
|
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
|
-
|
|
61
|
+
}),
|
|
62
|
+
readIndexObject: async (e) => g(e, async () => {
|
|
63
|
+
const a = await navigator.storage.getDirectory(), t = await (await (await w(a, e, !1)).getFile()).text();
|
|
64
|
+
return d(t);
|
|
65
|
+
}),
|
|
66
|
+
writeIndexObject: async (e, a) => g(e, async () => {
|
|
67
|
+
const o = await navigator.storage.getDirectory(), r = await w(o, e, !0), t = i(a);
|
|
68
|
+
if (typeof t != "string")
|
|
69
|
+
throw new TypeError("serialize() must return a string");
|
|
70
|
+
const n = await r.createWritable();
|
|
71
|
+
try {
|
|
72
|
+
const c = new TextEncoder().encode(t);
|
|
73
|
+
await n.write({ type: "write", position: 0, data: c }), await n.truncate(c.byteLength), await n.close();
|
|
74
|
+
} catch (c) {
|
|
75
|
+
throw await n.abort(), c;
|
|
76
|
+
}
|
|
77
|
+
}),
|
|
78
|
+
listFilesRecursive: async (e) => {
|
|
79
|
+
const a = await navigator.storage.getDirectory(), o = await s(a, e, !1), r = [];
|
|
80
|
+
for await (const t of o.values())
|
|
81
|
+
if (t.kind === "file")
|
|
82
|
+
r.push(t.name);
|
|
83
|
+
else if (t.kind === "directory") {
|
|
84
|
+
const n = await f.listFilesRecursive(`${e}/${t.name}`);
|
|
85
|
+
r.push(...n.map((c) => `${t.name}/${c}`));
|
|
86
|
+
}
|
|
87
|
+
return r;
|
|
88
|
+
},
|
|
89
|
+
removeEntry: async (e, a) => {
|
|
90
|
+
const o = await navigator.storage.getDirectory(), r = e.split("/").filter(Boolean), t = r.pop();
|
|
91
|
+
if (!t)
|
|
92
|
+
throw new Error("Invalid path");
|
|
93
|
+
await (r.length > 0 ? await s(o, r.join("/"), !1) : o).removeEntry(t, { recursive: !!a?.recursive });
|
|
53
94
|
}
|
|
54
|
-
}
|
|
95
|
+
};
|
|
96
|
+
return p(f, l);
|
|
55
97
|
}
|
|
56
98
|
export {
|
|
57
|
-
|
|
99
|
+
D as default
|
|
58
100
|
};
|
|
59
101
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":["import { createPersistenceAdapter } from '@signaldb/core';\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 filename - 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(filename, options) {\n const { serialize = JSON.stringify, deserialize = JSON.parse } = options || {};\n let savePromise = null;\n /**\n * Retrieves the items from the OPFS file.\n * @returns A promise that resolves to an array of items.\n */\n async function getItems() {\n const opfsRoot = await navigator.storage.getDirectory();\n const existingFileHandle = await opfsRoot.getFileHandle(filename, { create: true });\n const contents = await existingFileHandle.getFile().then(value => value.text());\n return deserialize(contents || '[]');\n }\n return createPersistenceAdapter({\n async register(onChange) {\n const opfsRoot = await navigator.storage.getDirectory();\n await opfsRoot.getFileHandle(filename, { create: true });\n void onChange();\n },\n async load() {\n if (savePromise)\n await savePromise;\n const items = await getItems();\n return { items };\n },\n async save(_items, { added, modified, removed }) {\n if (savePromise)\n await savePromise;\n const opfsRoot = await navigator.storage.getDirectory();\n const existingFileHandle = await opfsRoot.getFileHandle(filename, { create: true });\n if (added.length === 0 && modified.length === 0 && removed.length === 0) {\n const writeStream = await existingFileHandle.createWritable();\n await writeStream.write(serialize(_items));\n await writeStream.close();\n await savePromise;\n return;\n }\n savePromise = getItems()\n .then((currentItems) => {\n const items = [...currentItems];\n added.forEach((item) => {\n const index = items.findIndex(({ id }) => id === item.id);\n /* istanbul ignore if -- @preserve */\n if (index !== -1) {\n items[index] = item;\n return;\n }\n items.push(item);\n });\n modified.forEach((item) => {\n const index = items.findIndex(({ id }) => id === item.id);\n /* istanbul ignore if -- @preserve */\n if (index === -1) {\n items.push(item);\n return;\n }\n items[index] = item;\n });\n removed.forEach((item) => {\n const index = items.findIndex(({ id }) => id === item.id);\n /* istanbul ignore if -- @preserve */\n if (index === -1)\n return;\n items.splice(index, 1);\n });\n return items;\n })\n .then(async (items) => {\n const writeStream = await existingFileHandle.createWritable();\n await writeStream.write(serialize(items));\n await writeStream.close();\n })\n .then(() => {\n savePromise = null;\n });\n await savePromise;\n },\n });\n}\n"],"names":["createOPFSAdapter","filename","options","serialize","deserialize","savePromise","getItems","contents","value","createPersistenceAdapter","onChange","_items","added","modified","removed","existingFileHandle","writeStream","currentItems","items","item","index","id"],"mappings":";AA4BwB,SAAAA,EAAkBC,GAAUC,GAAS;AACnD,QAAA,EAAE,WAAAC,IAAY,KAAK,WAAW,aAAAC,IAAc,KAAK,UAAUF,KAAW,CAAC;AAC7E,MAAIG,IAAc;AAKlB,iBAAeC,IAAW;AAGhB,UAAAC,IAAW,OADU,OADV,MAAM,UAAU,QAAQ,aAAa,GACZ,cAAcN,GAAU,EAAE,QAAQ,IAAM,GACxC,UAAU,KAAK,CAAAO,MAASA,EAAM,MAAM;AACvE,WAAAJ,EAAYG,KAAY,IAAI;AAAA,EAAA;AAEvC,SAAOE,EAAyB;AAAA,IAC5B,MAAM,SAASC,GAAU;AAErB,aADiB,MAAM,UAAU,QAAQ,aAAa,GACvC,cAAcT,GAAU,EAAE,QAAQ,IAAM,GAClDS,EAAS;AAAA,IAClB;AAAA,IACA,MAAM,OAAO;AACL,aAAAL,KACM,MAAAA,GAEH,EAAE,OADK,MAAMC,EAAS,EACd;AAAA,IACnB;AAAA,IACA,MAAM,KAAKK,GAAQ,EAAE,OAAAC,GAAO,UAAAC,GAAU,SAAAC,KAAW;AACzC,MAAAT,KACM,MAAAA;AAEJ,YAAAU,IAAqB,OADV,MAAM,UAAU,QAAQ,aAAa,GACZ,cAAcd,GAAU,EAAE,QAAQ,IAAM;AAC9E,UAAAW,EAAM,WAAW,KAAKC,EAAS,WAAW,KAAKC,EAAQ,WAAW,GAAG;AAC/D,cAAAE,IAAc,MAAMD,EAAmB,eAAe;AAC5D,cAAMC,EAAY,MAAMb,EAAUQ,CAAM,CAAC,GACzC,MAAMK,EAAY,MAAM,GAClB,MAAAX;AACN;AAAA,MAAA;AAEJ,MAAAA,IAAcC,EAAS,EAClB,KAAK,CAACW,MAAiB;AAClB,cAAAC,IAAQ,CAAC,GAAGD,CAAY;AACxB,eAAAL,EAAA,QAAQ,CAACO,MAAS;AACd,gBAAAC,IAAQF,EAAM,UAAU,CAAC,EAAE,IAAAG,QAASA,MAAOF,EAAK,EAAE;AAAA,UAAA;AAExD,cAAIC,MAAU,IAAI;AACd,YAAAF,EAAME,CAAK,IAAID;AACf;AAAA,UAAA;AAEJ,UAAAD,EAAM,KAAKC,CAAI;AAAA,QAAA,CAClB,GACQN,EAAA,QAAQ,CAACM,MAAS;AACjB,gBAAAC,IAAQF,EAAM,UAAU,CAAC,EAAE,IAAAG,QAASA,MAAOF,EAAK,EAAE;AAAA,UAAA;AAExD,cAAIC,MAAU,IAAI;AACd,YAAAF,EAAM,KAAKC,CAAI;AACf;AAAA,UAAA;AAEJ,UAAAD,EAAME,CAAK,IAAID;AAAA,QAAA,CAClB,GACOL,EAAA,QAAQ,CAACK,MAAS;AAChB,gBAAAC,IAAQF,EAAM,UAAU,CAAC,EAAE,IAAAG,QAASA,MAAOF,EAAK,EAAE;AAAA,UAAA;AAExD,UAAIC,MAAU,MAERF,EAAA,OAAOE,GAAO,CAAC;AAAA,QAAA,CACxB,GACMF;AAAA,MAAA,CACV,EACI,KAAK,OAAOA,MAAU;AACjB,cAAAF,IAAc,MAAMD,EAAmB,eAAe;AAC5D,cAAMC,EAAY,MAAMb,EAAUe,CAAK,CAAC,GACxC,MAAMF,EAAY,MAAM;AAAA,MAAA,CAC3B,EACI,KAAK,MAAM;AACE,QAAAX,IAAA;AAAA,MAAA,CACjB,GACK,MAAAA;AAAA,IAAA;AAAA,EACV,CACH;AACL;"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":["import createGenericFSAdapter from '@signaldb/generic-fs';\nimport { serializeValue } from '@signaldb/core';\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) {\n const replacement = '_';\n let name = input.normalize('NFC');\n const escapeRegex = (s) => s.replaceAll(/[-/\\\\^$*+?.()|[\\]{}]/g, String.raw `\\$&`);\n name = name.replaceAll('/', replacement);\n name = name.replaceAll(new RegExp(`${escapeRegex(replacement)}{2,}`, 'g'), replacement);\n if (!name)\n name = 'unnamed';\n return name;\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(path, fn) {\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 * 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(folderName, options) {\n const { serialize = JSON.stringify, deserialize = JSON.parse, } = options || {};\n const ensureDirectoryExists = async (rootDirectory, directoryPath, createIfMissing) => {\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 const getFileHandleForPath = async (rootDirectory, fullPath, createIfMissing) => {\n const parts = fullPath.split('/').filter(Boolean);\n const fileName = parts.pop();\n const directoryHandle = await ensureDirectoryExists(rootDirectory, parts.join('/'), createIfMissing);\n return directoryHandle.getFileHandle(fileName, { create: createIfMissing });\n };\n const driver = {\n fileNameForId: id => Promise.resolve(toSafeFilename(serializeValue(id))),\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 fileExists: async (path) => {\n const rootDirectory = await navigator.storage.getDirectory();\n try {\n await getFileHandleForPath(rootDirectory, path, false);\n return true;\n }\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 }\n catch {\n return false;\n }\n }\n return true;\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 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 }\n catch (error) {\n await writableStream.abort();\n throw error;\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 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 }\n catch (error) {\n await writableStream.abort();\n throw error;\n }\n }),\n listFilesRecursive: async (directoryPath) => {\n const rootDirectory = await navigator.storage.getDirectory();\n const directoryHandle = await ensureDirectoryExists(rootDirectory, directoryPath, false);\n const files = [];\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);\n }\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 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)\n 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(driver, folderName);\n}\n"],"names":["toSafeFilename","input","name","escapeRegex","withPathLock","path","fn","lockName","createOPFSAdapter","folderName","options","serialize","deserialize","ensureDirectoryExists","rootDirectory","directoryPath","createIfMissing","parts","current","part","getFileHandleForPath","fullPath","fileName","driver","serializeValue","id","key","directory","text","value","handle","writableStream","encoded","error","directoryHandle","files","entry","subFiles","f","removeOptions","pathParts","createGenericFSAdapter"],"mappings":"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"}
|
package/dist/index.umd.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(function(
|
|
1
|
+
(function(c,w){typeof exports=="object"&&typeof module<"u"?module.exports=w(require("@signaldb/generic-fs"),require("@signaldb/core")):typeof define=="function"&&define.amd?define(["@signaldb/generic-fs","@signaldb/core"],w):(c=typeof globalThis<"u"?globalThis:c||self,c.SignalDB=w(c.createGenericFSAdapter,c.core))})(this,(function(c,w){"use strict";function p(y){let i=y.normalize("NFC");const g=l=>l.replaceAll(/[-/\\^$*+?.()|[\]{}]/g,String.raw`\$&`);return i=i.replaceAll("/","_"),i=i.replaceAll(new RegExp(`${g("_")}{2,}`,"g"),"_"),i||(i="unnamed"),i}async function f(y,u){const i=`opfs:${y}`;return navigator.locks.request(i,{mode:"exclusive"},u)}function h(y,u){const{serialize:i=JSON.stringify,deserialize:g=JSON.parse}=u||{},l=async(e,a,n)=>{const r=a.split("/").filter(Boolean);let t=e;for(const o of r)t=await t.getDirectoryHandle(o,{create:n});return t},d=async(e,a,n)=>{const r=a.split("/").filter(Boolean),t=r.pop();return(await l(e,r.join("/"),n)).getFileHandle(t,{create:n})},m={fileNameForId:e=>Promise.resolve(p(w.serializeValue(e))),fileNameForIndexKey:e=>Promise.resolve(p(e)),joinPath:(...e)=>Promise.resolve(e.join("/")),ensureDir:async e=>{const a=await navigator.storage.getDirectory();await l(a,e,!0)},fileExists:async e=>{const a=await navigator.storage.getDirectory();try{return await d(a,e,!1),!0}catch{const n=e.split("/").filter(Boolean);let r=a;for(const t of n)try{r=await r.getDirectoryHandle(t)}catch{return!1}return!0}},readObject:async e=>f(e,async()=>{const a=await navigator.storage.getDirectory(),t=await(await(await d(a,e,!1)).getFile()).text();return g(t)}),writeObject:async(e,a)=>f(e,async()=>{const n=await navigator.storage.getDirectory(),r=await d(n,e,!0),t=i(a);if(typeof t!="string")throw new TypeError("serialize() must return a string");const o=await r.createWritable();try{const s=new TextEncoder().encode(t);await o.write({type:"write",position:0,data:s}),await o.truncate(s.byteLength),await o.close()}catch(s){throw await o.abort(),s}}),readIndexObject:async e=>f(e,async()=>{const a=await navigator.storage.getDirectory(),t=await(await(await d(a,e,!1)).getFile()).text();return g(t)}),writeIndexObject:async(e,a)=>f(e,async()=>{const n=await navigator.storage.getDirectory(),r=await d(n,e,!0),t=i(a);if(typeof t!="string")throw new TypeError("serialize() must return a string");const o=await r.createWritable();try{const s=new TextEncoder().encode(t);await o.write({type:"write",position:0,data:s}),await o.truncate(s.byteLength),await o.close()}catch(s){throw await o.abort(),s}}),listFilesRecursive:async e=>{const a=await navigator.storage.getDirectory(),n=await l(a,e,!1),r=[];for await(const t of n.values())if(t.kind==="file")r.push(t.name);else if(t.kind==="directory"){const o=await m.listFilesRecursive(`${e}/${t.name}`);r.push(...o.map(s=>`${t.name}/${s}`))}return r},removeEntry:async(e,a)=>{const n=await navigator.storage.getDirectory(),r=e.split("/").filter(Boolean),t=r.pop();if(!t)throw new Error("Invalid path");await(r.length>0?await l(n,r.join("/"),!1):n).removeEntry(t,{recursive:!!a?.recursive})}};return c(m,y)}return h}));
|
|
2
2
|
//# sourceMappingURL=index.umd.js.map
|
package/dist/index.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.umd.js","sources":["../src/index.ts"],"sourcesContent":["import { createPersistenceAdapter } from '@signaldb/core';\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 filename - 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(filename, options) {\n const { serialize = JSON.stringify, deserialize = JSON.parse } = options || {};\n let savePromise = null;\n /**\n * Retrieves the items from the OPFS file.\n * @returns A promise that resolves to an array of items.\n */\n async function getItems() {\n const opfsRoot = await navigator.storage.getDirectory();\n const existingFileHandle = await opfsRoot.getFileHandle(filename, { create: true });\n const contents = await existingFileHandle.getFile().then(value => value.text());\n return deserialize(contents || '[]');\n }\n return createPersistenceAdapter({\n async register(onChange) {\n const opfsRoot = await navigator.storage.getDirectory();\n await opfsRoot.getFileHandle(filename, { create: true });\n void onChange();\n },\n async load() {\n if (savePromise)\n await savePromise;\n const items = await getItems();\n return { items };\n },\n async save(_items, { added, modified, removed }) {\n if (savePromise)\n await savePromise;\n const opfsRoot = await navigator.storage.getDirectory();\n const existingFileHandle = await opfsRoot.getFileHandle(filename, { create: true });\n if (added.length === 0 && modified.length === 0 && removed.length === 0) {\n const writeStream = await existingFileHandle.createWritable();\n await writeStream.write(serialize(_items));\n await writeStream.close();\n await savePromise;\n return;\n }\n savePromise = getItems()\n .then((currentItems) => {\n const items = [...currentItems];\n added.forEach((item) => {\n const index = items.findIndex(({ id }) => id === item.id);\n /* istanbul ignore if -- @preserve */\n if (index !== -1) {\n items[index] = item;\n return;\n }\n items.push(item);\n });\n modified.forEach((item) => {\n const index = items.findIndex(({ id }) => id === item.id);\n /* istanbul ignore if -- @preserve */\n if (index === -1) {\n items.push(item);\n return;\n }\n items[index] = item;\n });\n removed.forEach((item) => {\n const index = items.findIndex(({ id }) => id === item.id);\n /* istanbul ignore if -- @preserve */\n if (index === -1)\n return;\n items.splice(index, 1);\n });\n return items;\n })\n .then(async (items) => {\n const writeStream = await existingFileHandle.createWritable();\n await writeStream.write(serialize(items));\n await writeStream.close();\n })\n .then(() => {\n savePromise = null;\n });\n await savePromise;\n },\n });\n}\n"],"names":["createOPFSAdapter","filename","options","serialize","deserialize","savePromise","getItems","contents","value","createPersistenceAdapter","onChange","_items","added","modified","removed","existingFileHandle","writeStream","currentItems","items","item","index","id"],"mappings":"4QA4BwB,SAAAA,EAAkBC,EAAUC,EAAS,CACnD,KAAA,CAAE,UAAAC,EAAY,KAAK,UAAW,YAAAC,EAAc,KAAK,OAAUF,GAAW,CAAC,EAC7E,IAAIG,EAAc,KAKlB,eAAeC,GAAW,CAGhB,MAAAC,EAAW,MADU,MADV,MAAM,UAAU,QAAQ,aAAa,GACZ,cAAcN,EAAU,CAAE,OAAQ,GAAM,GACxC,UAAU,KAAKO,GAASA,EAAM,MAAM,EACvE,OAAAJ,EAAYG,GAAY,IAAI,CAAA,CAEvC,OAAOE,2BAAyB,CAC5B,MAAM,SAASC,EAAU,CAErB,MADiB,MAAM,UAAU,QAAQ,aAAa,GACvC,cAAcT,EAAU,CAAE,OAAQ,GAAM,EAClDS,EAAS,CAClB,EACA,MAAM,MAAO,CACL,OAAAL,GACM,MAAAA,EAEH,CAAE,MADK,MAAMC,EAAS,CACd,CACnB,EACA,MAAM,KAAKK,EAAQ,CAAE,MAAAC,EAAO,SAAAC,EAAU,QAAAC,GAAW,CACzCT,GACM,MAAAA,EAEJ,MAAAU,EAAqB,MADV,MAAM,UAAU,QAAQ,aAAa,GACZ,cAAcd,EAAU,CAAE,OAAQ,GAAM,EAC9E,GAAAW,EAAM,SAAW,GAAKC,EAAS,SAAW,GAAKC,EAAQ,SAAW,EAAG,CAC/D,MAAAE,EAAc,MAAMD,EAAmB,eAAe,EAC5D,MAAMC,EAAY,MAAMb,EAAUQ,CAAM,CAAC,EACzC,MAAMK,EAAY,MAAM,EAClB,MAAAX,EACN,MAAA,CAEJA,EAAcC,EAAS,EAClB,KAAMW,GAAiB,CAClB,MAAAC,EAAQ,CAAC,GAAGD,CAAY,EACxB,OAAAL,EAAA,QAASO,GAAS,CACd,MAAAC,EAAQF,EAAM,UAAU,CAAC,CAAE,GAAAG,KAASA,IAAOF,EAAK,EAAE,EAAA,qCAExD,GAAIC,IAAU,GAAI,CACdF,EAAME,CAAK,EAAID,EACf,MAAA,CAEJD,EAAM,KAAKC,CAAI,CAAA,CAClB,EACQN,EAAA,QAASM,GAAS,CACjB,MAAAC,EAAQF,EAAM,UAAU,CAAC,CAAE,GAAAG,KAASA,IAAOF,EAAK,EAAE,EAAA,qCAExD,GAAIC,IAAU,GAAI,CACdF,EAAM,KAAKC,CAAI,EACf,MAAA,CAEJD,EAAME,CAAK,EAAID,CAAA,CAClB,EACOL,EAAA,QAASK,GAAS,CAChB,MAAAC,EAAQF,EAAM,UAAU,CAAC,CAAE,GAAAG,KAASA,IAAOF,EAAK,EAAE,EAAA,qCAEpDC,IAAU,IAERF,EAAA,OAAOE,EAAO,CAAC,CAAA,CACxB,EACMF,CAAA,CACV,EACI,KAAK,MAAOA,GAAU,CACjB,MAAAF,EAAc,MAAMD,EAAmB,eAAe,EAC5D,MAAMC,EAAY,MAAMb,EAAUe,CAAK,CAAC,EACxC,MAAMF,EAAY,MAAM,CAAA,CAC3B,EACI,KAAK,IAAM,CACEX,EAAA,IAAA,CACjB,EACK,MAAAA,CAAA,CACV,CACH,CACL"}
|
|
1
|
+
{"version":3,"file":"index.umd.js","sources":["../src/index.ts"],"sourcesContent":["import createGenericFSAdapter from '@signaldb/generic-fs';\nimport { serializeValue } from '@signaldb/core';\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) {\n const replacement = '_';\n let name = input.normalize('NFC');\n const escapeRegex = (s) => s.replaceAll(/[-/\\\\^$*+?.()|[\\]{}]/g, String.raw `\\$&`);\n name = name.replaceAll('/', replacement);\n name = name.replaceAll(new RegExp(`${escapeRegex(replacement)}{2,}`, 'g'), replacement);\n if (!name)\n name = 'unnamed';\n return name;\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(path, fn) {\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 * 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(folderName, options) {\n const { serialize = JSON.stringify, deserialize = JSON.parse, } = options || {};\n const ensureDirectoryExists = async (rootDirectory, directoryPath, createIfMissing) => {\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 const getFileHandleForPath = async (rootDirectory, fullPath, createIfMissing) => {\n const parts = fullPath.split('/').filter(Boolean);\n const fileName = parts.pop();\n const directoryHandle = await ensureDirectoryExists(rootDirectory, parts.join('/'), createIfMissing);\n return directoryHandle.getFileHandle(fileName, { create: createIfMissing });\n };\n const driver = {\n fileNameForId: id => Promise.resolve(toSafeFilename(serializeValue(id))),\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 fileExists: async (path) => {\n const rootDirectory = await navigator.storage.getDirectory();\n try {\n await getFileHandleForPath(rootDirectory, path, false);\n return true;\n }\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 }\n catch {\n return false;\n }\n }\n return true;\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 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 }\n catch (error) {\n await writableStream.abort();\n throw error;\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 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 }\n catch (error) {\n await writableStream.abort();\n throw error;\n }\n }),\n listFilesRecursive: async (directoryPath) => {\n const rootDirectory = await navigator.storage.getDirectory();\n const directoryHandle = await ensureDirectoryExists(rootDirectory, directoryPath, false);\n const files = [];\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);\n }\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 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)\n 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(driver, folderName);\n}\n"],"names":["toSafeFilename","input","name","escapeRegex","s","withPathLock","path","fn","lockName","createOPFSAdapter","folderName","options","serialize","deserialize","ensureDirectoryExists","rootDirectory","directoryPath","createIfMissing","parts","current","part","getFileHandleForPath","fullPath","fileName","driver","serializeValue","id","key","directory","text","value","handle","writableStream","encoded","error","directoryHandle","files","entry","subFiles","f","removeOptions","pathParts","createGenericFSAdapter"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signaldb/opfs",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "rimraf dist && vite build",
|
|
@@ -53,6 +53,9 @@
|
|
|
53
53
|
"dist"
|
|
54
54
|
],
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@signaldb/core": "1
|
|
56
|
+
"@signaldb/core": "2.0.0-beta.1"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"@signaldb/generic-fs": "^2.0.0-beta.1"
|
|
57
60
|
}
|
|
58
61
|
}
|