@signaldb/localstorage 2.0.0-beta.5 → 2.0.0-beta.7

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.
Files changed (49) hide show
  1. package/dist/base/core/src/AsyncDataAdapter.d.ts +64 -0
  2. package/dist/base/core/src/AutoFetchDataAdapter.d.ts +112 -0
  3. package/dist/base/core/src/Collection/Cursor.d.ts +113 -0
  4. package/dist/base/core/src/Collection/Observer.d.ts +64 -0
  5. package/dist/base/core/src/Collection/index.d.ts +294 -0
  6. package/dist/base/core/src/Collection/types.d.ts +28 -0
  7. package/dist/base/core/src/DataAdapter.d.ts +35 -0
  8. package/dist/base/core/src/DefaultDataAdapter.d.ts +35 -0
  9. package/dist/base/core/src/WorkerDataAdapter.d.ts +25 -0
  10. package/dist/base/core/src/WorkerDataAdapterHost.d.ts +62 -0
  11. package/dist/base/core/src/createIndex.d.ts +7 -0
  12. package/dist/base/core/src/createIndexProvider.d.ts +8 -0
  13. package/dist/base/core/src/createReactivityAdapter.d.ts +8 -0
  14. package/dist/base/core/src/createStorageAdapter.d.ts +9 -0
  15. package/dist/base/core/src/getIndexInfo.d.ts +39 -0
  16. package/dist/base/core/src/index.d.ts +22 -0
  17. package/dist/base/core/src/types/Dependency.d.ts +4 -0
  18. package/dist/base/core/src/types/IndexProvider.d.ts +26 -0
  19. package/dist/base/core/src/types/Modifier.d.ts +46 -0
  20. package/dist/base/core/src/types/ReactivityAdapter.d.ts +6 -0
  21. package/dist/base/core/src/types/Selector.d.ts +46 -0
  22. package/dist/base/core/src/types/Signal.d.ts +4 -0
  23. package/dist/base/core/src/types/StorageAdapter.d.ts +20 -0
  24. package/dist/base/core/src/utils/EventEmitter.d.ts +71 -0
  25. package/dist/base/core/src/utils/batchOnNextTick.d.ts +16 -0
  26. package/dist/base/core/src/utils/compact.d.ts +9 -0
  27. package/dist/base/core/src/utils/createSignal.d.ts +14 -0
  28. package/dist/base/core/src/utils/deepClone.d.ts +17 -0
  29. package/dist/base/core/src/utils/get.d.ts +9 -0
  30. package/dist/base/core/src/utils/getMatchingKeys.d.ts +19 -0
  31. package/dist/base/core/src/utils/intersection.d.ts +9 -0
  32. package/dist/base/core/src/utils/isEqual.d.ts +14 -0
  33. package/dist/base/core/src/utils/isFieldExpression.d.ts +11 -0
  34. package/dist/base/core/src/utils/match.d.ts +12 -0
  35. package/dist/base/core/src/utils/modify.d.ts +14 -0
  36. package/dist/base/core/src/utils/project.d.ts +15 -0
  37. package/dist/base/core/src/utils/queryId.d.ts +9 -0
  38. package/dist/base/core/src/utils/randomId.d.ts +7 -0
  39. package/dist/base/core/src/utils/reactiveOrAsync.d.ts +59 -0
  40. package/dist/base/core/src/utils/serializeValue.d.ts +12 -0
  41. package/dist/base/core/src/utils/set.d.ts +13 -0
  42. package/dist/base/core/src/utils/sortItems.d.ts +12 -0
  43. package/dist/base/core/src/utils/uniqueBy.d.ts +10 -0
  44. package/dist/index.mjs +151 -165
  45. package/dist/index.mjs.map +1 -1
  46. package/dist/index.umd.js +2 -2
  47. package/dist/index.umd.js.map +1 -1
  48. package/package.json +2 -2
  49. /package/dist/{index.d.ts → storage-adapters/localstorage/src/index.d.ts} +0 -0
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Compares two values for deep equality.
3
+ * @param a - The first value to compare.
4
+ * @param b - The second value to compare.
5
+ * @returns - Returns `true` if the two values are deeply equal, otherwise `false`.
6
+ * @example
7
+ * isEqual({ a: 1 }, { a: 1 }); // true
8
+ * isEqual([1, 2], [1, 2]); // true
9
+ * isEqual(new Date(0), new Date(0)); // true
10
+ * isEqual(/abc/, /abc/); // true
11
+ * isEqual({ a: 1 }, { a: 2 }); // false
12
+ * isEqual(null, null); // true
13
+ */
14
+ export default function isEqual<T, K>(a: T, b: K): boolean;
@@ -0,0 +1,11 @@
1
+ import type { FieldExpression } from '../types/Selector';
2
+ /**
3
+ * Determines whether a given object is a valid field expression.
4
+ * A field expression is an object containing query operators supported by MongoDB-style queries.
5
+ * @template T - The type of the field expression.
6
+ * @param expression - The object to test.
7
+ * @returns A boolean indicating whether the object is a valid field expression.
8
+ * - `true` if the object contains only recognized query operators.
9
+ * - `false` otherwise.
10
+ */
11
+ export default function isFieldExpression<T>(expression: any): expression is FieldExpression<T>;
@@ -0,0 +1,12 @@
1
+ import type Selector from '../types/Selector';
2
+ type BaseItem = Record<string, any>;
3
+ /**
4
+ * Tests whether a given item matches a specified selector.
5
+ * Uses the `mingo` library to evaluate the query.
6
+ * @template T - The type of the item being tested.
7
+ * @param item - The item to test against the selector.
8
+ * @param selector - The query selector used to match the item.
9
+ * @returns A boolean indicating whether the item matches the selector.
10
+ */
11
+ export default function match<T extends BaseItem = BaseItem>(item: T, selector: Selector<T>): boolean;
12
+ export {};
@@ -0,0 +1,14 @@
1
+ import type Modifier from '../types/Modifier';
2
+ /**
3
+ * Applies a modifier to an object and returns a new modified object.
4
+ * @template T - The type of the object to be modified.
5
+ * @param item - The object to be modified.
6
+ * @param modifier - The modifier to apply. This can be any transformation logic.
7
+ * @returns - Returns a new object with the modifications applied.
8
+ * @example
9
+ * const item = { a: 1, b: 2 }
10
+ * const modifier = { $set: { b: 3, c: 4 } }
11
+ * const result = modify(item, modifier)
12
+ * // result: { a: 1, b: 3, c: 4 }
13
+ */
14
+ export default function modify<T extends Record<string, any>>(item: T, modifier: Modifier): T;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Projects the fields of an object based on a specified fields configuration.
3
+ * Supports inclusion (`1`) and exclusion (`0`) of specific fields. Creates a new object
4
+ * with the desired fields included or excluded, based on the configuration.
5
+ * @template T - The type of the object being projected.
6
+ * @param item - The original object to project fields from.
7
+ * @param fields - An object defining the fields to include (`1`) or exclude (`0`).
8
+ * - Keys are the field names, and values are either `1` (include) or `0` (exclude).
9
+ * @returns A new object with the specified fields included or excluded.
10
+ * - If all fields are set to `0`, the excluded fields are removed from the result.
11
+ * - If fields are set to `1`, only the included fields are retained.
12
+ */
13
+ export default function project<T extends Record<string, any>>(item: T, fields: {
14
+ [P in keyof T]?: 0 | 1;
15
+ } & Record<string, 0 | 1>): T;
@@ -0,0 +1,9 @@
1
+ import type { QueryOptions } from '../DataAdapter';
2
+ import type Selector from '../types/Selector';
3
+ /**
4
+ * Generates a unique identifier for a query based on its selector and options.
5
+ * @param selector - The selector object.
6
+ * @param options - The query options object (optional).
7
+ * @returns A unique identifier string for the query.
8
+ */
9
+ export default function queryId(selector: Selector<any>, options?: QueryOptions<any>): string;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * creates a random id
3
+ * @returns a random string of 16 characters
4
+ * @example
5
+ * randomId() // '1234567890abcdef'
6
+ */
7
+ export default function randomId(): string;
@@ -0,0 +1,59 @@
1
+ export type MaybePromise<T> = T | Promise<T>;
2
+ /**
3
+ * Options that control execution mode (and potential future mode-specific behavior).
4
+ * Keep this minimal; you can extend it later (e.g. signal, timeoutMs, debugLabel).
5
+ */
6
+ export type ModeOptions = {
7
+ async?: boolean;
8
+ };
9
+ /**
10
+ * A generator helper that makes TypeScript infer the “synchronous value type” for maybe-async expressions.
11
+ *
12
+ * Usage:
13
+ * const doc = yield* unwrap(Collection.findOne(...))
14
+ * const list = yield* unwrap(Collection.find(...).fetch())
15
+ *
16
+ * Runtime note:
17
+ * This does not “unwrap” Promises by itself. It yields the value/Promise to the runner and returns the
18
+ * value that the runner feeds back via `.next(...)`.
19
+ * @param value The value (or Promise of a value) to yield to the runner.
20
+ * @returns A generator that yields `value` and resolves to the runner-supplied unwrapped `T`.
21
+ */
22
+ export declare function unwrap<T>(value: MaybePromise<T>): Generator<MaybePromise<T>, T, T>;
23
+ /**
24
+ * Generator shape used by the factory.
25
+ *
26
+ * `TThis` is the type of `this` inside the generator.
27
+ * `Args` are the method parameters (excluding the mode flag).
28
+ * `TReturn` is the final return value of the workflow.
29
+ * `TNext` is the type that is yielded/awaited and fed back via `.next(...)`.
30
+ *
31
+ * Note:
32
+ * - For best inference at yield sites, prefer `yield* unwrap(expr)` for maybe-async expressions.
33
+ */
34
+ export type ReactiveOrAsyncGen<TThis, Arguments extends any[], TReturn, TNext> = (this: TThis, a: boolean, ...args: Arguments) => Generator<MaybePromise<TNext>, TReturn, TNext>;
35
+ /**
36
+ * The method type produced from the generator signature.
37
+ * Adds overloads so that `{ async: true }` yields a `Promise<...>` return type.
38
+ */
39
+ export type ReactiveOrAsyncMethod<TThis, P extends any[], R, N> = {
40
+ (this: TThis, ...args: P): R;
41
+ (this: TThis, ...args: [...P, ModeOptions?]): MaybePromise<R>;
42
+ (this: TThis, ...args: [...P, {
43
+ async: true;
44
+ }]): Promise<R>;
45
+ } & {
46
+ /** Exposes the underlying generator for composition via `yield* method.generator.call(this, a, ...)` */
47
+ generator: (this: TThis, a: boolean, ...args: P) => Generator<MaybePromise<N>, R, N>;
48
+ };
49
+ /**
50
+ * Factory that turns a generator workflow into a callable method that can run in sync (reactive) or async mode.
51
+ *
52
+ * Call style:
53
+ * fn(a, b) -> sync/reactive return
54
+ * await fn(a, b, { async: true }) -> async return
55
+ * @param gen Generator workflow. Receives `(a)` which indicates async mode and should `yield`/`yield* unwrap(...)`
56
+ * any values that may be Promises.
57
+ * @returns A callable method with overloads plus a `.generator` property for composition.
58
+ */
59
+ export default function reactiveOrAsync<TThis, P extends any[], R, N>(gen: (this: TThis, a: boolean, ...args: P) => Generator<MaybePromise<N>, R, N>): ReactiveOrAsyncMethod<TThis, P, R, N>;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Serializes a value into a string representation.
3
+ * Handles various types, including strings, numbers, booleans, dates, and objects.
4
+ * Falls back to JSON stringification for unsupported types.
5
+ * @param value - The value to serialize.
6
+ * - Strings are returned as-is.
7
+ * - Numbers and booleans are converted to their string representation.
8
+ * - Dates are converted to ISO string format.
9
+ * - Other values are stringified using `JSON.stringify`.
10
+ * @returns A string representation of the value.
11
+ */
12
+ export default function serializeValue(value: any): string | null;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Sets a value at a specified path within an object. Creates nested structures
3
+ * (arrays or objects) as needed to set the value at the correct location. Supports
4
+ * deleting the key if the value is `undefined` and the `deleteIfUndefined` flag is set to `true`.
5
+ * @template T - The type of the object to modify.
6
+ * @template K - The type of the value to set.
7
+ * @param object - The object to modify. The object is mutated directly.
8
+ * @param path - The path (dot or bracket notation) where the value should be set.
9
+ * @param value - The value to set at the specified path.
10
+ * @param deleteIfUndefined - A boolean indicating whether to delete the key if the value is `undefined` (default: `false`).
11
+ * @returns The modified object.
12
+ */
13
+ export default function set<T extends object, K>(object: T, path: string, value: K, deleteIfUndefined?: boolean): T;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Sorts an array of items based on multiple fields and their specified sort order.
3
+ * Uses the `fast-sort` library for efficient sorting.
4
+ * @template T - The type of the items in the array.
5
+ * @param items - The array of items to be sorted.
6
+ * @param sortFields - An object defining the sort order for each field.
7
+ * - Keys are the field names, and values are either `1` (ascending) or `-1` (descending).
8
+ * @returns A new array of items sorted based on the specified fields and their order.
9
+ */
10
+ export default function sortItems<T extends Record<string, any>>(items: T[], sortFields: {
11
+ [P in keyof T]?: -1 | 1;
12
+ } & Record<string, -1 | 1>): T[];
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Filters an array to ensure unique values based on a specified key or transformation function.
3
+ * @template T - The type of the elements in the array.
4
+ * @param array - The array to filter for unique values.
5
+ * @param fn - A key or transformation function to determine uniqueness.
6
+ * - If a key is provided, it will use the corresponding property of each element for uniqueness.
7
+ * - If a function is provided, it will use the return value of the function applied to each element for uniqueness.
8
+ * @returns A new array containing only unique elements based on the specified key or transformation.
9
+ */
10
+ export default function uniqueBy<T>(array: T[], fn: keyof T | ((item: T) => any)): T[];
package/dist/index.mjs CHANGED
@@ -1,166 +1,152 @@
1
- import { createStorageAdapter as C, get as f, serializeValue as E } from "@signaldb/core";
2
- function T(M, g) {
3
- const c = globalThis.localStorage;
4
- if (c == null)
5
- throw new Error("localStorage is not available in this environment");
6
- const y = g?.serialize || ((e) => JSON.stringify(e)), w = g?.deserialize || ((e) => JSON.parse(e)), $ = g?.databaseName || "signaldb", b = `${M}`, d = `${$}-${b}`, u = (e) => `${d}-index-${e}`, i = [], l = () => {
7
- const e = c.getItem(d);
8
- if (!e)
9
- return [];
10
- try {
11
- const o = w(e);
12
- return Array.isArray(o) ? o : [];
13
- } catch {
14
- return [];
15
- }
16
- }, h = (e) => {
17
- c.setItem(d, y(e));
18
- }, z = async (e) => {
19
- const o = c.getItem(u(e));
20
- if (!o)
21
- throw new Error(`Index on field "${e}" does not exist`);
22
- let t;
23
- try {
24
- t = w(o);
25
- } catch {
26
- throw new Error(`Corrupted index on field "${e}"`);
27
- }
28
- const n = /* @__PURE__ */ new Map();
29
- return Object.entries(t).forEach(([s, r]) => {
30
- n.has(s) || n.set(s, /* @__PURE__ */ new Set()), r.forEach((a) => n.get(s)?.add(a));
31
- }), n;
32
- }, v = (e, o) => {
33
- const t = {};
34
- o.forEach((n, s) => {
35
- t[String(E(s))] = [...n];
36
- }), c.setItem(u(e), y(t));
37
- }, A = async (e, o = l()) => {
38
- const t = /* @__PURE__ */ new Map();
39
- o.forEach((n) => {
40
- const s = f(n, e);
41
- s != null && (t.has(s) || t.set(s, /* @__PURE__ */ new Set()), t.get(s)?.add(n.id));
42
- }), v(e, t);
43
- }, m = (e) => String(E(e)), D = (e) => {
44
- const o = c.getItem(u(e));
45
- if (!o)
46
- return;
47
- let t;
48
- try {
49
- t = w(o);
50
- } catch {
51
- throw new Error(`Corrupted index on field "${e}"`);
52
- }
53
- const n = /* @__PURE__ */ new Map();
54
- return Object.entries(t).forEach(([s, r]) => {
55
- n.set(s, new Set(r));
56
- }), n;
57
- }, p = (e, o, t, n, s) => {
58
- e.has(o) || e.set(o, { adds: /* @__PURE__ */ new Map(), removes: /* @__PURE__ */ new Map() });
59
- const r = e.get(o);
60
- if (!r)
61
- return;
62
- const a = t === "add" ? r.adds : r.removes;
63
- a.has(n) || a.set(n, /* @__PURE__ */ new Set()), a.get(n)?.add(s);
64
- }, I = (e) => {
65
- e.forEach((o, t) => {
66
- const n = D(t);
67
- n && (o.removes.forEach((s, r) => {
68
- const a = n.get(r);
69
- a && (s.forEach((x) => a.delete(x)), a.size === 0 && n.delete(r));
70
- }), o.adds.forEach((s, r) => {
71
- let a = n.get(r);
72
- a || (a = /* @__PURE__ */ new Set(), n.set(r, a)), s.forEach((x) => a.add(x));
73
- }), v(t, n));
74
- });
75
- }, K = (e, o, t, n, s) => {
76
- const r = t == null ? void 0 : m(t), a = n == null ? void 0 : m(n);
77
- r !== a && (r != null && p(e, o, "remove", r, s), a != null && p(e, o, "add", a, s));
78
- }, N = (e, o, t) => {
79
- if (o)
80
- for (const n of i)
81
- K(e, n, f(o, n), f(t, n), t.id);
82
- else
83
- for (const n of i) {
84
- const s = f(t, n);
85
- s != null && p(e, n, "add", m(s), t.id);
86
- }
87
- }, O = (e, o) => {
88
- for (const t of i) {
89
- const n = f(o, t);
90
- n != null && p(e, t, "remove", m(n), o.id);
91
- }
92
- }, S = (e) => {
93
- const o = l(), t = new Map(o.map((s) => [s.id, s])), n = /* @__PURE__ */ new Map();
94
- for (const s of e) {
95
- const r = t.get(s.id);
96
- N(n, r, s), t.set(s.id, s);
97
- }
98
- h([...t.values()]), I(n);
99
- };
100
- return C({
101
- // lifecycle methods
102
- setup: async () => {
103
- c.getItem(d) == null && h([]);
104
- const e = `${d}-index-`;
105
- for (let o = 0; o < c.length; o++) {
106
- const t = c.key(o);
107
- if (t && t.startsWith(e)) {
108
- const n = t.slice(e.length);
109
- i.includes(n) || i.push(n);
110
- }
111
- }
112
- },
113
- teardown: async () => {
114
- },
115
- // data retrieval methods
116
- readAll: async () => l(),
117
- readIds: async (e) => {
118
- const o = l(), t = new Set(e);
119
- return o.filter((n) => t.has(n.id));
120
- },
121
- // index methods
122
- createIndex: async (e) => {
123
- if (e === "id")
124
- throw new Error("Cannot create index on id field");
125
- i.includes(e) || i.push(e), await A(e);
126
- },
127
- dropIndex: async (e) => {
128
- if (i.includes(e)) {
129
- const t = i.indexOf(e);
130
- i.splice(t, 1);
131
- }
132
- const o = u(e);
133
- if (c.getItem(o) == null)
134
- throw new Error(`Index on field "${e}" does not exist`);
135
- c.removeItem(o);
136
- },
137
- readIndex: z,
138
- // data manipulation methods
139
- insert: async (e) => {
140
- S(e);
141
- },
142
- replace: async (e) => {
143
- S(e);
144
- },
145
- remove: async (e) => {
146
- const o = l(), t = new Map(o.map((r) => [r.id, r])), n = /* @__PURE__ */ new Map();
147
- new Set(e.map((r) => r.id)).forEach((r) => {
148
- const a = t.get(r);
149
- a && (O(n, a), t.delete(r));
150
- }), h([...t.values()]), I(n);
151
- },
152
- removeAll: async () => {
153
- h([]);
154
- const e = `${d}-index-`, o = [];
155
- for (let t = 0; t < c.length; t++) {
156
- const n = c.key(t);
157
- n && n.startsWith(e) && o.push(n);
158
- }
159
- o.forEach((t) => c.removeItem(t)), i.splice(0);
160
- }
161
- });
1
+ import { createStorageAdapter as e, get as t, serializeValue as n } from "@signaldb/core";
2
+ //#region src/index.ts
3
+ function r(r, i) {
4
+ let a = globalThis.localStorage;
5
+ if (a == null) throw Error("localStorage is not available in this environment");
6
+ let o = i?.serialize || ((e) => JSON.stringify(e)), s = i?.deserialize || ((e) => JSON.parse(e)), c = `${i?.databaseName || "signaldb"}-${`${r}`}`, l = (e) => `${c}-index-${e}`, u = [], d = () => {
7
+ let e = a.getItem(c);
8
+ if (!e) return [];
9
+ try {
10
+ let t = s(e);
11
+ return Array.isArray(t) ? t : [];
12
+ } catch {
13
+ return [];
14
+ }
15
+ }, f = (e) => {
16
+ a.setItem(c, o(e));
17
+ }, p = async (e) => {
18
+ let t = a.getItem(l(e));
19
+ if (!t) throw Error(`Index on field "${e}" does not exist`);
20
+ let n;
21
+ try {
22
+ n = s(t);
23
+ } catch {
24
+ throw Error(`Corrupted index on field "${e}"`);
25
+ }
26
+ let r = /* @__PURE__ */ new Map();
27
+ return Object.entries(n).forEach(([e, t]) => {
28
+ r.has(e) || r.set(e, /* @__PURE__ */ new Set()), t.forEach((t) => r.get(e)?.add(t));
29
+ }), r;
30
+ }, m = (e, t) => {
31
+ let r = {};
32
+ t.forEach((e, t) => {
33
+ r[String(n(t))] = [...e];
34
+ }), a.setItem(l(e), o(r));
35
+ }, h = async (e, n = d()) => {
36
+ let r = /* @__PURE__ */ new Map();
37
+ n.forEach((n) => {
38
+ let i = t(n, e);
39
+ i != null && (r.has(i) || r.set(i, /* @__PURE__ */ new Set()), r.get(i)?.add(n.id));
40
+ }), m(e, r);
41
+ }, g = (e) => String(n(e)), _ = (e) => {
42
+ let t = a.getItem(l(e));
43
+ if (!t) return;
44
+ let n;
45
+ try {
46
+ n = s(t);
47
+ } catch {
48
+ throw Error(`Corrupted index on field "${e}"`);
49
+ }
50
+ let r = /* @__PURE__ */ new Map();
51
+ return Object.entries(n).forEach(([e, t]) => {
52
+ r.set(e, new Set(t));
53
+ }), r;
54
+ }, v = (e, t, n, r, i) => {
55
+ e.has(t) || e.set(t, {
56
+ adds: /* @__PURE__ */ new Map(),
57
+ removes: /* @__PURE__ */ new Map()
58
+ });
59
+ let a = e.get(t);
60
+ if (!a) return;
61
+ let o = n === "add" ? a.adds : a.removes;
62
+ o.has(r) || o.set(r, /* @__PURE__ */ new Set()), o.get(r)?.add(i);
63
+ }, y = (e) => {
64
+ e.forEach((e, t) => {
65
+ let n = _(t);
66
+ n && (e.removes.forEach((e, t) => {
67
+ let r = n.get(t);
68
+ r && (e.forEach((e) => r.delete(e)), r.size === 0 && n.delete(t));
69
+ }), e.adds.forEach((e, t) => {
70
+ let r = n.get(t);
71
+ r || (r = /* @__PURE__ */ new Set(), n.set(t, r)), e.forEach((e) => r.add(e));
72
+ }), m(t, n));
73
+ });
74
+ }, b = (e, t, n, r, i) => {
75
+ let a = n == null ? void 0 : g(n), o = r == null ? void 0 : g(r);
76
+ a !== o && (a != null && v(e, t, "remove", a, i), o != null && v(e, t, "add", o, i));
77
+ }, x = (e, n, r) => {
78
+ if (n) for (let i of u) b(e, i, t(n, i), t(r, i), r.id);
79
+ else for (let n of u) {
80
+ let i = t(r, n);
81
+ i != null && v(e, n, "add", g(i), r.id);
82
+ }
83
+ }, S = (e, n) => {
84
+ for (let r of u) {
85
+ let i = t(n, r);
86
+ i != null && v(e, r, "remove", g(i), n.id);
87
+ }
88
+ }, C = (e) => {
89
+ let t = d(), n = new Map(t.map((e) => [e.id, e])), r = /* @__PURE__ */ new Map();
90
+ for (let t of e) x(r, n.get(t.id), t), n.set(t.id, t);
91
+ f([...n.values()]), y(r);
92
+ };
93
+ return e({
94
+ setup: async () => {
95
+ a.getItem(c) ?? f([]);
96
+ let e = `${c}-index-`;
97
+ for (let t = 0; t < a.length; t++) {
98
+ let n = a.key(t);
99
+ if (n && n.startsWith(e)) {
100
+ let t = n.slice(e.length);
101
+ u.includes(t) || u.push(t);
102
+ }
103
+ }
104
+ },
105
+ teardown: async () => {},
106
+ readAll: async () => d(),
107
+ readIds: async (e) => {
108
+ let t = d(), n = new Set(e);
109
+ return t.filter((e) => n.has(e.id));
110
+ },
111
+ createIndex: async (e) => {
112
+ if (e === "id") throw Error("Cannot create index on id field");
113
+ u.includes(e) || u.push(e), await h(e);
114
+ },
115
+ dropIndex: async (e) => {
116
+ if (u.includes(e)) {
117
+ let t = u.indexOf(e);
118
+ u.splice(t, 1);
119
+ }
120
+ let t = l(e);
121
+ if (a.getItem(t) == null) throw Error(`Index on field "${e}" does not exist`);
122
+ a.removeItem(t);
123
+ },
124
+ readIndex: p,
125
+ insert: async (e) => {
126
+ C(e);
127
+ },
128
+ replace: async (e) => {
129
+ C(e);
130
+ },
131
+ remove: async (e) => {
132
+ let t = d(), n = new Map(t.map((e) => [e.id, e])), r = /* @__PURE__ */ new Map();
133
+ new Set(e.map((e) => e.id)).forEach((e) => {
134
+ let t = n.get(e);
135
+ t && (S(r, t), n.delete(e));
136
+ }), f([...n.values()]), y(r);
137
+ },
138
+ removeAll: async () => {
139
+ f([]);
140
+ let e = `${c}-index-`, t = [];
141
+ for (let n = 0; n < a.length; n++) {
142
+ let r = a.key(n);
143
+ r && r.startsWith(e) && t.push(r);
144
+ }
145
+ t.forEach((e) => a.removeItem(e)), u.splice(0);
146
+ }
147
+ });
162
148
  }
163
- export {
164
- T as default
165
- };
166
- //# sourceMappingURL=index.mjs.map
149
+ //#endregion
150
+ export { r as default };
151
+
152
+ //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":["import { createStorageAdapter, get, serializeValue } from '@signaldb/core';\n/**\n * Creates a storage adapter for managing a SignalDB collection using localStorage.\n * @param name - A unique name for the collection, used as part of the localStorage key.\n * @param options - Optional configuration for the adapter.\n * @param options.databaseName - An optional name for the database to namespace the storage (default: 'signaldb').\n * @param options.serialize - A function to serialize items to a string (default: `JSON.stringify`).\n * @param options.deserialize - A function to deserialize a string into items (default: `JSON.parse`).\n * @returns A SignalDB storage adapter for managing data in localStorage.\n */\nexport default function createLocalStorageAdapter(name, options) {\n const localStorage = globalThis.localStorage;\n if (localStorage == null) {\n throw new Error('localStorage is not available in this environment');\n }\n const serialize = options?.serialize || (data => JSON.stringify(data));\n const deserialize = options?.deserialize || (input => JSON.parse(input));\n const databaseName = options?.databaseName || 'signaldb';\n const storeName = `${name}`;\n // We use a single key that namespaces by database and store names\n const storageKey = `${databaseName}-${storeName}`;\n const indexKeyFor = (field) => `${storageKey}-index-${field}`;\n const indices = [];\n const readFromStorage = () => {\n const serialized = localStorage.getItem(storageKey);\n if (!serialized)\n return [];\n try {\n const parsed = deserialize(serialized);\n return Array.isArray(parsed) ? parsed : [];\n }\n catch {\n // If parsing fails, treat as empty to avoid corrupting runtime\n return [];\n }\n };\n const writeToStorage = (items) => {\n localStorage.setItem(storageKey, serialize(items));\n };\n const readIndex = async (field) => {\n const serialized = localStorage.getItem(indexKeyFor(field));\n if (!serialized)\n throw new Error(`Index on field \"${field}\" does not exist`);\n let data;\n try {\n data = deserialize(serialized);\n }\n catch {\n throw new Error(`Corrupted index on field \"${field}\"`);\n }\n const index = new Map();\n Object.entries(data).forEach(([key, ids]) => {\n if (!index.has(key))\n index.set(key, new Set());\n ids.forEach(id => index.get(key)?.add(id));\n });\n return index;\n };\n const saveIndexMap = (field, index) => {\n const safeIndex = {};\n index.forEach((ids, key) => {\n safeIndex[String(serializeValue(key))] = [...ids];\n });\n localStorage.setItem(indexKeyFor(field), serialize(safeIndex));\n };\n const ensureIndex = async (field, items = readFromStorage()) => {\n const index = new Map();\n items.forEach((item) => {\n const fieldValue = get(item, field);\n if (fieldValue == null)\n return;\n if (!index.has(fieldValue))\n index.set(fieldValue, new Set());\n index.get(fieldValue)?.add(item.id);\n });\n saveIndexMap(field, index);\n };\n // --- Delta indexing helpers ---\n const safeKeyFor = (value) => String(serializeValue(value));\n const loadIndexMap = (field) => {\n const serialized = localStorage.getItem(indexKeyFor(field));\n if (!serialized)\n return undefined;\n let data;\n try {\n data = deserialize(serialized);\n }\n catch {\n throw new Error(`Corrupted index on field \"${field}\"`);\n }\n const index = new Map();\n Object.entries(data).forEach(([key, ids]) => {\n index.set(key, new Set(ids));\n });\n return index;\n };\n const addToDelta = (deltas, field, kind, key, id) => {\n if (!deltas.has(field)) {\n deltas.set(field, { adds: new Map(), removes: new Map() });\n }\n const delta = deltas.get(field);\n if (!delta)\n return;\n const target = kind === 'add' ? delta.adds : delta.removes;\n if (!target.has(key))\n target.set(key, new Set());\n target.get(key)?.add(id);\n };\n const applyIndexDeltas = (deltas) => {\n // Update only the indices that have changes\n deltas.forEach((delta, field) => {\n const index = loadIndexMap(field);\n // If the index doesn't exist, skip (we only maintain indices that were created)\n if (!index)\n return;\n // Apply removals\n delta.removes.forEach((ids, key) => {\n const set = index.get(key);\n if (!set)\n return;\n ids.forEach(id => set.delete(id));\n if (set.size === 0)\n index.delete(key);\n });\n // Apply additions\n delta.adds.forEach((ids, key) => {\n let set = index.get(key);\n if (!set) {\n set = new Set();\n index.set(key, set);\n }\n ids.forEach(id => set.add(id));\n });\n saveIndexMap(field, index);\n });\n };\n const addDeltaForChange = (deltas, field, oldValue, newValue, id) => {\n const oldKey = oldValue == null ? undefined : safeKeyFor(oldValue);\n const newKey = newValue == null ? undefined : safeKeyFor(newValue);\n if (oldKey === newKey)\n return;\n if (oldKey != null)\n addToDelta(deltas, field, 'remove', oldKey, id);\n if (newKey != null)\n addToDelta(deltas, field, 'add', newKey, id);\n };\n const accumulateUpsertDelta = (deltas, existing, next) => {\n if (existing) {\n for (const field of indices) {\n addDeltaForChange(deltas, field, get(existing, field), get(next, field), next.id);\n }\n }\n else {\n for (const field of indices) {\n const value = get(next, field);\n if (value == null)\n continue;\n addToDelta(deltas, field, 'add', safeKeyFor(value), next.id);\n }\n }\n };\n const accumulateRemoveDelta = (deltas, existing) => {\n for (const field of indices) {\n const value = get(existing, field);\n if (value == null)\n continue;\n addToDelta(deltas, field, 'remove', safeKeyFor(value), existing.id);\n }\n };\n const upsertItems = (itemsToUpsert) => {\n const items = readFromStorage();\n const byId = new Map(items.map(item => [item.id, item]));\n const deltas = new Map();\n for (const item of itemsToUpsert) {\n const existing = byId.get(item.id);\n accumulateUpsertDelta(deltas, existing, item);\n byId.set(item.id, item);\n }\n writeToStorage([...byId.values()]);\n applyIndexDeltas(deltas);\n };\n return createStorageAdapter({\n // lifecycle methods\n setup: async () => {\n // For localStorage, there is no database to open; we just ensure the key exists\n if (localStorage.getItem(storageKey) == null) {\n writeToStorage([]);\n }\n // Hydrate known index fields from existing keys so that we can keep them updated across sessions\n const prefix = `${storageKey}-index-`;\n for (let i = 0; i < localStorage.length; i++) {\n const k = localStorage.key(i);\n if (k && k.startsWith(prefix)) {\n const field = k.slice(prefix.length);\n if (!indices.includes(field))\n indices.push(field);\n }\n }\n },\n teardown: async () => {\n // no-op\n },\n // data retrieval methods\n readAll: async () => {\n return readFromStorage();\n },\n readIds: async (ids) => {\n const items = readFromStorage();\n const idSet = new Set(ids);\n return items.filter(item => idSet.has(item.id));\n },\n // index methods\n createIndex: async (field) => {\n if (field === 'id')\n throw new Error('Cannot create index on id field');\n if (!indices.includes(field))\n indices.push(field);\n await ensureIndex(field);\n },\n dropIndex: async (field) => {\n if (indices.includes(field)) {\n const i = indices.indexOf(field);\n indices.splice(i, 1);\n }\n const key = indexKeyFor(field);\n if (localStorage.getItem(key) == null) {\n throw new Error(`Index on field \"${field}\" does not exist`);\n }\n localStorage.removeItem(key);\n },\n readIndex,\n // data manipulation methods\n insert: async (newItems) => {\n upsertItems(newItems);\n },\n replace: async (itemsToReplace) => {\n upsertItems(itemsToReplace);\n },\n remove: async (itemsToRemove) => {\n const items = readFromStorage();\n const byId = new Map(items.map(item => [item.id, item]));\n const deltas = new Map();\n const removeSet = new Set(itemsToRemove.map(item => item.id));\n removeSet.forEach((id) => {\n const existing = byId.get(id);\n if (!existing)\n return;\n accumulateRemoveDelta(deltas, existing);\n byId.delete(id);\n });\n writeToStorage([...byId.values()]);\n applyIndexDeltas(deltas);\n },\n removeAll: async () => {\n writeToStorage([]);\n // remove all index keys for this store\n const prefix = `${storageKey}-index-`;\n const keysToRemove = [];\n for (let i = 0; i < localStorage.length; i++) {\n const k = localStorage.key(i);\n if (k && k.startsWith(prefix))\n keysToRemove.push(k);\n }\n keysToRemove.forEach(k => localStorage.removeItem(k));\n indices.splice(0);\n },\n });\n}\n"],"names":["createLocalStorageAdapter","name","options","localStorage","serialize","data","deserialize","input","databaseName","storeName","storageKey","indexKeyFor","field","indices","readFromStorage","serialized","parsed","writeToStorage","items","readIndex","index","key","ids","id","saveIndexMap","safeIndex","serializeValue","ensureIndex","item","fieldValue","get","safeKeyFor","value","loadIndexMap","addToDelta","deltas","kind","delta","target","applyIndexDeltas","set","addDeltaForChange","oldValue","newValue","oldKey","newKey","accumulateUpsertDelta","existing","next","accumulateRemoveDelta","upsertItems","itemsToUpsert","byId","createStorageAdapter","prefix","i","k","idSet","newItems","itemsToReplace","itemsToRemove","keysToRemove"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { createStorageAdapter, get, serializeValue } from '@signaldb/core'\n\n/**\n * Creates a storage adapter for managing a SignalDB collection using localStorage.\n * @param name - A unique name for the collection, used as part of the localStorage key.\n * @param options - Optional configuration for the adapter.\n * @param options.databaseName - An optional name for the database to namespace the storage (default: 'signaldb').\n * @param options.serialize - A function to serialize items to a string (default: `JSON.stringify`).\n * @param options.deserialize - A function to deserialize a string into items (default: `JSON.parse`).\n * @returns A SignalDB storage adapter for managing data in localStorage.\n */\nexport default function createLocalStorageAdapter<\n T extends { id: I } & Record<string, any>,\n I,\n>(\n name: string,\n options?: {\n databaseName?: string,\n serialize?: (items: any) => string,\n deserialize?: (itemsString: string) => any,\n },\n\n) {\n const localStorage = globalThis.localStorage\n if (localStorage == null) {\n throw new Error('localStorage is not available in this environment')\n }\n\n const serialize = options?.serialize || (data => JSON.stringify(data))\n const deserialize = options?.deserialize || (input => JSON.parse(input))\n const databaseName = options?.databaseName || 'signaldb'\n const storeName = `${name}`\n\n // We use a single key that namespaces by database and store names\n const storageKey = `${databaseName}-${storeName}`\n\n const indexKeyFor = (field: string) => `${storageKey}-index-${field}`\n const indices: string[] = []\n\n const readFromStorage = (): T[] => {\n const serialized = localStorage.getItem(storageKey)\n if (!serialized) return []\n try {\n const parsed = deserialize(serialized)\n return Array.isArray(parsed) ? (parsed as T[]) : []\n } catch {\n // If parsing fails, treat as empty to avoid corrupting runtime\n return []\n }\n }\n\n const writeToStorage = (items: T[]) => {\n localStorage.setItem(storageKey, serialize(items))\n }\n\n const readIndex = async (field: string) => {\n const serialized = localStorage.getItem(indexKeyFor(field))\n if (!serialized) throw new Error(`Index on field \"${field}\" does not exist`)\n let data: Record<string, I[]>\n try {\n data = deserialize(serialized)\n } catch {\n throw new Error(`Corrupted index on field \"${field}\"`)\n }\n const index = new Map<any, Set<I>>()\n Object.entries(data).forEach(([key, ids]) => {\n if (!index.has(key)) index.set(key, new Set())\n ids.forEach(id => index.get(key)?.add(id))\n })\n return index\n }\n\n const saveIndexMap = (field: string, index: Map<any, Set<I>>) => {\n const safeIndex: Record<string, I[]> = {}\n index.forEach((ids, key) => {\n safeIndex[String(serializeValue(key))] = [...ids]\n })\n localStorage.setItem(indexKeyFor(field), serialize(safeIndex))\n }\n\n const ensureIndex = async (\n field: string,\n items: T[] = readFromStorage(),\n ) => {\n const index = new Map<any, Set<I>>()\n items.forEach((item) => {\n const fieldValue = get(item, field)\n if (fieldValue == null) return\n if (!index.has(fieldValue)) index.set(fieldValue, new Set())\n index.get(fieldValue)?.add(item.id)\n })\n saveIndexMap(field, index)\n }\n\n // --- Delta indexing helpers ---\n const safeKeyFor = (value: any) => String(serializeValue(value))\n\n const loadIndexMap = (field: string): Map<string, Set<I>> | undefined => {\n const serialized = localStorage.getItem(indexKeyFor(field))\n if (!serialized) return undefined\n let data: Record<string, I[]>\n try {\n data = deserialize(serialized)\n } catch {\n throw new Error(`Corrupted index on field \"${field}\"`)\n }\n const index = new Map<string, Set<I>>()\n Object.entries(data).forEach(([key, ids]) => {\n index.set(key, new Set(ids))\n })\n return index\n }\n\n type IndexDelta = {\n adds: Map<string, Set<I>>,\n removes: Map<string, Set<I>>,\n }\n\n const addToDelta = (\n deltas: Map<string, IndexDelta>,\n field: string,\n kind: 'add' | 'remove',\n key: string,\n id: I,\n ) => {\n if (!deltas.has(field)) {\n deltas.set(field, { adds: new Map(), removes: new Map() })\n }\n const delta = deltas.get(field)\n if (!delta) return\n const target = kind === 'add' ? delta.adds : delta.removes\n if (!target.has(key)) target.set(key, new Set<I>())\n target.get(key)?.add(id)\n }\n\n const applyIndexDeltas = (deltas: Map<string, IndexDelta>) => {\n // Update only the indices that have changes\n deltas.forEach((delta, field) => {\n const index = loadIndexMap(field)\n // If the index doesn't exist, skip (we only maintain indices that were created)\n if (!index) return\n\n // Apply removals\n delta.removes.forEach((ids, key) => {\n const set = index.get(key)\n if (!set) return\n ids.forEach(id => set.delete(id))\n if (set.size === 0) index.delete(key)\n })\n\n // Apply additions\n delta.adds.forEach((ids, key) => {\n let set = index.get(key)\n if (!set) {\n set = new Set<I>()\n index.set(key, set)\n }\n ids.forEach(id => set.add(id))\n })\n\n saveIndexMap(field, index)\n })\n }\n\n const addDeltaForChange = (\n deltas: Map<string, IndexDelta>,\n field: string,\n oldValue: any,\n newValue: any,\n id: I,\n ) => {\n const oldKey = oldValue == null ? undefined : safeKeyFor(oldValue)\n const newKey = newValue == null ? undefined : safeKeyFor(newValue)\n if (oldKey === newKey) return\n if (oldKey != null) addToDelta(deltas, field, 'remove', oldKey, id)\n if (newKey != null) addToDelta(deltas, field, 'add', newKey, id)\n }\n\n const accumulateUpsertDelta = (\n deltas: Map<string, IndexDelta>,\n existing: T | undefined,\n next: T,\n ) => {\n if (existing) {\n for (const field of indices) {\n addDeltaForChange(deltas, field, get(existing, field), get(next, field), next.id)\n }\n } else {\n for (const field of indices) {\n const value = get(next, field)\n if (value == null) continue\n addToDelta(deltas, field, 'add', safeKeyFor(value), next.id)\n }\n }\n }\n\n const accumulateRemoveDelta = (\n deltas: Map<string, IndexDelta>,\n existing: T,\n ) => {\n for (const field of indices) {\n const value = get(existing, field)\n if (value == null) continue\n addToDelta(deltas, field, 'remove', safeKeyFor(value), existing.id)\n }\n }\n\n const upsertItems = (itemsToUpsert: T[]) => {\n const items = readFromStorage()\n const byId = new Map<I, T>(items.map(item => [item.id, item]))\n\n const deltas = new Map<string, IndexDelta>()\n\n for (const item of itemsToUpsert) {\n const existing = byId.get(item.id)\n accumulateUpsertDelta(deltas, existing, item)\n byId.set(item.id, item)\n }\n\n writeToStorage([...byId.values()])\n applyIndexDeltas(deltas)\n }\n\n return createStorageAdapter<T, I>({\n // lifecycle methods\n setup: async () => {\n // For localStorage, there is no database to open; we just ensure the key exists\n if (localStorage.getItem(storageKey) == null) {\n writeToStorage([])\n }\n // Hydrate known index fields from existing keys so that we can keep them updated across sessions\n const prefix = `${storageKey}-index-`\n for (let i = 0; i < localStorage.length; i++) {\n const k = localStorage.key(i)\n if (k && k.startsWith(prefix)) {\n const field = k.slice(prefix.length)\n if (!indices.includes(field)) indices.push(field)\n }\n }\n },\n teardown: async () => {\n // no-op\n },\n\n // data retrieval methods\n readAll: async () => {\n return readFromStorage()\n },\n readIds: async (ids) => {\n const items = readFromStorage()\n const idSet = new Set<I>(ids)\n return items.filter(item => idSet.has(item.id))\n },\n\n // index methods\n createIndex: async (field) => {\n if (field === 'id') throw new Error('Cannot create index on id field')\n if (!indices.includes(field)) indices.push(field)\n await ensureIndex(field)\n },\n dropIndex: async (field) => {\n if (indices.includes(field)) {\n const i = indices.indexOf(field)\n indices.splice(i, 1)\n }\n const key = indexKeyFor(field)\n if (localStorage.getItem(key) == null) {\n throw new Error(`Index on field \"${field}\" does not exist`)\n }\n localStorage.removeItem(key)\n },\n readIndex,\n\n // data manipulation methods\n insert: async (newItems) => {\n upsertItems(newItems)\n },\n replace: async (itemsToReplace) => {\n upsertItems(itemsToReplace)\n },\n remove: async (itemsToRemove) => {\n const items = readFromStorage()\n const byId = new Map<I, T>(items.map(item => [item.id, item]))\n\n const deltas = new Map<string, IndexDelta>()\n const removeSet = new Set<I>(itemsToRemove.map(item => item.id))\n\n removeSet.forEach((id) => {\n const existing = byId.get(id)\n if (!existing) return\n accumulateRemoveDelta(deltas, existing)\n byId.delete(id)\n })\n\n writeToStorage([...byId.values()])\n applyIndexDeltas(deltas)\n },\n removeAll: async () => {\n writeToStorage([])\n // remove all index keys for this store\n const prefix = `${storageKey}-index-`\n const keysToRemove: string[] = []\n for (let i = 0; i < localStorage.length; i++) {\n const k = localStorage.key(i)\n if (k && k.startsWith(prefix)) keysToRemove.push(k)\n }\n keysToRemove.forEach(k => localStorage.removeItem(k))\n indices.splice(0)\n },\n })\n}\n"],"mappings":"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"}