corebasic 1.0.212 → 1.0.214
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +10 -0
- package/dist/libs/ObjectId.js +114 -0
- package/dist/libs/auth.js +103 -0
- package/dist/libs/compression.js +11 -0
- package/dist/libs/cpp.js +154 -0
- package/dist/libs/dip/suffix/date.js +89 -0
- package/dist/libs/dip/suffix/index.js +227 -0
- package/dist/libs/dip/suffix/policy.js +113 -0
- package/dist/libs/dip/suffix/query.js +279 -0
- package/dist/libs/dip/suffix/suffix.js +99 -0
- package/dist/libs/dip/suffix/tests/date.test.js +187 -0
- package/dist/libs/dip/suffix/tests/index.test.js +441 -0
- package/dist/libs/dip/suffix/tests/policy.applySuffixPolicy.test.js +489 -0
- package/dist/libs/dip/suffix/tests/policy.test.js +329 -0
- package/dist/libs/dip/suffix/tests/suffix.test.js +316 -0
- package/dist/libs/dip.js +102 -0
- package/dist/libs/dipper.js +73 -0
- package/dist/libs/elabase.js +656 -0
- package/dist/libs/features.js +366 -0
- package/dist/libs/kafka.js +121 -0
- package/dist/libs/messaging.js +130 -0
- package/dist/libs/mobilecodes.js +562 -0
- package/dist/libs/session.js +96 -0
- package/dist/libs/utils.js +297 -0
- package/package.json +3 -2
- package/tsc-notes +14 -0
- package/tsconfig.json +48 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { describe, test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { matchPolicy, overlaps, contains, intersects, applySuffixPolicy } from "../policy.js";
|
|
4
|
+
/* ============================================================
|
|
5
|
+
* overlaps()
|
|
6
|
+
* ============================================================ */
|
|
7
|
+
describe("overlaps()", () => {
|
|
8
|
+
test("overlaps: identical ranges", () => {
|
|
9
|
+
assert.equal(overlaps({ from: 10, to: 20 }, { from: 10, to: 20 }), true);
|
|
10
|
+
});
|
|
11
|
+
test("overlaps: partial overlap", () => {
|
|
12
|
+
assert.equal(overlaps({ from: 10, to: 20 }, { from: 15, to: 25 }), true);
|
|
13
|
+
});
|
|
14
|
+
test("overlaps: no overlap", () => {
|
|
15
|
+
assert.equal(overlaps({ from: 10, to: 20 }, { from: 21, to: 30 }), false);
|
|
16
|
+
});
|
|
17
|
+
test("overlaps: open ended lower", () => {
|
|
18
|
+
assert.equal(overlaps({ to: 10 }, { from: 5 }), true);
|
|
19
|
+
});
|
|
20
|
+
test("overlaps: open ended upper", () => {
|
|
21
|
+
assert.equal(overlaps({ from: 20 }, { to: 10 }), false);
|
|
22
|
+
});
|
|
23
|
+
describe("overlaps() boundary semantics", () => {
|
|
24
|
+
test("touching: open/open does not overlap", () => {
|
|
25
|
+
assert.equal(overlaps({ to: 10, toOp: "$lt" }, { from: 10, fromOp: "$gt" }), false);
|
|
26
|
+
});
|
|
27
|
+
test("touching: closed/closed overlaps", () => {
|
|
28
|
+
assert.equal(overlaps({ to: 10, toOp: "$lte" }, { from: 10, fromOp: "$gte" }), true);
|
|
29
|
+
});
|
|
30
|
+
test("touching: closed/open does not overlap", () => {
|
|
31
|
+
assert.equal(overlaps({ to: 10, toOp: "$lte" }, { from: 10, fromOp: "$gt" }), false);
|
|
32
|
+
});
|
|
33
|
+
test("touching: open/closed does not overlap", () => {
|
|
34
|
+
assert.equal(overlaps({ to: 10, toOp: "$lt" }, { from: 10, fromOp: "$gte" }), false);
|
|
35
|
+
});
|
|
36
|
+
test("touching: reverse order closed/closed overlaps", () => {
|
|
37
|
+
assert.equal(overlaps({ from: 10, fromOp: "$gte" }, { to: 10, toOp: "$lte" }), true);
|
|
38
|
+
});
|
|
39
|
+
test("touching: reverse order open/open does not overlap", () => {
|
|
40
|
+
assert.equal(overlaps({ from: 10, fromOp: "$gt" }, { to: 10, toOp: "$lt" }), false);
|
|
41
|
+
});
|
|
42
|
+
test("identical single-point closed intervals overlap", () => {
|
|
43
|
+
assert.equal(overlaps({
|
|
44
|
+
from: 10,
|
|
45
|
+
to: 10,
|
|
46
|
+
fromOp: "$gte",
|
|
47
|
+
toOp: "$lte"
|
|
48
|
+
}, {
|
|
49
|
+
from: 10,
|
|
50
|
+
to: 10,
|
|
51
|
+
fromOp: "$gte",
|
|
52
|
+
toOp: "$lte"
|
|
53
|
+
}), true);
|
|
54
|
+
});
|
|
55
|
+
test("identical single-point open intervals do not overlap", () => {
|
|
56
|
+
assert.equal(overlaps({
|
|
57
|
+
from: 10,
|
|
58
|
+
to: 10,
|
|
59
|
+
fromOp: "$gt",
|
|
60
|
+
toOp: "$lt"
|
|
61
|
+
}, {
|
|
62
|
+
from: 10,
|
|
63
|
+
to: 10,
|
|
64
|
+
fromOp: "$gt",
|
|
65
|
+
toOp: "$lt"
|
|
66
|
+
}), false);
|
|
67
|
+
});
|
|
68
|
+
test("infinite lower bound with touching closed endpoint overlaps", () => {
|
|
69
|
+
assert.equal(overlaps({ to: 5, toOp: "$lte" }, { from: 5, fromOp: "$gte" }), true);
|
|
70
|
+
});
|
|
71
|
+
test("infinite lower bound with touching open endpoint does not overlap", () => {
|
|
72
|
+
assert.equal(overlaps({ to: 5, toOp: "$lt" }, { from: 5, fromOp: "$gt" }), false);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
/* ============================================================
|
|
77
|
+
* contains()
|
|
78
|
+
* ============================================================ */
|
|
79
|
+
describe("contains()", () => {
|
|
80
|
+
test("contains: inside", () => {
|
|
81
|
+
assert.equal(contains({ from: 10, to: 20 }, 15), true);
|
|
82
|
+
});
|
|
83
|
+
test("contains: below", () => {
|
|
84
|
+
assert.equal(contains({ from: 10, to: 20 }, 5), false);
|
|
85
|
+
});
|
|
86
|
+
test("contains: above", () => {
|
|
87
|
+
assert.equal(contains({ from: 10, to: 20 }, 25), false);
|
|
88
|
+
});
|
|
89
|
+
test("contains: lower bound", () => {
|
|
90
|
+
assert.equal(contains({ from: 10, to: 20 }, 10), true);
|
|
91
|
+
});
|
|
92
|
+
test("contains: upper bound", () => {
|
|
93
|
+
assert.equal(contains({ from: 10, to: 20 }, 20), true);
|
|
94
|
+
});
|
|
95
|
+
test("contains: gt excludes lower bound", () => {
|
|
96
|
+
assert.equal(contains({ from: 5, fromOp: "$gt" }, 5), false);
|
|
97
|
+
});
|
|
98
|
+
test("contains: gte includes lower bound", () => {
|
|
99
|
+
assert.equal(contains({ from: 5, fromOp: "$gte" }, 5), true);
|
|
100
|
+
});
|
|
101
|
+
test("contains: lt excludes upper bound", () => {
|
|
102
|
+
assert.equal(contains({ to: 10, toOp: "$lt" }, 10), false);
|
|
103
|
+
});
|
|
104
|
+
test("contains: lte includes upper bound", () => {
|
|
105
|
+
assert.equal(contains({ to: 10, toOp: "$lte" }, 10), true);
|
|
106
|
+
});
|
|
107
|
+
test("contains: gt/lt excludes both endpoints", () => {
|
|
108
|
+
const bounds = {
|
|
109
|
+
from: 5,
|
|
110
|
+
fromOp: "$gt",
|
|
111
|
+
to: 10,
|
|
112
|
+
toOp: "$lt"
|
|
113
|
+
};
|
|
114
|
+
assert.equal(contains(bounds, 5), false);
|
|
115
|
+
assert.equal(contains(bounds, 10), false);
|
|
116
|
+
assert.equal(contains(bounds, 7), true);
|
|
117
|
+
});
|
|
118
|
+
test("contains: gte/lte includes both endpoints", () => {
|
|
119
|
+
const bounds = {
|
|
120
|
+
from: 5,
|
|
121
|
+
fromOp: "$gte",
|
|
122
|
+
to: 10,
|
|
123
|
+
toOp: "$lte"
|
|
124
|
+
};
|
|
125
|
+
assert.equal(contains(bounds, 5), true);
|
|
126
|
+
assert.equal(contains(bounds, 10), true);
|
|
127
|
+
assert.equal(contains(bounds, 7), true);
|
|
128
|
+
});
|
|
129
|
+
test("contains: gt/lte mixed", () => {
|
|
130
|
+
const bounds = {
|
|
131
|
+
from: 5,
|
|
132
|
+
fromOp: "$gt",
|
|
133
|
+
to: 10,
|
|
134
|
+
toOp: "$lte"
|
|
135
|
+
};
|
|
136
|
+
assert.equal(contains(bounds, 5), false);
|
|
137
|
+
assert.equal(contains(bounds, 10), true);
|
|
138
|
+
});
|
|
139
|
+
test("contains: gte/lt mixed", () => {
|
|
140
|
+
const bounds = {
|
|
141
|
+
from: 5,
|
|
142
|
+
fromOp: "$gte",
|
|
143
|
+
to: 10,
|
|
144
|
+
toOp: "$lt"
|
|
145
|
+
};
|
|
146
|
+
assert.equal(contains(bounds, 5), true);
|
|
147
|
+
assert.equal(contains(bounds, 10), false);
|
|
148
|
+
});
|
|
149
|
+
test("contains: value below lower bound", () => {
|
|
150
|
+
assert.equal(contains({ from: 5, fromOp: "$gte" }, 4), false);
|
|
151
|
+
});
|
|
152
|
+
test("contains: value above upper bound", () => {
|
|
153
|
+
assert.equal(contains({ to: 10, toOp: "$lte" }, 11), false);
|
|
154
|
+
});
|
|
155
|
+
test("contains: open lower bound", () => {
|
|
156
|
+
assert.equal(contains({ to: 10, toOp: "$lte" }, -1000), true);
|
|
157
|
+
});
|
|
158
|
+
test("contains: open upper bound", () => {
|
|
159
|
+
assert.equal(contains({ from: 5, fromOp: "$gte" }, 1000), true);
|
|
160
|
+
});
|
|
161
|
+
test("contains: completely unbounded", () => {
|
|
162
|
+
assert.equal(contains({}, -100), true);
|
|
163
|
+
assert.equal(contains({}, 100), true);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
/* ============================================================
|
|
167
|
+
* intersects()
|
|
168
|
+
* ============================================================ */
|
|
169
|
+
describe("intersects()", () => {
|
|
170
|
+
test("intersects: value ↔ value", () => {
|
|
171
|
+
assert.equal(intersects(["A"], null, ["A"], null), true);
|
|
172
|
+
});
|
|
173
|
+
test("intersects: value ↔ value miss", () => {
|
|
174
|
+
assert.equal(intersects(["A"], null, ["B"], null), false);
|
|
175
|
+
});
|
|
176
|
+
test("intersects: value ↔ range", () => {
|
|
177
|
+
assert.equal(intersects([10], null, [], { from: 5 }), true);
|
|
178
|
+
});
|
|
179
|
+
test("intersects: value ↔ range miss", () => {
|
|
180
|
+
assert.equal(intersects([2], null, [], { from: 5 }), false);
|
|
181
|
+
});
|
|
182
|
+
test("intersects: range ↔ value", () => {
|
|
183
|
+
assert.equal(intersects([], { from: 5 }, [10], null), true);
|
|
184
|
+
});
|
|
185
|
+
test("intersects: range ↔ value miss", () => {
|
|
186
|
+
assert.equal(intersects([], { from: 5 }, [2], null), false);
|
|
187
|
+
});
|
|
188
|
+
test("intersects: range ↔ range", () => {
|
|
189
|
+
assert.equal(intersects([], { from: 10 }, [], { from: 20 }), true);
|
|
190
|
+
});
|
|
191
|
+
test("intersects: range ↔ range miss", () => {
|
|
192
|
+
assert.equal(intersects([], { to: 5 }, [], { from: 10 }), false);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
/* ============================================================
|
|
196
|
+
* matchPolicy()
|
|
197
|
+
* ============================================================ */
|
|
198
|
+
describe("matchPolicy()", () => {
|
|
199
|
+
test("empty when", () => {
|
|
200
|
+
assert.equal(matchPolicy({}, {}), true);
|
|
201
|
+
});
|
|
202
|
+
test("simple equality", () => {
|
|
203
|
+
assert.equal(matchPolicy({ status: "Active" }, { status: "Active" }), true);
|
|
204
|
+
});
|
|
205
|
+
test("simple equality fail", () => {
|
|
206
|
+
assert.equal(matchPolicy({ status: "Pending" }, { status: "Active" }), false);
|
|
207
|
+
});
|
|
208
|
+
test("$in subset", () => {
|
|
209
|
+
assert.equal(matchPolicy({ status: { $in: ["Active", "Pending"] } }, { status: { $in: ["Active"] } }), true);
|
|
210
|
+
});
|
|
211
|
+
test("$in miss", () => {
|
|
212
|
+
assert.equal(matchPolicy({ status: { $in: ["Pending"] } }, { status: { $in: ["Active"] } }), false);
|
|
213
|
+
});
|
|
214
|
+
test("value ↔ range", () => {
|
|
215
|
+
assert.equal(matchPolicy({ age: 10 }, { age: { $gt: 5 } }), true);
|
|
216
|
+
});
|
|
217
|
+
test("value ↔ range miss", () => {
|
|
218
|
+
assert.equal(matchPolicy({ age: 2 }, { age: { $gt: 5 } }), false);
|
|
219
|
+
});
|
|
220
|
+
test("range ↔ value", () => {
|
|
221
|
+
assert.equal(matchPolicy({ age: { $gt: 5 } }, { age: 10 }), true);
|
|
222
|
+
});
|
|
223
|
+
test("range ↔ value miss", () => {
|
|
224
|
+
assert.equal(matchPolicy({ age: { $lt: 5 } }, { age: 10 }), false);
|
|
225
|
+
});
|
|
226
|
+
test("range ↔ range overlap", () => {
|
|
227
|
+
assert.equal(matchPolicy({ age: { $gt: 15 } }, { age: { $gt: 10 } }), true);
|
|
228
|
+
});
|
|
229
|
+
test("range ↔ range miss", () => {
|
|
230
|
+
assert.equal(matchPolicy({ age: { $lt: 5 } }, { age: { $gt: 10 } }), false);
|
|
231
|
+
});
|
|
232
|
+
test("multiple keys", () => {
|
|
233
|
+
assert.equal(matchPolicy({
|
|
234
|
+
status: "Active",
|
|
235
|
+
age: 20
|
|
236
|
+
}, {
|
|
237
|
+
status: "Active",
|
|
238
|
+
age: { $gt: 10 }
|
|
239
|
+
}), true);
|
|
240
|
+
});
|
|
241
|
+
test("multiple keys fail", () => {
|
|
242
|
+
assert.equal(matchPolicy({
|
|
243
|
+
status: "Pending",
|
|
244
|
+
age: 20
|
|
245
|
+
}, {
|
|
246
|
+
status: "Active",
|
|
247
|
+
age: { $gt: 10 }
|
|
248
|
+
}), false);
|
|
249
|
+
});
|
|
250
|
+
test("$and flatten", () => {
|
|
251
|
+
assert.equal(matchPolicy({
|
|
252
|
+
status: "Active",
|
|
253
|
+
region: "IN"
|
|
254
|
+
}, {
|
|
255
|
+
$and: [
|
|
256
|
+
{ status: "Active" },
|
|
257
|
+
{ region: "IN" }
|
|
258
|
+
]
|
|
259
|
+
}), true);
|
|
260
|
+
});
|
|
261
|
+
test("$and flatten fail", () => {
|
|
262
|
+
assert.equal(matchPolicy({
|
|
263
|
+
status: "Active",
|
|
264
|
+
region: "US"
|
|
265
|
+
}, {
|
|
266
|
+
$and: [
|
|
267
|
+
{ status: "Active" },
|
|
268
|
+
{ region: "IN" }
|
|
269
|
+
]
|
|
270
|
+
}), false);
|
|
271
|
+
});
|
|
272
|
+
test("$or merge semantics", () => {
|
|
273
|
+
assert.equal(matchPolicy({
|
|
274
|
+
status: {
|
|
275
|
+
$in: ["Active", "Pending"]
|
|
276
|
+
}
|
|
277
|
+
}, {
|
|
278
|
+
$or: [
|
|
279
|
+
{ status: "Active" },
|
|
280
|
+
{ status: "Pending" }
|
|
281
|
+
]
|
|
282
|
+
}), true);
|
|
283
|
+
});
|
|
284
|
+
test("$ne throws", () => {
|
|
285
|
+
assert.throws(() => {
|
|
286
|
+
matchPolicy({ status: "Deleted" }, {
|
|
287
|
+
status: {
|
|
288
|
+
$ne: "Deleted"
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
test("$nin throws", () => {
|
|
294
|
+
assert.throws(() => {
|
|
295
|
+
matchPolicy({ status: "Deleted" }, {
|
|
296
|
+
status: {
|
|
297
|
+
$nin: ["Deleted"]
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
test("$not throws", () => {
|
|
303
|
+
assert.throws(() => {
|
|
304
|
+
matchPolicy({ age: 10 }, {
|
|
305
|
+
age: {
|
|
306
|
+
$not: {
|
|
307
|
+
$gt: 5
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
test("conservative planner: identical lt", () => {
|
|
314
|
+
assert.equal(matchPolicy({ age: { $lt: 10 } }, { age: { $lt: 10 } }), true);
|
|
315
|
+
});
|
|
316
|
+
test("conservative planner: identical gt", () => {
|
|
317
|
+
assert.equal(matchPolicy({ age: { $gt: 10 } }, { age: { $gt: 10 } }), true);
|
|
318
|
+
});
|
|
319
|
+
test("mixed value + range", () => {
|
|
320
|
+
assert.equal(matchPolicy({
|
|
321
|
+
age: {
|
|
322
|
+
$in: [10],
|
|
323
|
+
$gt: 20
|
|
324
|
+
}
|
|
325
|
+
}, {
|
|
326
|
+
age: 10
|
|
327
|
+
}), true);
|
|
328
|
+
});
|
|
329
|
+
});
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { describe, test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { suffix } from "../suffix.js";
|
|
4
|
+
describe("suffix()", () => {
|
|
5
|
+
test("returns empty suffixes when no policies exist", async () => {
|
|
6
|
+
const result = await suffix({}, [], true, {});
|
|
7
|
+
assert.deepEqual(result, []);
|
|
8
|
+
});
|
|
9
|
+
test("generates string suffix from fixed value", async () => {
|
|
10
|
+
const result = await suffix({}, [
|
|
11
|
+
{
|
|
12
|
+
type: "string",
|
|
13
|
+
value: "hello"
|
|
14
|
+
}
|
|
15
|
+
], true, {});
|
|
16
|
+
assert.deepEqual(result, [
|
|
17
|
+
"/hello"
|
|
18
|
+
]);
|
|
19
|
+
});
|
|
20
|
+
test("trims fixed string values", async () => {
|
|
21
|
+
const result = await suffix({}, [
|
|
22
|
+
{
|
|
23
|
+
type: "string",
|
|
24
|
+
value: " hello "
|
|
25
|
+
}
|
|
26
|
+
], true, {});
|
|
27
|
+
assert.deepEqual(result, [
|
|
28
|
+
"/hello"
|
|
29
|
+
]);
|
|
30
|
+
});
|
|
31
|
+
test("rejects empty fixed value", async () => {
|
|
32
|
+
await assert.rejects(() => suffix({}, [
|
|
33
|
+
{
|
|
34
|
+
type: "string",
|
|
35
|
+
value: " "
|
|
36
|
+
}
|
|
37
|
+
], true, {}));
|
|
38
|
+
});
|
|
39
|
+
test("uses document key for string policy", async () => {
|
|
40
|
+
const result = await suffix({
|
|
41
|
+
name: "alice"
|
|
42
|
+
}, [
|
|
43
|
+
{
|
|
44
|
+
type: "string",
|
|
45
|
+
key: "name"
|
|
46
|
+
}
|
|
47
|
+
], true, {});
|
|
48
|
+
assert.deepEqual(result, [
|
|
49
|
+
"/alice"
|
|
50
|
+
]);
|
|
51
|
+
});
|
|
52
|
+
test("supports multiple policies and cartesian expansion", async () => {
|
|
53
|
+
const result = await suffix({
|
|
54
|
+
category: "books",
|
|
55
|
+
year: 2026
|
|
56
|
+
}, [
|
|
57
|
+
{
|
|
58
|
+
type: "string",
|
|
59
|
+
key: "category"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
type: "number",
|
|
63
|
+
key: "year"
|
|
64
|
+
}
|
|
65
|
+
], true, {});
|
|
66
|
+
assert.deepEqual(result, [
|
|
67
|
+
"/books/2026"
|
|
68
|
+
]);
|
|
69
|
+
});
|
|
70
|
+
test("supports multiple values from document key", async () => {
|
|
71
|
+
const result = await suffix({
|
|
72
|
+
tags: [
|
|
73
|
+
"a",
|
|
74
|
+
"b",
|
|
75
|
+
"c"
|
|
76
|
+
]
|
|
77
|
+
}, [
|
|
78
|
+
{
|
|
79
|
+
type: "string",
|
|
80
|
+
key: "tags"
|
|
81
|
+
}
|
|
82
|
+
], true, {});
|
|
83
|
+
assert.deepEqual(result.sort(), [
|
|
84
|
+
"/a",
|
|
85
|
+
"/b",
|
|
86
|
+
"/c"
|
|
87
|
+
]);
|
|
88
|
+
});
|
|
89
|
+
test("number policy accepts numeric values", async () => {
|
|
90
|
+
const result = await suffix({
|
|
91
|
+
age: 42
|
|
92
|
+
}, [
|
|
93
|
+
{
|
|
94
|
+
type: "number",
|
|
95
|
+
key: "age"
|
|
96
|
+
}
|
|
97
|
+
], true, {});
|
|
98
|
+
assert.deepEqual(result, [
|
|
99
|
+
"/42"
|
|
100
|
+
]);
|
|
101
|
+
});
|
|
102
|
+
test("rejects invalid suffix policy type", async () => {
|
|
103
|
+
await assert.rejects(() => suffix({}, [
|
|
104
|
+
{
|
|
105
|
+
type: "boolean",
|
|
106
|
+
value: true
|
|
107
|
+
}
|
|
108
|
+
], true, {}));
|
|
109
|
+
});
|
|
110
|
+
test("defaults type to string", async () => {
|
|
111
|
+
const result = await suffix({
|
|
112
|
+
id: "abc"
|
|
113
|
+
}, [
|
|
114
|
+
{
|
|
115
|
+
key: "id"
|
|
116
|
+
}
|
|
117
|
+
], true, {});
|
|
118
|
+
assert.deepEqual(result, [
|
|
119
|
+
"/abc"
|
|
120
|
+
]);
|
|
121
|
+
});
|
|
122
|
+
test("rejects invalid date type without format", async () => {
|
|
123
|
+
await assert.rejects(() => suffix({
|
|
124
|
+
created: "2026-07-09"
|
|
125
|
+
}, [
|
|
126
|
+
{
|
|
127
|
+
type: "date",
|
|
128
|
+
key: "created"
|
|
129
|
+
}
|
|
130
|
+
], true, {}));
|
|
131
|
+
});
|
|
132
|
+
test("rejects date format containing slash", async () => {
|
|
133
|
+
await assert.rejects(() => suffix({
|
|
134
|
+
created: "2026-07-09"
|
|
135
|
+
}, [
|
|
136
|
+
{
|
|
137
|
+
type: "date:YYYY/MM/DD",
|
|
138
|
+
key: "created"
|
|
139
|
+
}
|
|
140
|
+
], true, {}));
|
|
141
|
+
});
|
|
142
|
+
test("formats date suffix", async () => {
|
|
143
|
+
const result = await suffix({
|
|
144
|
+
created: "2026-07-09"
|
|
145
|
+
}, [
|
|
146
|
+
{
|
|
147
|
+
type: "date:YYYY-MM-DD",
|
|
148
|
+
key: "created"
|
|
149
|
+
}
|
|
150
|
+
], true, {});
|
|
151
|
+
assert.deepEqual(result, [
|
|
152
|
+
"/2026-07-09"
|
|
153
|
+
]);
|
|
154
|
+
});
|
|
155
|
+
test("deduplicates suffixes", async () => {
|
|
156
|
+
const result = await suffix({
|
|
157
|
+
tags: [
|
|
158
|
+
"a",
|
|
159
|
+
"a",
|
|
160
|
+
"b"
|
|
161
|
+
]
|
|
162
|
+
}, [
|
|
163
|
+
{
|
|
164
|
+
type: "string",
|
|
165
|
+
key: "tags"
|
|
166
|
+
}
|
|
167
|
+
], true, {});
|
|
168
|
+
assert.deepEqual(result.sort(), [
|
|
169
|
+
"/a",
|
|
170
|
+
"/b"
|
|
171
|
+
]);
|
|
172
|
+
});
|
|
173
|
+
test("rejects missing insert key", async () => {
|
|
174
|
+
await assert.rejects(() => suffix({}, [
|
|
175
|
+
{
|
|
176
|
+
type: "string",
|
|
177
|
+
key: "missing"
|
|
178
|
+
}
|
|
179
|
+
], true, {}));
|
|
180
|
+
});
|
|
181
|
+
test("number range expands inclusive bounds", async () => {
|
|
182
|
+
const result = await suffix({
|
|
183
|
+
score: {
|
|
184
|
+
$gte: 1,
|
|
185
|
+
$lte: 3
|
|
186
|
+
}
|
|
187
|
+
}, [
|
|
188
|
+
{
|
|
189
|
+
type: "number",
|
|
190
|
+
key: "score"
|
|
191
|
+
}
|
|
192
|
+
], true, {});
|
|
193
|
+
assert.deepEqual(result.sort(), [
|
|
194
|
+
"/1",
|
|
195
|
+
"/2",
|
|
196
|
+
"/3"
|
|
197
|
+
]);
|
|
198
|
+
});
|
|
199
|
+
test("number range uses explicit min/max", async () => {
|
|
200
|
+
const result = await suffix({
|
|
201
|
+
score: {
|
|
202
|
+
$gte: 1,
|
|
203
|
+
$lte: 2
|
|
204
|
+
}
|
|
205
|
+
}, [
|
|
206
|
+
{
|
|
207
|
+
type: "number",
|
|
208
|
+
key: "score",
|
|
209
|
+
min: 5,
|
|
210
|
+
max: 7
|
|
211
|
+
}
|
|
212
|
+
], true, {});
|
|
213
|
+
assert.deepEqual(result.sort(), ["/1", "/2"]);
|
|
214
|
+
});
|
|
215
|
+
test("number range uses policy max fallback", async () => {
|
|
216
|
+
const result = await suffix({
|
|
217
|
+
score: {
|
|
218
|
+
$gte: 5
|
|
219
|
+
}
|
|
220
|
+
}, [
|
|
221
|
+
{
|
|
222
|
+
type: "number",
|
|
223
|
+
key: "score",
|
|
224
|
+
max: 7
|
|
225
|
+
}
|
|
226
|
+
], true, {});
|
|
227
|
+
assert.deepEqual(result.sort(), ["/5", "/6", "/7"]);
|
|
228
|
+
});
|
|
229
|
+
test("number range uses policy min fallback", async () => {
|
|
230
|
+
const result = await suffix({
|
|
231
|
+
score: {
|
|
232
|
+
$lte: 7
|
|
233
|
+
}
|
|
234
|
+
}, [
|
|
235
|
+
{
|
|
236
|
+
type: "number",
|
|
237
|
+
key: "score",
|
|
238
|
+
min: 5
|
|
239
|
+
}
|
|
240
|
+
], true, {});
|
|
241
|
+
assert.deepEqual(result.sort(), ["/5", "/6", "/7"]);
|
|
242
|
+
});
|
|
243
|
+
test("date range expands dates", async () => {
|
|
244
|
+
const result = await suffix({
|
|
245
|
+
created: {
|
|
246
|
+
$gte: "2026-01-01",
|
|
247
|
+
$lte: "2026-01-03"
|
|
248
|
+
}
|
|
249
|
+
}, [
|
|
250
|
+
{
|
|
251
|
+
type: "date:YYYY-MM-DD",
|
|
252
|
+
key: "created"
|
|
253
|
+
}
|
|
254
|
+
], true, {});
|
|
255
|
+
assert.deepEqual(result.sort(), [
|
|
256
|
+
"/2026-01-01",
|
|
257
|
+
"/2026-01-02",
|
|
258
|
+
"/2026-01-03"
|
|
259
|
+
]);
|
|
260
|
+
});
|
|
261
|
+
test("throws when ls threshold exceeded for number range", async () => {
|
|
262
|
+
await assert.rejects(() => suffix({
|
|
263
|
+
value: {
|
|
264
|
+
$gte: 1,
|
|
265
|
+
$lte: 100
|
|
266
|
+
}
|
|
267
|
+
}, [
|
|
268
|
+
{
|
|
269
|
+
type: "number",
|
|
270
|
+
key: "value",
|
|
271
|
+
ls_threshold: 5
|
|
272
|
+
}
|
|
273
|
+
], true, {}));
|
|
274
|
+
});
|
|
275
|
+
test("throws when ls threshold exceeded for date range", async () => {
|
|
276
|
+
await assert.rejects(() => suffix({
|
|
277
|
+
created: {
|
|
278
|
+
$gte: "2026-01-01",
|
|
279
|
+
$lte: "2026-01-10"
|
|
280
|
+
}
|
|
281
|
+
}, [
|
|
282
|
+
{
|
|
283
|
+
type: "date:YYYY-MM-DD",
|
|
284
|
+
key: "created",
|
|
285
|
+
ls_threshold: 2
|
|
286
|
+
}
|
|
287
|
+
], true, {}));
|
|
288
|
+
});
|
|
289
|
+
test("creates cartesian product for multiple suffix values", async () => {
|
|
290
|
+
const result = await suffix({
|
|
291
|
+
a: [
|
|
292
|
+
"x",
|
|
293
|
+
"y"
|
|
294
|
+
],
|
|
295
|
+
b: [
|
|
296
|
+
1,
|
|
297
|
+
2
|
|
298
|
+
]
|
|
299
|
+
}, [
|
|
300
|
+
{
|
|
301
|
+
type: "string",
|
|
302
|
+
key: "a"
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
type: "number",
|
|
306
|
+
key: "b"
|
|
307
|
+
}
|
|
308
|
+
], true, {});
|
|
309
|
+
assert.deepEqual(result.sort(), [
|
|
310
|
+
"/x/1",
|
|
311
|
+
"/x/2",
|
|
312
|
+
"/y/1",
|
|
313
|
+
"/y/2"
|
|
314
|
+
]);
|
|
315
|
+
});
|
|
316
|
+
});
|