@signaldb/svelte 2.0.0-beta.2 → 2.0.0-beta.21

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 (105) hide show
  1. package/dist/base/core/src/AsyncDataAdapter.d.ts +112 -0
  2. package/dist/base/core/src/AsyncDataAdapter.js +549 -0
  3. package/dist/base/core/src/AutoFetchDataAdapter.d.ts +112 -0
  4. package/dist/base/core/src/AutoFetchDataAdapter.js +612 -0
  5. package/dist/base/core/src/Collection/Cursor.d.ts +165 -0
  6. package/dist/base/core/src/Collection/Cursor.js +352 -0
  7. package/dist/base/core/src/Collection/Observer.d.ts +95 -0
  8. package/dist/base/core/src/Collection/Observer.js +227 -0
  9. package/dist/base/core/src/Collection/index.d.ts +341 -0
  10. package/dist/base/core/src/Collection/index.js +761 -0
  11. package/dist/base/core/src/Collection/types.d.ts +28 -0
  12. package/dist/base/core/src/Collection/types.js +2 -0
  13. package/dist/base/core/src/DataAdapter.d.ts +54 -0
  14. package/dist/base/core/src/DataAdapter.js +2 -0
  15. package/dist/base/core/src/DefaultDataAdapter.d.ts +35 -0
  16. package/dist/base/core/src/DefaultDataAdapter.js +408 -0
  17. package/dist/base/core/src/WorkerDataAdapter.d.ts +79 -0
  18. package/dist/base/core/src/WorkerDataAdapter.js +749 -0
  19. package/dist/base/core/src/WorkerDataAdapterHost.d.ts +64 -0
  20. package/dist/base/core/src/WorkerDataAdapterHost.js +430 -0
  21. package/dist/base/core/src/createIndex.d.ts +7 -0
  22. package/dist/base/core/src/createIndex.js +120 -0
  23. package/dist/base/core/src/createIndexProvider.d.ts +8 -0
  24. package/dist/base/core/src/createIndexProvider.js +11 -0
  25. package/dist/base/core/src/createReactivityAdapter.d.ts +8 -0
  26. package/dist/base/core/src/createReactivityAdapter.js +11 -0
  27. package/dist/base/core/src/createStorageAdapter.d.ts +9 -0
  28. package/dist/base/core/src/createStorageAdapter.js +11 -0
  29. package/dist/base/core/src/getIndexInfo.d.ts +39 -0
  30. package/dist/base/core/src/getIndexInfo.js +188 -0
  31. package/dist/base/core/src/index.d.ts +26 -0
  32. package/dist/base/core/src/index.js +39 -0
  33. package/dist/base/core/src/types/Dependency.d.ts +4 -0
  34. package/dist/base/core/src/types/Dependency.js +2 -0
  35. package/dist/base/core/src/types/IndexProvider.d.ts +26 -0
  36. package/dist/base/core/src/types/IndexProvider.js +2 -0
  37. package/dist/base/core/src/types/Modifier.d.ts +46 -0
  38. package/dist/base/core/src/types/Modifier.js +3 -0
  39. package/dist/base/core/src/types/ReactivityAdapter.d.ts +6 -0
  40. package/dist/base/core/src/types/ReactivityAdapter.js +2 -0
  41. package/dist/base/core/src/types/Selector.d.ts +46 -0
  42. package/dist/base/core/src/types/Selector.js +3 -0
  43. package/dist/base/core/src/types/Signal.d.ts +4 -0
  44. package/dist/base/core/src/types/Signal.js +2 -0
  45. package/dist/base/core/src/types/StorageAdapter.d.ts +28 -0
  46. package/dist/base/core/src/types/StorageAdapter.js +2 -0
  47. package/dist/base/core/src/utils/EventEmitter.d.ts +71 -0
  48. package/dist/base/core/src/utils/EventEmitter.js +144 -0
  49. package/dist/base/core/src/utils/applyQueryOptions.d.ts +15 -0
  50. package/dist/base/core/src/utils/applyQueryOptions.js +36 -0
  51. package/dist/base/core/src/utils/batchOnNextTick.d.ts +16 -0
  52. package/dist/base/core/src/utils/batchOnNextTick.js +72 -0
  53. package/dist/base/core/src/utils/compact.d.ts +9 -0
  54. package/dist/base/core/src/utils/compact.js +21 -0
  55. package/dist/base/core/src/utils/createSignal.d.ts +14 -0
  56. package/dist/base/core/src/utils/createSignal.js +38 -0
  57. package/dist/base/core/src/utils/deepClone.d.ts +17 -0
  58. package/dist/base/core/src/utils/deepClone.js +68 -0
  59. package/dist/base/core/src/utils/get.d.ts +9 -0
  60. package/dist/base/core/src/utils/get.js +27 -0
  61. package/dist/base/core/src/utils/getMatchingKeys.d.ts +19 -0
  62. package/dist/base/core/src/utils/getMatchingKeys.js +49 -0
  63. package/dist/base/core/src/utils/idIndexQuery.d.ts +20 -0
  64. package/dist/base/core/src/utils/idIndexQuery.js +49 -0
  65. package/dist/base/core/src/utils/incrementalQueryUpdate.d.ts +62 -0
  66. package/dist/base/core/src/utils/incrementalQueryUpdate.js +197 -0
  67. package/dist/base/core/src/utils/intersection.d.ts +9 -0
  68. package/dist/base/core/src/utils/intersection.js +16 -0
  69. package/dist/base/core/src/utils/isEqual.d.ts +14 -0
  70. package/dist/base/core/src/utils/isEqual.js +43 -0
  71. package/dist/base/core/src/utils/isFieldExpression.d.ts +11 -0
  72. package/dist/base/core/src/utils/isFieldExpression.js +52 -0
  73. package/dist/base/core/src/utils/match.d.ts +12 -0
  74. package/dist/base/core/src/utils/match.js +16 -0
  75. package/dist/base/core/src/utils/modify.d.ts +14 -0
  76. package/dist/base/core/src/utils/modify.js +29 -0
  77. package/dist/base/core/src/utils/project.d.ts +15 -0
  78. package/dist/base/core/src/utils/project.js +43 -0
  79. package/dist/base/core/src/utils/projectItems.d.ts +12 -0
  80. package/dist/base/core/src/utils/projectItems.js +25 -0
  81. package/dist/base/core/src/utils/queryDelta.d.ts +83 -0
  82. package/dist/base/core/src/utils/queryDelta.js +231 -0
  83. package/dist/base/core/src/utils/queryId.d.ts +9 -0
  84. package/dist/base/core/src/utils/queryId.js +61 -0
  85. package/dist/base/core/src/utils/randomId.d.ts +7 -0
  86. package/dist/base/core/src/utils/randomId.js +12 -0
  87. package/dist/base/core/src/utils/reactiveOrAsync.d.ts +59 -0
  88. package/dist/base/core/src/utils/reactiveOrAsync.js +93 -0
  89. package/dist/base/core/src/utils/serializeValue.d.ts +12 -0
  90. package/dist/base/core/src/utils/serializeValue.js +27 -0
  91. package/dist/base/core/src/utils/set.d.ts +13 -0
  92. package/dist/base/core/src/utils/set.js +43 -0
  93. package/dist/base/core/src/utils/sortItems.d.ts +12 -0
  94. package/dist/base/core/src/utils/sortItems.js +23 -0
  95. package/dist/base/core/src/utils/storageIndexQuery.d.ts +20 -0
  96. package/dist/base/core/src/utils/storageIndexQuery.js +75 -0
  97. package/dist/base/core/src/utils/uniqueBy.d.ts +10 -0
  98. package/dist/base/core/src/utils/uniqueBy.js +19 -0
  99. package/dist/index.cjs +38 -0
  100. package/dist/index.cjs.map +1 -0
  101. package/dist/index.mjs +38 -0
  102. package/dist/index.mjs.map +1 -0
  103. package/dist/reactivity-adapters/svelte/index.svelte.d.ts +10 -0
  104. package/dist/reactivity-adapters/svelte/index.svelte.js +36 -0
  105. package/package.json +3 -3
