@signaldb/core 2.0.0-beta.1 → 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/.vite/manifest.json +5 -5
- package/dist/AsyncDataAdapter.d.ts +1 -1
- package/dist/AutoFetchDataAdapter.d.ts +1 -1
- package/dist/Collection/Cursor.d.ts +3 -2
- package/dist/Collection/index.d.ts +15 -12
- package/dist/Collection/types.d.ts +1 -0
- package/dist/DataAdapter.d.ts +3 -2
- package/dist/DefaultDataAdapter.d.ts +1 -1
- package/dist/WorkerDataAdapter.d.ts +1 -1
- package/dist/index.cjs12.js +4 -1
- package/dist/index.cjs13.js +3 -3
- package/dist/index.cjs14.js +43 -21
- package/dist/index.cjs15.js +3 -1
- package/dist/index.cjs17.js +4 -2
- package/dist/index.cjs19.js +0 -1
- package/dist/index.cjs2.js +1 -0
- package/dist/index.cjs20.js +10 -1
- package/dist/index.cjs21.js +1 -1
- package/dist/index.cjs22.js +1 -1
- package/dist/index.cjs24.js +1 -1
- package/dist/index.cjs26.js +1 -1
- package/dist/index.cjs29.js +5 -7
- package/dist/index.cjs3.js +16 -1
- package/dist/index.cjs30.js +40 -3
- package/dist/index.cjs31.js +27 -5
- package/dist/index.cjs32.js +7 -27
- package/dist/index.cjs33.js +3 -40
- package/dist/index.d.ts +1 -1
- package/dist/index12.mjs +4 -1
- package/dist/index13.mjs +3 -3
- package/dist/index14.mjs +43 -21
- package/dist/index15.mjs +3 -1
- package/dist/index17.mjs +4 -2
- package/dist/index19.mjs +0 -1
- package/dist/index2.mjs +1 -0
- package/dist/index20.mjs +10 -1
- package/dist/index21.mjs +1 -1
- package/dist/index22.mjs +1 -1
- package/dist/index24.mjs +1 -1
- package/dist/index26.mjs +1 -1
- package/dist/index29.mjs +5 -7
- package/dist/index3.mjs +16 -1
- package/dist/index30.mjs +40 -3
- package/dist/index31.mjs +27 -5
- package/dist/index32.mjs +7 -27
- package/dist/index33.mjs +3 -40
- package/dist/utils/deepClone.d.ts +1 -1
- package/package.json +3 -3
package/dist/.vite/manifest.json
CHANGED
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
]
|
|
121
121
|
},
|
|
122
122
|
"src/createIndexProvider.ts": {
|
|
123
|
-
"file": "index.
|
|
123
|
+
"file": "index.cjs33.js",
|
|
124
124
|
"name": "createIndexProvider",
|
|
125
125
|
"src": "src/createIndexProvider.ts"
|
|
126
126
|
},
|
|
@@ -205,7 +205,7 @@
|
|
|
205
205
|
]
|
|
206
206
|
},
|
|
207
207
|
"src/utils/intersection.ts": {
|
|
208
|
-
"file": "index.
|
|
208
|
+
"file": "index.cjs29.js",
|
|
209
209
|
"name": "utils/intersection",
|
|
210
210
|
"src": "src/utils/intersection.ts"
|
|
211
211
|
},
|
|
@@ -215,7 +215,7 @@
|
|
|
215
215
|
"src": "src/utils/isEqual.ts"
|
|
216
216
|
},
|
|
217
217
|
"src/utils/isFieldExpression.ts": {
|
|
218
|
-
"file": "index.
|
|
218
|
+
"file": "index.cjs30.js",
|
|
219
219
|
"name": "utils/isFieldExpression",
|
|
220
220
|
"src": "src/utils/isFieldExpression.ts"
|
|
221
221
|
},
|
|
@@ -257,7 +257,7 @@
|
|
|
257
257
|
"src": "src/utils/serializeValue.ts"
|
|
258
258
|
},
|
|
259
259
|
"src/utils/set.ts": {
|
|
260
|
-
"file": "index.
|
|
260
|
+
"file": "index.cjs31.js",
|
|
261
261
|
"name": "utils/set",
|
|
262
262
|
"src": "src/utils/set.ts"
|
|
263
263
|
},
|
|
@@ -270,7 +270,7 @@
|
|
|
270
270
|
]
|
|
271
271
|
},
|
|
272
272
|
"src/utils/uniqueBy.ts": {
|
|
273
|
-
"file": "index.
|
|
273
|
+
"file": "index.cjs32.js",
|
|
274
274
|
"name": "utils/uniqueBy",
|
|
275
275
|
"src": "src/utils/uniqueBy.ts"
|
|
276
276
|
}
|
|
@@ -27,7 +27,7 @@ export default class AsyncDataAdapter implements DataAdapter {
|
|
|
27
27
|
private collectionIndices;
|
|
28
28
|
private queries;
|
|
29
29
|
constructor(options: AsyncDataAdapterOptions);
|
|
30
|
-
createCollectionBackend<T extends BaseItem<I>, I = any, U =
|
|
30
|
+
createCollectionBackend<T extends BaseItem<I>, I = any, E extends BaseItem = T, U = E>(collection: Collection<T, I, E, U>, indices: string[]): CollectionBackend<T, I>;
|
|
31
31
|
private setupStorage;
|
|
32
32
|
private ensureStorageAdapter;
|
|
33
33
|
/**
|
|
@@ -71,7 +71,7 @@ export default class AutoFetchDataAdapter implements DataAdapter {
|
|
|
71
71
|
private idRefCounts;
|
|
72
72
|
private autoloadIds;
|
|
73
73
|
constructor(options: AutoFetchDataAdapterOptions);
|
|
74
|
-
createCollectionBackend<T extends BaseItem<I>, I = any, U =
|
|
74
|
+
createCollectionBackend<T extends BaseItem<I>, I = any, E extends BaseItem = T, U = E>(collection: Collection<T, I, E, U>, indices: string[]): CollectionBackend<T, I>;
|
|
75
75
|
private forceRefetchAll;
|
|
76
76
|
private fetchAndIngest;
|
|
77
77
|
private purgeSelector;
|
|
@@ -15,7 +15,7 @@ export interface CursorOptions<T extends BaseItem, U = T, Async extends boolean
|
|
|
15
15
|
* Represents a cursor for querying and observing a filtered, sorted, and transformed
|
|
16
16
|
* subset of items from a collection. Supports reactivity and field tracking.
|
|
17
17
|
* @template T - The type of the items in the collection.
|
|
18
|
-
* @template U - The transformed item type after applying
|
|
18
|
+
* @template U - The transformed item type after applying transform (default is T).
|
|
19
19
|
*/
|
|
20
20
|
export default class Cursor<T extends BaseItem, U = T, Async extends boolean = false> {
|
|
21
21
|
private observer;
|
|
@@ -37,6 +37,7 @@ export default class Cursor<T extends BaseItem, U = T, Async extends boolean = f
|
|
|
37
37
|
* @param options.limit - The maximum number of items to return in the result set.
|
|
38
38
|
* @param options.reactive - A reactivity adapter to enable observing changes in the cursor's result set.
|
|
39
39
|
* @param options.fieldTracking - A boolean to enable fine-grained field tracking for reactivity.
|
|
40
|
+
* @param options.transformAll - A function that will be able to solve the n+1 problem
|
|
40
41
|
*/
|
|
41
42
|
constructor(getItems: Async extends true ? () => Promise<T[]> : () => T[], options?: CursorOptions<T, U, Async>);
|
|
42
43
|
private addGetters;
|
|
@@ -102,7 +103,7 @@ export default class Cursor<T extends BaseItem, U = T, Async extends boolean = f
|
|
|
102
103
|
* @param skipInitial - A boolean indicating whether to skip the initial notification of the current result set.
|
|
103
104
|
* @returns A function to stop observing changes.
|
|
104
105
|
*/
|
|
105
|
-
observeChanges(callbacks: ObserveCallbacks<
|
|
106
|
+
observeChanges(callbacks: ObserveCallbacks<T>, skipInitial?: boolean): () => void;
|
|
106
107
|
/**
|
|
107
108
|
* Forces the cursor to re-evaluate its result set by re-fetching items
|
|
108
109
|
* from the collection. This is useful when the underlying data or query
|
|
@@ -6,12 +6,12 @@ import type DataAdapter from '../DataAdapter';
|
|
|
6
6
|
import type { QueryOptions } from '../DataAdapter';
|
|
7
7
|
import type StorageAdapter from '../types/StorageAdapter';
|
|
8
8
|
import Cursor from './Cursor';
|
|
9
|
-
import type { BaseItem, FindOptions, Transform } from './types';
|
|
10
|
-
export type { BaseItem, Transform, SortSpecifier, FieldSpecifier, FindOptions } from './types';
|
|
9
|
+
import type { BaseItem, FindOptions, Transform, TransformAll } from './types';
|
|
10
|
+
export type { BaseItem, Transform, TransformAll, SortSpecifier, FieldSpecifier, FindOptions } from './types';
|
|
11
11
|
export type { CursorOptions } from './Cursor';
|
|
12
12
|
export type { ObserveCallbacks } from './Observer';
|
|
13
13
|
export { default as createIndex } from '../createIndex';
|
|
14
|
-
export interface CollectionOptions<T extends BaseItem<I>, I, U =
|
|
14
|
+
export interface CollectionOptions<T extends BaseItem<I>, I, E extends BaseItem = T, U = E> {
|
|
15
15
|
/**
|
|
16
16
|
* @deprecated Use new constructor parameters instead.
|
|
17
17
|
*/
|
|
@@ -22,19 +22,20 @@ export interface CollectionOptions<T extends BaseItem<I>, I, U = T> {
|
|
|
22
22
|
persistence?: StorageAdapter<T, I>;
|
|
23
23
|
primaryKeyGenerator?: (item: Omit<T, 'id'>) => I;
|
|
24
24
|
reactivity?: ReactivityAdapter;
|
|
25
|
-
transform?: Transform<
|
|
25
|
+
transform?: Transform<E, U>;
|
|
26
|
+
transformAll?: TransformAll<T, E>;
|
|
26
27
|
indices?: string[];
|
|
27
28
|
enableDebugMode?: boolean;
|
|
28
29
|
fieldTracking?: boolean;
|
|
29
30
|
}
|
|
30
|
-
interface CollectionEvents<T extends BaseItem, U =
|
|
31
|
+
interface CollectionEvents<T extends BaseItem, E extends BaseItem = T, U = E> {
|
|
31
32
|
'added': (item: T) => void;
|
|
32
33
|
'changed': (item: T, modifier: Modifier<T>) => void;
|
|
33
34
|
'removed': (item: T) => void;
|
|
34
35
|
'observer.created': <O extends QueryOptions<T>>(selector?: Selector<T>, options?: O) => void;
|
|
35
36
|
'observer.disposed': <O extends QueryOptions<T>>(selector?: Selector<T>, options?: O) => void;
|
|
36
37
|
'getItems': (selector: Selector<T> | undefined) => void;
|
|
37
|
-
'find': <O extends FindOptions<T, Async>, Async extends boolean>(selector: Selector<T> | undefined, options: O | undefined, cursor: Cursor<
|
|
38
|
+
'find': <O extends FindOptions<T, Async>, Async extends boolean>(selector: Selector<T> | undefined, options: O | undefined, cursor: Cursor<E, U, Async>) => void;
|
|
38
39
|
'findOne': <O extends QueryOptions<T>>(selector: Selector<T>, options: O | undefined, item: U | undefined) => void;
|
|
39
40
|
'insert': (item: Omit<T, 'id'> & Partial<Pick<T, 'id'>>) => void;
|
|
40
41
|
'updateOne': (selector: Selector<T>, modifier: Modifier<T>) => void;
|
|
@@ -44,7 +45,7 @@ interface CollectionEvents<T extends BaseItem, U = T> {
|
|
|
44
45
|
'removeMany': (selector: Selector<T>) => void;
|
|
45
46
|
'validate': (item: T) => void;
|
|
46
47
|
'_debug.getItems': (callstack: string, selector: Selector<T> | undefined, measuredTime: number) => void;
|
|
47
|
-
'_debug.find': <O extends FindOptions<T, Async>, Async extends boolean>(callstack: string, selector: Selector<T> | undefined, options: O | undefined, cursor: Cursor<
|
|
48
|
+
'_debug.find': <O extends FindOptions<T, Async>, Async extends boolean>(callstack: string, selector: Selector<T> | undefined, options: O | undefined, cursor: Cursor<E, U, Async>) => void;
|
|
48
49
|
'_debug.findOne': <O extends FindOptions<T, Async>, Async extends boolean>(callstack: string, selector: Selector<T>, options: O | undefined, item: U | undefined) => void;
|
|
49
50
|
'_debug.insert': (callstack: string, item: Omit<T, 'id'> & Partial<Pick<T, 'id'>>) => void;
|
|
50
51
|
'_debug.updateOne': (callstack: string, selector: Selector<T>, modifier: Modifier<T>) => void;
|
|
@@ -61,14 +62,14 @@ interface CollectionEvents<T extends BaseItem, U = T> {
|
|
|
61
62
|
* @template I - The type of the unique identifier for the items.
|
|
62
63
|
* @template U - The transformed item type after applying transformations (default is T).
|
|
63
64
|
*/
|
|
64
|
-
export default class Collection<T extends BaseItem<I> = BaseItem, I = any, U =
|
|
65
|
+
export default class Collection<T extends BaseItem<I> = BaseItem, I = any, E extends BaseItem = T, U = E> extends EventEmitter<CollectionEvents<T, E, U>> {
|
|
65
66
|
private static collections;
|
|
66
67
|
private static debugMode;
|
|
67
68
|
private static batchOperationInProgress;
|
|
68
69
|
private static fieldTracking;
|
|
69
70
|
private static onCreationCallbacks;
|
|
70
71
|
private static onDisposeCallbacks;
|
|
71
|
-
static getCollections(): Collection<any, any, any>[];
|
|
72
|
+
static getCollections(): Collection<any, any, any, any>[];
|
|
72
73
|
static onCreation(callback: (collection: Collection<any>) => void): void;
|
|
73
74
|
static onDispose(callback: (collection: Collection<any>) => void): void;
|
|
74
75
|
/**
|
|
@@ -120,9 +121,10 @@ export default class Collection<T extends BaseItem<I> = BaseItem, I = any, U = T
|
|
|
120
121
|
* @param options.indices - An array of index providers for optimized querying.
|
|
121
122
|
* @param options.enableDebugMode - A boolean to enable or disable debug mode.
|
|
122
123
|
* @param options.fieldTracking - A boolean to enable or disable field tracking by default.
|
|
124
|
+
* @param options.transformAll - A function that will be able to solve the n+1 problem
|
|
123
125
|
*/
|
|
124
|
-
constructor(options?: CollectionOptions<T, I, U>);
|
|
125
|
-
constructor(name: string, dataAdapter: DataAdapter, options?: CollectionOptions<T, I, U>);
|
|
126
|
+
constructor(options?: CollectionOptions<T, I, E, U>);
|
|
127
|
+
constructor(name: string, dataAdapter: DataAdapter, options?: CollectionOptions<T, I, E, U>);
|
|
126
128
|
isBatchOperationInProgress(): boolean;
|
|
127
129
|
/**
|
|
128
130
|
* Checks whether the collection is currently performing a pull operation
|
|
@@ -184,6 +186,7 @@ export default class Collection<T extends BaseItem<I> = BaseItem, I = any, U = T
|
|
|
184
186
|
private profile;
|
|
185
187
|
private executeInDebugMode;
|
|
186
188
|
private transform;
|
|
189
|
+
private transformAll;
|
|
187
190
|
private getItem;
|
|
188
191
|
private getItems;
|
|
189
192
|
private withPushState;
|
|
@@ -202,7 +205,7 @@ export default class Collection<T extends BaseItem<I> = BaseItem, I = any, U = T
|
|
|
202
205
|
* @param [options] - Options for the find operation, such as limit and sort.
|
|
203
206
|
* @returns A cursor to fetch and observe the matching items.
|
|
204
207
|
*/
|
|
205
|
-
find<Async extends boolean = false, O extends FindOptions<T, Async> = FindOptions<T, Async>>(selector?: Selector<T>, options?: O): Cursor<
|
|
208
|
+
find<Async extends boolean = false, O extends FindOptions<T, Async> = FindOptions<T, Async>>(selector?: Selector<T>, options?: O): Cursor<E, U, Async>;
|
|
206
209
|
/**
|
|
207
210
|
* Finds a single item in the collection based on a selector and optional options.
|
|
208
211
|
* ⚡️ this function is reactive!
|
|
@@ -4,6 +4,7 @@ export type BaseItem<I = any> = {
|
|
|
4
4
|
id: I;
|
|
5
5
|
} & Record<string, any>;
|
|
6
6
|
export type Transform<T, U = T> = ((document: T) => U) | null | undefined;
|
|
7
|
+
export type TransformAll<T extends BaseItem, O extends BaseItem = T> = ((items: T[], fields: FieldSpecifier<O> | undefined) => O[]) | null | undefined;
|
|
7
8
|
export type SortSpecifier<T> = {
|
|
8
9
|
[P in keyof T]?: -1 | 1;
|
|
9
10
|
} & Record<string, -1 | 1>;
|
package/dist/DataAdapter.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface QueryOptions<T extends BaseItem> {
|
|
|
12
12
|
/** Dictionary of fields to return or exclude. */
|
|
13
13
|
fields?: FieldSpecifier<T> | undefined;
|
|
14
14
|
}
|
|
15
|
+
export type StateChangeCallback = (state: 'active' | 'complete' | 'error') => void;
|
|
15
16
|
export interface CollectionBackend<T extends BaseItem<I>, I> {
|
|
16
17
|
insert(item: T): Promise<T>;
|
|
17
18
|
updateOne(selector: Selector<T>, modifier: Modifier<T>): Promise<T[]>;
|
|
@@ -25,10 +26,10 @@ export interface CollectionBackend<T extends BaseItem<I>, I> {
|
|
|
25
26
|
getQueryError<O extends QueryOptions<T>>(selector: Selector<T>, options: O): Error | null;
|
|
26
27
|
getQueryResult<O extends QueryOptions<T>>(selector: Selector<T>, options: O): T[];
|
|
27
28
|
executeQuery<O extends QueryOptions<T>>(selector: Selector<T>, options: O): Promise<T[]>;
|
|
28
|
-
onQueryStateChange<O extends QueryOptions<T>>(selector: Selector<T>, options: O, callback:
|
|
29
|
+
onQueryStateChange<O extends QueryOptions<T>>(selector: Selector<T>, options: O, callback: StateChangeCallback): () => void;
|
|
29
30
|
dispose(): Promise<void>;
|
|
30
31
|
isReady(): Promise<void>;
|
|
31
32
|
}
|
|
32
33
|
export default interface DataAdapter {
|
|
33
|
-
createCollectionBackend<T extends BaseItem<I>, I = any, U =
|
|
34
|
+
createCollectionBackend<T extends BaseItem<I>, I = any, E extends BaseItem = T, U = E>(collection: Collection<T, I, E, U>, indices: string[]): CollectionBackend<T, I>;
|
|
34
35
|
}
|
|
@@ -28,6 +28,6 @@ export default class DefaultDataAdapter implements DataAdapter {
|
|
|
28
28
|
private flushQueuedQueryUpdates;
|
|
29
29
|
private executeAndCacheQuery;
|
|
30
30
|
private updateQueries;
|
|
31
|
-
createCollectionBackend<T extends BaseItem<I>, I = any, U =
|
|
31
|
+
createCollectionBackend<T extends BaseItem<I>, I = any, E extends BaseItem = T, U = E>(collection: Collection<T, I, E, U>, indices: string[]): CollectionBackend<T, I>;
|
|
32
32
|
}
|
|
33
33
|
export {};
|
|
@@ -20,6 +20,6 @@ export default class WorkerDataAdapter implements DataAdapter {
|
|
|
20
20
|
private exec;
|
|
21
21
|
private enqueueBatched;
|
|
22
22
|
private updateQuery;
|
|
23
|
-
createCollectionBackend<T extends BaseItem<I>, I = any, U =
|
|
23
|
+
createCollectionBackend<T extends BaseItem<I>, I = any, E extends BaseItem = T, U = E>(collection: Collection<T, I, E, U>, indices: string[]): CollectionBackend<T, I>;
|
|
24
24
|
}
|
|
25
25
|
export {};
|
package/dist/index.cjs12.js
CHANGED
|
@@ -324,7 +324,10 @@ class DefaultDataAdapter {
|
|
|
324
324
|
getQueryResult: (selector, options) => {
|
|
325
325
|
const isQueryActive = this.activeQueries.get(collection.name)?.has(queryId(selector, options));
|
|
326
326
|
if (isQueryActive) {
|
|
327
|
-
|
|
327
|
+
const results = this.cachedQueryResults.get(collection.name)?.get(queryId(selector, options));
|
|
328
|
+
if (!results)
|
|
329
|
+
throw new Error("Cached query results are not defined!");
|
|
330
|
+
return results;
|
|
328
331
|
}
|
|
329
332
|
return this.executeQuery(collection, selector, options);
|
|
330
333
|
},
|
package/dist/index.cjs13.js
CHANGED
|
@@ -142,8 +142,8 @@ class AsyncDataAdapter {
|
|
|
142
142
|
const storage = this.storageAdapters.get(collectionName);
|
|
143
143
|
if (!storage)
|
|
144
144
|
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
145
|
-
await Promise.all(indices.map((index) => storage.createIndex(index)));
|
|
146
145
|
await storage.setup();
|
|
146
|
+
await Promise.all(indices.map((index) => storage.createIndex(index)));
|
|
147
147
|
}
|
|
148
148
|
ensureStorageAdapter(name) {
|
|
149
149
|
if (this.storageAdapters.has(name))
|
|
@@ -327,8 +327,8 @@ class AsyncDataAdapter {
|
|
|
327
327
|
const storage = this.storageAdapters.get(collectionName);
|
|
328
328
|
if (!storage)
|
|
329
329
|
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
330
|
-
const
|
|
331
|
-
if (
|
|
330
|
+
const existingItems = await storage.readIds([newItem.id]);
|
|
331
|
+
if (existingItems.length > 0)
|
|
332
332
|
throw new Error(`Item with id ${String(newItem.id)} already exists`);
|
|
333
333
|
await storage.insert([newItem]);
|
|
334
334
|
await this.checkQueryUpdates(collectionName, [newItem]);
|
package/dist/index.cjs14.js
CHANGED
|
@@ -91,13 +91,15 @@ class WorkerDataAdapter {
|
|
|
91
91
|
state: "active",
|
|
92
92
|
error: null,
|
|
93
93
|
items: [],
|
|
94
|
+
stateChangeCallbacks: [],
|
|
95
|
+
eventHandler: existing?.eventHandler,
|
|
94
96
|
...existing,
|
|
95
97
|
...update
|
|
96
98
|
};
|
|
97
99
|
collectionQueries.set(id, newState);
|
|
98
100
|
this.queries[collectionName] = collectionQueries;
|
|
99
101
|
}
|
|
100
|
-
createCollectionBackend(collection, indices
|
|
102
|
+
createCollectionBackend(collection, indices) {
|
|
101
103
|
this.queries[collection.name] = /* @__PURE__ */ new Map();
|
|
102
104
|
void this.exec("registerCollection", collection.name, indices);
|
|
103
105
|
this.collectionReady.set(collection.name, this.exec("isReady", collection.name));
|
|
@@ -125,24 +127,6 @@ class WorkerDataAdapter {
|
|
|
125
127
|
registerQuery: (selector, options) => {
|
|
126
128
|
this.updateQuery(collection.name, { selector, options }, { state: "active", error: null, items: [] });
|
|
127
129
|
void this.exec("registerQuery", collection.name, selector, options);
|
|
128
|
-
},
|
|
129
|
-
unregisterQuery: (selector, options) => {
|
|
130
|
-
this.queries[collection.name]?.delete(queryId(selector, options));
|
|
131
|
-
void this.exec("unregisterQuery", collection.name, selector, options);
|
|
132
|
-
},
|
|
133
|
-
getQueryState: (selector, options) => {
|
|
134
|
-
const query = this.queries[collection.name]?.get(queryId(selector, options));
|
|
135
|
-
return query?.state || "active";
|
|
136
|
-
},
|
|
137
|
-
getQueryError: (selector, options) => {
|
|
138
|
-
const query = this.queries[collection.name]?.get(queryId(selector, options));
|
|
139
|
-
return query?.error || null;
|
|
140
|
-
},
|
|
141
|
-
getQueryResult: (selector, options) => {
|
|
142
|
-
const query = this.queries[collection.name]?.get(queryId(selector, options));
|
|
143
|
-
return query?.items || [];
|
|
144
|
-
},
|
|
145
|
-
onQueryStateChange: (selector, options, callback) => {
|
|
146
130
|
const handler = (event) => {
|
|
147
131
|
const { type, data, workerId, error } = event.data;
|
|
148
132
|
if (type !== "queryUpdate")
|
|
@@ -161,11 +145,49 @@ class WorkerDataAdapter {
|
|
|
161
145
|
selector: responseSelector,
|
|
162
146
|
options: responseOptions
|
|
163
147
|
}, { state, error, items });
|
|
164
|
-
|
|
148
|
+
const query = this.queries[collection.name]?.get(queryId(selector, options));
|
|
149
|
+
if (!query)
|
|
150
|
+
return;
|
|
151
|
+
query.stateChangeCallbacks.forEach((callback) => callback(state));
|
|
165
152
|
};
|
|
166
153
|
this.worker.addEventListener("message", handler);
|
|
154
|
+
this.updateQuery(collection.name, { selector, options }, { eventHandler: handler });
|
|
155
|
+
},
|
|
156
|
+
unregisterQuery: (selector, options) => {
|
|
157
|
+
const qid = queryId(selector, options);
|
|
158
|
+
const query = this.queries[collection.name]?.get(qid);
|
|
159
|
+
if (query?.eventHandler) {
|
|
160
|
+
this.worker.removeEventListener("message", query.eventHandler);
|
|
161
|
+
}
|
|
162
|
+
this.queries[collection.name]?.delete(qid);
|
|
163
|
+
void this.exec("unregisterQuery", collection.name, selector, options);
|
|
164
|
+
},
|
|
165
|
+
getQueryState: (selector, options) => {
|
|
166
|
+
const query = this.queries[collection.name]?.get(queryId(selector, options));
|
|
167
|
+
return query?.state || "active";
|
|
168
|
+
},
|
|
169
|
+
getQueryError: (selector, options) => {
|
|
170
|
+
const query = this.queries[collection.name]?.get(queryId(selector, options));
|
|
171
|
+
return query?.error || null;
|
|
172
|
+
},
|
|
173
|
+
getQueryResult: (selector, options) => {
|
|
174
|
+
const query = this.queries[collection.name]?.get(queryId(selector, options));
|
|
175
|
+
return query?.items || [];
|
|
176
|
+
},
|
|
177
|
+
onQueryStateChange: (selector, options, callback) => {
|
|
178
|
+
this.updateQuery(collection.name, { selector, options }, {
|
|
179
|
+
stateChangeCallbacks: [
|
|
180
|
+
...this.queries[collection.name]?.get(queryId(selector, options))?.stateChangeCallbacks || [],
|
|
181
|
+
callback
|
|
182
|
+
]
|
|
183
|
+
});
|
|
167
184
|
return () => {
|
|
168
|
-
this.
|
|
185
|
+
const currentCallbacks = this.queries[collection.name]?.get(queryId(selector, options))?.stateChangeCallbacks;
|
|
186
|
+
if (!currentCallbacks)
|
|
187
|
+
throw new Error("State change callbacks are not defined!");
|
|
188
|
+
this.updateQuery(collection.name, { selector, options }, {
|
|
189
|
+
stateChangeCallbacks: currentCallbacks.filter((existingCallback) => existingCallback !== callback)
|
|
190
|
+
});
|
|
169
191
|
};
|
|
170
192
|
},
|
|
171
193
|
executeQuery: (selector, options) => this.exec("executeQuery", collection.name, selector, options),
|
package/dist/index.cjs15.js
CHANGED
|
@@ -156,6 +156,8 @@ class WorkerDataAdapterHost {
|
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
async executeQuery(collectionName, selector, options) {
|
|
159
|
+
if (selector === null)
|
|
160
|
+
return [];
|
|
159
161
|
const items = await this.queryItems(collectionName, selector || {});
|
|
160
162
|
const { sort, skip, limit, fields } = options || {};
|
|
161
163
|
const sorted = sort ? sortItems(items, sort) : items;
|
|
@@ -249,7 +251,7 @@ class WorkerDataAdapterHost {
|
|
|
249
251
|
const existingItems = await this.executeQuery(collectionName, { id: { $in: input.map((i) => i[0].id) } });
|
|
250
252
|
const result = input.map(([item]) => {
|
|
251
253
|
if (item.id == null)
|
|
252
|
-
|
|
254
|
+
return new Error("Item must have an id");
|
|
253
255
|
if (existingItems.some((existing) => existing.id === item.id)) {
|
|
254
256
|
return new Error(`Item with id ${item.id} already exists`);
|
|
255
257
|
}
|
package/dist/index.cjs17.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const isEqual = require("./index.cjs6.js");
|
|
3
|
-
const uniqueBy = require("./index.
|
|
3
|
+
const uniqueBy = require("./index.cjs32.js");
|
|
4
4
|
class Observer {
|
|
5
5
|
previousItems = [];
|
|
6
6
|
callbacks;
|
|
@@ -53,7 +53,9 @@ class Observer {
|
|
|
53
53
|
runChecks(getItems) {
|
|
54
54
|
const result = getItems();
|
|
55
55
|
if (result instanceof Promise) {
|
|
56
|
-
|
|
56
|
+
result.then((newItems) => this.checkItems(newItems)).catch((error) => {
|
|
57
|
+
console.error("Error while asynchronously querying items", error);
|
|
58
|
+
});
|
|
57
59
|
} else {
|
|
58
60
|
this.checkItems(result);
|
|
59
61
|
}
|
package/dist/index.cjs19.js
CHANGED
package/dist/index.cjs2.js
CHANGED
|
@@ -28,6 +28,7 @@ class Cursor {
|
|
|
28
28
|
* @param options.limit - The maximum number of items to return in the result set.
|
|
29
29
|
* @param options.reactive - A reactivity adapter to enable observing changes in the cursor's result set.
|
|
30
30
|
* @param options.fieldTracking - A boolean to enable fine-grained field tracking for reactivity.
|
|
31
|
+
* @param options.transformAll - A function that will be able to solve the n+1 problem
|
|
31
32
|
*/
|
|
32
33
|
constructor(getItems, options) {
|
|
33
34
|
this.getItems = getItems;
|
package/dist/index.cjs20.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
function isEmptyOptions(options) {
|
|
3
|
+
if (options == null)
|
|
4
|
+
return true;
|
|
5
|
+
if (typeof options !== "object")
|
|
6
|
+
return false;
|
|
7
|
+
if (Array.isArray(options))
|
|
8
|
+
return false;
|
|
9
|
+
return Object.keys(options).length === 0;
|
|
10
|
+
}
|
|
2
11
|
function queryId(selector, options) {
|
|
3
12
|
const selectorId = JSON.stringify(selector);
|
|
4
|
-
const optionsId = options
|
|
13
|
+
const optionsId = isEmptyOptions(options) ? -1 : JSON.stringify(options);
|
|
5
14
|
return `${selectorId}:${optionsId}`;
|
|
6
15
|
}
|
|
7
16
|
module.exports = queryId;
|
package/dist/index.cjs21.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const createIndexProvider = require("./index.
|
|
2
|
+
const createIndexProvider = require("./index.cjs33.js");
|
|
3
3
|
const get = require("./index.cjs10.js");
|
|
4
4
|
const getMatchingKeys = require("./index.cjs26.js");
|
|
5
5
|
const serializeValue = require("./index.cjs11.js");
|
package/dist/index.cjs22.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
|
|
3
|
-
const intersection = require("./index.
|
|
3
|
+
const intersection = require("./index.cjs29.js");
|
|
4
4
|
function getMergedIndexInfo(queryFunctions, selector) {
|
|
5
5
|
return queryFunctions.reduce((memoOrPromise, queryFunction) => {
|
|
6
6
|
const resultOrPromise = queryFunction(selector);
|
package/dist/index.cjs24.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const get = require("./index.cjs10.js");
|
|
3
|
-
const set = require("./index.
|
|
3
|
+
const set = require("./index.cjs31.js");
|
|
4
4
|
function project(item, fields) {
|
|
5
5
|
const allFieldsDeactivated = Object.values(fields).every((value) => value === 0);
|
|
6
6
|
if (allFieldsDeactivated) {
|
package/dist/index.cjs26.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const isFieldExpression = require("./index.
|
|
2
|
+
const isFieldExpression = require("./index.cjs30.js");
|
|
3
3
|
const serializeValue = require("./index.cjs11.js");
|
|
4
4
|
function getMatchingKeys(field, selector) {
|
|
5
5
|
const result = { include: null, exclude: null };
|
package/dist/index.cjs29.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
function
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
return !set.has(value) && set.add(value);
|
|
7
|
-
});
|
|
2
|
+
function intersection(...arrays) {
|
|
3
|
+
if (arrays.length === 0)
|
|
4
|
+
return [];
|
|
5
|
+
return [...new Set(arrays.reduce((a, b) => a.filter((c) => b.includes(c))))];
|
|
8
6
|
}
|
|
9
|
-
module.exports =
|
|
7
|
+
module.exports = intersection;
|
package/dist/index.cjs3.js
CHANGED
|
@@ -194,6 +194,11 @@ class Collection extends EventEmitter {
|
|
|
194
194
|
return item;
|
|
195
195
|
return this.options.transform(item);
|
|
196
196
|
}
|
|
197
|
+
transformAll(items, fields) {
|
|
198
|
+
if (!this.options.transformAll)
|
|
199
|
+
return items;
|
|
200
|
+
return this.options.transformAll(deepClone.default(items), fields);
|
|
201
|
+
}
|
|
197
202
|
getItem(selector, options) {
|
|
198
203
|
const itemsOrPromise = this.getItems(selector, { ...options, limit: 1 });
|
|
199
204
|
if (itemsOrPromise instanceof Promise) {
|
|
@@ -254,7 +259,17 @@ class Collection extends EventEmitter {
|
|
|
254
259
|
throw new Error("Collection is disposed");
|
|
255
260
|
if (selector !== void 0 && (!selector || typeof selector !== "object"))
|
|
256
261
|
throw new Error("Invalid selector");
|
|
257
|
-
const
|
|
262
|
+
const getTransformedItems = () => {
|
|
263
|
+
const itemsOrPromise = this.getItems(selector, options || {});
|
|
264
|
+
if (itemsOrPromise instanceof Promise) {
|
|
265
|
+
return itemsOrPromise.then((items2) => {
|
|
266
|
+
return this.transformAll(items2, options?.fields);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
const items = itemsOrPromise;
|
|
270
|
+
return this.transformAll(items, options?.fields);
|
|
271
|
+
};
|
|
272
|
+
const cursor = new Cursor.default(getTransformedItems, {
|
|
258
273
|
reactive: this.options.reactivity,
|
|
259
274
|
fieldTracking: this.fieldTracking,
|
|
260
275
|
...options,
|
package/dist/index.cjs30.js
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
const expressionKeys = /* @__PURE__ */ new Set([
|
|
3
|
+
"$eq",
|
|
4
|
+
"$gt",
|
|
5
|
+
"$gte",
|
|
6
|
+
"$lt",
|
|
7
|
+
"$lte",
|
|
8
|
+
"$in",
|
|
9
|
+
"$nin",
|
|
10
|
+
"$ne",
|
|
11
|
+
"$exists",
|
|
12
|
+
"$not",
|
|
13
|
+
"$expr",
|
|
14
|
+
"$jsonSchema",
|
|
15
|
+
"$mod",
|
|
16
|
+
"$regex",
|
|
17
|
+
"$options",
|
|
18
|
+
"$text",
|
|
19
|
+
"$where",
|
|
20
|
+
"$all",
|
|
21
|
+
"$elemMatch",
|
|
22
|
+
"$size",
|
|
23
|
+
"$bitsAllClear",
|
|
24
|
+
"$bitsAllSet",
|
|
25
|
+
"$bitsAnyClear",
|
|
26
|
+
"$bitsAnySet"
|
|
27
|
+
]);
|
|
28
|
+
function isFieldExpression(expression) {
|
|
29
|
+
if (typeof expression !== "object" || expression == null) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const keys = Object.keys(expression);
|
|
33
|
+
if (keys.length === 0) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
const hasInvalidKeys = keys.some((key) => !expressionKeys.has(key));
|
|
37
|
+
if (hasInvalidKeys)
|
|
38
|
+
return false;
|
|
39
|
+
const hasValidKeys = keys.every((key) => expressionKeys.has(key));
|
|
40
|
+
return hasValidKeys;
|
|
4
41
|
}
|
|
5
|
-
module.exports =
|
|
42
|
+
module.exports = isFieldExpression;
|
package/dist/index.cjs31.js
CHANGED
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
function
|
|
3
|
-
if (
|
|
4
|
-
return
|
|
5
|
-
|
|
2
|
+
function set(object, path, value, deleteIfUndefined = false) {
|
|
3
|
+
if (object == null)
|
|
4
|
+
return object;
|
|
5
|
+
const segments = path.split(/[.[\]]/g);
|
|
6
|
+
if (segments[0] === "")
|
|
7
|
+
segments.shift();
|
|
8
|
+
if (segments.at(-1) === "")
|
|
9
|
+
segments.pop();
|
|
10
|
+
const apply = (node) => {
|
|
11
|
+
if (segments.length > 1) {
|
|
12
|
+
const key = segments.shift();
|
|
13
|
+
const nextIsNumber = !Number.isNaN(Number.parseInt(segments[0], 10));
|
|
14
|
+
if (node[key] === void 0) {
|
|
15
|
+
node[key] = nextIsNumber ? [] : {};
|
|
16
|
+
}
|
|
17
|
+
apply(node[key]);
|
|
18
|
+
} else {
|
|
19
|
+
if (deleteIfUndefined && value === void 0) {
|
|
20
|
+
delete node[segments[0]];
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
node[segments[0]] = value;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
apply(object);
|
|
27
|
+
return object;
|
|
6
28
|
}
|
|
7
|
-
module.exports =
|
|
29
|
+
module.exports = set;
|
package/dist/index.cjs32.js
CHANGED
|
@@ -1,29 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
function
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (segments.at(-1) === "")
|
|
9
|
-
segments.pop();
|
|
10
|
-
const apply = (node) => {
|
|
11
|
-
if (segments.length > 1) {
|
|
12
|
-
const key = segments.shift();
|
|
13
|
-
const nextIsNumber = !Number.isNaN(Number.parseInt(segments[0], 10));
|
|
14
|
-
if (node[key] === void 0) {
|
|
15
|
-
node[key] = nextIsNumber ? [] : {};
|
|
16
|
-
}
|
|
17
|
-
apply(node[key]);
|
|
18
|
-
} else {
|
|
19
|
-
if (deleteIfUndefined && value === void 0) {
|
|
20
|
-
delete node[segments[0]];
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
node[segments[0]] = value;
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
apply(object);
|
|
27
|
-
return object;
|
|
2
|
+
function uniqueBy(array, fn) {
|
|
3
|
+
const set = /* @__PURE__ */ new Set();
|
|
4
|
+
return array.filter((element) => {
|
|
5
|
+
const value = typeof fn === "function" ? fn(element) : element[fn];
|
|
6
|
+
return !set.has(value) && set.add(value);
|
|
7
|
+
});
|
|
28
8
|
}
|
|
29
|
-
module.exports =
|
|
9
|
+
module.exports = uniqueBy;
|
package/dist/index.cjs33.js
CHANGED
|
@@ -1,42 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
"$gt",
|
|
5
|
-
"$gte",
|
|
6
|
-
"$lt",
|
|
7
|
-
"$lte",
|
|
8
|
-
"$in",
|
|
9
|
-
"$nin",
|
|
10
|
-
"$ne",
|
|
11
|
-
"$exists",
|
|
12
|
-
"$not",
|
|
13
|
-
"$expr",
|
|
14
|
-
"$jsonSchema",
|
|
15
|
-
"$mod",
|
|
16
|
-
"$regex",
|
|
17
|
-
"$options",
|
|
18
|
-
"$text",
|
|
19
|
-
"$where",
|
|
20
|
-
"$all",
|
|
21
|
-
"$elemMatch",
|
|
22
|
-
"$size",
|
|
23
|
-
"$bitsAllClear",
|
|
24
|
-
"$bitsAllSet",
|
|
25
|
-
"$bitsAnyClear",
|
|
26
|
-
"$bitsAnySet"
|
|
27
|
-
]);
|
|
28
|
-
function isFieldExpression(expression) {
|
|
29
|
-
if (typeof expression !== "object" || expression == null) {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
const keys = Object.keys(expression);
|
|
33
|
-
if (keys.length === 0) {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
const hasInvalidKeys = keys.some((key) => !expressionKeys.has(key));
|
|
37
|
-
if (hasInvalidKeys)
|
|
38
|
-
return false;
|
|
39
|
-
const hasValidKeys = keys.every((key) => expressionKeys.has(key));
|
|
40
|
-
return hasValidKeys;
|
|
2
|
+
function createIndexProvider(definition) {
|
|
3
|
+
return definition;
|
|
41
4
|
}
|
|
42
|
-
module.exports =
|
|
5
|
+
module.exports = createIndexProvider;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export type { default as ReactivityAdapter } from './types/ReactivityAdapter';
|
|
|
2
2
|
export type { default as StorageAdapter, Changeset, } from './types/StorageAdapter';
|
|
3
3
|
export type { default as Selector } from './types/Selector';
|
|
4
4
|
export type { default as Modifier } from './types/Modifier';
|
|
5
|
-
export type { BaseItem, ObserveCallbacks, CursorOptions, Transform, SortSpecifier, FieldSpecifier, FindOptions, CollectionOptions, } from './Collection';
|
|
5
|
+
export type { BaseItem, ObserveCallbacks, CursorOptions, Transform, TransformAll, SortSpecifier, FieldSpecifier, FindOptions, CollectionOptions, } from './Collection';
|
|
6
6
|
export type { default as DataAdapter } from './DataAdapter';
|
|
7
7
|
export { default as Cursor } from './Collection/Cursor';
|
|
8
8
|
export { default as Collection } from './Collection';
|
package/dist/index12.mjs
CHANGED
|
@@ -323,7 +323,10 @@ class DefaultDataAdapter {
|
|
|
323
323
|
getQueryResult: (selector, options) => {
|
|
324
324
|
const isQueryActive = this.activeQueries.get(collection.name)?.has(queryId(selector, options));
|
|
325
325
|
if (isQueryActive) {
|
|
326
|
-
|
|
326
|
+
const results = this.cachedQueryResults.get(collection.name)?.get(queryId(selector, options));
|
|
327
|
+
if (!results)
|
|
328
|
+
throw new Error("Cached query results are not defined!");
|
|
329
|
+
return results;
|
|
327
330
|
}
|
|
328
331
|
return this.executeQuery(collection, selector, options);
|
|
329
332
|
},
|
package/dist/index13.mjs
CHANGED
|
@@ -141,8 +141,8 @@ class AsyncDataAdapter {
|
|
|
141
141
|
const storage = this.storageAdapters.get(collectionName);
|
|
142
142
|
if (!storage)
|
|
143
143
|
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
144
|
-
await Promise.all(indices.map((index) => storage.createIndex(index)));
|
|
145
144
|
await storage.setup();
|
|
145
|
+
await Promise.all(indices.map((index) => storage.createIndex(index)));
|
|
146
146
|
}
|
|
147
147
|
ensureStorageAdapter(name) {
|
|
148
148
|
if (this.storageAdapters.has(name))
|
|
@@ -326,8 +326,8 @@ class AsyncDataAdapter {
|
|
|
326
326
|
const storage = this.storageAdapters.get(collectionName);
|
|
327
327
|
if (!storage)
|
|
328
328
|
throw new Error(`No persistence adapter for collection ${collectionName}`);
|
|
329
|
-
const
|
|
330
|
-
if (
|
|
329
|
+
const existingItems = await storage.readIds([newItem.id]);
|
|
330
|
+
if (existingItems.length > 0)
|
|
331
331
|
throw new Error(`Item with id ${String(newItem.id)} already exists`);
|
|
332
332
|
await storage.insert([newItem]);
|
|
333
333
|
await this.checkQueryUpdates(collectionName, [newItem]);
|
package/dist/index14.mjs
CHANGED
|
@@ -90,13 +90,15 @@ class WorkerDataAdapter {
|
|
|
90
90
|
state: "active",
|
|
91
91
|
error: null,
|
|
92
92
|
items: [],
|
|
93
|
+
stateChangeCallbacks: [],
|
|
94
|
+
eventHandler: existing?.eventHandler,
|
|
93
95
|
...existing,
|
|
94
96
|
...update
|
|
95
97
|
};
|
|
96
98
|
collectionQueries.set(id, newState);
|
|
97
99
|
this.queries[collectionName] = collectionQueries;
|
|
98
100
|
}
|
|
99
|
-
createCollectionBackend(collection, indices
|
|
101
|
+
createCollectionBackend(collection, indices) {
|
|
100
102
|
this.queries[collection.name] = /* @__PURE__ */ new Map();
|
|
101
103
|
void this.exec("registerCollection", collection.name, indices);
|
|
102
104
|
this.collectionReady.set(collection.name, this.exec("isReady", collection.name));
|
|
@@ -124,24 +126,6 @@ class WorkerDataAdapter {
|
|
|
124
126
|
registerQuery: (selector, options) => {
|
|
125
127
|
this.updateQuery(collection.name, { selector, options }, { state: "active", error: null, items: [] });
|
|
126
128
|
void this.exec("registerQuery", collection.name, selector, options);
|
|
127
|
-
},
|
|
128
|
-
unregisterQuery: (selector, options) => {
|
|
129
|
-
this.queries[collection.name]?.delete(queryId(selector, options));
|
|
130
|
-
void this.exec("unregisterQuery", collection.name, selector, options);
|
|
131
|
-
},
|
|
132
|
-
getQueryState: (selector, options) => {
|
|
133
|
-
const query = this.queries[collection.name]?.get(queryId(selector, options));
|
|
134
|
-
return query?.state || "active";
|
|
135
|
-
},
|
|
136
|
-
getQueryError: (selector, options) => {
|
|
137
|
-
const query = this.queries[collection.name]?.get(queryId(selector, options));
|
|
138
|
-
return query?.error || null;
|
|
139
|
-
},
|
|
140
|
-
getQueryResult: (selector, options) => {
|
|
141
|
-
const query = this.queries[collection.name]?.get(queryId(selector, options));
|
|
142
|
-
return query?.items || [];
|
|
143
|
-
},
|
|
144
|
-
onQueryStateChange: (selector, options, callback) => {
|
|
145
129
|
const handler = (event) => {
|
|
146
130
|
const { type, data, workerId, error } = event.data;
|
|
147
131
|
if (type !== "queryUpdate")
|
|
@@ -160,11 +144,49 @@ class WorkerDataAdapter {
|
|
|
160
144
|
selector: responseSelector,
|
|
161
145
|
options: responseOptions
|
|
162
146
|
}, { state, error, items });
|
|
163
|
-
|
|
147
|
+
const query = this.queries[collection.name]?.get(queryId(selector, options));
|
|
148
|
+
if (!query)
|
|
149
|
+
return;
|
|
150
|
+
query.stateChangeCallbacks.forEach((callback) => callback(state));
|
|
164
151
|
};
|
|
165
152
|
this.worker.addEventListener("message", handler);
|
|
153
|
+
this.updateQuery(collection.name, { selector, options }, { eventHandler: handler });
|
|
154
|
+
},
|
|
155
|
+
unregisterQuery: (selector, options) => {
|
|
156
|
+
const qid = queryId(selector, options);
|
|
157
|
+
const query = this.queries[collection.name]?.get(qid);
|
|
158
|
+
if (query?.eventHandler) {
|
|
159
|
+
this.worker.removeEventListener("message", query.eventHandler);
|
|
160
|
+
}
|
|
161
|
+
this.queries[collection.name]?.delete(qid);
|
|
162
|
+
void this.exec("unregisterQuery", collection.name, selector, options);
|
|
163
|
+
},
|
|
164
|
+
getQueryState: (selector, options) => {
|
|
165
|
+
const query = this.queries[collection.name]?.get(queryId(selector, options));
|
|
166
|
+
return query?.state || "active";
|
|
167
|
+
},
|
|
168
|
+
getQueryError: (selector, options) => {
|
|
169
|
+
const query = this.queries[collection.name]?.get(queryId(selector, options));
|
|
170
|
+
return query?.error || null;
|
|
171
|
+
},
|
|
172
|
+
getQueryResult: (selector, options) => {
|
|
173
|
+
const query = this.queries[collection.name]?.get(queryId(selector, options));
|
|
174
|
+
return query?.items || [];
|
|
175
|
+
},
|
|
176
|
+
onQueryStateChange: (selector, options, callback) => {
|
|
177
|
+
this.updateQuery(collection.name, { selector, options }, {
|
|
178
|
+
stateChangeCallbacks: [
|
|
179
|
+
...this.queries[collection.name]?.get(queryId(selector, options))?.stateChangeCallbacks || [],
|
|
180
|
+
callback
|
|
181
|
+
]
|
|
182
|
+
});
|
|
166
183
|
return () => {
|
|
167
|
-
this.
|
|
184
|
+
const currentCallbacks = this.queries[collection.name]?.get(queryId(selector, options))?.stateChangeCallbacks;
|
|
185
|
+
if (!currentCallbacks)
|
|
186
|
+
throw new Error("State change callbacks are not defined!");
|
|
187
|
+
this.updateQuery(collection.name, { selector, options }, {
|
|
188
|
+
stateChangeCallbacks: currentCallbacks.filter((existingCallback) => existingCallback !== callback)
|
|
189
|
+
});
|
|
168
190
|
};
|
|
169
191
|
},
|
|
170
192
|
executeQuery: (selector, options) => this.exec("executeQuery", collection.name, selector, options),
|
package/dist/index15.mjs
CHANGED
|
@@ -155,6 +155,8 @@ class WorkerDataAdapterHost {
|
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
async executeQuery(collectionName, selector, options) {
|
|
158
|
+
if (selector === null)
|
|
159
|
+
return [];
|
|
158
160
|
const items = await this.queryItems(collectionName, selector || {});
|
|
159
161
|
const { sort, skip, limit, fields } = options || {};
|
|
160
162
|
const sorted = sort ? sortItems(items, sort) : items;
|
|
@@ -248,7 +250,7 @@ class WorkerDataAdapterHost {
|
|
|
248
250
|
const existingItems = await this.executeQuery(collectionName, { id: { $in: input.map((i) => i[0].id) } });
|
|
249
251
|
const result = input.map(([item]) => {
|
|
250
252
|
if (item.id == null)
|
|
251
|
-
|
|
253
|
+
return new Error("Item must have an id");
|
|
252
254
|
if (existingItems.some((existing) => existing.id === item.id)) {
|
|
253
255
|
return new Error(`Item with id ${item.id} already exists`);
|
|
254
256
|
}
|
package/dist/index17.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import isEqual from "./index6.mjs";
|
|
2
|
-
import uniqueBy from "./
|
|
2
|
+
import uniqueBy from "./index32.mjs";
|
|
3
3
|
class Observer {
|
|
4
4
|
previousItems = [];
|
|
5
5
|
callbacks;
|
|
@@ -52,7 +52,9 @@ class Observer {
|
|
|
52
52
|
runChecks(getItems) {
|
|
53
53
|
const result = getItems();
|
|
54
54
|
if (result instanceof Promise) {
|
|
55
|
-
|
|
55
|
+
result.then((newItems) => this.checkItems(newItems)).catch((error) => {
|
|
56
|
+
console.error("Error while asynchronously querying items", error);
|
|
57
|
+
});
|
|
56
58
|
} else {
|
|
57
59
|
this.checkItems(result);
|
|
58
60
|
}
|
package/dist/index19.mjs
CHANGED
package/dist/index2.mjs
CHANGED
|
@@ -26,6 +26,7 @@ class Cursor {
|
|
|
26
26
|
* @param options.limit - The maximum number of items to return in the result set.
|
|
27
27
|
* @param options.reactive - A reactivity adapter to enable observing changes in the cursor's result set.
|
|
28
28
|
* @param options.fieldTracking - A boolean to enable fine-grained field tracking for reactivity.
|
|
29
|
+
* @param options.transformAll - A function that will be able to solve the n+1 problem
|
|
29
30
|
*/
|
|
30
31
|
constructor(getItems, options) {
|
|
31
32
|
this.getItems = getItems;
|
package/dist/index20.mjs
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
function isEmptyOptions(options) {
|
|
2
|
+
if (options == null)
|
|
3
|
+
return true;
|
|
4
|
+
if (typeof options !== "object")
|
|
5
|
+
return false;
|
|
6
|
+
if (Array.isArray(options))
|
|
7
|
+
return false;
|
|
8
|
+
return Object.keys(options).length === 0;
|
|
9
|
+
}
|
|
1
10
|
function queryId(selector, options) {
|
|
2
11
|
const selectorId = JSON.stringify(selector);
|
|
3
|
-
const optionsId = options
|
|
12
|
+
const optionsId = isEmptyOptions(options) ? -1 : JSON.stringify(options);
|
|
4
13
|
return `${selectorId}:${optionsId}`;
|
|
5
14
|
}
|
|
6
15
|
export {
|
package/dist/index21.mjs
CHANGED
package/dist/index22.mjs
CHANGED
package/dist/index24.mjs
CHANGED
package/dist/index26.mjs
CHANGED
package/dist/index29.mjs
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
function
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
return !set.has(value) && set.add(value);
|
|
6
|
-
});
|
|
1
|
+
function intersection(...arrays) {
|
|
2
|
+
if (arrays.length === 0)
|
|
3
|
+
return [];
|
|
4
|
+
return [...new Set(arrays.reduce((a, b) => a.filter((c) => b.includes(c))))];
|
|
7
5
|
}
|
|
8
6
|
export {
|
|
9
|
-
|
|
7
|
+
intersection as default
|
|
10
8
|
};
|
package/dist/index3.mjs
CHANGED
|
@@ -192,6 +192,11 @@ class Collection extends EventEmitter {
|
|
|
192
192
|
return item;
|
|
193
193
|
return this.options.transform(item);
|
|
194
194
|
}
|
|
195
|
+
transformAll(items, fields) {
|
|
196
|
+
if (!this.options.transformAll)
|
|
197
|
+
return items;
|
|
198
|
+
return this.options.transformAll(deepClone(items), fields);
|
|
199
|
+
}
|
|
195
200
|
getItem(selector, options) {
|
|
196
201
|
const itemsOrPromise = this.getItems(selector, { ...options, limit: 1 });
|
|
197
202
|
if (itemsOrPromise instanceof Promise) {
|
|
@@ -252,7 +257,17 @@ class Collection extends EventEmitter {
|
|
|
252
257
|
throw new Error("Collection is disposed");
|
|
253
258
|
if (selector !== void 0 && (!selector || typeof selector !== "object"))
|
|
254
259
|
throw new Error("Invalid selector");
|
|
255
|
-
const
|
|
260
|
+
const getTransformedItems = () => {
|
|
261
|
+
const itemsOrPromise = this.getItems(selector, options || {});
|
|
262
|
+
if (itemsOrPromise instanceof Promise) {
|
|
263
|
+
return itemsOrPromise.then((items2) => {
|
|
264
|
+
return this.transformAll(items2, options?.fields);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
const items = itemsOrPromise;
|
|
268
|
+
return this.transformAll(items, options?.fields);
|
|
269
|
+
};
|
|
270
|
+
const cursor = new Cursor(getTransformedItems, {
|
|
256
271
|
reactive: this.options.reactivity,
|
|
257
272
|
fieldTracking: this.fieldTracking,
|
|
258
273
|
...options,
|
package/dist/index30.mjs
CHANGED
|
@@ -1,6 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const expressionKeys = /* @__PURE__ */ new Set([
|
|
2
|
+
"$eq",
|
|
3
|
+
"$gt",
|
|
4
|
+
"$gte",
|
|
5
|
+
"$lt",
|
|
6
|
+
"$lte",
|
|
7
|
+
"$in",
|
|
8
|
+
"$nin",
|
|
9
|
+
"$ne",
|
|
10
|
+
"$exists",
|
|
11
|
+
"$not",
|
|
12
|
+
"$expr",
|
|
13
|
+
"$jsonSchema",
|
|
14
|
+
"$mod",
|
|
15
|
+
"$regex",
|
|
16
|
+
"$options",
|
|
17
|
+
"$text",
|
|
18
|
+
"$where",
|
|
19
|
+
"$all",
|
|
20
|
+
"$elemMatch",
|
|
21
|
+
"$size",
|
|
22
|
+
"$bitsAllClear",
|
|
23
|
+
"$bitsAllSet",
|
|
24
|
+
"$bitsAnyClear",
|
|
25
|
+
"$bitsAnySet"
|
|
26
|
+
]);
|
|
27
|
+
function isFieldExpression(expression) {
|
|
28
|
+
if (typeof expression !== "object" || expression == null) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const keys = Object.keys(expression);
|
|
32
|
+
if (keys.length === 0) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const hasInvalidKeys = keys.some((key) => !expressionKeys.has(key));
|
|
36
|
+
if (hasInvalidKeys)
|
|
37
|
+
return false;
|
|
38
|
+
const hasValidKeys = keys.every((key) => expressionKeys.has(key));
|
|
39
|
+
return hasValidKeys;
|
|
3
40
|
}
|
|
4
41
|
export {
|
|
5
|
-
|
|
42
|
+
isFieldExpression as default
|
|
6
43
|
};
|
package/dist/index31.mjs
CHANGED
|
@@ -1,8 +1,30 @@
|
|
|
1
|
-
function
|
|
2
|
-
if (
|
|
3
|
-
return
|
|
4
|
-
|
|
1
|
+
function set(object, path, value, deleteIfUndefined = false) {
|
|
2
|
+
if (object == null)
|
|
3
|
+
return object;
|
|
4
|
+
const segments = path.split(/[.[\]]/g);
|
|
5
|
+
if (segments[0] === "")
|
|
6
|
+
segments.shift();
|
|
7
|
+
if (segments.at(-1) === "")
|
|
8
|
+
segments.pop();
|
|
9
|
+
const apply = (node) => {
|
|
10
|
+
if (segments.length > 1) {
|
|
11
|
+
const key = segments.shift();
|
|
12
|
+
const nextIsNumber = !Number.isNaN(Number.parseInt(segments[0], 10));
|
|
13
|
+
if (node[key] === void 0) {
|
|
14
|
+
node[key] = nextIsNumber ? [] : {};
|
|
15
|
+
}
|
|
16
|
+
apply(node[key]);
|
|
17
|
+
} else {
|
|
18
|
+
if (deleteIfUndefined && value === void 0) {
|
|
19
|
+
delete node[segments[0]];
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
node[segments[0]] = value;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
apply(object);
|
|
26
|
+
return object;
|
|
5
27
|
}
|
|
6
28
|
export {
|
|
7
|
-
|
|
29
|
+
set as default
|
|
8
30
|
};
|
package/dist/index32.mjs
CHANGED
|
@@ -1,30 +1,10 @@
|
|
|
1
|
-
function
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
if (segments.at(-1) === "")
|
|
8
|
-
segments.pop();
|
|
9
|
-
const apply = (node) => {
|
|
10
|
-
if (segments.length > 1) {
|
|
11
|
-
const key = segments.shift();
|
|
12
|
-
const nextIsNumber = !Number.isNaN(Number.parseInt(segments[0], 10));
|
|
13
|
-
if (node[key] === void 0) {
|
|
14
|
-
node[key] = nextIsNumber ? [] : {};
|
|
15
|
-
}
|
|
16
|
-
apply(node[key]);
|
|
17
|
-
} else {
|
|
18
|
-
if (deleteIfUndefined && value === void 0) {
|
|
19
|
-
delete node[segments[0]];
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
node[segments[0]] = value;
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
apply(object);
|
|
26
|
-
return object;
|
|
1
|
+
function uniqueBy(array, fn) {
|
|
2
|
+
const set = /* @__PURE__ */ new Set();
|
|
3
|
+
return array.filter((element) => {
|
|
4
|
+
const value = typeof fn === "function" ? fn(element) : element[fn];
|
|
5
|
+
return !set.has(value) && set.add(value);
|
|
6
|
+
});
|
|
27
7
|
}
|
|
28
8
|
export {
|
|
29
|
-
|
|
9
|
+
uniqueBy as default
|
|
30
10
|
};
|
package/dist/index33.mjs
CHANGED
|
@@ -1,43 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
"$gt",
|
|
4
|
-
"$gte",
|
|
5
|
-
"$lt",
|
|
6
|
-
"$lte",
|
|
7
|
-
"$in",
|
|
8
|
-
"$nin",
|
|
9
|
-
"$ne",
|
|
10
|
-
"$exists",
|
|
11
|
-
"$not",
|
|
12
|
-
"$expr",
|
|
13
|
-
"$jsonSchema",
|
|
14
|
-
"$mod",
|
|
15
|
-
"$regex",
|
|
16
|
-
"$options",
|
|
17
|
-
"$text",
|
|
18
|
-
"$where",
|
|
19
|
-
"$all",
|
|
20
|
-
"$elemMatch",
|
|
21
|
-
"$size",
|
|
22
|
-
"$bitsAllClear",
|
|
23
|
-
"$bitsAllSet",
|
|
24
|
-
"$bitsAnyClear",
|
|
25
|
-
"$bitsAnySet"
|
|
26
|
-
]);
|
|
27
|
-
function isFieldExpression(expression) {
|
|
28
|
-
if (typeof expression !== "object" || expression == null) {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
const keys = Object.keys(expression);
|
|
32
|
-
if (keys.length === 0) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
const hasInvalidKeys = keys.some((key) => !expressionKeys.has(key));
|
|
36
|
-
if (hasInvalidKeys)
|
|
37
|
-
return false;
|
|
38
|
-
const hasValidKeys = keys.every((key) => expressionKeys.has(key));
|
|
39
|
-
return hasValidKeys;
|
|
1
|
+
function createIndexProvider(definition) {
|
|
2
|
+
return definition;
|
|
40
3
|
}
|
|
41
4
|
export {
|
|
42
|
-
|
|
5
|
+
createIndexProvider as default
|
|
43
6
|
};
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @template T - The type of the value to clone.
|
|
5
5
|
* @param value - The value to deep clone.
|
|
6
6
|
* @returns A deep copy of the provided value.
|
|
7
|
-
* @throws An error if the value is a function, as cloning functions is not supported.
|
|
7
|
+
* @throws {Error} An error if the value is a function, as cloning functions is not supported.
|
|
8
8
|
*/
|
|
9
9
|
export declare function clone<T>(value: T): T;
|
|
10
10
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signaldb/core",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.2",
|
|
4
4
|
"description": "SignalDB is a client-side database that provides a simple MongoDB-like interface to the data with first-class typescript support to achieve an optimistic UI. Data persistence can be achieved by using storage providers that store the data through a JSON interface to places such as localStorage.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "rimraf dist && vite build",
|
|
@@ -55,9 +55,9 @@
|
|
|
55
55
|
],
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"fast-sort": "^3.4.1",
|
|
58
|
-
"mingo": "^
|
|
58
|
+
"mingo": "^7.1.1"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"zod": "^4.
|
|
61
|
+
"zod": "^4.3.5"
|
|
62
62
|
}
|
|
63
63
|
}
|