corebasic 1.0.206 → 1.0.208

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.
@@ -0,0 +1,622 @@
1
+ import { describe, test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+
4
+ import { suffix } from "../suffix.js";
5
+
6
+
7
+ describe("suffix()", () => {
8
+
9
+
10
+ test("returns empty suffixes when no policies exist", async () => {
11
+
12
+ const result = await suffix(
13
+ {},
14
+ [],
15
+ true,
16
+ {}
17
+ );
18
+
19
+ assert.deepEqual(result, []);
20
+
21
+ });
22
+
23
+
24
+
25
+ test("generates string suffix from fixed value", async () => {
26
+
27
+ const result = await suffix(
28
+ {},
29
+ [
30
+ {
31
+ type: "string",
32
+ value: "hello"
33
+ }
34
+ ],
35
+ true,
36
+ {}
37
+ );
38
+
39
+
40
+ assert.deepEqual(
41
+ result,
42
+ [
43
+ "/hello"
44
+ ]
45
+ );
46
+
47
+ });
48
+
49
+
50
+
51
+ test("trims fixed string values", async () => {
52
+
53
+ const result = await suffix(
54
+ {},
55
+ [
56
+ {
57
+ type: "string",
58
+ value: " hello "
59
+ }
60
+ ],
61
+ true,
62
+ {}
63
+ );
64
+
65
+
66
+ assert.deepEqual(
67
+ result,
68
+ [
69
+ "/hello"
70
+ ]
71
+ );
72
+
73
+ });
74
+
75
+
76
+
77
+ test("rejects empty fixed value", async () => {
78
+
79
+ await assert.rejects(
80
+ () =>
81
+ suffix(
82
+ {},
83
+ [
84
+ {
85
+ type: "string",
86
+ value: " "
87
+ }
88
+ ],
89
+ true,
90
+ {}
91
+ )
92
+ );
93
+
94
+ });
95
+
96
+
97
+
98
+ test("uses document key for string policy", async () => {
99
+
100
+ const result = await suffix(
101
+ {
102
+ name: "alice"
103
+ },
104
+ [
105
+ {
106
+ type: "string",
107
+ key: "name"
108
+ }
109
+ ],
110
+ true,
111
+ {}
112
+ );
113
+
114
+
115
+ assert.deepEqual(
116
+ result,
117
+ [
118
+ "/alice"
119
+ ]
120
+ );
121
+
122
+ });
123
+
124
+
125
+
126
+ test("supports multiple policies and cartesian expansion", async () => {
127
+
128
+ const result = await suffix(
129
+ {
130
+ category: "books",
131
+ year: 2026
132
+ },
133
+ [
134
+ {
135
+ type: "string",
136
+ key: "category"
137
+ },
138
+ {
139
+ type: "number",
140
+ key: "year"
141
+ }
142
+ ],
143
+ true,
144
+ {}
145
+ );
146
+
147
+
148
+ assert.deepEqual(
149
+ result,
150
+ [
151
+ "/books/2026"
152
+ ]
153
+ );
154
+
155
+ });
156
+
157
+
158
+
159
+ test("supports multiple values from document key", async () => {
160
+
161
+ const result = await suffix(
162
+ {
163
+ tags: [
164
+ "a",
165
+ "b",
166
+ "c"
167
+ ]
168
+ },
169
+ [
170
+ {
171
+ type: "string",
172
+ key: "tags"
173
+ }
174
+ ],
175
+ true,
176
+ {}
177
+ );
178
+
179
+
180
+ assert.deepEqual(
181
+ result.sort(),
182
+ [
183
+ "/a",
184
+ "/b",
185
+ "/c"
186
+ ]
187
+ );
188
+
189
+ });
190
+
191
+
192
+
193
+ test("number policy accepts numeric values", async () => {
194
+
195
+ const result = await suffix(
196
+ {
197
+ age: 42
198
+ },
199
+ [
200
+ {
201
+ type: "number",
202
+ key: "age"
203
+ }
204
+ ],
205
+ true,
206
+ {}
207
+ );
208
+
209
+
210
+ assert.deepEqual(
211
+ result,
212
+ [
213
+ "/42"
214
+ ]
215
+ );
216
+
217
+ });
218
+
219
+
220
+
221
+ test("rejects invalid suffix policy type", async () => {
222
+
223
+ await assert.rejects(
224
+ () =>
225
+ suffix(
226
+ {},
227
+ [
228
+ {
229
+ type: "boolean",
230
+ value: true
231
+ }
232
+ ],
233
+ true,
234
+ {}
235
+ )
236
+ );
237
+
238
+ });
239
+
240
+
241
+
242
+ test("defaults type to string", async () => {
243
+
244
+ const result = await suffix(
245
+ {
246
+ id: "abc"
247
+ },
248
+ [
249
+ {
250
+ key: "id"
251
+ }
252
+ ],
253
+ true,
254
+ {}
255
+ );
256
+
257
+
258
+ assert.deepEqual(
259
+ result,
260
+ [
261
+ "/abc"
262
+ ]
263
+ );
264
+
265
+ });
266
+
267
+
268
+
269
+ test("rejects invalid date type without format", async () => {
270
+
271
+ await assert.rejects(
272
+ () =>
273
+ suffix(
274
+ {
275
+ created: "2026-07-09"
276
+ },
277
+ [
278
+ {
279
+ type: "date",
280
+ key: "created"
281
+ }
282
+ ],
283
+ true,
284
+ {}
285
+ )
286
+ );
287
+
288
+ });
289
+
290
+
291
+
292
+ test("rejects date format containing slash", async () => {
293
+
294
+ await assert.rejects(
295
+ () =>
296
+ suffix(
297
+ {
298
+ created: "2026-07-09"
299
+ },
300
+ [
301
+ {
302
+ type: "date:YYYY/MM/DD",
303
+ key: "created"
304
+ }
305
+ ],
306
+ true,
307
+ {}
308
+ )
309
+ );
310
+
311
+ });
312
+
313
+
314
+
315
+ test("formats date suffix", async () => {
316
+
317
+ const result = await suffix(
318
+ {
319
+ created: "2026-07-09"
320
+ },
321
+ [
322
+ {
323
+ type: "date:YYYY-MM-DD",
324
+ key: "created"
325
+ }
326
+ ],
327
+ true,
328
+ {}
329
+ );
330
+
331
+
332
+ assert.deepEqual(
333
+ result,
334
+ [
335
+ "/2026-07-09"
336
+ ]
337
+ );
338
+
339
+ });
340
+
341
+
342
+
343
+ test("deduplicates suffixes", async () => {
344
+
345
+ const result = await suffix(
346
+ {
347
+ tags: [
348
+ "a",
349
+ "a",
350
+ "b"
351
+ ]
352
+ },
353
+ [
354
+ {
355
+ type: "string",
356
+ key: "tags"
357
+ }
358
+ ],
359
+ true,
360
+ {}
361
+ );
362
+
363
+
364
+ assert.deepEqual(
365
+ result.sort(),
366
+ [
367
+ "/a",
368
+ "/b"
369
+ ]
370
+ );
371
+
372
+ });
373
+
374
+
375
+
376
+ test("rejects missing insert key", async () => {
377
+
378
+ await assert.rejects(
379
+ () =>
380
+ suffix(
381
+ {},
382
+ [
383
+ {
384
+ type: "string",
385
+ key: "missing"
386
+ }
387
+ ],
388
+ true,
389
+ {}
390
+ )
391
+ );
392
+
393
+ });
394
+
395
+
396
+
397
+ test("number range expands inclusive bounds", async () => {
398
+
399
+ const result = await suffix(
400
+ {
401
+ score: {
402
+ $gte: 1,
403
+ $lte: 3
404
+ }
405
+ },
406
+ [
407
+ {
408
+ type: "number",
409
+ key: "score"
410
+ }
411
+ ],
412
+ true,
413
+ {}
414
+ );
415
+
416
+
417
+ assert.deepEqual(
418
+ result.sort(),
419
+ [
420
+ "/1",
421
+ "/2",
422
+ "/3"
423
+ ]
424
+ );
425
+
426
+ });
427
+
428
+
429
+
430
+ test("number range uses explicit min/max", async () => {
431
+
432
+ const result = await suffix(
433
+ {
434
+ score: {
435
+ $gte: 1,
436
+ $lte: 2
437
+ }
438
+ },
439
+ [
440
+ {
441
+ type: "number",
442
+ key: "score",
443
+ min: 5,
444
+ max: 7
445
+ }
446
+ ],
447
+ true,
448
+ {}
449
+ );
450
+ assert.deepEqual(result.sort(), ["/1", "/2"]);
451
+ });
452
+
453
+
454
+ test("number range uses policy max fallback", async () => {
455
+ const result = await suffix(
456
+ {
457
+ score: {
458
+ $gte: 5
459
+ }
460
+ },
461
+ [
462
+ {
463
+ type: "number",
464
+ key: "score",
465
+ max: 7
466
+ }
467
+ ],
468
+ true,
469
+ {}
470
+ );
471
+ assert.deepEqual(result.sort(), ["/5", "/6", "/7"]);
472
+ });
473
+
474
+ test("number range uses policy min fallback", async () => {
475
+ const result = await suffix(
476
+ {
477
+ score: {
478
+ $lte: 7
479
+ }
480
+ },
481
+ [
482
+ {
483
+ type: "number",
484
+ key: "score",
485
+ min: 5
486
+ }
487
+ ],
488
+ true,
489
+ {}
490
+ );
491
+ assert.deepEqual(result.sort(), ["/5", "/6", "/7"]);
492
+ });
493
+
494
+ test("date range expands dates", async () => {
495
+
496
+ const result = await suffix(
497
+ {
498
+ created: {
499
+ $gte: "2026-01-01",
500
+ $lte: "2026-01-03"
501
+ }
502
+ },
503
+ [
504
+ {
505
+ type: "date:YYYY-MM-DD",
506
+ key: "created"
507
+ }
508
+ ],
509
+ true,
510
+ {}
511
+ );
512
+
513
+
514
+ assert.deepEqual(
515
+ result.sort(),
516
+ [
517
+ "/2026-01-01",
518
+ "/2026-01-02",
519
+ "/2026-01-03"
520
+ ]
521
+ );
522
+
523
+ });
524
+
525
+
526
+
527
+ test("throws when ls threshold exceeded for number range", async () => {
528
+
529
+ await assert.rejects(
530
+ () =>
531
+ suffix(
532
+ {
533
+ value: {
534
+ $gte: 1,
535
+ $lte: 100
536
+ }
537
+ },
538
+ [
539
+ {
540
+ type: "number",
541
+ key: "value",
542
+ ls_threshold: 5
543
+ }
544
+ ],
545
+ true,
546
+ {}
547
+ )
548
+ );
549
+
550
+ });
551
+
552
+
553
+
554
+ test("throws when ls threshold exceeded for date range", async () => {
555
+
556
+ await assert.rejects(
557
+ () =>
558
+ suffix(
559
+ {
560
+ created: {
561
+ $gte: "2026-01-01",
562
+ $lte: "2026-01-10"
563
+ }
564
+ },
565
+ [
566
+ {
567
+ type: "date:YYYY-MM-DD",
568
+ key: "created",
569
+ ls_threshold: 2
570
+ }
571
+ ],
572
+ true,
573
+ {}
574
+ )
575
+ );
576
+
577
+ });
578
+
579
+
580
+
581
+ test("creates cartesian product for multiple suffix values", async () => {
582
+
583
+ const result = await suffix(
584
+ {
585
+ a: [
586
+ "x",
587
+ "y"
588
+ ],
589
+ b: [
590
+ 1,
591
+ 2
592
+ ]
593
+ },
594
+ [
595
+ {
596
+ type: "string",
597
+ key: "a"
598
+ },
599
+ {
600
+ type: "number",
601
+ key: "b"
602
+ }
603
+ ],
604
+ true,
605
+ {}
606
+ );
607
+
608
+
609
+ assert.deepEqual(
610
+ result.sort(),
611
+ [
612
+ "/x/1",
613
+ "/x/2",
614
+ "/y/1",
615
+ "/y/2"
616
+ ]
617
+ );
618
+
619
+ });
620
+
621
+
622
+ });
package/libs/dip.js CHANGED
@@ -67,9 +67,9 @@ export const IdempotentDip = () => {
67
67
 
68
68
  export const idip = async (message, callback, cleanup) => {
69
69
  if (!callback)
70
- throw {message: "Dip.idip() callback argument undefined"}
70
+ throw new Error("Dip.idip() callback argument undefined")
71
71
  if (!cleanup)
72
- throw {message: "Dip.idip() cleanup argument undefined"}
72
+ throw new Error("Dip.idip() cleanup argument undefined")
73
73
  async function doCleanup(IDip) {
74
74
  if (typeof cleanup === 'object') {
75
75
  for (let collection in cleanup)
@@ -91,7 +91,7 @@ export const idip = async (message, callback, cleanup) => {
91
91
 
92
92
 
93
93
  export const insertKeys = (message, meta) => {
94
- if (!isDipMeta(meta)) throw {message: 'Error: Provided meta is not an instance of DipMeta in Dip.insertKeys()'}
94
+ if (!isDipMeta(meta)) throw new Error('Error: Provided meta is not an instance of DipMeta in Dip.insertKeys()')
95
95
  return {
96
96
  "created": message.date, "updated": message.date,
97
97
  "createdBy": message.meta.staff,