@enbox/dwn-sql-store 0.0.9 → 0.0.10
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/LICENSE +3 -2
- package/dist/esm/src/utils/filter.js +32 -1
- package/dist/esm/src/utils/filter.js.map +1 -1
- package/package.json +2 -2
- package/src/utils/filter.ts +35 -1
package/LICENSE
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
Apache License
|
|
2
3
|
Version 2.0, January 2004
|
|
3
4
|
http://www.apache.org/licenses/
|
|
@@ -186,7 +187,7 @@
|
|
|
186
187
|
same "printed page" as the copyright notice for easier
|
|
187
188
|
identification within third-party archives.
|
|
188
189
|
|
|
189
|
-
Copyright
|
|
190
|
+
Copyright 2025 Liran Cohen
|
|
190
191
|
|
|
191
192
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
193
|
you may not use this file except in compliance with the License.
|
|
@@ -198,4 +199,4 @@
|
|
|
198
199
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
200
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
201
|
See the License for the specific language governing permissions and
|
|
201
|
-
limitations under the License.
|
|
202
|
+
limitations under the License.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DynamicModule } from 'kysely';
|
|
1
|
+
import { DynamicModule, sql } from 'kysely';
|
|
2
2
|
import { sanitizedValue, sanitizeFiltersAndSeparateTags } from './sanitize.js';
|
|
3
3
|
/**
|
|
4
4
|
* Takes multiple Filters and returns a single query.
|
|
@@ -36,6 +36,15 @@ function processFilter(eb, andOperands, filter) {
|
|
|
36
36
|
andOperands.push(eb(column, 'in', value));
|
|
37
37
|
}
|
|
38
38
|
else if (typeof value === 'object') { // RangeFilter
|
|
39
|
+
// Detect prefix-style range filters created by `constructPrefixFilterAsRangeFilter`
|
|
40
|
+
// which uses `{ gte: prefix, lt: prefix + '\uffff' }`. The U+FFFF sentinel does not
|
|
41
|
+
// sort correctly under ICU/libc collation rules (e.g. PostgreSQL's en_US.UTF-8),
|
|
42
|
+
// so we convert these to a collation-safe SQL LIKE expression.
|
|
43
|
+
if (isPrefixRangeFilter(value)) {
|
|
44
|
+
const prefix = escapeLikePattern(value.gte);
|
|
45
|
+
andOperands.push(sql `${sql.ref(property)} LIKE ${prefix + '%'}`);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
39
48
|
if (value.gt) {
|
|
40
49
|
andOperands.push(eb(column, '>', sanitizedValue(value.gt)));
|
|
41
50
|
}
|
|
@@ -54,6 +63,28 @@ function processFilter(eb, andOperands, filter) {
|
|
|
54
63
|
}
|
|
55
64
|
}
|
|
56
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Returns `true` if the given RangeFilter matches the pattern produced by
|
|
68
|
+
* `constructPrefixFilterAsRangeFilter`: `{ gte: <prefix>, lt: <prefix> + '\uffff' }`.
|
|
69
|
+
*/
|
|
70
|
+
function isPrefixRangeFilter(value) {
|
|
71
|
+
if (typeof value.gte !== 'string' || typeof value.lt !== 'string') {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
// Only two keys: gte and lt (no gt, lte, or other properties)
|
|
75
|
+
const keys = Object.keys(value);
|
|
76
|
+
if (keys.length !== 2 || !keys.includes('gte') || !keys.includes('lt')) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return value.lt === value.gte + '\uffff';
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Escapes SQL LIKE meta-characters (`%`, `_`) in the given string so that
|
|
83
|
+
* they are matched literally.
|
|
84
|
+
*/
|
|
85
|
+
function escapeLikePattern(input) {
|
|
86
|
+
return input.replace(/%/g, '\\%').replace(/_/g, '\\_');
|
|
87
|
+
}
|
|
57
88
|
/**
|
|
58
89
|
* Processes each property in the tags filter as an AND operand and adds it to the `andOperands` array.
|
|
59
90
|
* If a property has an array of values it will treat it as a OneOf (IN) within the overall AND query.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../../../../src/utils/filter.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../../../../src/utils/filter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enbox/dwn-sql-store",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "SQL backed implementations of DWN MessageStore, DataStore, and StateIndex",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@aws-sdk/client-s3": "^3.700.0",
|
|
27
27
|
"@aws-sdk/lib-storage": "^3.700.0",
|
|
28
|
-
"@enbox/dwn-sdk-js": "0.1.
|
|
28
|
+
"@enbox/dwn-sdk-js": "0.1.1",
|
|
29
29
|
"@ipld/dag-cbor": "9.0.5",
|
|
30
30
|
"interface-blockstore": "5.2.3",
|
|
31
31
|
"interface-store": "5.1.2",
|
package/src/utils/filter.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { DwnDatabaseType } from '../types.js';
|
|
|
2
2
|
import type { Filter } from '@enbox/dwn-sdk-js';
|
|
3
3
|
import type { ExpressionBuilder, OperandExpression, SelectQueryBuilder, SqlBool } from 'kysely';
|
|
4
4
|
|
|
5
|
-
import { DynamicModule } from 'kysely';
|
|
5
|
+
import { DynamicModule, sql } from 'kysely';
|
|
6
6
|
import { sanitizedValue, sanitizeFiltersAndSeparateTags } from './sanitize.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -52,6 +52,16 @@ function processFilter<DB = DwnDatabaseType, TB extends keyof DB = keyof DB>(
|
|
|
52
52
|
if (Array.isArray(value)) { // OneOfFilter
|
|
53
53
|
andOperands.push(eb(column, 'in', value));
|
|
54
54
|
} else if (typeof value === 'object') { // RangeFilter
|
|
55
|
+
// Detect prefix-style range filters created by `constructPrefixFilterAsRangeFilter`
|
|
56
|
+
// which uses `{ gte: prefix, lt: prefix + '\uffff' }`. The U+FFFF sentinel does not
|
|
57
|
+
// sort correctly under ICU/libc collation rules (e.g. PostgreSQL's en_US.UTF-8),
|
|
58
|
+
// so we convert these to a collation-safe SQL LIKE expression.
|
|
59
|
+
if (isPrefixRangeFilter(value)) {
|
|
60
|
+
const prefix = escapeLikePattern(value.gte as string);
|
|
61
|
+
andOperands.push(sql`${sql.ref(property)} LIKE ${prefix + '%'}`);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
55
65
|
if (value.gt) {
|
|
56
66
|
andOperands.push(eb(column, '>', sanitizedValue(value.gt)));
|
|
57
67
|
}
|
|
@@ -70,6 +80,30 @@ function processFilter<DB = DwnDatabaseType, TB extends keyof DB = keyof DB>(
|
|
|
70
80
|
}
|
|
71
81
|
}
|
|
72
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Returns `true` if the given RangeFilter matches the pattern produced by
|
|
85
|
+
* `constructPrefixFilterAsRangeFilter`: `{ gte: <prefix>, lt: <prefix> + '\uffff' }`.
|
|
86
|
+
*/
|
|
87
|
+
function isPrefixRangeFilter(value: Record<string, unknown>): boolean {
|
|
88
|
+
if (typeof value.gte !== 'string' || typeof value.lt !== 'string') {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
// Only two keys: gte and lt (no gt, lte, or other properties)
|
|
92
|
+
const keys = Object.keys(value);
|
|
93
|
+
if (keys.length !== 2 || !keys.includes('gte') || !keys.includes('lt')) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
return value.lt === value.gte + '\uffff';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Escapes SQL LIKE meta-characters (`%`, `_`) in the given string so that
|
|
101
|
+
* they are matched literally.
|
|
102
|
+
*/
|
|
103
|
+
function escapeLikePattern(input: string): string {
|
|
104
|
+
return input.replace(/%/g, '\\%').replace(/_/g, '\\_');
|
|
105
|
+
}
|
|
106
|
+
|
|
73
107
|
/**
|
|
74
108
|
* Processes each property in the tags filter as an AND operand and adds it to the `andOperands` array.
|
|
75
109
|
* If a property has an array of values it will treat it as a OneOf (IN) within the overall AND query.
|