@@ -0,0 +1,62 @@
1
+ import type { QueryOptions } from '../DataAdapter';
2
+ import type { BaseItem } from '../Collection/types';
3
+ import type Selector from '../types/Selector';
4
+ /**
5
+ * The items a write created, updated or removed, from the point of view of a store.
6
+ *
7
+ * `upserts` are the *current* state of every item that still exists; `deletes` are the ids of items
8
+ * that no longer do. A write that changes an item's id contributes to both. Callers are responsible
9
+ * for that split — an "affected items" list holding an item's state from before and after a write
10
+ * cannot be told apart from two unrelated items here.
11
+ */
12
+ export interface QueryChangeset<T extends BaseItem> {
13
+ upserts: T[];
14
+ deletes: any[];
15
+ }
16
+ /**
17
+ * Recomputes a query's result from its previous result and the change that was just written,
18
+ * without going back to the store.
19
+ *
20
+ * A store re-executing the query instead reads every item it holds (or every item an index points
21
+ * at) and filters, sorts and projects the lot — for a write that touched one row. This does the
22
+ * same job in the size of the write, which is what a query's result costs to keep up to date when
23
+ * the change that affects it is already in hand.
24
+ *
25
+ * Returns `null` when the previous result is not enough to answer, and the caller has to re-execute
26
+ * the query after all:
27
+ * - `limit` or `skip`: the result is a window onto a larger set, and an item leaving the window has
28
+ * to be replaced by one the previous result never contained.
29
+ * - `fields` together with a `sort` the projection does not keep: the previous items are
30
+ * projected, so a sort key the projection dropped is no longer there to sort by. A projection
31
+ * that keeps every sort key is fine, and is the common case — a list sorted by the same date it
32
+ * displays.
33
+ * - a `null` selector, which matches nothing and is not worth a special case.
34
+ * @template T - The type of the items.
35
+ * @param previous - The query's previous result.
36
+ * @param selector - The query's selector.
37
+ * @param options - The query's options.
38
+ * @param changes - The items the write created, updated or removed.
39
+ * @returns The new result, or `null` when the query has to be re-executed.
40
+ */
41
+ export default function incrementalQueryUpdate<T extends BaseItem>(previous: T[], selector: Selector<T>, options: QueryOptions<T> | undefined, changes: QueryChangeset<T>): T[] | null;
42
+ /**
43
+ * Folds a change into a query's result, whatever the query's options.
44
+ *
45
+ * The unguarded version of `incrementalQueryUpdate`, for the places where the alternative is not a
46
+ * more accurate answer but a wrong one — layering a write that has not been confirmed yet on top of
47
+ * the last confirmed result, say. For a query returning everything it matches, this is exact. For a
48
+ * window onto a larger set it is the closest the window itself can get: an item that no longer
49
+ * belongs is dropped, one that does is placed, and the window is trimmed back to its length — but
50
+ * an item pulled in from beyond the window is not something the window knows about.
51
+ *
52
+ * What it never does is re-examine the items already in the result. They matched when the store
53
+ * produced them, they still match, and asking again is both wasteful and — for a projected result,
54
+ * whose items no longer carry the fields the selector names — wrong.
55
+ * @template T - The type of the items.
56
+ * @param previous - The query's previous result.
57
+ * @param selector - The query's selector.
58
+ * @param options - The query's options.
59
+ * @param changes - The items the write created, updated or removed.
60
+ * @returns The resulting items.
61
+ */
62
+ export declare function mergeChangesetIntoResult<T extends BaseItem>(previous: T[], selector: Selector<T>, options: QueryOptions<T> | undefined, changes: QueryChangeset<T>): T[];
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = incrementalQueryUpdate;
7
+ exports.mergeChangesetIntoResult = mergeChangesetIntoResult;
8
+ const match_1 = __importDefault(require("./match"));
9
+ const projectItems_1 = __importDefault(require("./projectItems"));
10
+ const sortItems_1 = __importDefault(require("./sortItems"));
11
+ /**
12
+ * Recomputes a query's result from its previous result and the change that was just written,
13
+ * without going back to the store.
14
+ *
15
+ * A store re-executing the query instead reads every item it holds (or every item an index points
16
+ * at) and filters, sorts and projects the lot — for a write that touched one row. This does the
17
+ * same job in the size of the write, which is what a query's result costs to keep up to date when
18
+ * the change that affects it is already in hand.
19
+ *
20
+ * Returns `null` when the previous result is not enough to answer, and the caller has to re-execute
21
+ * the query after all:
22
+ * - `limit` or `skip`: the result is a window onto a larger set, and an item leaving the window has
23
+ * to be replaced by one the previous result never contained.
24
+ * - `fields` together with a `sort` the projection does not keep: the previous items are
25
+ * projected, so a sort key the projection dropped is no longer there to sort by. A projection
26
+ * that keeps every sort key is fine, and is the common case — a list sorted by the same date it
27
+ * displays.
28
+ * - a `null` selector, which matches nothing and is not worth a special case.
29
+ * @template T - The type of the items.
30
+ * @param previous - The query's previous result.
31
+ * @param selector - The query's selector.
32
+ * @param options - The query's options.
33
+ * @param changes - The items the write created, updated or removed.
34
+ * @returns The new result, or `null` when the query has to be re-executed.
35
+ */
36
+ function incrementalQueryUpdate(previous, selector, options, changes) {
37
+ if (selector == null)
38
+ return null;
39
+ const { sort, skip, limit, fields } = options || {};
40
+ if (skip != null)
41
+ return null;
42
+ if (fields != null && sort != null && !sortKeysSurviveProjection(sort, fields))
43
+ return null;
44
+ if (limit != null && !windowStaysClosed(previous, selector, options, changes))
45
+ return null;
46
+ return mergeChangesetIntoResult(previous, selector, options, changes);
47
+ }
48
+ /**
49
+ * Whether a projection keeps every field a sort is keyed on.
50
+ *
51
+ * The previous result is the projected items, so this decides whether they still carry what the
52
+ * sort needs. Both projection modes are covered, because `project` treats an all-zero spec as an
53
+ * exclusion and anything else as an inclusion:
54
+ *
55
+ * - An inclusion keeps a key when the key itself is included, or an ancestor of it is (`{a: 1}`
56
+ * keeps `a.b`), and always keeps `id` unless the spec excludes it outright.
57
+ * - An exclusion keeps a key unless the key or an ancestor of it is excluded.
58
+ *
59
+ * Anything it cannot account for is a "no": re-executing is slower, not wrong.
60
+ * @template T - The type of the items.
61
+ * @param sort - The query's sort.
62
+ * @param fields - The query's projection.
63
+ * @returns `true` when every sort key survives the projection.
64
+ */
65
+ function sortKeysSurviveProjection(sort, fields) {
66
+ const entries = Object.entries(fields);
67
+ if (entries.length === 0)
68
+ return true;
69
+ const isExclusion = entries.every(([, value]) => value === 0);
70
+ // `a.b.c` is kept by `a`, by `a.b` and by `a.b.c`, and dropped by any of them under an
71
+ // exclusion — so both modes ask about the key and each of its ancestors.
72
+ const pathsFor = (key) => key
73
+ .split('.')
74
+ .map((_, index, parts) => parts.slice(0, index + 1).join('.'));
75
+ return Object.keys(sort).every((key) => {
76
+ const paths = pathsFor(key);
77
+ if (isExclusion)
78
+ return paths.every(path => fields[path] !== 0);
79
+ if (key === 'id')
80
+ return fields.id !== 0;
81
+ return paths.some(path => fields[path] === 1);
82
+ });
83
+ }
84
+ /**
85
+ * Whether two items are in the given order under the given sort, deciding ties against the caller.
86
+ *
87
+ * Uses the sort itself rather than a comparator of its own: a rule about which side of a window an
88
+ * item falls on is only as good as its agreement with the ordering that drew the window. A tie
89
+ * comes back as `false`, because a tie is exactly the case where an item could belong on either
90
+ * side and the answer has to be taken from the store.
91
+ * @template T - The type of the items.
92
+ * @param item - The item whose position is in question.
93
+ * @param edge - The item at the edge of the window.
94
+ * @param sort - The query's sort.
95
+ * @returns `true` when `item` sorts strictly before `edge`.
96
+ */
97
+ function sortsBefore(item, edge, sort) {
98
+ return (0, sortItems_1.default)([edge, item], sort)[0] === item;
99
+ }
100
+ /**
101
+ * Whether a change to a windowed query can be answered from the window alone.
102
+ *
103
+ * A window holds the first `limit` items in sort order, and nothing about what lies beyond it. An
104
+ * item leaving the window therefore has to be replaced by one the window has never seen, and that
105
+ * answer can only come from the store. An item arriving is a different matter: it takes its place
106
+ * and pushes the last one out, and where that one goes is not the window's problem.
107
+ *
108
+ * The one case where none of this applies is a window that was never full, because then the query
109
+ * already returns everything it matches and there is no "beyond".
110
+ * @template T - The type of the items.
111
+ * @param previous - The query's previous result.
112
+ * @param selector - The query's selector.
113
+ * @param options - The query's options.
114
+ * @param changes - The items the write created, updated or removed.
115
+ * @returns `true` when the new window follows from the old one and the change.
116
+ */
117
+ function windowStaysClosed(previous, selector, options, changes) {
118
+ const { sort, limit, fields } = options || {};
119
+ if (limit == null || previous.length < limit)
120
+ return true;
121
+ // A full window needs an edge to compare against, and comparing needs the field the sort is on
122
+ // to still be there.
123
+ if (sort == null || fields != null)
124
+ return false;
125
+ const edge = previous.at(-1);
126
+ const runnerUp = previous.at(-2);
127
+ // Which item is *the* edge has to be beyond doubt. Two items sorting equally at the end of the
128
+ // window are interchangeable, and so is the question of which of them a write displaces.
129
+ if (runnerUp != null && !sortsBefore(runnerUp, edge, sort))
130
+ return false;
131
+ const inWindow = new Set(previous.map(item => item.id));
132
+ if (changes.deletes.some(id => inWindow.has(id)))
133
+ return false;
134
+ return changes.upserts.every((item) => {
135
+ if (item === edge)
136
+ return true;
137
+ const before = sortsBefore(item, edge, sort);
138
+ if (!inWindow.has(item.id)) {
139
+ // Coming from outside: it either takes a place inside, displacing the edge, or stays where
140
+ // it was. Sorting *equally* to the edge is the one answer the window cannot give, because
141
+ // the store may just as well have kept the edge and left this one out.
142
+ if (!(0, match_1.default)(item, selector))
143
+ return true;
144
+ return before || sortsBefore(edge, item, sort);
145
+ }
146
+ // An item that no longer matches leaves a place open, and what fills it is beyond the window.
147
+ if (!(0, match_1.default)(item, selector))
148
+ return false;
149
+ if (before)
150
+ return true;
151
+ // Not before the edge. The one item that may sit *on* the edge is the edge itself, and only
152
+ // while it has not actually moved — an edge that slides outwards gives up the last place, and
153
+ // what takes it is something the window has never seen.
154
+ return item.id === edge.id && !sortsBefore(edge, item, sort);
155
+ });
156
+ }
157
+ /**
158
+ * Folds a change into a query's result, whatever the query's options.
159
+ *
160
+ * The unguarded version of `incrementalQueryUpdate`, for the places where the alternative is not a
161
+ * more accurate answer but a wrong one — layering a write that has not been confirmed yet on top of
162
+ * the last confirmed result, say. For a query returning everything it matches, this is exact. For a
163
+ * window onto a larger set it is the closest the window itself can get: an item that no longer
164
+ * belongs is dropped, one that does is placed, and the window is trimmed back to its length — but
165
+ * an item pulled in from beyond the window is not something the window knows about.
166
+ *
167
+ * What it never does is re-examine the items already in the result. They matched when the store
168
+ * produced them, they still match, and asking again is both wasteful and — for a projected result,
169
+ * whose items no longer carry the fields the selector names — wrong.
170
+ * @template T - The type of the items.
171
+ * @param previous - The query's previous result.
172
+ * @param selector - The query's selector.
173
+ * @param options - The query's options.
174
+ * @param changes - The items the write created, updated or removed.
175
+ * @returns The resulting items.
176
+ */
177
+ function mergeChangesetIntoResult(previous, selector, options, changes) {
178
+ if (selector == null)
179
+ return [];
180
+ const { sort, limit, fields } = options || {};
181
+ const byId = new Map();
182
+ previous.forEach(item => byId.set(item.id, item));
183
+ changes.deletes.forEach(id => byId.delete(id));
184
+ changes.upserts.forEach((item) => {
185
+ // Matched against the unprojected item: the selector is free to name fields the projection
186
+ // drops, and the stored result would have no answer for those.
187
+ if ((0, match_1.default)(item, selector)) {
188
+ byId.set(item.id, (0, projectItems_1.default)([item], fields)[0]);
189
+ }
190
+ else {
191
+ byId.delete(item.id);
192
+ }
193
+ });
194
+ const items = [...byId.values()];
195
+ const sorted = sort ? (0, sortItems_1.default)(items, sort) : items;
196
+ return limit == null ? sorted : sorted.slice(0, limit);
197
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Computes the intersection of multiple arrays, returning an array of unique elements
3
+ * that are present in all the input arrays.
4
+ * @template T - The type of elements in the arrays.
5
+ * @param arrays - A variable number of arrays to compute the intersection of.
6
+ * @returns An array containing the unique elements found in all the input arrays.
7
+ * - If no arrays are provided, returns an empty array.
8
+ */
9
+ export default function intersection<T>(...arrays: T[][]): T[];
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = intersection;
4
+ /**
5
+ * Computes the intersection of multiple arrays, returning an array of unique elements
6
+ * that are present in all the input arrays.
7
+ * @template T - The type of elements in the arrays.
8
+ * @param arrays - A variable number of arrays to compute the intersection of.
9
+ * @returns An array containing the unique elements found in all the input arrays.
10
+ * - If no arrays are provided, returns an empty array.
11
+ */
12
+ function intersection(...arrays) {
13
+ if (arrays.length === 0)
14
+ return [];
15
+ return [...new Set(arrays.reduce((a, b) => a.filter(c => b.includes(c))))];
16
+ }
@@ -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,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = isEqual;
4
+ /**
5
+ * Compares two values for deep equality.
6
+ * @param a - The first value to compare.
7
+ * @param b - The second value to compare.
8
+ * @returns - Returns `true` if the two values are deeply equal, otherwise `false`.
9
+ * @example
10
+ * isEqual({ a: 1 }, { a: 1 }); // true
11
+ * isEqual([1, 2], [1, 2]); // true
12
+ * isEqual(new Date(0), new Date(0)); // true
13
+ * isEqual(/abc/, /abc/); // true
14
+ * isEqual({ a: 1 }, { a: 2 }); // false
15
+ * isEqual(null, null); // true
16
+ */
17
+ function isEqual(a, b) {
18
+ if (Object.is(a, b))
19
+ return true;
20
+ if (a instanceof RegExp && b instanceof RegExp)
21
+ return a.toString() === b.toString();
22
+ if (a instanceof Date && b instanceof Date)
23
+ return a.getTime() === b.getTime();
24
+ if (typeof a !== 'object')
25
+ return false;
26
+ if (typeof b !== 'object')
27
+ return false;
28
+ if (a === null)
29
+ return false;
30
+ if (b === null)
31
+ return false;
32
+ const aKeys = Object.keys(a);
33
+ const bKeys = Object.keys(b);
34
+ if (aKeys.length !== bKeys.length)
35
+ return false;
36
+ for (const key of aKeys) {
37
+ if (!bKeys.includes(key))
38
+ return false;
39
+ if (!isEqual(a[key], b[key]))
40
+ return false;
41
+ }
42
+ return true;
43
+ }
@@ -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,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = isFieldExpression;
4
+ const expressionKeys = new Set([
5
+ '$eq',
6
+ '$gt',
7
+ '$gte',
8
+ '$lt',
9
+ '$lte',
10
+ '$in',
11
+ '$nin',
12
+ '$ne',
13
+ '$exists',
14
+ '$not',
15
+ '$expr',
16
+ '$jsonSchema',
17
+ '$mod',
18
+ '$regex',
19
+ '$options',
20
+ '$text',
21
+ '$where',
22
+ '$all',
23
+ '$elemMatch',
24
+ '$size',
25
+ '$bitsAllClear',
26
+ '$bitsAllSet',
27
+ '$bitsAnyClear',
28
+ '$bitsAnySet',
29
+ ]);
30
+ /**
31
+ * Determines whether a given object is a valid field expression.
32
+ * A field expression is an object containing query operators supported by MongoDB-style queries.
33
+ * @template T - The type of the field expression.
34
+ * @param expression - The object to test.
35
+ * @returns A boolean indicating whether the object is a valid field expression.
36
+ * - `true` if the object contains only recognized query operators.
37
+ * - `false` otherwise.
38
+ */
39
+ function isFieldExpression(expression) {
40
+ if (typeof expression !== 'object' || expression == null) {
41
+ return false;
42
+ }
43
+ const keys = Object.keys(expression);
44
+ if (keys.length === 0) {
45
+ return false;
46
+ }
47
+ const hasInvalidKeys = keys.some(key => !expressionKeys.has(key));
48
+ if (hasInvalidKeys)
49
+ return false;
50
+ const hasValidKeys = keys.every(key => expressionKeys.has(key));
51
+ return hasValidKeys;
52
+ }
@@ -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,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = match;
4
+ const mingo_1 = require("mingo");
5
+ /**
6
+ * Tests whether a given item matches a specified selector.
7
+ * Uses the `mingo` library to evaluate the query.
8
+ * @template T - The type of the item being tested.
9
+ * @param item - The item to test against the selector.
10
+ * @param selector - The query selector used to match the item.
11
+ * @returns A boolean indicating whether the item matches the selector.
12
+ */
13
+ function match(item, selector) {
14
+ const query = new mingo_1.Query(selector);
15
+ return query.test(item);
16
+ }
@@ -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,29 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = modify;
7
+ const mingo_1 = require("mingo");
8
+ const deepClone_1 = __importDefault(require("./deepClone"));
9
+ /**
10
+ * Applies a modifier to an object and returns a new modified object.
11
+ * @template T - The type of the object to be modified.
12
+ * @param item - The object to be modified.
13
+ * @param modifier - The modifier to apply. This can be any transformation logic.
14
+ * @returns - Returns a new object with the modifications applied.
15
+ * @example
16
+ * const item = { a: 1, b: 2 }
17
+ * const modifier = { $set: { b: 3, c: 4 } }
18
+ * const result = modify(item, modifier)
19
+ * // result: { a: 1, b: 3, c: 4 }
20
+ */
21
+ function modify(item, modifier) {
22
+ const hasOperators = Object.keys(modifier).some(key => key.startsWith('$'));
23
+ if (!hasOperators)
24
+ return modifier;
25
+ const clonedItem = (0, deepClone_1.default)(item);
26
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
27
+ (0, mingo_1.update)(clonedItem, modifier);
28
+ return clonedItem;
29
+ }
@@ -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,43 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = project;
7
+ const get_1 = __importDefault(require("./get"));
8
+ const set_1 = __importDefault(require("./set"));
9
+ /**
10
+ * Projects the fields of an object based on a specified fields configuration.
11
+ * Supports inclusion (`1`) and exclusion (`0`) of specific fields. Creates a new object
12
+ * with the desired fields included or excluded, based on the configuration.
13
+ * @template T - The type of the object being projected.
14
+ * @param item - The original object to project fields from.
15
+ * @param fields - An object defining the fields to include (`1`) or exclude (`0`).
16
+ * - Keys are the field names, and values are either `1` (include) or `0` (exclude).
17
+ * @returns A new object with the specified fields included or excluded.
18
+ * - If all fields are set to `0`, the excluded fields are removed from the result.
19
+ * - If fields are set to `1`, only the included fields are retained.
20
+ */
21
+ function project(item, fields) {
22
+ const allFieldsDeactivated = Object.values(fields).every(value => value === 0);
23
+ if (allFieldsDeactivated) {
24
+ const result = { ...item };
25
+ Object.keys(fields).forEach((key) => {
26
+ const fieldValue = (0, get_1.default)(item, key);
27
+ if (fieldValue === undefined)
28
+ return;
29
+ (0, set_1.default)(result, key, undefined, true);
30
+ });
31
+ return result;
32
+ }
33
+ const result = {};
34
+ Object.entries(fields).forEach(([key, value]) => {
35
+ const fieldValue = (0, get_1.default)(item, key);
36
+ if (fieldValue === undefined)
37
+ return;
38
+ if (fieldValue == null && value !== 1)
39
+ return;
40
+ (0, set_1.default)(result, key, value === 1 ? fieldValue : undefined);
41
+ });
42
+ return result;
43
+ }
@@ -0,0 +1,12 @@
1
+ import type { QueryOptions } from '../DataAdapter';
2
+ import type { BaseItem } from '../Collection/types';
3
+ /**
4
+ * Applies a query's field projection to a list of items, keeping the primary key unless the
5
+ * projection excludes it outright. Returns the items untouched when there is no projection, so a
6
+ * caller does not have to check for one first.
7
+ * @template T - The type of the items.
8
+ * @param items - The items to project.
9
+ * @param fields - The projection, or `undefined` for none.
10
+ * @returns The projected items.
11
+ */
12
+ export default function projectItems<T extends BaseItem>(items: T[], fields: QueryOptions<T>['fields']): T[];
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = projectItems;
7
+ const project_1 = __importDefault(require("./project"));
8
+ /**
9
+ * Applies a query's field projection to a list of items, keeping the primary key unless the
10
+ * projection excludes it outright. Returns the items untouched when there is no projection, so a
11
+ * caller does not have to check for one first.
12
+ * @template T - The type of the items.
13
+ * @param items - The items to project.
14
+ * @param fields - The projection, or `undefined` for none.
15
+ * @returns The projected items.
16
+ */
17
+ function projectItems(items, fields) {
18
+ if (!fields)
19
+ return items;
20
+ const idExcluded = fields.id === 0;
21
+ return items.map(item => ({
22
+ ...idExcluded ? {} : { id: item.id },
23
+ ...(0, project_1.default)(item, fields),
24
+ }));
25
+ }