@signaldb/opfs 2.0.0-beta.0 → 2.0.0-beta.2

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.mjs CHANGED
@@ -1,270 +1,101 @@
1
- import { createStorageAdapter as $, get as b, serializeValue as M } from "@signaldb/core";
2
- function H(e, P) {
3
- const m = e.joinPath(P, "items"), v = e.joinPath(P, "index"), x = async () => {
4
- if (!await e.fileExists(await m).catch(() => !1))
5
- return [];
6
- const r = await e.listFilesRecursive(await m), i = [];
7
- return await Promise.all(r.map(async (n) => {
8
- const a = await e.joinPath(await m, n), c = await e.readObject(a);
9
- Array.isArray(c) && i.push(...c);
10
- })), i;
11
- }, S = async (r, i = x()) => {
12
- const n = await i, a = await e.joinPath(await v, await e.fileNameForIndexKey(r));
13
- await e.removeEntry(a, { recursive: !0 }).catch(() => {
14
- }), await e.ensureDir(a);
15
- const c = /* @__PURE__ */ new Map();
16
- for (const l of n) {
17
- const d = b(l, r);
18
- d != null && (c.has(d) || c.set(d, /* @__PURE__ */ new Set()), c.get(d)?.add(l.id));
19
- }
20
- const w = {};
21
- for (const [l, d] of c) {
22
- const u = String(l), I = await e.fileNameForIndexKey(u);
23
- w[I] || (w[I] = []), w[I].push({ [u]: [...d] });
24
- }
25
- await Promise.all(Object.entries(w).map(async ([l, d]) => {
26
- const u = await e.joinPath(a, l);
27
- await e.writeIndexObject(u, d);
28
- }));
29
- }, D = async (r) => {
30
- const i = await e.joinPath(await v, await e.fileNameForIndexKey(r));
31
- if (!await e.fileExists(i).catch(() => !1))
32
- throw new Error(`Index on field "${r}" does not exist`);
33
- const n = await e.listFilesRecursive(i), a = /* @__PURE__ */ new Map();
34
- return await Promise.all(n.map(async (c) => {
35
- const w = await e.joinPath(i, c), l = await e.readIndexObject(w);
36
- if (Array.isArray(l))
37
- for (const d of l)
38
- for (const [u, I] of Object.entries(d)) {
39
- a.has(u) || a.set(u, /* @__PURE__ */ new Set());
40
- for (const j of I)
41
- a.get(u)?.add(j);
42
- }
43
- })), a;
44
- }, t = [], o = (r, i, n, a, c) => {
45
- r.has(i) || r.set(i, { adds: /* @__PURE__ */ new Map(), removes: /* @__PURE__ */ new Map() });
46
- const w = r.get(i);
47
- if (!w)
48
- return;
49
- const l = n === "add" ? w.adds : w.removes;
50
- l.has(a) || l.set(a, /* @__PURE__ */ new Set()), l.get(a)?.add(c);
51
- }, y = (r, i, n, a, c) => {
52
- const w = n == null ? void 0 : String(n), l = a == null ? void 0 : String(a);
53
- w !== l && (w != null && o(r, i, "remove", w, c), l != null && o(r, i, "add", l, c));
54
- }, f = (r, i, n) => {
55
- if (i)
56
- for (const a of t)
57
- y(r, a, b(i, a), b(n, a), n.id);
58
- else
59
- for (const a of t) {
60
- const c = b(n, a);
61
- c != null && o(r, a, "add", String(c), n.id);
62
- }
63
- }, s = (r, i) => {
64
- for (const n of t) {
65
- const a = b(i, n);
66
- a != null && o(r, n, "remove", String(a), i.id);
67
- }
68
- }, h = async (r) => {
69
- await Promise.all([...r.entries()].map(async ([i, n]) => {
70
- const a = await e.joinPath(await v, await e.fileNameForIndexKey(i));
71
- if (!await e.fileExists(a).catch(() => !1))
72
- return;
73
- await e.ensureDir(a);
74
- const c = /* @__PURE__ */ new Set([
75
- ...n.adds.keys(),
76
- ...n.removes.keys()
77
- ]), w = /* @__PURE__ */ new Map();
78
- await Promise.all([...c].map(async (d) => {
79
- w.set(d, await e.fileNameForIndexKey(d));
80
- }));
81
- const l = new Set(w.values());
82
- await Promise.all([...l].map(async (d) => {
83
- const u = await e.joinPath(a, d), I = await e.readIndexObject(u), j = /* @__PURE__ */ new Map();
84
- if (Array.isArray(I))
85
- for (const E of I)
86
- for (const [p, g] of Object.entries(E))
87
- j.set(p, new Set(g));
88
- if (n.removes.forEach((E, p) => {
89
- if (w.get(p) !== d)
90
- return;
91
- const g = j.get(p);
92
- g && (E.forEach((A) => g.delete(A)), g.size === 0 && j.delete(p));
93
- }), n.adds.forEach((E, p) => {
94
- if (w.get(p) !== d)
95
- return;
96
- let g = j.get(p);
97
- g || (g = /* @__PURE__ */ new Set(), j.set(p, g)), E.forEach((A) => g.add(A));
98
- }), j.size === 0) {
99
- await e.fileExists(u).catch(() => !1) && await e.removeEntry(u).catch(() => {
100
- });
101
- return;
102
- }
103
- const N = [];
104
- j.forEach((E, p) => {
105
- N.push({ [p]: [...E] });
106
- }), await e.writeIndexObject(u, N);
107
- }));
108
- }));
109
- }, O = async (r, i) => {
110
- const n = /* @__PURE__ */ new Map();
111
- await Promise.all(r.map(async (a) => {
112
- const c = await e.joinPath(await m, await e.fileNameForId(a.id)), w = await e.readObject(c), l = Array.isArray(w) ? w : [], d = l.findIndex((u) => u.id === a.id);
113
- if (i === "insert") {
114
- if (d !== -1)
115
- throw new Error(`Item with id "${String(a.id)}" already exists`);
116
- f(n, void 0, a), await e.writeObject(c, [...l, a]);
117
- } else {
118
- if (d === -1)
119
- throw new Error(`Item with id "${String(a.id)}" does not exist`);
120
- f(n, l[d], a);
121
- const u = [...l];
122
- u[d] = a, await e.writeObject(c, u);
123
- }
124
- })), await h(n);
125
- };
126
- return $({
127
- setup: async () => {
128
- await e.ensureDir(P);
129
- },
130
- teardown: async () => {
131
- },
132
- readAll: x,
133
- readIds: async (r) => (await Promise.all(r.map(async (i) => {
134
- const n = await e.joinPath(await m, await e.fileNameForId(i)), a = await e.readObject(n);
135
- return Array.isArray(a) ? a.find((c) => c.id === i) ?? null : null;
136
- }))).filter((i) => i != null),
137
- createIndex: async (r) => {
138
- t.push(r), await S(r);
139
- },
140
- dropIndex: async (r) => {
141
- const i = await e.joinPath(await v, await e.fileNameForIndexKey(r));
142
- if (!await e.fileExists(i).catch(() => !1))
143
- throw new Error(`Index on field "${r}" does not exist`);
144
- await e.removeEntry(i, { recursive: !0 });
145
- },
146
- readIndex: D,
147
- insert: async (r) => {
148
- await O(r, "insert");
149
- },
150
- replace: async (r) => {
151
- await O(r, "replace");
152
- },
153
- remove: async (r) => {
154
- const i = /* @__PURE__ */ new Map();
155
- await Promise.all(r.map(async (n) => {
156
- const a = await e.joinPath(await m, await e.fileNameForId(n.id)), c = await e.readObject(a), w = Array.isArray(c) ? c : [], l = w.findIndex((u) => u.id === n.id);
157
- if (l === -1)
158
- throw new Error(`Item with id "${String(n.id)}" does not exist`);
159
- s(i, w[l]);
160
- const d = [...w];
161
- d.splice(l, 1), await (d.length === 0 ? e.removeEntry(a) : e.writeObject(a, d));
162
- })), await h(i);
163
- },
164
- removeAll: async () => {
165
- await e.fileExists(P).catch(() => !1) && await e.removeEntry(P, { recursive: !0 });
166
- }
167
- });
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;
168
7
  }
