@furystack/core 15.1.0 → 15.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/esm/filter-items.d.ts +11 -0
- package/esm/filter-items.d.ts.map +1 -0
- package/esm/filter-items.js +130 -0
- package/esm/filter-items.js.map +1 -0
- package/esm/filter-items.spec.d.ts +2 -0
- package/esm/filter-items.spec.d.ts.map +1 -0
- package/esm/filter-items.spec.js +174 -0
- package/esm/filter-items.spec.js.map +1 -0
- package/esm/in-memory-store.d.ts +0 -2
- package/esm/in-memory-store.d.ts.map +1 -1
- package/esm/in-memory-store.js +4 -117
- package/esm/in-memory-store.js.map +1 -1
- package/esm/index.d.ts +1 -0
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/esm/models/physical-store.d.ts +5 -3
- package/esm/models/physical-store.d.ts.map +1 -1
- package/esm/store-manager.d.ts +6 -0
- package/esm/store-manager.d.ts.map +1 -1
- package/esm/store-manager.js +6 -0
- package/esm/store-manager.js.map +1 -1
- package/package.json +1 -1
- package/src/filter-items.spec.ts +216 -0
- package/src/filter-items.ts +148 -0
- package/src/in-memory-store.ts +4 -136
- package/src/index.ts +1 -0
- package/src/models/physical-store.ts +5 -3
- package/src/store-manager.ts +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [15.2.1] - 2026-02-26
|
|
4
|
+
|
|
5
|
+
### 📝 Documentation
|
|
6
|
+
|
|
7
|
+
- Added JSDoc recommendations to `PhysicalStore` and `StoreManager.getStoreFor()` pointing to `DataSet` as the preferred write gateway for application-level code
|
|
8
|
+
|
|
9
|
+
## [15.2.0] - 2026-02-22
|
|
10
|
+
|
|
11
|
+
### ✨ Features
|
|
12
|
+
|
|
13
|
+
### Public `filterItems()` function
|
|
14
|
+
|
|
15
|
+
Extracted the filter logic from `InMemoryStore` into a standalone `filterItems()` function, now exported from `@furystack/core`. This allows consumers to filter arrays using `FilterType` expressions without needing a store instance.
|
|
16
|
+
|
|
17
|
+
**Usage:**
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { filterItems } from '@furystack/core'
|
|
21
|
+
|
|
22
|
+
const results = filterItems(myArray, {
|
|
23
|
+
name: { $startsWith: 'foo' },
|
|
24
|
+
age: { $gte: 18 },
|
|
25
|
+
})
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### ♻️ Refactoring
|
|
29
|
+
|
|
30
|
+
- `InMemoryStore` now delegates to the public `filterItems()` function internally instead of using a private method
|
|
31
|
+
|
|
3
32
|
## [15.1.0] - 2026-02-19
|
|
4
33
|
|
|
5
34
|
### ✨ Features
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FilterType } from './models/physical-store.js';
|
|
2
|
+
/**
|
|
3
|
+
* Filters an array of items using a {@link FilterType} expression.
|
|
4
|
+
* Supports field-level operators ($eq, $ne, $in, $nin, $gt, $gte, $lt, $lte, $startsWith, $endsWith, $like, $regex)
|
|
5
|
+
* and logical operators ($and, $or).
|
|
6
|
+
* @param values The array of items to filter
|
|
7
|
+
* @param filter The filter expression to apply
|
|
8
|
+
* @returns The filtered array of items
|
|
9
|
+
*/
|
|
10
|
+
export declare function filterItems<T>(values: T[], filter?: FilterType<T>): T[];
|
|
11
|
+
//# sourceMappingURL=filter-items.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter-items.d.ts","sourceRoot":"","sources":["../src/filter-items.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AAyB5D;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAkHvE"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { isLogicalOperator, isOperator } from './models/physical-store.js';
|
|
2
|
+
const escapeRegexMeta = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
3
|
+
const evaluateLike = (value, likeString) => {
|
|
4
|
+
const likeRegex = `^${likeString.split('%').map(escapeRegexMeta).join('.*')}$`;
|
|
5
|
+
return value.match(new RegExp(likeRegex, 'i'));
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Filters an array of items using a {@link FilterType} expression.
|
|
9
|
+
* Supports field-level operators ($eq, $ne, $in, $nin, $gt, $gte, $lt, $lte, $startsWith, $endsWith, $like, $regex)
|
|
10
|
+
* and logical operators ($and, $or).
|
|
11
|
+
* @param values The array of items to filter
|
|
12
|
+
* @param filter The filter expression to apply
|
|
13
|
+
* @returns The filtered array of items
|
|
14
|
+
*/
|
|
15
|
+
export function filterItems(values, filter) {
|
|
16
|
+
if (!filter) {
|
|
17
|
+
return values;
|
|
18
|
+
}
|
|
19
|
+
return values.filter((item) => {
|
|
20
|
+
const filterRecord = filter;
|
|
21
|
+
const itemRecord = item;
|
|
22
|
+
for (const key in filterRecord) {
|
|
23
|
+
if (isLogicalOperator(key)) {
|
|
24
|
+
const filterValue = filterRecord[key];
|
|
25
|
+
switch (key) {
|
|
26
|
+
case '$and':
|
|
27
|
+
if (filterValue.some((v) => !filterItems([item], v).length)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
break;
|
|
31
|
+
case '$or':
|
|
32
|
+
if (filterValue.some((v) => filterItems([item], v).length)) {
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
default:
|
|
37
|
+
throw new Error(`The logical operation '${key}' is not a valid operation`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const fieldFilter = filterRecord[key];
|
|
42
|
+
if (typeof fieldFilter === 'object' && fieldFilter !== null) {
|
|
43
|
+
for (const filterKey in fieldFilter) {
|
|
44
|
+
if (isOperator(filterKey)) {
|
|
45
|
+
const itemValue = itemRecord[key];
|
|
46
|
+
const filterValue = fieldFilter[filterKey];
|
|
47
|
+
switch (filterKey) {
|
|
48
|
+
case '$eq':
|
|
49
|
+
if (filterValue !== itemValue) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
break;
|
|
53
|
+
case '$ne':
|
|
54
|
+
if (filterValue === itemValue) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
case '$in':
|
|
59
|
+
if (!filterValue.includes(itemValue)) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
break;
|
|
63
|
+
case '$nin':
|
|
64
|
+
if (filterValue.includes(itemValue)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
break;
|
|
68
|
+
case '$lt':
|
|
69
|
+
if (itemValue < filterValue) {
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
case '$lte':
|
|
74
|
+
if (itemValue <= filterValue) {
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
case '$gt':
|
|
79
|
+
if (itemValue > filterValue) {
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
case '$gte':
|
|
84
|
+
if (itemValue >= filterValue) {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
case '$regex':
|
|
89
|
+
try {
|
|
90
|
+
if (!new RegExp(filterValue).test(String(itemValue))) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
throw new Error(`Invalid regular expression for $regex filter on field '${key}': ${e.message}`, { cause: e });
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
case '$startsWith':
|
|
99
|
+
if (!itemValue.startsWith(filterValue)) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
103
|
+
case '$endsWith':
|
|
104
|
+
if (!itemValue.endsWith(filterValue)) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
case '$like':
|
|
109
|
+
if (!evaluateLike(itemValue, filterValue)) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
default:
|
|
114
|
+
throw new Error(`The expression (${filterKey}) is not a supported filter operation`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
throw new Error(`The filter key '${filterKey}' is not a valid operation`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
throw new Error(`The filter has to be an object, got ${typeof fieldFilter} for field '${key}'`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return true;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=filter-items.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter-items.js","sourceRoot":"","sources":["../src/filter-items.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter-items.spec.d.ts","sourceRoot":"","sources":["../src/filter-items.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { filterItems } from './filter-items.js';
|
|
3
|
+
const items = [
|
|
4
|
+
{ id: 1, name: 'Alice', age: 30, active: true },
|
|
5
|
+
{ id: 2, name: 'Bob', age: 25, active: false },
|
|
6
|
+
{ id: 3, name: 'Charlie', age: 35, active: true },
|
|
7
|
+
{ id: 4, name: 'Diana', age: 28, active: false },
|
|
8
|
+
{ id: 5, name: 'Eve', age: 22, active: true },
|
|
9
|
+
];
|
|
10
|
+
describe('filterItems', () => {
|
|
11
|
+
describe('no filter', () => {
|
|
12
|
+
it('should return all items when filter is undefined', () => {
|
|
13
|
+
expect(filterItems(items, undefined)).toEqual(items);
|
|
14
|
+
});
|
|
15
|
+
it('should return all items when filter is not provided', () => {
|
|
16
|
+
expect(filterItems(items)).toEqual(items);
|
|
17
|
+
});
|
|
18
|
+
it('should return an empty array when input is empty', () => {
|
|
19
|
+
expect(filterItems([], { name: { $eq: 'Alice' } })).toEqual([]);
|
|
20
|
+
});
|
|
21
|
+
it('should return all items when filter is an empty object', () => {
|
|
22
|
+
expect(filterItems(items, {})).toEqual(items);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe('$eq', () => {
|
|
26
|
+
it('should return items matching the exact value', () => {
|
|
27
|
+
const result = filterItems(items, { name: { $eq: 'Alice' } });
|
|
28
|
+
expect(result).toEqual([items[0]]);
|
|
29
|
+
});
|
|
30
|
+
it('should return empty array when no items match', () => {
|
|
31
|
+
const result = filterItems(items, { name: { $eq: 'Nobody' } });
|
|
32
|
+
expect(result).toEqual([]);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe('$ne', () => {
|
|
36
|
+
it('should return items not matching the value', () => {
|
|
37
|
+
const result = filterItems(items, { name: { $ne: 'Alice' } });
|
|
38
|
+
expect(result).toEqual([items[1], items[2], items[3], items[4]]);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe('$in', () => {
|
|
42
|
+
it('should return items whose value is in the array', () => {
|
|
43
|
+
const result = filterItems(items, { name: { $in: ['Alice', 'Bob'] } });
|
|
44
|
+
expect(result).toEqual([items[0], items[1]]);
|
|
45
|
+
});
|
|
46
|
+
it('should return empty array when no values match', () => {
|
|
47
|
+
const result = filterItems(items, { name: { $in: ['Nobody'] } });
|
|
48
|
+
expect(result).toEqual([]);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('$nin', () => {
|
|
52
|
+
it('should return items whose value is not in the array', () => {
|
|
53
|
+
const result = filterItems(items, { name: { $nin: ['Alice', 'Bob'] } });
|
|
54
|
+
expect(result).toEqual([items[2], items[3], items[4]]);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe('$gt', () => {
|
|
58
|
+
it('should return items with value greater than the threshold', () => {
|
|
59
|
+
const result = filterItems(items, { age: { $gt: 30 } });
|
|
60
|
+
expect(result).toEqual([items[2]]);
|
|
61
|
+
});
|
|
62
|
+
it('should exclude items equal to the threshold', () => {
|
|
63
|
+
const result = filterItems(items, { age: { $gt: 35 } });
|
|
64
|
+
expect(result).toEqual([]);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe('$gte', () => {
|
|
68
|
+
it('should return items with value greater than or equal to the threshold', () => {
|
|
69
|
+
const result = filterItems(items, { age: { $gte: 30 } });
|
|
70
|
+
expect(result).toEqual([items[0], items[2]]);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe('$lt', () => {
|
|
74
|
+
it('should return items with value less than the threshold', () => {
|
|
75
|
+
const result = filterItems(items, { age: { $lt: 25 } });
|
|
76
|
+
expect(result).toEqual([items[4]]);
|
|
77
|
+
});
|
|
78
|
+
it('should exclude items equal to the threshold', () => {
|
|
79
|
+
const result = filterItems(items, { age: { $lt: 22 } });
|
|
80
|
+
expect(result).toEqual([]);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe('$lte', () => {
|
|
84
|
+
it('should return items with value less than or equal to the threshold', () => {
|
|
85
|
+
const result = filterItems(items, { age: { $lte: 25 } });
|
|
86
|
+
expect(result).toEqual([items[1], items[4]]);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe('$startsWith', () => {
|
|
90
|
+
it('should return items whose string field starts with the value', () => {
|
|
91
|
+
const result = filterItems(items, { name: { $startsWith: 'Al' } });
|
|
92
|
+
expect(result).toEqual([items[0]]);
|
|
93
|
+
});
|
|
94
|
+
it('should return empty array when no items match', () => {
|
|
95
|
+
const result = filterItems(items, { name: { $startsWith: 'Zz' } });
|
|
96
|
+
expect(result).toEqual([]);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe('$endsWith', () => {
|
|
100
|
+
it('should return items whose string field ends with the value', () => {
|
|
101
|
+
const result = filterItems(items, { name: { $endsWith: 'ce' } });
|
|
102
|
+
expect(result).toEqual([items[0]]);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
describe('$like', () => {
|
|
106
|
+
it('should match with % wildcards', () => {
|
|
107
|
+
const result = filterItems(items, { name: { $like: 'A%' } });
|
|
108
|
+
expect(result).toEqual([items[0]]);
|
|
109
|
+
});
|
|
110
|
+
it('should match with % in the middle', () => {
|
|
111
|
+
const result = filterItems(items, { name: { $like: 'A%e' } });
|
|
112
|
+
expect(result).toEqual([items[0]]);
|
|
113
|
+
});
|
|
114
|
+
it('should be case-insensitive', () => {
|
|
115
|
+
const result = filterItems(items, { name: { $like: 'alice' } });
|
|
116
|
+
expect(result).toEqual([items[0]]);
|
|
117
|
+
});
|
|
118
|
+
it('should escape regex metacharacters in the pattern', () => {
|
|
119
|
+
const testItems = [{ value: 'foo.bar' }, { value: 'fooXbar' }];
|
|
120
|
+
const result = filterItems(testItems, { value: { $like: 'foo.bar' } });
|
|
121
|
+
expect(result).toEqual([{ value: 'foo.bar' }]);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
describe('$regex', () => {
|
|
125
|
+
it('should return items matching the regex', () => {
|
|
126
|
+
const result = filterItems(items, { name: { $regex: '^[A-C]' } });
|
|
127
|
+
expect(result).toEqual([items[0], items[1], items[2]]);
|
|
128
|
+
});
|
|
129
|
+
it('should return empty array when no items match', () => {
|
|
130
|
+
const result = filterItems(items, { name: { $regex: '^Z' } });
|
|
131
|
+
expect(result).toEqual([]);
|
|
132
|
+
});
|
|
133
|
+
it('should throw on invalid regex syntax', () => {
|
|
134
|
+
expect(() => filterItems(items, { name: { $regex: '[invalid' } })).toThrow();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
describe('$and', () => {
|
|
138
|
+
it('should return items matching all conditions', () => {
|
|
139
|
+
const result = filterItems(items, { $and: [{ age: { $gte: 25 } }, { active: { $eq: true } }] });
|
|
140
|
+
expect(result).toEqual([items[0], items[2]]);
|
|
141
|
+
});
|
|
142
|
+
it('should return empty array when conditions are mutually exclusive', () => {
|
|
143
|
+
const result = filterItems(items, { $and: [{ name: { $eq: 'Alice' } }, { name: { $eq: 'Bob' } }] });
|
|
144
|
+
expect(result).toEqual([]);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
describe('$or', () => {
|
|
148
|
+
it('should return items matching any condition', () => {
|
|
149
|
+
const result = filterItems(items, { $or: [{ name: { $eq: 'Alice' } }, { name: { $eq: 'Bob' } }] });
|
|
150
|
+
expect(result).toEqual([items[0], items[1]]);
|
|
151
|
+
});
|
|
152
|
+
it('should return empty array when no conditions match', () => {
|
|
153
|
+
const result = filterItems(items, { $or: [{ name: { $eq: 'Nobody' } }, { name: { $eq: 'Nope' } }] });
|
|
154
|
+
expect(result).toEqual([]);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
describe('combined field-level filters', () => {
|
|
158
|
+
it('should AND multiple field filters together', () => {
|
|
159
|
+
const result = filterItems(items, { age: { $gte: 25 }, active: { $eq: true } });
|
|
160
|
+
expect(result).toEqual([items[0], items[2]]);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
describe('error cases', () => {
|
|
164
|
+
it('should throw when a field filter is not an object', () => {
|
|
165
|
+
const badFilter = { name: 'Alice' };
|
|
166
|
+
expect(() => filterItems(items, badFilter)).toThrow("The filter has to be an object, got string for field 'name'");
|
|
167
|
+
});
|
|
168
|
+
it('should throw for an unsupported filter operation', () => {
|
|
169
|
+
const badFilter = { name: { $unknown: 'x' } };
|
|
170
|
+
expect(() => filterItems(items, badFilter)).toThrow("The filter key '$unknown' is not a valid operation");
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
//# sourceMappingURL=filter-items.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter-items.spec.js","sourceRoot":"","sources":["../src/filter-items.spec.ts"],"names":[],"mappings":"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"}
|
package/esm/in-memory-store.d.ts
CHANGED
|
@@ -21,8 +21,6 @@ export declare class InMemoryStore<T, TPrimaryKey extends keyof T> extends Event
|
|
|
21
21
|
add(...entries: T[]): Promise<CreateResult<T>>;
|
|
22
22
|
cache: Map<T[TPrimaryKey], T>;
|
|
23
23
|
get: (key: T[TPrimaryKey], select?: Array<keyof T>) => Promise<Awaited<T> | undefined>;
|
|
24
|
-
private evaluateLike;
|
|
25
|
-
private filterInternal;
|
|
26
24
|
find<TFields extends Array<keyof T>>(searchOptions: FindOptions<T, TFields>): Promise<PartialResult<T, TFields>[]>;
|
|
27
25
|
count(filter?: FilterType<T>): Promise<number>;
|
|
28
26
|
update(id: T[TPrimaryKey], data: T): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"in-memory-store.d.ts","sourceRoot":"","sources":["../src/in-memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"in-memory-store.d.ts","sourceRoot":"","sources":["../src/in-memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAIrH,qBAAa,aAAa,CAAC,CAAC,EAAE,WAAW,SAAS,MAAM,CAAC,CACvD,SAAQ,QAAQ,CAAC;IACf,aAAa,EAAE;QAAE,MAAM,EAAE,CAAC,CAAA;KAAE,CAAA;IAC5B,eAAe,EAAE;QAAE,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;KAAE,CAAA;IAC3D,eAAe,EAAE;QAAE,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC,CAAA;KAAE,CAAA;CACzC,CACD,YAAW,aAAa,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC;IAE3C;;;OAGG;IACU,MAAM,CAAC,GAAG,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAOrD,GAAG,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAapD,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAY;IACzC,GAAG,GAAI,KAAK,CAAC,CAAC,WAAW,CAAC,EAAE,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,qCAGzD;IAEY,IAAI,CAAC,OAAO,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC,EAAE,OAAO,CAAC;IA4B3E,KAAK,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;IAI5B,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,CAAC;IAWxC,CAAC,MAAM,CAAC,OAAO,CAAC;IAKvB,SAAgB,UAAU,EAAE,WAAW,CAAA;IACvC,SAAgB,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAA;IAEvC;;;;;OAKG;gBACS,OAAO,EAAE;QACnB;;WAEG;QACH,UAAU,EAAE,WAAW,CAAA;QACvB;;WAEG;QACH,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,CAAA;KACxB;CAKF"}
|
package/esm/in-memory-store.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EventHub } from '@furystack/utils';
|
|
2
|
-
import {
|
|
2
|
+
import { selectFields } from './models/physical-store.js';
|
|
3
|
+
import { filterItems } from './filter-items.js';
|
|
3
4
|
export class InMemoryStore extends EventHub {
|
|
4
5
|
/**
|
|
5
6
|
*
|
|
@@ -28,122 +29,8 @@ export class InMemoryStore extends EventHub {
|
|
|
28
29
|
const item = this.cache.get(key);
|
|
29
30
|
return Promise.resolve(item && select ? selectFields(item, ...select) : item);
|
|
30
31
|
};
|
|
31
|
-
evaluateLike = (value, likeString) => {
|
|
32
|
-
const likeRegex = `^${likeString.replace(/%/g, '.*')}$`;
|
|
33
|
-
return value.match(new RegExp(likeRegex, 'i'));
|
|
34
|
-
};
|
|
35
|
-
filterInternal(values, filter) {
|
|
36
|
-
if (!filter) {
|
|
37
|
-
return values;
|
|
38
|
-
}
|
|
39
|
-
return values.filter((item) => {
|
|
40
|
-
const filterRecord = filter;
|
|
41
|
-
const itemRecord = item;
|
|
42
|
-
for (const key in filterRecord) {
|
|
43
|
-
if (isLogicalOperator(key)) {
|
|
44
|
-
const filterValue = filterRecord[key];
|
|
45
|
-
switch (key) {
|
|
46
|
-
case '$and':
|
|
47
|
-
if (filterValue.some((v) => !this.filterInternal([item], v).length)) {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
break;
|
|
51
|
-
case '$or':
|
|
52
|
-
if (filterValue.some((v) => this.filterInternal([item], v).length)) {
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
return false;
|
|
56
|
-
default:
|
|
57
|
-
throw new Error(`The logical operation '${key}' is not a valid operation`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
const fieldFilter = filterRecord[key];
|
|
62
|
-
if (typeof fieldFilter === 'object' && fieldFilter !== null) {
|
|
63
|
-
for (const filterKey in fieldFilter) {
|
|
64
|
-
if (isOperator(filterKey)) {
|
|
65
|
-
const itemValue = itemRecord[key];
|
|
66
|
-
const filterValue = fieldFilter[filterKey];
|
|
67
|
-
switch (filterKey) {
|
|
68
|
-
case '$eq':
|
|
69
|
-
if (filterValue !== itemValue) {
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
break;
|
|
73
|
-
case '$ne':
|
|
74
|
-
if (filterValue === itemValue) {
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
break;
|
|
78
|
-
case '$in':
|
|
79
|
-
if (!filterValue.includes(itemValue)) {
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
break;
|
|
83
|
-
case '$nin':
|
|
84
|
-
if (filterValue.includes(itemValue)) {
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
break;
|
|
88
|
-
case '$lt':
|
|
89
|
-
if (itemValue < filterValue) {
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
return false;
|
|
93
|
-
case '$lte':
|
|
94
|
-
if (itemValue <= filterValue) {
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
return false;
|
|
98
|
-
case '$gt':
|
|
99
|
-
if (itemValue > filterValue) {
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
|
-
return false;
|
|
103
|
-
case '$gte':
|
|
104
|
-
if (itemValue >= filterValue) {
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
return false;
|
|
108
|
-
case '$regex':
|
|
109
|
-
if (!new RegExp(filterValue).test(String(itemValue))) {
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
break;
|
|
113
|
-
case '$startsWith':
|
|
114
|
-
if (!itemValue.startsWith(filterValue)) {
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
break;
|
|
118
|
-
case '$endsWith':
|
|
119
|
-
if (!itemValue.endsWith(filterValue)) {
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
break;
|
|
123
|
-
case '$like':
|
|
124
|
-
if (!this.evaluateLike(itemValue, filterValue)) {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
break;
|
|
128
|
-
default:
|
|
129
|
-
throw new Error(`The expression (${filterKey}) is not supported by '${this.constructor.name}'!`);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
throw new Error(`The filter key '${filterKey}' is not a valid operation`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
throw new Error(`The filter has to be an object, got ${typeof fieldFilter} for field '${key}'`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return true;
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
32
|
async find(searchOptions) {
|
|
146
|
-
let value =
|
|
33
|
+
let value = filterItems([...this.cache.values()], searchOptions.filter);
|
|
147
34
|
if (searchOptions.order) {
|
|
148
35
|
const orderRecord = searchOptions.order;
|
|
149
36
|
for (const fieldName of Object.keys(searchOptions.order)) {
|
|
@@ -168,7 +55,7 @@ export class InMemoryStore extends EventHub {
|
|
|
168
55
|
return value;
|
|
169
56
|
}
|
|
170
57
|
async count(filter) {
|
|
171
|
-
return
|
|
58
|
+
return filterItems([...this.cache.values()], filter).length;
|
|
172
59
|
}
|
|
173
60
|
async update(id, data) {
|
|
174
61
|
if (!this.cache.has(id)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"in-memory-store.js","sourceRoot":"","sources":["../src/in-memory-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAE3C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"in-memory-store.js","sourceRoot":"","sources":["../src/in-memory-store.ts"],"names":[],"mappings":"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"}
|
package/esm/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './errors/index.js';
|
|
2
2
|
export * from './models/physical-store.js';
|
|
3
3
|
export * from './models/user.js';
|
|
4
|
+
export * from './filter-items.js';
|
|
4
5
|
export * from './in-memory-store.js';
|
|
5
6
|
export * from './store-manager.js';
|
|
6
7
|
export * from './global-disposables.js';
|
package/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,kBAAkB,CAAA;AAChC,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,yBAAyB,CAAA;AACvC,cAAc,uBAAuB,CAAA;AACrC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,cAAc,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,yBAAyB,CAAA;AACvC,cAAc,uBAAuB,CAAA;AACrC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,cAAc,CAAA"}
|
package/esm/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from './errors/index.js';
|
|
2
2
|
export * from './models/physical-store.js';
|
|
3
3
|
export * from './models/user.js';
|
|
4
|
+
export * from './filter-items.js';
|
|
4
5
|
export * from './in-memory-store.js';
|
|
5
6
|
export * from './store-manager.js';
|
|
6
7
|
export * from './global-disposables.js';
|
package/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,kBAAkB,CAAA;AAChC,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,yBAAyB,CAAA;AACvC,cAAc,uBAAuB,CAAA;AACrC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,cAAc,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,sBAAsB,CAAA;AACpC,cAAc,oBAAoB,CAAA;AAClC,cAAc,yBAAyB,CAAA;AACvC,cAAc,uBAAuB,CAAA;AACrC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,cAAc,CAAA"}
|
|
@@ -64,12 +64,14 @@ export declare const selectFields: <T extends object, TField extends Array<keyof
|
|
|
64
64
|
/**
|
|
65
65
|
* Interface that defines a physical store implementation.
|
|
66
66
|
*
|
|
67
|
-
* **
|
|
67
|
+
* **Recommended:** `DataSet` from `@furystack/repository` is the preferred write gateway for application-level code.
|
|
68
|
+
*
|
|
69
|
+
* **Important:** Writing directly to a physical store bypasses the Repository `DataSet` layer.
|
|
68
70
|
* This means authorization, modification hooks, and DataSet events (used by entity sync) will **not** be triggered.
|
|
69
71
|
* For any write operation that should be observable by other parts of the system (e.g. entity sync, audit logging),
|
|
70
|
-
* use the corresponding
|
|
72
|
+
* use the corresponding `DataSet` method instead via `getDataSetFor()` from `@furystack/repository`.
|
|
71
73
|
*
|
|
72
|
-
* @see
|
|
74
|
+
* @see `DataSet` from `@furystack/repository` for the authorized, event-emitting write gateway
|
|
73
75
|
*/
|
|
74
76
|
export interface PhysicalStore<T, TPrimaryKey extends keyof T, TWriteableData = WithOptionalId<T, TPrimaryKey>> extends EventHub<{
|
|
75
77
|
onEntityAdded: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"physical-store.d.ts","sourceRoot":"","sources":["../../src/models/physical-store.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"physical-store.d.ts","sourceRoot":"","sources":["../../src/models/physical-store.ts"],"names":[],"mappings":"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"}
|
package/esm/store-manager.d.ts
CHANGED
|
@@ -12,6 +12,12 @@ export declare class StoreManager implements AsyncDisposable {
|
|
|
12
12
|
/**
|
|
13
13
|
* Returns a store model for a constructable object.
|
|
14
14
|
* Throws error if no store is registered
|
|
15
|
+
*
|
|
16
|
+
* **Note:** For application-level data access, prefer `getDataSetFor` from `@furystack/repository`.
|
|
17
|
+
* Writing through the DataSet ensures authorization, modification hooks, and change events
|
|
18
|
+
* (required for entity sync) are properly triggered.
|
|
19
|
+
* This method is intended for internal use by `Repository.createDataSet` and for physical store tests.
|
|
20
|
+
*
|
|
15
21
|
* @param model The Constructable object
|
|
16
22
|
* @param primaryKey The Primary Key field
|
|
17
23
|
* @throws if the Store is not registered
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-manager.d.ts","sourceRoot":"","sources":["../src/store-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAItD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAE/D;;GAEG;AACH,qBACa,YAAa,YAAW,eAAe;IAClD;;OAEG;IACU,CAAC,MAAM,CAAC,YAAY,CAAC;IAoBlC,OAAO,CAAC,MAAM,CAAkE;IAEhF
|
|
1
|
+
{"version":3,"file":"store-manager.d.ts","sourceRoot":"","sources":["../src/store-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAItD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAE/D;;GAEG;AACH,qBACa,YAAa,YAAW,eAAe;IAClD;;OAEG;IACU,CAAC,MAAM,CAAC,YAAY,CAAC;IAoBlC,OAAO,CAAC,MAAM,CAAkE;IAEhF;;;;;;;;;;;;;OAaG;IACI,WAAW,CAAC,CAAC,EAAE,WAAW,SAAS,MAAM,CAAC,EAAE,KAAK,SAAS,aAAa,CAAC,CAAC,EAAE,WAAW,CAAC,EAC5F,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,UAAU,EAAE,WAAW,GASJ,KAAK;IAG1B;;;;OAIG;IACI,QAAQ,CAAC,CAAC,EAAE,WAAW,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,WAAW,CAAC;CAIrF"}
|
package/esm/store-manager.js
CHANGED
|
@@ -33,6 +33,12 @@ let StoreManager = class StoreManager {
|
|
|
33
33
|
/**
|
|
34
34
|
* Returns a store model for a constructable object.
|
|
35
35
|
* Throws error if no store is registered
|
|
36
|
+
*
|
|
37
|
+
* **Note:** For application-level data access, prefer `getDataSetFor` from `@furystack/repository`.
|
|
38
|
+
* Writing through the DataSet ensures authorization, modification hooks, and change events
|
|
39
|
+
* (required for entity sync) are properly triggered.
|
|
40
|
+
* This method is intended for internal use by `Repository.createDataSet` and for physical store tests.
|
|
41
|
+
*
|
|
36
42
|
* @param model The Constructable object
|
|
37
43
|
* @param primaryKey The Primary Key field
|
|
38
44
|
* @throws if the Store is not registered
|
package/esm/store-manager.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-manager.js","sourceRoot":"","sources":["../src/store-manager.ts"],"names":[],"mappings":";;;;;;AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAG9D;;GAEG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAY;IACvB;;OAEG;IACI,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC;QAChC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,UAAU,CACrC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE;YACvD,IAAI,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;gBACxB,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAA;YACzB,CAAC;YACD,IAAI,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC7B,MAAM,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAA;YACpC,CAAC;QACH,CAAC,CAAC,CACH,CAAA;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,CAAA;QAC3D,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG,IAAI,eAAe,CAC/B,uCAAuC,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAgB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAChH,KAAK,CACN,CAAA;YACD,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC;IACO,MAAM,GAAyD,IAAI,GAAG,EAAE,CAAA;IAEhF
|
|
1
|
+
{"version":3,"file":"store-manager.js","sourceRoot":"","sources":["../src/store-manager.ts"],"names":[],"mappings":";;;;;;AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAG9D;;GAEG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAY;IACvB;;OAEG;IACI,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC;QAChC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,UAAU,CACrC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE;YACvD,IAAI,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;gBACxB,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAA;YACzB,CAAC;YACD,IAAI,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC7B,MAAM,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAA;YACpC,CAAC;QACH,CAAC,CAAC,CACH,CAAA;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,CAAA;QAC3D,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG,IAAI,eAAe,CAC/B,uCAAuC,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAgB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAChH,KAAK,CACN,CAAA;YACD,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC;IACO,MAAM,GAAyD,IAAI,GAAG,EAAE,CAAA;IAEhF;;;;;;;;;;;;;OAaG;IACI,WAAW,CAChB,KAAuB,EACvB,UAAuB;QAEvB,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACvC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,KAAK,CAAC,wBAAwB,KAAK,CAAC,IAAI,GAAG,CAAC,CAAA;QACpD,CAAC;QACD,IAAI,UAAU,KAAK,QAAQ,CAAC,UAAU,EAAE,CAAC;YACvC,MAAM,KAAK,CAAC,wBAAwB,CAAC,CAAA;QACvC,CAAC;QACD,OAAO,QAAiB,CAAA;IAC1B,CAAC;IAED;;;;OAIG;IACI,QAAQ,CAAiC,KAAoC;QAClF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAgC,CAAC,CAAA;QAC9D,OAAO,IAAI,CAAA;IACb,CAAC;CACF,CAAA;AA/DY,YAAY;IADxB,UAAU,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;GACzB,YAAY,CA+DxB"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { filterItems } from './filter-items.js'
|
|
3
|
+
import type { FilterType } from './models/physical-store.js'
|
|
4
|
+
|
|
5
|
+
type TestItem = {
|
|
6
|
+
id: number
|
|
7
|
+
name: string
|
|
8
|
+
age: number
|
|
9
|
+
active: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const items: TestItem[] = [
|
|
13
|
+
{ id: 1, name: 'Alice', age: 30, active: true },
|
|
14
|
+
{ id: 2, name: 'Bob', age: 25, active: false },
|
|
15
|
+
{ id: 3, name: 'Charlie', age: 35, active: true },
|
|
16
|
+
{ id: 4, name: 'Diana', age: 28, active: false },
|
|
17
|
+
{ id: 5, name: 'Eve', age: 22, active: true },
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
describe('filterItems', () => {
|
|
21
|
+
describe('no filter', () => {
|
|
22
|
+
it('should return all items when filter is undefined', () => {
|
|
23
|
+
expect(filterItems(items, undefined)).toEqual(items)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('should return all items when filter is not provided', () => {
|
|
27
|
+
expect(filterItems(items)).toEqual(items)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('should return an empty array when input is empty', () => {
|
|
31
|
+
expect(filterItems<TestItem>([], { name: { $eq: 'Alice' } })).toEqual([])
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should return all items when filter is an empty object', () => {
|
|
35
|
+
expect(filterItems(items, {} as FilterType<TestItem>)).toEqual(items)
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
describe('$eq', () => {
|
|
40
|
+
it('should return items matching the exact value', () => {
|
|
41
|
+
const result = filterItems(items, { name: { $eq: 'Alice' } })
|
|
42
|
+
expect(result).toEqual([items[0]])
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('should return empty array when no items match', () => {
|
|
46
|
+
const result = filterItems(items, { name: { $eq: 'Nobody' } })
|
|
47
|
+
expect(result).toEqual([])
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
describe('$ne', () => {
|
|
52
|
+
it('should return items not matching the value', () => {
|
|
53
|
+
const result = filterItems(items, { name: { $ne: 'Alice' } })
|
|
54
|
+
expect(result).toEqual([items[1], items[2], items[3], items[4]])
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
describe('$in', () => {
|
|
59
|
+
it('should return items whose value is in the array', () => {
|
|
60
|
+
const result = filterItems(items, { name: { $in: ['Alice', 'Bob'] } })
|
|
61
|
+
expect(result).toEqual([items[0], items[1]])
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should return empty array when no values match', () => {
|
|
65
|
+
const result = filterItems(items, { name: { $in: ['Nobody'] } })
|
|
66
|
+
expect(result).toEqual([])
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
describe('$nin', () => {
|
|
71
|
+
it('should return items whose value is not in the array', () => {
|
|
72
|
+
const result = filterItems(items, { name: { $nin: ['Alice', 'Bob'] } })
|
|
73
|
+
expect(result).toEqual([items[2], items[3], items[4]])
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
describe('$gt', () => {
|
|
78
|
+
it('should return items with value greater than the threshold', () => {
|
|
79
|
+
const result = filterItems(items, { age: { $gt: 30 } })
|
|
80
|
+
expect(result).toEqual([items[2]])
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('should exclude items equal to the threshold', () => {
|
|
84
|
+
const result = filterItems(items, { age: { $gt: 35 } })
|
|
85
|
+
expect(result).toEqual([])
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
describe('$gte', () => {
|
|
90
|
+
it('should return items with value greater than or equal to the threshold', () => {
|
|
91
|
+
const result = filterItems(items, { age: { $gte: 30 } })
|
|
92
|
+
expect(result).toEqual([items[0], items[2]])
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
describe('$lt', () => {
|
|
97
|
+
it('should return items with value less than the threshold', () => {
|
|
98
|
+
const result = filterItems(items, { age: { $lt: 25 } })
|
|
99
|
+
expect(result).toEqual([items[4]])
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('should exclude items equal to the threshold', () => {
|
|
103
|
+
const result = filterItems(items, { age: { $lt: 22 } })
|
|
104
|
+
expect(result).toEqual([])
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
describe('$lte', () => {
|
|
109
|
+
it('should return items with value less than or equal to the threshold', () => {
|
|
110
|
+
const result = filterItems(items, { age: { $lte: 25 } })
|
|
111
|
+
expect(result).toEqual([items[1], items[4]])
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
describe('$startsWith', () => {
|
|
116
|
+
it('should return items whose string field starts with the value', () => {
|
|
117
|
+
const result = filterItems(items, { name: { $startsWith: 'Al' } })
|
|
118
|
+
expect(result).toEqual([items[0]])
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('should return empty array when no items match', () => {
|
|
122
|
+
const result = filterItems(items, { name: { $startsWith: 'Zz' } })
|
|
123
|
+
expect(result).toEqual([])
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
describe('$endsWith', () => {
|
|
128
|
+
it('should return items whose string field ends with the value', () => {
|
|
129
|
+
const result = filterItems(items, { name: { $endsWith: 'ce' } })
|
|
130
|
+
expect(result).toEqual([items[0]])
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
describe('$like', () => {
|
|
135
|
+
it('should match with % wildcards', () => {
|
|
136
|
+
const result = filterItems(items, { name: { $like: 'A%' } })
|
|
137
|
+
expect(result).toEqual([items[0]])
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('should match with % in the middle', () => {
|
|
141
|
+
const result = filterItems(items, { name: { $like: 'A%e' } })
|
|
142
|
+
expect(result).toEqual([items[0]])
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('should be case-insensitive', () => {
|
|
146
|
+
const result = filterItems(items, { name: { $like: 'alice' } })
|
|
147
|
+
expect(result).toEqual([items[0]])
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('should escape regex metacharacters in the pattern', () => {
|
|
151
|
+
type Item = { value: string }
|
|
152
|
+
const testItems: Item[] = [{ value: 'foo.bar' }, { value: 'fooXbar' }]
|
|
153
|
+
const result = filterItems(testItems, { value: { $like: 'foo.bar' } } as FilterType<Item>)
|
|
154
|
+
expect(result).toEqual([{ value: 'foo.bar' }])
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
describe('$regex', () => {
|
|
159
|
+
it('should return items matching the regex', () => {
|
|
160
|
+
const result = filterItems(items, { name: { $regex: '^[A-C]' } })
|
|
161
|
+
expect(result).toEqual([items[0], items[1], items[2]])
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('should return empty array when no items match', () => {
|
|
165
|
+
const result = filterItems(items, { name: { $regex: '^Z' } })
|
|
166
|
+
expect(result).toEqual([])
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('should throw on invalid regex syntax', () => {
|
|
170
|
+
expect(() => filterItems(items, { name: { $regex: '[invalid' } })).toThrow()
|
|
171
|
+
})
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
describe('$and', () => {
|
|
175
|
+
it('should return items matching all conditions', () => {
|
|
176
|
+
const result = filterItems(items, { $and: [{ age: { $gte: 25 } }, { active: { $eq: true } }] })
|
|
177
|
+
expect(result).toEqual([items[0], items[2]])
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('should return empty array when conditions are mutually exclusive', () => {
|
|
181
|
+
const result = filterItems(items, { $and: [{ name: { $eq: 'Alice' } }, { name: { $eq: 'Bob' } }] })
|
|
182
|
+
expect(result).toEqual([])
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
describe('$or', () => {
|
|
187
|
+
it('should return items matching any condition', () => {
|
|
188
|
+
const result = filterItems(items, { $or: [{ name: { $eq: 'Alice' } }, { name: { $eq: 'Bob' } }] })
|
|
189
|
+
expect(result).toEqual([items[0], items[1]])
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
it('should return empty array when no conditions match', () => {
|
|
193
|
+
const result = filterItems(items, { $or: [{ name: { $eq: 'Nobody' } }, { name: { $eq: 'Nope' } }] })
|
|
194
|
+
expect(result).toEqual([])
|
|
195
|
+
})
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
describe('combined field-level filters', () => {
|
|
199
|
+
it('should AND multiple field filters together', () => {
|
|
200
|
+
const result = filterItems(items, { age: { $gte: 25 }, active: { $eq: true } })
|
|
201
|
+
expect(result).toEqual([items[0], items[2]])
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
describe('error cases', () => {
|
|
206
|
+
it('should throw when a field filter is not an object', () => {
|
|
207
|
+
const badFilter = { name: 'Alice' } as unknown as FilterType<TestItem>
|
|
208
|
+
expect(() => filterItems(items, badFilter)).toThrow("The filter has to be an object, got string for field 'name'")
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('should throw for an unsupported filter operation', () => {
|
|
212
|
+
const badFilter = { name: { $unknown: 'x' } } as unknown as FilterType<TestItem>
|
|
213
|
+
expect(() => filterItems(items, badFilter)).toThrow("The filter key '$unknown' is not a valid operation")
|
|
214
|
+
})
|
|
215
|
+
})
|
|
216
|
+
})
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { FilterType } from './models/physical-store.js'
|
|
2
|
+
import { isLogicalOperator, isOperator } from './models/physical-store.js'
|
|
3
|
+
|
|
4
|
+
type FieldOperatorFilter = {
|
|
5
|
+
$eq?: unknown
|
|
6
|
+
$ne?: unknown
|
|
7
|
+
$in?: unknown[]
|
|
8
|
+
$nin?: unknown[]
|
|
9
|
+
$gt?: unknown
|
|
10
|
+
$gte?: unknown
|
|
11
|
+
$lt?: unknown
|
|
12
|
+
$lte?: unknown
|
|
13
|
+
$startsWith?: string
|
|
14
|
+
$endsWith?: string
|
|
15
|
+
$like?: string
|
|
16
|
+
$regex?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const escapeRegexMeta = (str: string) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
20
|
+
|
|
21
|
+
const evaluateLike = (value: string, likeString: string) => {
|
|
22
|
+
const likeRegex = `^${likeString.split('%').map(escapeRegexMeta).join('.*')}$`
|
|
23
|
+
return value.match(new RegExp(likeRegex, 'i'))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Filters an array of items using a {@link FilterType} expression.
|
|
28
|
+
* Supports field-level operators ($eq, $ne, $in, $nin, $gt, $gte, $lt, $lte, $startsWith, $endsWith, $like, $regex)
|
|
29
|
+
* and logical operators ($and, $or).
|
|
30
|
+
* @param values The array of items to filter
|
|
31
|
+
* @param filter The filter expression to apply
|
|
32
|
+
* @returns The filtered array of items
|
|
33
|
+
*/
|
|
34
|
+
export function filterItems<T>(values: T[], filter?: FilterType<T>): T[] {
|
|
35
|
+
if (!filter) {
|
|
36
|
+
return values
|
|
37
|
+
}
|
|
38
|
+
return values.filter((item) => {
|
|
39
|
+
const filterRecord = filter as Record<string, Array<FilterType<T>> | FieldOperatorFilter | undefined>
|
|
40
|
+
const itemRecord = item as Record<string, unknown>
|
|
41
|
+
|
|
42
|
+
for (const key in filterRecord) {
|
|
43
|
+
if (isLogicalOperator(key)) {
|
|
44
|
+
const filterValue = filterRecord[key] as Array<FilterType<T>>
|
|
45
|
+
switch (key) {
|
|
46
|
+
case '$and':
|
|
47
|
+
if (filterValue.some((v: FilterType<T>) => !filterItems([item], v).length)) {
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
break
|
|
51
|
+
case '$or':
|
|
52
|
+
if (filterValue.some((v: FilterType<T>) => filterItems([item], v).length)) {
|
|
53
|
+
break
|
|
54
|
+
}
|
|
55
|
+
return false
|
|
56
|
+
default:
|
|
57
|
+
throw new Error(`The logical operation '${key}' is not a valid operation`)
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
const fieldFilter = filterRecord[key] as FieldOperatorFilter | undefined
|
|
61
|
+
if (typeof fieldFilter === 'object' && fieldFilter !== null) {
|
|
62
|
+
for (const filterKey in fieldFilter) {
|
|
63
|
+
if (isOperator(filterKey)) {
|
|
64
|
+
const itemValue = itemRecord[key]
|
|
65
|
+
const filterValue = fieldFilter[filterKey as keyof FieldOperatorFilter]
|
|
66
|
+
switch (filterKey) {
|
|
67
|
+
case '$eq':
|
|
68
|
+
if (filterValue !== itemValue) {
|
|
69
|
+
return false
|
|
70
|
+
}
|
|
71
|
+
break
|
|
72
|
+
case '$ne':
|
|
73
|
+
if (filterValue === itemValue) {
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
break
|
|
77
|
+
case '$in':
|
|
78
|
+
if (!(filterValue as unknown[]).includes(itemValue)) {
|
|
79
|
+
return false
|
|
80
|
+
}
|
|
81
|
+
break
|
|
82
|
+
case '$nin':
|
|
83
|
+
if ((filterValue as unknown[]).includes(itemValue)) {
|
|
84
|
+
return false
|
|
85
|
+
}
|
|
86
|
+
break
|
|
87
|
+
case '$lt':
|
|
88
|
+
if ((itemValue as number) < (filterValue as number)) {
|
|
89
|
+
break
|
|
90
|
+
}
|
|
91
|
+
return false
|
|
92
|
+
case '$lte':
|
|
93
|
+
if ((itemValue as number) <= (filterValue as number)) {
|
|
94
|
+
break
|
|
95
|
+
}
|
|
96
|
+
return false
|
|
97
|
+
case '$gt':
|
|
98
|
+
if ((itemValue as number) > (filterValue as number)) {
|
|
99
|
+
break
|
|
100
|
+
}
|
|
101
|
+
return false
|
|
102
|
+
case '$gte':
|
|
103
|
+
if ((itemValue as number) >= (filterValue as number)) {
|
|
104
|
+
break
|
|
105
|
+
}
|
|
106
|
+
return false
|
|
107
|
+
case '$regex':
|
|
108
|
+
try {
|
|
109
|
+
if (!new RegExp(filterValue as string).test(String(itemValue))) {
|
|
110
|
+
return false
|
|
111
|
+
}
|
|
112
|
+
} catch (e) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`Invalid regular expression for $regex filter on field '${key}': ${(e as Error).message}`,
|
|
115
|
+
{ cause: e },
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
break
|
|
119
|
+
case '$startsWith':
|
|
120
|
+
if (!(itemValue as string).startsWith(filterValue as string)) {
|
|
121
|
+
return false
|
|
122
|
+
}
|
|
123
|
+
break
|
|
124
|
+
case '$endsWith':
|
|
125
|
+
if (!(itemValue as string).endsWith(filterValue as string)) {
|
|
126
|
+
return false
|
|
127
|
+
}
|
|
128
|
+
break
|
|
129
|
+
case '$like':
|
|
130
|
+
if (!evaluateLike(itemValue as string, filterValue as string)) {
|
|
131
|
+
return false
|
|
132
|
+
}
|
|
133
|
+
break
|
|
134
|
+
default:
|
|
135
|
+
throw new Error(`The expression (${filterKey}) is not a supported filter operation`)
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
throw new Error(`The filter key '${filterKey}' is not a valid operation`)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
throw new Error(`The filter has to be an object, got ${typeof fieldFilter} for field '${key}'`)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return true
|
|
147
|
+
})
|
|
148
|
+
}
|
package/src/in-memory-store.ts
CHANGED
|
@@ -1,25 +1,8 @@
|
|
|
1
1
|
import type { Constructable } from '@furystack/inject'
|
|
2
2
|
import { EventHub } from '@furystack/utils'
|
|
3
3
|
import type { CreateResult, FilterType, FindOptions, PartialResult, PhysicalStore } from './models/physical-store.js'
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Helper type representing all possible field filter operations
|
|
8
|
-
*/
|
|
9
|
-
type FieldOperatorFilter = {
|
|
10
|
-
$eq?: unknown
|
|
11
|
-
$ne?: unknown
|
|
12
|
-
$in?: unknown[]
|
|
13
|
-
$nin?: unknown[]
|
|
14
|
-
$gt?: unknown
|
|
15
|
-
$gte?: unknown
|
|
16
|
-
$lt?: unknown
|
|
17
|
-
$lte?: unknown
|
|
18
|
-
$startsWith?: string
|
|
19
|
-
$endsWith?: string
|
|
20
|
-
$like?: string
|
|
21
|
-
$regex?: string
|
|
22
|
-
}
|
|
4
|
+
import { selectFields } from './models/physical-store.js'
|
|
5
|
+
import { filterItems } from './filter-items.js'
|
|
23
6
|
|
|
24
7
|
export class InMemoryStore<T, TPrimaryKey extends keyof T>
|
|
25
8
|
extends EventHub<{
|
|
@@ -59,123 +42,8 @@ export class InMemoryStore<T, TPrimaryKey extends keyof T>
|
|
|
59
42
|
return Promise.resolve(item && select ? selectFields(item, ...select) : item)
|
|
60
43
|
}
|
|
61
44
|
|
|
62
|
-
private evaluateLike = (value: string, likeString: string) => {
|
|
63
|
-
const likeRegex = `^${likeString.replace(/%/g, '.*')}$`
|
|
64
|
-
return value.match(new RegExp(likeRegex, 'i'))
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
private filterInternal(values: T[], filter?: FilterType<T>): T[] {
|
|
68
|
-
if (!filter) {
|
|
69
|
-
return values
|
|
70
|
-
}
|
|
71
|
-
return values.filter((item) => {
|
|
72
|
-
const filterRecord = filter as Record<string, Array<FilterType<T>> | FieldOperatorFilter | undefined>
|
|
73
|
-
const itemRecord = item as Record<string, unknown>
|
|
74
|
-
|
|
75
|
-
for (const key in filterRecord) {
|
|
76
|
-
if (isLogicalOperator(key)) {
|
|
77
|
-
const filterValue = filterRecord[key] as Array<FilterType<T>>
|
|
78
|
-
switch (key) {
|
|
79
|
-
case '$and':
|
|
80
|
-
if (filterValue.some((v: FilterType<T>) => !this.filterInternal([item], v).length)) {
|
|
81
|
-
return false
|
|
82
|
-
}
|
|
83
|
-
break
|
|
84
|
-
case '$or':
|
|
85
|
-
if (filterValue.some((v: FilterType<T>) => this.filterInternal([item], v).length)) {
|
|
86
|
-
break
|
|
87
|
-
}
|
|
88
|
-
return false
|
|
89
|
-
default:
|
|
90
|
-
throw new Error(`The logical operation '${key}' is not a valid operation`)
|
|
91
|
-
}
|
|
92
|
-
} else {
|
|
93
|
-
const fieldFilter = filterRecord[key] as FieldOperatorFilter | undefined
|
|
94
|
-
if (typeof fieldFilter === 'object' && fieldFilter !== null) {
|
|
95
|
-
for (const filterKey in fieldFilter) {
|
|
96
|
-
if (isOperator(filterKey)) {
|
|
97
|
-
const itemValue = itemRecord[key]
|
|
98
|
-
const filterValue = fieldFilter[filterKey as keyof FieldOperatorFilter]
|
|
99
|
-
switch (filterKey) {
|
|
100
|
-
case '$eq':
|
|
101
|
-
if (filterValue !== itemValue) {
|
|
102
|
-
return false
|
|
103
|
-
}
|
|
104
|
-
break
|
|
105
|
-
case '$ne':
|
|
106
|
-
if (filterValue === itemValue) {
|
|
107
|
-
return false
|
|
108
|
-
}
|
|
109
|
-
break
|
|
110
|
-
case '$in':
|
|
111
|
-
if (!(filterValue as unknown[]).includes(itemValue)) {
|
|
112
|
-
return false
|
|
113
|
-
}
|
|
114
|
-
break
|
|
115
|
-
|
|
116
|
-
case '$nin':
|
|
117
|
-
if ((filterValue as unknown[]).includes(itemValue)) {
|
|
118
|
-
return false
|
|
119
|
-
}
|
|
120
|
-
break
|
|
121
|
-
case '$lt':
|
|
122
|
-
if ((itemValue as number) < (filterValue as number)) {
|
|
123
|
-
break
|
|
124
|
-
}
|
|
125
|
-
return false
|
|
126
|
-
case '$lte':
|
|
127
|
-
if ((itemValue as number) <= (filterValue as number)) {
|
|
128
|
-
break
|
|
129
|
-
}
|
|
130
|
-
return false
|
|
131
|
-
case '$gt':
|
|
132
|
-
if ((itemValue as number) > (filterValue as number)) {
|
|
133
|
-
break
|
|
134
|
-
}
|
|
135
|
-
return false
|
|
136
|
-
case '$gte':
|
|
137
|
-
if ((itemValue as number) >= (filterValue as number)) {
|
|
138
|
-
break
|
|
139
|
-
}
|
|
140
|
-
return false
|
|
141
|
-
case '$regex':
|
|
142
|
-
if (!new RegExp(filterValue as string).test(String(itemValue))) {
|
|
143
|
-
return false
|
|
144
|
-
}
|
|
145
|
-
break
|
|
146
|
-
case '$startsWith':
|
|
147
|
-
if (!(itemValue as string).startsWith(filterValue as string)) {
|
|
148
|
-
return false
|
|
149
|
-
}
|
|
150
|
-
break
|
|
151
|
-
case '$endsWith':
|
|
152
|
-
if (!(itemValue as string).endsWith(filterValue as string)) {
|
|
153
|
-
return false
|
|
154
|
-
}
|
|
155
|
-
break
|
|
156
|
-
case '$like':
|
|
157
|
-
if (!this.evaluateLike(itemValue as string, filterValue as string)) {
|
|
158
|
-
return false
|
|
159
|
-
}
|
|
160
|
-
break
|
|
161
|
-
default:
|
|
162
|
-
throw new Error(`The expression (${filterKey}) is not supported by '${this.constructor.name}'!`)
|
|
163
|
-
}
|
|
164
|
-
} else {
|
|
165
|
-
throw new Error(`The filter key '${filterKey}' is not a valid operation`)
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
} else {
|
|
169
|
-
throw new Error(`The filter has to be an object, got ${typeof fieldFilter} for field '${key}'`)
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return true
|
|
174
|
-
})
|
|
175
|
-
}
|
|
176
|
-
|
|
177
45
|
public async find<TFields extends Array<keyof T>>(searchOptions: FindOptions<T, TFields>) {
|
|
178
|
-
let value: Array<PartialResult<T, TFields>> =
|
|
46
|
+
let value: Array<PartialResult<T, TFields>> = filterItems([...this.cache.values()], searchOptions.filter)
|
|
179
47
|
|
|
180
48
|
if (searchOptions.order) {
|
|
181
49
|
const orderRecord = searchOptions.order as Record<string, 'ASC' | 'DESC'>
|
|
@@ -203,7 +71,7 @@ export class InMemoryStore<T, TPrimaryKey extends keyof T>
|
|
|
203
71
|
}
|
|
204
72
|
|
|
205
73
|
public async count(filter?: FilterType<T>) {
|
|
206
|
-
return
|
|
74
|
+
return filterItems([...this.cache.values()], filter).length
|
|
207
75
|
}
|
|
208
76
|
|
|
209
77
|
public async update(id: T[TPrimaryKey], data: T) {
|
package/src/index.ts
CHANGED
|
@@ -90,12 +90,14 @@ export const selectFields = <T extends object, TField extends Array<keyof T>>(en
|
|
|
90
90
|
/**
|
|
91
91
|
* Interface that defines a physical store implementation.
|
|
92
92
|
*
|
|
93
|
-
* **
|
|
93
|
+
* **Recommended:** `DataSet` from `@furystack/repository` is the preferred write gateway for application-level code.
|
|
94
|
+
*
|
|
95
|
+
* **Important:** Writing directly to a physical store bypasses the Repository `DataSet` layer.
|
|
94
96
|
* This means authorization, modification hooks, and DataSet events (used by entity sync) will **not** be triggered.
|
|
95
97
|
* For any write operation that should be observable by other parts of the system (e.g. entity sync, audit logging),
|
|
96
|
-
* use the corresponding
|
|
98
|
+
* use the corresponding `DataSet` method instead via `getDataSetFor()` from `@furystack/repository`.
|
|
97
99
|
*
|
|
98
|
-
* @see
|
|
100
|
+
* @see `DataSet` from `@furystack/repository` for the authorized, event-emitting write gateway
|
|
99
101
|
*/
|
|
100
102
|
export interface PhysicalStore<
|
|
101
103
|
T,
|
package/src/store-manager.ts
CHANGED
|
@@ -37,6 +37,12 @@ export class StoreManager implements AsyncDisposable {
|
|
|
37
37
|
/**
|
|
38
38
|
* Returns a store model for a constructable object.
|
|
39
39
|
* Throws error if no store is registered
|
|
40
|
+
*
|
|
41
|
+
* **Note:** For application-level data access, prefer `getDataSetFor` from `@furystack/repository`.
|
|
42
|
+
* Writing through the DataSet ensures authorization, modification hooks, and change events
|
|
43
|
+
* (required for entity sync) are properly triggered.
|
|
44
|
+
* This method is intended for internal use by `Repository.createDataSet` and for physical store tests.
|
|
45
|
+
*
|
|
40
46
|
* @param model The Constructable object
|
|
41
47
|
* @param primaryKey The Primary Key field
|
|
42
48
|
* @throws if the Store is not registered
|