169
- function F(e) {
170
- let m = e.normalize("NFC");
171
- const v = (x) => x.replaceAll(/[-/\\^$*+?.()|[\]{}]/g, String.raw`\$&`);
172
- return m = m.replaceAll("/", "_"), m = m.replaceAll(new RegExp(`${v("_")}{2,}`, "g"), "_"), m || (m = "unnamed"), m;
8
+ async function g(l, y) {
9
+ const i = `opfs:${l}`;
10
+ return navigator.locks.request(i, { mode: "exclusive" }, y);
173
11
  }
174
- function R(e, P) {
175
- const { serialize: m = JSON.stringify, deserialize: v = JSON.parse } = P || {}, x = async (t, o, y) => {
176
- const f = o.split("/").filter(Boolean);
177
- let s = t;
178
- for (const h of f)
179
- s = await s.getDirectoryHandle(h, { create: y });
180
- return s;
181
- }, S = async (t, o, y) => {
182
- const f = o.split("/").filter(Boolean), s = f.pop();
183
- return (await x(t, f.join("/"), y)).getFileHandle(s, { create: y });
184
- }, D = {
185
- fileNameForId: (t) => {
186
- if (typeof t != "string" && typeof t != "number")
187
- return Promise.resolve(F(M(t)));
188
- const o = typeof t == "string" ? t : t.toString(16).padStart(4, "0"), y = F(o.slice(0, 2) || "00"), f = F(o.slice(2, 4) || "00");
189
- return Promise.resolve(`${y}/${f}/${F(o)}`);
190
- },
191
- fileNameForIndexKey: (t) => Promise.resolve(F(t)),
192
- joinPath: (...t) => Promise.resolve(t.join("/")),
193
- ensureDir: async (t) => {
194
- const o = await navigator.storage.getDirectory();
195
- await x(o, t, !0);
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);
196
29
  },
197
- fileExists: async (t) => {
198
- const o = await navigator.storage.getDirectory();
30
+ fileExists: async (e) => {
31
+ const a = await navigator.storage.getDirectory();
199
32
  try {
200
- return await S(o, t, !1), !0;
33
+ return await w(a, e, !1), !0;
201
34
  } catch {
202
- const y = t.split("/").filter(Boolean);
203
- let f = o;
204
- for (const s of y)
35
+ const o = e.split("/").filter(Boolean);
36
+ let r = a;
37
+ for (const t of o)
205
38
  try {
206
- f = await f.getDirectoryHandle(s);
39
+ r = await r.getDirectoryHandle(t);
207
40
  } catch {
208
41
  return !1;
209
42
  }
210
43
  return !0;
211
44
  }
212
45
  },
213
- readObject: async (t) => {
214
- const o = await navigator.storage.getDirectory();
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();
215
55
  try {
216
- const s = await (await (await S(o, t, !1)).getFile()).text();
217
- return v(s);
218
- } catch {
219
- return null;
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;
220
60
  }
221
- },
222
- writeObject: async (t, o) => {
223
- const y = await navigator.storage.getDirectory(), s = await (await S(y, t, !0)).createWritable(), h = m(o);
224
- await s.write(h), await s.close();
225
- },
226
- readIndexObject: async (t) => {
227
- const o = await navigator.storage.getDirectory();
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();
228
71
  try {
229
- const s = await (await (await S(o, t, !1)).getFile()).text();
230
- return v(s);
231
- } catch {
232
- return null;
233
- }
234
- },
235
- writeIndexObject: async (t, o) => {
236
- const y = await navigator.storage.getDirectory(), s = await (await S(y, t, !0)).createWritable(), h = m(o);
237
- await s.write(h), await s.close();
238
- },
239
- listFilesRecursive: async (t) => {
240
- const o = await navigator.storage.getDirectory();
241
- let y;
242
- try {
243
- y = await x(o, t, !1);
244
- } catch {
245
- /* istanbul ignore next -- @preserve */
246
- return [];
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;
247
76
  }
248
- const f = [];
249
- for await (const s of y.values())
250
- if (s.kind === "file")
251
- f.push(s.name);
252
- else if (s.kind === "directory") {
253
- const h = await D.listFilesRecursive(`${t}/${s.name}`);
254
- f.push(...h.map((O) => `${s.name}/${O}`));
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}`));
255
86
  }
256
- return f;
87
+ return r;
257
88
  },
258
- removeEntry: async (t, o) => {
259
- const y = await navigator.storage.getDirectory(), f = t.split("/").filter(Boolean), s = f.pop();
260
- if (!s)
89
+ removeEntry: async (e, a) => {
90
+ const o = await navigator.storage.getDirectory(), r = e.split("/").filter(Boolean), t = r.pop();
91
+ if (!t)
261
92
  throw new Error("Invalid path");
262
- await (f.length > 0 ? await x(y, f.join("/"), !1) : y).removeEntry(s, { recursive: !!o?.recursive });
93
+ await (r.length > 0 ? await s(o, r.join("/"), !1) : o).removeEntry(t, { recursive: !!a?.recursive });
263
94
  }
264
95
  };
265
- return H(D, e);
96
+ return p(f, l);
266
97
  }
267
98
  export {
268
- R as default
99
+ D as default
269
100
  };
270
101
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../../generic-fs/dist/index.mjs","../src/index.ts"],"sourcesContent":["import { createStorageAdapter as K, get as m } from \"@signaldb/core\";\nfunction R(t, x) {\n const h = t.joinPath(x, \"items\"), p = t.joinPath(x, \"index\"), g = async () => {\n if (!await t.fileExists(await h).catch(() => !1))\n return [];\n const n = await t.listFilesRecursive(await h), s = [];\n return await Promise.all(n.map(async (a) => {\n const i = await t.joinPath(await h, a), c = await t.readObject(i);\n Array.isArray(c) && s.push(...c);\n })), s;\n }, O = async (e, n = g()) => {\n const s = await n, a = await t.joinPath(await p, await t.fileNameForIndexKey(e));\n await t.removeEntry(a, { recursive: !0 }).catch(() => {\n }), await t.ensureDir(a);\n const i = /* @__PURE__ */ new Map();\n for (const o of s) {\n const r = m(o, e);\n r != null && (i.has(r) || i.set(r, /* @__PURE__ */ new Set()), i.get(r)?.add(o.id));\n }\n const c = {};\n for (const [o, r] of i) {\n const w = String(o), l = await t.fileNameForIndexKey(w);\n c[l] || (c[l] = []), c[l].push({ [w]: [...r] });\n }\n await Promise.all(Object.entries(c).map(async ([o, r]) => {\n const w = await t.joinPath(a, o);\n await t.writeIndexObject(w, r);\n }));\n }, D = async (e) => {\n const n = await t.joinPath(await p, await t.fileNameForIndexKey(e));\n if (!await t.fileExists(n).catch(() => !1))\n throw new Error(`Index on field \"${e}\" does not exist`);\n const a = await t.listFilesRecursive(n), i = /* @__PURE__ */ new Map();\n return await Promise.all(a.map(async (c) => {\n const o = await t.joinPath(n, c), r = await t.readIndexObject(o);\n if (Array.isArray(r))\n for (const w of r)\n for (const [l, j] of Object.entries(w)) {\n i.has(l) || i.set(l, /* @__PURE__ */ new Set());\n for (const d of j)\n i.get(l)?.add(d);\n }\n })), i;\n }, I = [], P = (e, n, s, a, i) => {\n e.has(n) || e.set(n, { adds: /* @__PURE__ */ new Map(), removes: /* @__PURE__ */ new Map() });\n const c = e.get(n);\n if (!c)\n return;\n const o = s === \"add\" ? c.adds : c.removes;\n o.has(a) || o.set(a, /* @__PURE__ */ new Set()), o.get(a)?.add(i);\n }, M = (e, n, s, a, i) => {\n const c = s == null ? void 0 : String(s), o = a == null ? void 0 : String(a);\n c !== o && (c != null && P(e, n, \"remove\", c, i), o != null && P(e, n, \"add\", o, i));\n }, S = (e, n, s) => {\n if (n)\n for (const a of I)\n M(e, a, m(n, a), m(s, a), s.id);\n else\n for (const a of I) {\n const i = m(s, a);\n i != null && P(e, a, \"add\", String(i), s.id);\n }\n }, k = (e, n) => {\n for (const s of I) {\n const a = m(n, s);\n a != null && P(e, s, \"remove\", String(a), n.id);\n }\n }, F = async (e) => {\n await Promise.all([...e.entries()].map(async ([n, s]) => {\n const a = await t.joinPath(await p, await t.fileNameForIndexKey(n));\n if (!await t.fileExists(a).catch(() => !1))\n return;\n await t.ensureDir(a);\n const c = /* @__PURE__ */ new Set([\n ...s.adds.keys(),\n ...s.removes.keys()\n ]), o = /* @__PURE__ */ new Map();\n await Promise.all([...c].map(async (w) => {\n o.set(w, await t.fileNameForIndexKey(w));\n }));\n const r = new Set(o.values());\n await Promise.all([...r].map(async (w) => {\n const l = await t.joinPath(a, w), j = await t.readIndexObject(l), d = /* @__PURE__ */ new Map();\n if (Array.isArray(j))\n for (const y of j)\n for (const [f, u] of Object.entries(y))\n d.set(f, new Set(u));\n if (s.removes.forEach((y, f) => {\n if (o.get(f) !== w)\n return;\n const u = d.get(f);\n u && (y.forEach((E) => u.delete(E)), u.size === 0 && d.delete(f));\n }), s.adds.forEach((y, f) => {\n if (o.get(f) !== w)\n return;\n let u = d.get(f);\n u || (u = /* @__PURE__ */ new Set(), d.set(f, u)), y.forEach((E) => u.add(E));\n }), d.size === 0) {\n await t.fileExists(l).catch(() => !1) && await t.removeEntry(l).catch(() => {\n });\n return;\n }\n const b = [];\n d.forEach((y, f) => {\n b.push({ [f]: [...y] });\n }), await t.writeIndexObject(l, b);\n }));\n }));\n }, A = async (e, n) => {\n const s = /* @__PURE__ */ new Map();\n await Promise.all(e.map(async (a) => {\n const i = await t.joinPath(await h, await t.fileNameForId(a.id)), c = await t.readObject(i), o = Array.isArray(c) ? c : [], r = o.findIndex((w) => w.id === a.id);\n if (n === \"insert\") {\n if (r !== -1)\n throw new Error(`Item with id \"${String(a.id)}\" already exists`);\n S(s, void 0, a), await t.writeObject(i, [...o, a]);\n } else {\n if (r === -1)\n throw new Error(`Item with id \"${String(a.id)}\" does not exist`);\n S(s, o[r], a);\n const w = [...o];\n w[r] = a, await t.writeObject(i, w);\n }\n })), await F(s);\n };\n return K({\n setup: async () => {\n await t.ensureDir(x);\n },\n teardown: async () => {\n },\n readAll: g,\n readIds: async (e) => (await Promise.all(e.map(async (s) => {\n const a = await t.joinPath(await h, await t.fileNameForId(s)), i = await t.readObject(a);\n return Array.isArray(i) ? i.find((c) => c.id === s) ?? null : null;\n }))).filter((s) => s != null),\n createIndex: async (e) => {\n I.push(e), await O(e);\n },\n dropIndex: async (e) => {\n const n = await t.joinPath(await p, await t.fileNameForIndexKey(e));\n if (!await t.fileExists(n).catch(() => !1))\n throw new Error(`Index on field \"${e}\" does not exist`);\n await t.removeEntry(n, { recursive: !0 });\n },\n readIndex: D,\n insert: async (e) => {\n await A(e, \"insert\");\n },\n replace: async (e) => {\n await A(e, \"replace\");\n },\n remove: async (e) => {\n const n = /* @__PURE__ */ new Map();\n await Promise.all(e.map(async (s) => {\n const a = await t.joinPath(await h, await t.fileNameForId(s.id)), i = await t.readObject(a), c = Array.isArray(i) ? i : [], o = c.findIndex((w) => w.id === s.id);\n if (o === -1)\n throw new Error(`Item with id \"${String(s.id)}\" does not exist`);\n k(n, c[o]);\n const r = [...c];\n r.splice(o, 1), await (r.length === 0 ? t.removeEntry(a) : t.writeObject(a, r));\n })), await F(n);\n },\n removeAll: async () => {\n await t.fileExists(x).catch(() => !1) && await t.removeEntry(x, { recursive: !0 });\n }\n });\n}\nexport {\n R as default\n};\n//# sourceMappingURL=index.mjs.map\n","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 * 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) => {\n if (typeof id !== 'string' && typeof id !== 'number') {\n return Promise.resolve(toSafeFilename(serializeValue(id)));\n }\n const idString = typeof id === 'string' ? id : id.toString(16).padStart(4, '0');\n const firstShard = toSafeFilename(idString.slice(0, 2) || '00');\n const secondShard = toSafeFilename(idString.slice(2, 4) || '00');\n return Promise.resolve(`${firstShard}/${secondShard}/${toSafeFilename(idString)}`);\n },\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) => {\n const rootDirectory = await navigator.storage.getDirectory();\n try {\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 catch {\n return null;\n }\n },\n writeObject: async (path, value) => {\n const rootDirectory = await navigator.storage.getDirectory();\n const handle = await getFileHandleForPath(rootDirectory, path, true);\n const writableStream = await handle.createWritable();\n const text = serialize(value);\n await writableStream.write(text);\n await writableStream.close();\n },\n readIndexObject: async (path) => {\n const rootDirectory = await navigator.storage.getDirectory();\n try {\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 catch {\n return null;\n }\n },\n writeIndexObject: async (path, value) => {\n const rootDirectory = await navigator.storage.getDirectory();\n const handle = await getFileHandleForPath(rootDirectory, path, true);\n const writableStream = await handle.createWritable();\n const text = serialize(value);\n await writableStream.write(text);\n await writableStream.close();\n },\n listFilesRecursive: async (directoryPath) => {\n const rootDirectory = await navigator.storage.getDirectory();\n let directoryHandle;\n try {\n directoryHandle = await ensureDirectoryExists(rootDirectory, directoryPath, false);\n }\n catch {\n /* istanbul ignore next -- @preserve */\n return [];\n }\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":["R","t","x","h","p","g","n","s","a","i","O","e","o","r","m","c","w","l","j","d","I","P","M","S","k","F","y","f","u","E","b","A","K","toSafeFilename","input","name","escapeRegex","createOPFSAdapter","folderName","options","serialize","deserialize","ensureDirectoryExists","rootDirectory","directoryPath","createIfMissing","parts","current","part","getFileHandleForPath","fullPath","fileName","driver","id","serializeValue","idString","firstShard","secondShard","key","path","directory","text","value","writableStream","directoryHandle","files","entry","subFiles","removeOptions","pathParts","createGenericFSAdapter"],"mappings":"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"}
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(h,D){typeof exports=="object"&&typeof module<"u"?module.exports=D(require("@signaldb/core")):typeof define=="function"&&define.amd?define(["@signaldb/core"],D):(h=typeof globalThis<"u"?globalThis:h||self,h.SignalDB=D(h.core))})(this,(function(h){"use strict";function D(e,S){const m=e.joinPath(S,"items"),x=e.joinPath(S,"index"),j=async()=>{if(!await e.fileExists(await m).catch(()=>!1))return[];const r=await e.listFilesRecursive(await m),i=[];return await Promise.all(r.map(async n=>{const t=await e.joinPath(await m,n),c=await e.readObject(t);Array.isArray(c)&&i.push(...c)})),i},b=async(r,i=j())=>{const n=await i,t=await e.joinPath(await x,await e.fileNameForIndexKey(r));await e.removeEntry(t,{recursive:!0}).catch(()=>{}),await e.ensureDir(t);const c=new Map;for(const l of n){const d=h.get(l,r);d!=null&&(c.has(d)||c.set(d,new Set),c.get(d)?.add(l.id))}const w={};for(const[l,d]of c){const u=String(l),I=await e.fileNameForIndexKey(u);w[I]||(w[I]=[]),w[I].push({[u]:[...d]})}await Promise.all(Object.entries(w).map(async([l,d])=>{const u=await e.joinPath(t,l);await e.writeIndexObject(u,d)}))},O=async r=>{const i=await e.joinPath(await x,await e.fileNameForIndexKey(r));if(!await e.fileExists(i).catch(()=>!1))throw new Error(`Index on field "${r}" does not exist`);const n=await e.listFilesRecursive(i),t=new Map;return await Promise.all(n.map(async c=>{const w=await e.joinPath(i,c),l=await e.readIndexObject(w);if(Array.isArray(l))for(const d of l)for(const[u,I]of Object.entries(d)){t.has(u)||t.set(u,new Set);for(const P of I)t.get(u)?.add(P)}})),t},a=[],o=(r,i,n,t,c)=>{r.has(i)||r.set(i,{adds:new Map,removes:new Map});const w=r.get(i);if(!w)return;const l=n==="add"?w.adds:w.removes;l.has(t)||l.set(t,new Set),l.get(t)?.add(c)},y=(r,i,n,t,c)=>{const w=n==null?void 0:String(n),l=t==null?void 0:String(t);w!==l&&(w!=null&&o(r,i,"remove",w,c),l!=null&&o(r,i,"add",l,c))},f=(r,i,n)=>{if(i)for(const t of a)y(r,t,h.get(i,t),h.get(n,t),n.id);else for(const t of a){const c=h.get(n,t);c!=null&&o(r,t,"add",String(c),n.id)}},s=(r,i)=>{for(const n of a){const t=h.get(i,n);t!=null&&o(r,n,"remove",String(t),i.id)}},p=async r=>{await Promise.all([...r.entries()].map(async([i,n])=>{const t=await e.joinPath(await x,await e.fileNameForIndexKey(i));if(!await e.fileExists(t).catch(()=>!1))return;await e.ensureDir(t);const c=new Set([...n.adds.keys(),...n.removes.keys()]),w=new Map;await Promise.all([...c].map(async d=>{w.set(d,await e.fileNameForIndexKey(d))}));const l=new Set(w.values());await Promise.all([...l].map(async d=>{const u=await e.joinPath(t,d),I=await e.readIndexObject(u),P=new Map;if(Array.isArray(I))for(const E of I)for(const[g,v]of Object.entries(E))P.set(g,new Set(v));if(n.removes.forEach((E,g)=>{if(w.get(g)!==d)return;const v=P.get(g);v&&(E.forEach(N=>v.delete(N)),v.size===0&&P.delete(g))}),n.adds.forEach((E,g)=>{if(w.get(g)!==d)return;let v=P.get(g);v||(v=new Set,P.set(g,v)),E.forEach(N=>v.add(N))}),P.size===0){await e.fileExists(u).catch(()=>!1)&&await e.removeEntry(u).catch(()=>{});return}const $=[];P.forEach((E,g)=>{$.push({[g]:[...E]})}),await e.writeIndexObject(u,$)}))}))},A=async(r,i)=>{const n=new Map;await Promise.all(r.map(async t=>{const c=await e.joinPath(await m,await e.fileNameForId(t.id)),w=await e.readObject(c),l=Array.isArray(w)?w:[],d=l.findIndex(u=>u.id===t.id);if(i==="insert"){if(d!==-1)throw new Error(`Item with id "${String(t.id)}" already exists`);f(n,void 0,t),await e.writeObject(c,[...l,t])}else{if(d===-1)throw new Error(`Item with id "${String(t.id)}" does not exist`);f(n,l[d],t);const u=[...l];u[d]=t,await e.writeObject(c,u)}})),await p(n)};return h.createStorageAdapter({setup:async()=>{await e.ensureDir(S)},teardown:async()=>{},readAll:j,readIds:async r=>(await Promise.all(r.map(async i=>{const n=await e.joinPath(await m,await e.fileNameForId(i)),t=await e.readObject(n);return Array.isArray(t)?t.find(c=>c.id===i)??null:null}))).filter(i=>i!=null),createIndex:async r=>{a.push(r),await b(r)},dropIndex:async r=>{const i=await e.joinPath(await x,await e.fileNameForIndexKey(r));if(!await e.fileExists(i).catch(()=>!1))throw new Error(`Index on field "${r}" does not exist`);await e.removeEntry(i,{recursive:!0})},readIndex:O,insert:async r=>{await A(r,"insert")},replace:async r=>{await A(r,"replace")},remove:async r=>{const i=new Map;await Promise.all(r.map(async n=>{const t=await e.joinPath(await m,await e.fileNameForId(n.id)),c=await e.readObject(t),w=Array.isArray(c)?c:[],l=w.findIndex(u=>u.id===n.id);if(l===-1)throw new Error(`Item with id "${String(n.id)}" does not exist`);s(i,w[l]);const d=[...w];d.splice(l,1),await(d.length===0?e.removeEntry(t):e.writeObject(t,d))})),await p(i)},removeAll:async()=>{await e.fileExists(S).catch(()=>!1)&&await e.removeEntry(S,{recursive:!0})}})}function F(e){let m=e.normalize("NFC");const x=j=>j.replaceAll(/[-/\\^$*+?.()|[\]{}]/g,String.raw`\$&`);return m=m.replaceAll("/","_"),m=m.replaceAll(new RegExp(`${x("_")}{2,}`,"g"),"_"),m||(m="unnamed"),m}function M(e,S){const{serialize:m=JSON.stringify,deserialize:x=JSON.parse}=S||{},j=async(a,o,y)=>{const f=o.split("/").filter(Boolean);let s=a;for(const p of f)s=await s.getDirectoryHandle(p,{create:y});return s},b=async(a,o,y)=>{const f=o.split("/").filter(Boolean),s=f.pop();return(await j(a,f.join("/"),y)).getFileHandle(s,{create:y})},O={fileNameForId:a=>{if(typeof a!="string"&&typeof a!="number")return Promise.resolve(F(h.serializeValue(a)));const o=typeof a=="string"?a:a.toString(16).padStart(4,"0"),y=F(o.slice(0,2)||"00"),f=F(o.slice(2,4)||"00");return Promise.resolve(`${y}/${f}/${F(o)}`)},fileNameForIndexKey:a=>Promise.resolve(F(a)),joinPath:(...a)=>Promise.resolve(a.join("/")),ensureDir:async a=>{const o=await navigator.storage.getDirectory();await j(o,a,!0)},fileExists:async a=>{const o=await navigator.storage.getDirectory();try{return await b(o,a,!1),!0}catch{const y=a.split("/").filter(Boolean);let f=o;for(const s of y)try{f=await f.getDirectoryHandle(s)}catch{return!1}return!0}},readObject:async a=>{const o=await navigator.storage.getDirectory();try{const s=await(await(await b(o,a,!1)).getFile()).text();return x(s)}catch{return null}},writeObject:async(a,o)=>{const y=await navigator.storage.getDirectory(),s=await(await b(y,a,!0)).createWritable(),p=m(o);await s.write(p),await s.close()},readIndexObject:async a=>{const o=await navigator.storage.getDirectory();try{const s=await(await(await b(o,a,!1)).getFile()).text();return x(s)}catch{return null}},writeIndexObject:async(a,o)=>{const y=await navigator.storage.getDirectory(),s=await(await b(y,a,!0)).createWritable(),p=m(o);await s.write(p),await s.close()},listFilesRecursive:async a=>{const o=await navigator.storage.getDirectory();let y;try{y=await j(o,a,!1)}catch{/* istanbul ignore next -- @preserve */return[]}const f=[];for await(const s of y.values())if(s.kind==="file")f.push(s.name);else if(s.kind==="directory"){const p=await O.listFilesRecursive(`${a}/${s.name}`);f.push(...p.map(A=>`${s.name}/${A}`))}return f},removeEntry:async(a,o)=>{const y=await navigator.storage.getDirectory(),f=a.split("/").filter(Boolean),s=f.pop();if(!s)throw new Error("Invalid path");await(f.length>0?await j(y,f.join("/"),!1):y).removeEntry(s,{recursive:!!o?.recursive})}};return D(O,e)}return M}));
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.umd.js","sources":["../../generic-fs/dist/index.mjs","../src/index.ts"],"sourcesContent":["import { createStorageAdapter as K, get as m } from \"@signaldb/core\";\nfunction R(t, x) {\n const h = t.joinPath(x, \"items\"), p = t.joinPath(x, \"index\"), g = async () => {\n if (!await t.fileExists(await h).catch(() => !1))\n return [];\n const n = await t.listFilesRecursive(await h), s = [];\n return await Promise.all(n.map(async (a) => {\n const i = await t.joinPath(await h, a), c = await t.readObject(i);\n Array.isArray(c) && s.push(...c);\n })), s;\n }, O = async (e, n = g()) => {\n const s = await n, a = await t.joinPath(await p, await t.fileNameForIndexKey(e));\n await t.removeEntry(a, { recursive: !0 }).catch(() => {\n }), await t.ensureDir(a);\n const i = /* @__PURE__ */ new Map();\n for (const o of s) {\n const r = m(o, e);\n r != null && (i.has(r) || i.set(r, /* @__PURE__ */ new Set()), i.get(r)?.add(o.id));\n }\n const c = {};\n for (const [o, r] of i) {\n const w = String(o), l = await t.fileNameForIndexKey(w);\n c[l] || (c[l] = []), c[l].push({ [w]: [...r] });\n }\n await Promise.all(Object.entries(c).map(async ([o, r]) => {\n const w = await t.joinPath(a, o);\n await t.writeIndexObject(w, r);\n }));\n }, D = async (e) => {\n const n = await t.joinPath(await p, await t.fileNameForIndexKey(e));\n if (!await t.fileExists(n).catch(() => !1))\n throw new Error(`Index on field \"${e}\" does not exist`);\n const a = await t.listFilesRecursive(n), i = /* @__PURE__ */ new Map();\n return await Promise.all(a.map(async (c) => {\n const o = await t.joinPath(n, c), r = await t.readIndexObject(o);\n if (Array.isArray(r))\n for (const w of r)\n for (const [l, j] of Object.entries(w)) {\n i.has(l) || i.set(l, /* @__PURE__ */ new Set());\n for (const d of j)\n i.get(l)?.add(d);\n }\n })), i;\n }, I = [], P = (e, n, s, a, i) => {\n e.has(n) || e.set(n, { adds: /* @__PURE__ */ new Map(), removes: /* @__PURE__ */ new Map() });\n const c = e.get(n);\n if (!c)\n return;\n const o = s === \"add\" ? c.adds : c.removes;\n o.has(a) || o.set(a, /* @__PURE__ */ new Set()), o.get(a)?.add(i);\n }, M = (e, n, s, a, i) => {\n const c = s == null ? void 0 : String(s), o = a == null ? void 0 : String(a);\n c !== o && (c != null && P(e, n, \"remove\", c, i), o != null && P(e, n, \"add\", o, i));\n }, S = (e, n, s) => {\n if (n)\n for (const a of I)\n M(e, a, m(n, a), m(s, a), s.id);\n else\n for (const a of I) {\n const i = m(s, a);\n i != null && P(e, a, \"add\", String(i), s.id);\n }\n }, k = (e, n) => {\n for (const s of I) {\n const a = m(n, s);\n a != null && P(e, s, \"remove\", String(a), n.id);\n }\n }, F = async (e) => {\n await Promise.all([...e.entries()].map(async ([n, s]) => {\n const a = await t.joinPath(await p, await t.fileNameForIndexKey(n));\n if (!await t.fileExists(a).catch(() => !1))\n return;\n await t.ensureDir(a);\n const c = /* @__PURE__ */ new Set([\n ...s.adds.keys(),\n ...s.removes.keys()\n ]), o = /* @__PURE__ */ new Map();\n await Promise.all([...c].map(async (w) => {\n o.set(w, await t.fileNameForIndexKey(w));\n }));\n const r = new Set(o.values());\n await Promise.all([...r].map(async (w) => {\n const l = await t.joinPath(a, w), j = await t.readIndexObject(l), d = /* @__PURE__ */ new Map();\n if (Array.isArray(j))\n for (const y of j)\n for (const [f, u] of Object.entries(y))\n d.set(f, new Set(u));\n if (s.removes.forEach((y, f) => {\n if (o.get(f) !== w)\n return;\n const u = d.get(f);\n u && (y.forEach((E) => u.delete(E)), u.size === 0 && d.delete(f));\n }), s.adds.forEach((y, f) => {\n if (o.get(f) !== w)\n return;\n let u = d.get(f);\n u || (u = /* @__PURE__ */ new Set(), d.set(f, u)), y.forEach((E) => u.add(E));\n }), d.size === 0) {\n await t.fileExists(l).catch(() => !1) && await t.removeEntry(l).catch(() => {\n });\n return;\n }\n const b = [];\n d.forEach((y, f) => {\n b.push({ [f]: [...y] });\n }), await t.writeIndexObject(l, b);\n }));\n }));\n }, A = async (e, n) => {\n const s = /* @__PURE__ */ new Map();\n await Promise.all(e.map(async (a) => {\n const i = await t.joinPath(await h, await t.fileNameForId(a.id)), c = await t.readObject(i), o = Array.isArray(c) ? c : [], r = o.findIndex((w) => w.id === a.id);\n if (n === \"insert\") {\n if (r !== -1)\n throw new Error(`Item with id \"${String(a.id)}\" already exists`);\n S(s, void 0, a), await t.writeObject(i, [...o, a]);\n } else {\n if (r === -1)\n throw new Error(`Item with id \"${String(a.id)}\" does not exist`);\n S(s, o[r], a);\n const w = [...o];\n w[r] = a, await t.writeObject(i, w);\n }\n })), await F(s);\n };\n return K({\n setup: async () => {\n await t.ensureDir(x);\n },\n teardown: async () => {\n },\n readAll: g,\n readIds: async (e) => (await Promise.all(e.map(async (s) => {\n const a = await t.joinPath(await h, await t.fileNameForId(s)), i = await t.readObject(a);\n return Array.isArray(i) ? i.find((c) => c.id === s) ?? null : null;\n }))).filter((s) => s != null),\n createIndex: async (e) => {\n I.push(e), await O(e);\n },\n dropIndex: async (e) => {\n const n = await t.joinPath(await p, await t.fileNameForIndexKey(e));\n if (!await t.fileExists(n).catch(() => !1))\n throw new Error(`Index on field \"${e}\" does not exist`);\n await t.removeEntry(n, { recursive: !0 });\n },\n readIndex: D,\n insert: async (e) => {\n await A(e, \"insert\");\n },\n replace: async (e) => {\n await A(e, \"replace\");\n },\n remove: async (e) => {\n const n = /* @__PURE__ */ new Map();\n await Promise.all(e.map(async (s) => {\n const a = await t.joinPath(await h, await t.fileNameForId(s.id)), i = await t.readObject(a), c = Array.isArray(i) ? i : [], o = c.findIndex((w) => w.id === s.id);\n if (o === -1)\n throw new Error(`Item with id \"${String(s.id)}\" does not exist`);\n k(n, c[o]);\n const r = [...c];\n r.splice(o, 1), await (r.length === 0 ? t.removeEntry(a) : t.writeObject(a, r));\n })), await F(n);\n },\n removeAll: async () => {\n await t.fileExists(x).catch(() => !1) && await t.removeEntry(x, { recursive: !0 });\n }\n });\n}\nexport {\n R as default\n};\n//# sourceMappingURL=index.mjs.map\n","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 * 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) => {\n if (typeof id !== 'string' && typeof id !== 'number') {\n return Promise.resolve(toSafeFilename(serializeValue(id)));\n }\n const idString = typeof id === 'string' ? id : id.toString(16).padStart(4, '0');\n const firstShard = toSafeFilename(idString.slice(0, 2) || '00');\n const secondShard = toSafeFilename(idString.slice(2, 4) || '00');\n return Promise.resolve(`${firstShard}/${secondShard}/${toSafeFilename(idString)}`);\n },\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) => {\n const rootDirectory = await navigator.storage.getDirectory();\n try {\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 catch {\n return null;\n }\n },\n writeObject: async (path, value) => {\n const rootDirectory = await navigator.storage.getDirectory();\n const handle = await getFileHandleForPath(rootDirectory, path, true);\n const writableStream = await handle.createWritable();\n const text = serialize(value);\n await writableStream.write(text);\n await writableStream.close();\n },\n readIndexObject: async (path) => {\n const rootDirectory = await navigator.storage.getDirectory();\n try {\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 catch {\n return null;\n }\n },\n writeIndexObject: async (path, value) => {\n const rootDirectory = await navigator.storage.getDirectory();\n const handle = await getFileHandleForPath(rootDirectory, path, true);\n const writableStream = await handle.createWritable();\n const text = serialize(value);\n await writableStream.write(text);\n await writableStream.close();\n },\n listFilesRecursive: async (directoryPath) => {\n const rootDirectory = await navigator.storage.getDirectory();\n let directoryHandle;\n try {\n directoryHandle = await ensureDirectoryExists(rootDirectory, directoryPath, false);\n }\n catch {\n /* istanbul ignore next -- @preserve */\n return [];\n }\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":["R","t","x","h","p","g","n","s","a","i","O","e","o","r","m","c","w","l","D","j","d","I","P","M","S","k","F","y","f","u","E","b","K","toSafeFilename","input","name","escapeRegex","createOPFSAdapter","folderName","options","serialize","deserialize","ensureDirectoryExists","rootDirectory","directoryPath","createIfMissing","parts","current","part","getFileHandleForPath","fullPath","fileName","driver","id","serializeValue","idString","firstShard","secondShard","key","path","directory","text","value","writableStream","directoryHandle","files","entry","subFiles","removeOptions","pathParts","createGenericFSAdapter"],"mappings":"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"}
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": "2.0.0-beta.0",
3
+ "version": "2.0.0-beta.2",
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.0"
56
+ "@signaldb/core": "2.0.0-beta.2"
57
57
  },
58
58
  "dependencies": {
59
- "@signaldb/generic-fs": "^2.0.0-beta.0"
59
+ "@signaldb/generic-fs": "^2.0.0-beta.2"
60
60
  }
61
61
  }