corebasic 1.0.223 → 1.0.224
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/libs/dip/suffix/suffix.js +5 -2
- package/dist/libs/dip/suffix/tests/suffix.test.js +24 -23
- package/dist/libs/elabase.js +27 -14
- package/dist/libs/utils.js +12 -3
- package/libs/cpp.ts +1 -1
- package/libs/dip/suffix/policy.ts +1 -1
- package/libs/dip/suffix/suffix.ts +6 -3
- package/libs/dip/suffix/tests/suffix.test.ts +24 -23
- package/libs/elabase.ts +66 -20
- package/libs/mobilecodes.ts +4 -1
- package/libs/utils.ts +13 -3
- package/package.json +1 -1
- package/tsconfig.json +1 -1
|
@@ -79,8 +79,11 @@ export async function suffix(doc, policies, insertMode, arg) {
|
|
|
79
79
|
if (!keys.length && !insertMode) {
|
|
80
80
|
ls = true;
|
|
81
81
|
for (const suffix of suffixes) {
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
for (const collection of arg.collection) {
|
|
83
|
+
const dirs = (await Dip.operation("ls", { db: arg.db, collection: collection, suffix })).filter(dir => !dir.startsWith('chunk-'));
|
|
84
|
+
suffixAssociatedKeys[suffix] = suffixAssociatedKeys[suffix] ?? [];
|
|
85
|
+
suffixAssociatedKeys[suffix].push(...dirs);
|
|
86
|
+
}
|
|
84
87
|
}
|
|
85
88
|
console.warn(`Warn: Query omitted suffix policy key ${policy.key} in Dip. Falling back to Dip.operation(ls), incurring additional performance and network round-trip overhead.`);
|
|
86
89
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { describe, test } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import { suffix } from "../suffix.js";
|
|
4
|
+
const arg = { db: "test-db", collection: ["test-collection"] };
|
|
4
5
|
describe("suffix()", () => {
|
|
5
6
|
test("returns empty suffixes when no policies exist", async () => {
|
|
6
|
-
const result = await suffix({}, [], true,
|
|
7
|
+
const result = await suffix({}, [], true, arg);
|
|
7
8
|
assert.deepEqual(result, []);
|
|
8
9
|
});
|
|
9
10
|
test("generates string suffix from fixed value", async () => {
|
|
@@ -12,7 +13,7 @@ describe("suffix()", () => {
|
|
|
12
13
|
type: "string",
|
|
13
14
|
value: "hello"
|
|
14
15
|
}
|
|
15
|
-
], true,
|
|
16
|
+
], true, arg);
|
|
16
17
|
assert.deepEqual(result, [
|
|
17
18
|
"/hello"
|
|
18
19
|
]);
|
|
@@ -23,7 +24,7 @@ describe("suffix()", () => {
|
|
|
23
24
|
type: "string",
|
|
24
25
|
value: " hello "
|
|
25
26
|
}
|
|
26
|
-
], true,
|
|
27
|
+
], true, arg);
|
|
27
28
|
assert.deepEqual(result, [
|
|
28
29
|
"/hello"
|
|
29
30
|
]);
|
|
@@ -34,7 +35,7 @@ describe("suffix()", () => {
|
|
|
34
35
|
type: "string",
|
|
35
36
|
value: " "
|
|
36
37
|
}
|
|
37
|
-
], true,
|
|
38
|
+
], true, arg));
|
|
38
39
|
});
|
|
39
40
|
test("uses document key for string policy", async () => {
|
|
40
41
|
const result = await suffix({
|
|
@@ -44,7 +45,7 @@ describe("suffix()", () => {
|
|
|
44
45
|
type: "string",
|
|
45
46
|
key: "name"
|
|
46
47
|
}
|
|
47
|
-
], true,
|
|
48
|
+
], true, arg);
|
|
48
49
|
assert.deepEqual(result, [
|
|
49
50
|
"/alice"
|
|
50
51
|
]);
|
|
@@ -62,7 +63,7 @@ describe("suffix()", () => {
|
|
|
62
63
|
type: "number",
|
|
63
64
|
key: "year"
|
|
64
65
|
}
|
|
65
|
-
], true,
|
|
66
|
+
], true, arg);
|
|
66
67
|
assert.deepEqual(result, [
|
|
67
68
|
"/books/2026"
|
|
68
69
|
]);
|
|
@@ -79,7 +80,7 @@ describe("suffix()", () => {
|
|
|
79
80
|
type: "string",
|
|
80
81
|
key: "tags"
|
|
81
82
|
}
|
|
82
|
-
], true,
|
|
83
|
+
], true, arg);
|
|
83
84
|
assert.deepEqual(result.sort(), [
|
|
84
85
|
"/a",
|
|
85
86
|
"/b",
|
|
@@ -94,7 +95,7 @@ describe("suffix()", () => {
|
|
|
94
95
|
type: "number",
|
|
95
96
|
key: "age"
|
|
96
97
|
}
|
|
97
|
-
], true,
|
|
98
|
+
], true, arg);
|
|
98
99
|
assert.deepEqual(result, [
|
|
99
100
|
"/42"
|
|
100
101
|
]);
|
|
@@ -105,7 +106,7 @@ describe("suffix()", () => {
|
|
|
105
106
|
type: "boolean",
|
|
106
107
|
value: true
|
|
107
108
|
}
|
|
108
|
-
], true,
|
|
109
|
+
], true, arg));
|
|
109
110
|
});
|
|
110
111
|
test("defaults type to string", async () => {
|
|
111
112
|
const result = await suffix({
|
|
@@ -114,7 +115,7 @@ describe("suffix()", () => {
|
|
|
114
115
|
{
|
|
115
116
|
key: "id"
|
|
116
117
|
}
|
|
117
|
-
], true,
|
|
118
|
+
], true, arg);
|
|
118
119
|
assert.deepEqual(result, [
|
|
119
120
|
"/abc"
|
|
120
121
|
]);
|
|
@@ -127,7 +128,7 @@ describe("suffix()", () => {
|
|
|
127
128
|
type: "date",
|
|
128
129
|
key: "created"
|
|
129
130
|
}
|
|
130
|
-
], true,
|
|
131
|
+
], true, arg));
|
|
131
132
|
});
|
|
132
133
|
test("rejects date format containing slash", async () => {
|
|
133
134
|
await assert.rejects(() => suffix({
|
|
@@ -137,7 +138,7 @@ describe("suffix()", () => {
|
|
|
137
138
|
type: "date:YYYY/MM/DD",
|
|
138
139
|
key: "created"
|
|
139
140
|
}
|
|
140
|
-
], true,
|
|
141
|
+
], true, arg));
|
|
141
142
|
});
|
|
142
143
|
test("formats date suffix", async () => {
|
|
143
144
|
const result = await suffix({
|
|
@@ -147,7 +148,7 @@ describe("suffix()", () => {
|
|
|
147
148
|
type: "date:YYYY-MM-DD",
|
|
148
149
|
key: "created"
|
|
149
150
|
}
|
|
150
|
-
], true,
|
|
151
|
+
], true, arg);
|
|
151
152
|
assert.deepEqual(result, [
|
|
152
153
|
"/2026-07-09"
|
|
153
154
|
]);
|
|
@@ -164,7 +165,7 @@ describe("suffix()", () => {
|
|
|
164
165
|
type: "string",
|
|
165
166
|
key: "tags"
|
|
166
167
|
}
|
|
167
|
-
], true,
|
|
168
|
+
], true, arg);
|
|
168
169
|
assert.deepEqual(result.sort(), [
|
|
169
170
|
"/a",
|
|
170
171
|
"/b"
|
|
@@ -176,7 +177,7 @@ describe("suffix()", () => {
|
|
|
176
177
|
type: "string",
|
|
177
178
|
key: "missing"
|
|
178
179
|
}
|
|
179
|
-
], true,
|
|
180
|
+
], true, arg));
|
|
180
181
|
});
|
|
181
182
|
test("number range expands inclusive bounds", async () => {
|
|
182
183
|
const result = await suffix({
|
|
@@ -189,7 +190,7 @@ describe("suffix()", () => {
|
|
|
189
190
|
type: "number",
|
|
190
191
|
key: "score"
|
|
191
192
|
}
|
|
192
|
-
], true,
|
|
193
|
+
], true, arg);
|
|
193
194
|
assert.deepEqual(result.sort(), [
|
|
194
195
|
"/1",
|
|
195
196
|
"/2",
|
|
@@ -209,7 +210,7 @@ describe("suffix()", () => {
|
|
|
209
210
|
min: 5,
|
|
210
211
|
max: 7
|
|
211
212
|
}
|
|
212
|
-
], true,
|
|
213
|
+
], true, arg);
|
|
213
214
|
assert.deepEqual(result.sort(), ["/1", "/2"]);
|
|
214
215
|
});
|
|
215
216
|
test("number range uses policy max fallback", async () => {
|
|
@@ -223,7 +224,7 @@ describe("suffix()", () => {
|
|
|
223
224
|
key: "score",
|
|
224
225
|
max: 7
|
|
225
226
|
}
|
|
226
|
-
], true,
|
|
227
|
+
], true, arg);
|
|
227
228
|
assert.deepEqual(result.sort(), ["/5", "/6", "/7"]);
|
|
228
229
|
});
|
|
229
230
|
test("number range uses policy min fallback", async () => {
|
|
@@ -237,7 +238,7 @@ describe("suffix()", () => {
|
|
|
237
238
|
key: "score",
|
|
238
239
|
min: 5
|
|
239
240
|
}
|
|
240
|
-
], true,
|
|
241
|
+
], true, arg);
|
|
241
242
|
assert.deepEqual(result.sort(), ["/5", "/6", "/7"]);
|
|
242
243
|
});
|
|
243
244
|
test("date range expands dates", async () => {
|
|
@@ -251,7 +252,7 @@ describe("suffix()", () => {
|
|
|
251
252
|
type: "date:YYYY-MM-DD",
|
|
252
253
|
key: "created"
|
|
253
254
|
}
|
|
254
|
-
], true,
|
|
255
|
+
], true, arg);
|
|
255
256
|
assert.deepEqual(result.sort(), [
|
|
256
257
|
"/2026-01-01",
|
|
257
258
|
"/2026-01-02",
|
|
@@ -270,7 +271,7 @@ describe("suffix()", () => {
|
|
|
270
271
|
key: "value",
|
|
271
272
|
ls_threshold: 5
|
|
272
273
|
}
|
|
273
|
-
], true,
|
|
274
|
+
], true, arg));
|
|
274
275
|
});
|
|
275
276
|
test("throws when ls threshold exceeded for date range", async () => {
|
|
276
277
|
await assert.rejects(() => suffix({
|
|
@@ -284,7 +285,7 @@ describe("suffix()", () => {
|
|
|
284
285
|
key: "created",
|
|
285
286
|
ls_threshold: 2
|
|
286
287
|
}
|
|
287
|
-
], true,
|
|
288
|
+
], true, arg));
|
|
288
289
|
});
|
|
289
290
|
test("creates cartesian product for multiple suffix values", async () => {
|
|
290
291
|
const result = await suffix({
|
|
@@ -305,7 +306,7 @@ describe("suffix()", () => {
|
|
|
305
306
|
type: "number",
|
|
306
307
|
key: "b"
|
|
307
308
|
}
|
|
308
|
-
], true,
|
|
309
|
+
], true, arg);
|
|
309
310
|
assert.deepEqual(result.sort(), [
|
|
310
311
|
"/x/1",
|
|
311
312
|
"/x/2",
|
package/dist/libs/elabase.js
CHANGED
|
@@ -133,7 +133,7 @@ let batches = {};
|
|
|
133
133
|
// Ported
|
|
134
134
|
export function batchBegin() {
|
|
135
135
|
const uid = ++BATCH_COUNTER; // Safe. No skew in async ++BATCH_COUNTER as single thread and ++COUNTER is atomic in event loop sense. ⚠️ Only breaks on worker_threads or cluster (multi-process)
|
|
136
|
-
batches[uid] = [];
|
|
136
|
+
batches[uid.toString()] = [];
|
|
137
137
|
return uid;
|
|
138
138
|
}
|
|
139
139
|
// Ported
|
|
@@ -264,15 +264,18 @@ export const insert = async (meta, collection, value, options, extras) => {
|
|
|
264
264
|
validate_meta(meta, 'in Dip.insert()');
|
|
265
265
|
const topology_meta = validate_topology(meta, collection, 'in Dip.insert()'); // TODO: loop for arrays
|
|
266
266
|
let arg = {
|
|
267
|
-
collection: collection,
|
|
267
|
+
collection: typeof collection === "string" ? [collection] : collection,
|
|
268
268
|
insert: value,
|
|
269
269
|
options: options ? options : {},
|
|
270
270
|
syncMode: meta.syncMode,
|
|
271
|
+
db: extras?.db ?? meta?.db ?? db,
|
|
271
272
|
...(meta.DIP_URL ? { DIP_URL: meta.DIP_URL } : {}),
|
|
272
273
|
...(meta.DIP_DB ? { db: meta.DIP_DB } : {}),
|
|
274
|
+
suffix: [],
|
|
275
|
+
executor: "native",
|
|
273
276
|
};
|
|
274
277
|
arg = Object.assign(arg, extras);
|
|
275
|
-
let suffix_from_policy = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.insert, true, arg);
|
|
278
|
+
let suffix_from_policy = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.insert, true, { db: arg.db, collection: arg.collection });
|
|
276
279
|
let suffix_from_meta = generate_suffix(meta);
|
|
277
280
|
arg.suffix = (suffix_from_meta.length ? suffix_from_meta : [""]).flatMap(meta => (suffix_from_policy.length ? suffix_from_policy : [""]).map(policy => meta + policy));
|
|
278
281
|
arg.executor = meta.executor ?? "native";
|
|
@@ -302,15 +305,18 @@ export const query = async (meta, collection, query, options, extras) => {
|
|
|
302
305
|
validate_meta(meta, 'in Dip.query()');
|
|
303
306
|
const topology_meta = validate_topology(meta, collection, 'in Dip.query()'); // TODO: loop for arrays
|
|
304
307
|
let arg = {
|
|
305
|
-
collection: collection,
|
|
308
|
+
collection: typeof collection === "string" ? [collection] : collection,
|
|
306
309
|
query: query,
|
|
307
310
|
options: options ? options : {},
|
|
308
311
|
syncMode: meta.syncMode,
|
|
312
|
+
db: extras?.db ?? meta?.db ?? db,
|
|
309
313
|
...(meta.DIP_URL ? { DIP_URL: meta.DIP_URL } : {}),
|
|
310
314
|
...(meta.DIP_DB ? { db: meta.DIP_DB } : {}),
|
|
315
|
+
suffix: [],
|
|
316
|
+
executor: "native",
|
|
311
317
|
};
|
|
312
318
|
arg = Object.assign(arg, extras);
|
|
313
|
-
let suffix_from_policy = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.query, false, arg);
|
|
319
|
+
let suffix_from_policy = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.query, false, { db: arg.db, collection: arg.collection });
|
|
314
320
|
let suffix_from_meta = generate_suffix(meta);
|
|
315
321
|
arg.suffix = (suffix_from_meta.length ? suffix_from_meta : [""]).flatMap(meta => (suffix_from_policy.length ? suffix_from_policy : [""]).map(policy => meta + policy));
|
|
316
322
|
arg.executor = meta.executor ?? "native";
|
|
@@ -348,16 +354,19 @@ export const update = async (meta, collection, query, update, options, extras) =
|
|
|
348
354
|
validate_meta(meta, 'in Dip.update()');
|
|
349
355
|
const topology_meta = validate_topology(meta, collection, 'in Dip.update()'); // TODO: loop for arrays
|
|
350
356
|
let arg = {
|
|
351
|
-
collection: collection,
|
|
357
|
+
collection: typeof collection === "string" ? [collection] : collection,
|
|
352
358
|
query: query,
|
|
353
359
|
update: update,
|
|
354
360
|
options: options ? options : {},
|
|
355
361
|
syncMode: meta.syncMode,
|
|
362
|
+
db: extras?.db ?? meta?.db ?? db,
|
|
356
363
|
...(meta.DIP_URL ? { DIP_URL: meta.DIP_URL } : {}),
|
|
357
364
|
...(meta.DIP_DB ? { db: meta.DIP_DB } : {}),
|
|
365
|
+
suffix: [],
|
|
366
|
+
executor: "native",
|
|
358
367
|
};
|
|
359
368
|
arg = Object.assign(arg, extras);
|
|
360
|
-
let suffix_from_policy = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.query, false, arg);
|
|
369
|
+
let suffix_from_policy = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.query, false, { db: arg.db, collection: arg.collection });
|
|
361
370
|
let suffix_from_meta = generate_suffix(meta);
|
|
362
371
|
arg.suffix = (suffix_from_meta.length ? suffix_from_meta : [""]).flatMap(meta => (suffix_from_policy.length ? suffix_from_policy : [""]).map(policy => meta + policy));
|
|
363
372
|
arg.executor = meta.executor ?? "native";
|
|
@@ -385,16 +394,19 @@ export const remove = async (meta, collection, query, options, extras) => {
|
|
|
385
394
|
validate_meta(meta, 'in Dip.remove()');
|
|
386
395
|
const topology_meta = validate_topology(meta, collection, 'in Dip.remove()'); // TODO: loop for arrays
|
|
387
396
|
let arg = {
|
|
388
|
-
collection: collection,
|
|
397
|
+
collection: typeof collection === "string" ? [collection] : collection,
|
|
389
398
|
query: query,
|
|
390
399
|
options: options ? options : {},
|
|
391
400
|
delete: true,
|
|
392
401
|
syncMode: meta.syncMode,
|
|
402
|
+
db: extras?.db ?? meta?.db ?? db,
|
|
393
403
|
...(meta.DIP_URL ? { DIP_URL: meta.DIP_URL } : {}),
|
|
394
404
|
...(meta.DIP_DB ? { db: meta.DIP_DB } : {}),
|
|
405
|
+
suffix: [],
|
|
406
|
+
executor: "native",
|
|
395
407
|
};
|
|
396
408
|
arg = Object.assign(arg, extras);
|
|
397
|
-
let suffix_from_policy = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.query, false, arg);
|
|
409
|
+
let suffix_from_policy = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.query, false, { db: arg.db, collection: arg.collection });
|
|
398
410
|
let suffix_from_meta = generate_suffix(meta);
|
|
399
411
|
arg.suffix = (suffix_from_meta.length ? suffix_from_meta : [""]).flatMap(meta => (suffix_from_policy.length ? suffix_from_policy : [""]).map(policy => meta + policy));
|
|
400
412
|
arg.executor = meta.executor ?? "native";
|
|
@@ -541,13 +553,13 @@ function convert_to_array_buffer(value) {
|
|
|
541
553
|
return Essentials.stringToUtf8ArrayBuffer(JSON.stringify(value));
|
|
542
554
|
}
|
|
543
555
|
export function newRaw() {
|
|
544
|
-
let obj = { buffers: [], size: 0, hashes:
|
|
545
|
-
obj.add = value => {
|
|
556
|
+
let obj = { buffers: [], size: 0, hashes: new Map() };
|
|
557
|
+
obj.add = (value) => {
|
|
546
558
|
if (value === undefined)
|
|
547
559
|
return undefined;
|
|
548
560
|
const buf = (value instanceof ArrayBuffer) ? value : convert_to_array_buffer(value);
|
|
549
561
|
const hash = Essentials.defaultHash(buf);
|
|
550
|
-
for (
|
|
562
|
+
for (const item of obj.hashes.get(hash) ?? []) { // Conflict. Fallback to verify
|
|
551
563
|
if (Essentials.memcmpEqual(buf, item.buf))
|
|
552
564
|
return item.offset;
|
|
553
565
|
}
|
|
@@ -555,8 +567,9 @@ export function newRaw() {
|
|
|
555
567
|
obj.buffers.push({ buf, vlq });
|
|
556
568
|
let offset = obj.size;
|
|
557
569
|
obj.size += vlq.byteLength + buf.byteLength;
|
|
558
|
-
obj.hashes
|
|
559
|
-
|
|
570
|
+
if (!obj.hashes.get(hash))
|
|
571
|
+
obj.hashes.set(hash, []);
|
|
572
|
+
obj.hashes.get(hash).push({ offset, buf });
|
|
560
573
|
return offset;
|
|
561
574
|
};
|
|
562
575
|
return obj;
|
package/dist/libs/utils.js
CHANGED
|
@@ -331,13 +331,22 @@ export function parseMob(mob, code) {
|
|
|
331
331
|
return mob;
|
|
332
332
|
mob = mob.trim().replace(/(,| |-|\+)/g, ''); // remove comma, space, hyphen and plus
|
|
333
333
|
mob = mob.startsWith("0") ? mob.replace(/^0*/, '') : mob; // remove starting n zeroes
|
|
334
|
-
|
|
334
|
+
const entry = Mobile.codes[code];
|
|
335
|
+
if (!entry)
|
|
336
|
+
throw new Error('Error: Invalid code in Utils.parseMob()');
|
|
337
|
+
let prefix = entry.phone;
|
|
335
338
|
if (!mob.startsWith(prefix)) {
|
|
336
339
|
mob = `${prefix}${mob}`;
|
|
337
340
|
}
|
|
338
341
|
else { // mob.startsWith(prefix)
|
|
339
|
-
|
|
340
|
-
|
|
342
|
+
const phoneLengthItems = typeof entry.phoneLength === "number" ? [entry.phoneLength] : entry.phoneLength;
|
|
343
|
+
for (const phoneLength of phoneLengthItems) {
|
|
344
|
+
if (mob.length < phoneLength + prefix.length) // mob just happens to start with prefix
|
|
345
|
+
return `${prefix}${mob}`;
|
|
346
|
+
else if (mob.length === phoneLength + prefix.length)
|
|
347
|
+
return mob;
|
|
348
|
+
}
|
|
349
|
+
throw new Error('Error: Mismatched phoneLength in Utils.parseMob()');
|
|
341
350
|
}
|
|
342
351
|
return mob;
|
|
343
352
|
}
|
package/libs/cpp.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import crypto from 'node:crypto';
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
function readContent(buffer, valueRef, bytesRef) {
|
|
5
|
+
function readContent(buffer: Uint8Array, valueRef, bytesRef) { // Buffer is assignable to Uint8Array so no need for Buffer | Uint8Array
|
|
6
6
|
const MASK = 0x7F;
|
|
7
7
|
const MSB = 0x80;
|
|
8
8
|
const MAX_VLQ_BYTES = 10;
|
|
@@ -135,7 +135,7 @@ export function matchPolicy(query, when, g_obj?: Record<string, any>) {
|
|
|
135
135
|
|
|
136
136
|
|
|
137
137
|
|
|
138
|
-
type Arg = { db
|
|
138
|
+
type Arg = { db: string; collection: string[]; }
|
|
139
139
|
|
|
140
140
|
export async function applySuffixPolicy(COLLECTIONS_JSON, collections, query, insertMode?: boolean, arg?: Arg) {
|
|
141
141
|
collections = Array.isArray(collections) ? collections : [collections]
|
|
@@ -2,7 +2,7 @@ import {entries, reduceRanges, extractBounds, getNormalizedBounds, type Bounds}
|
|
|
2
2
|
import {formatDate, fillDates} from './date.ts'
|
|
3
3
|
import * as Dip from '../../elabase.ts'
|
|
4
4
|
|
|
5
|
-
type Arg = { db
|
|
5
|
+
type Arg = { db: string; collection: string[]; }
|
|
6
6
|
|
|
7
7
|
const EXPLICIT_SUFFIX_POLICY_TYPES = new Set([ "string", "date", "number" ]);
|
|
8
8
|
export async function suffix(doc, policies, insertMode?: boolean, arg?: Arg) {
|
|
@@ -87,8 +87,11 @@ export async function suffix(doc, policies, insertMode?: boolean, arg?: Arg) {
|
|
|
87
87
|
if (!keys.length && !insertMode) {
|
|
88
88
|
ls = true
|
|
89
89
|
for (const suffix of suffixes) {
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
for (const collection of arg.collection) {
|
|
91
|
+
const dirs = (await Dip.operation("ls", { db: arg.db, collection: collection, suffix })).filter(dir => !dir.startsWith('chunk-'))
|
|
92
|
+
suffixAssociatedKeys[suffix] = suffixAssociatedKeys[suffix] ?? []
|
|
93
|
+
suffixAssociatedKeys[suffix].push(...dirs)
|
|
94
|
+
}
|
|
92
95
|
}
|
|
93
96
|
console.warn(`Warn: Query omitted suffix policy key ${policy.key} in Dip. Falling back to Dip.operation(ls), incurring additional performance and network round-trip overhead.`)
|
|
94
97
|
}
|
|
@@ -3,6 +3,7 @@ import assert from "node:assert/strict";
|
|
|
3
3
|
|
|
4
4
|
import { suffix } from "../suffix.ts";
|
|
5
5
|
|
|
6
|
+
const arg = {db: "test-db", collection: ["test-collection"]}
|
|
6
7
|
|
|
7
8
|
describe("suffix()", () => {
|
|
8
9
|
|
|
@@ -13,7 +14,7 @@ describe("suffix()", () => {
|
|
|
13
14
|
{},
|
|
14
15
|
[],
|
|
15
16
|
true,
|
|
16
|
-
|
|
17
|
+
arg
|
|
17
18
|
);
|
|
18
19
|
|
|
19
20
|
assert.deepEqual(result, []);
|
|
@@ -33,7 +34,7 @@ describe("suffix()", () => {
|
|
|
33
34
|
}
|
|
34
35
|
],
|
|
35
36
|
true,
|
|
36
|
-
|
|
37
|
+
arg
|
|
37
38
|
);
|
|
38
39
|
|
|
39
40
|
|
|
@@ -59,7 +60,7 @@ describe("suffix()", () => {
|
|
|
59
60
|
}
|
|
60
61
|
],
|
|
61
62
|
true,
|
|
62
|
-
|
|
63
|
+
arg
|
|
63
64
|
);
|
|
64
65
|
|
|
65
66
|
|
|
@@ -87,7 +88,7 @@ describe("suffix()", () => {
|
|
|
87
88
|
}
|
|
88
89
|
],
|
|
89
90
|
true,
|
|
90
|
-
|
|
91
|
+
arg
|
|
91
92
|
)
|
|
92
93
|
);
|
|
93
94
|
|
|
@@ -108,7 +109,7 @@ describe("suffix()", () => {
|
|
|
108
109
|
}
|
|
109
110
|
],
|
|
110
111
|
true,
|
|
111
|
-
|
|
112
|
+
arg
|
|
112
113
|
);
|
|
113
114
|
|
|
114
115
|
|
|
@@ -141,7 +142,7 @@ describe("suffix()", () => {
|
|
|
141
142
|
}
|
|
142
143
|
],
|
|
143
144
|
true,
|
|
144
|
-
|
|
145
|
+
arg
|
|
145
146
|
);
|
|
146
147
|
|
|
147
148
|
|
|
@@ -173,7 +174,7 @@ describe("suffix()", () => {
|
|
|
173
174
|
}
|
|
174
175
|
],
|
|
175
176
|
true,
|
|
176
|
-
|
|
177
|
+
arg
|
|
177
178
|
);
|
|
178
179
|
|
|
179
180
|
|
|
@@ -203,7 +204,7 @@ describe("suffix()", () => {
|
|
|
203
204
|
}
|
|
204
205
|
],
|
|
205
206
|
true,
|
|
206
|
-
|
|
207
|
+
arg
|
|
207
208
|
);
|
|
208
209
|
|
|
209
210
|
|
|
@@ -231,7 +232,7 @@ describe("suffix()", () => {
|
|
|
231
232
|
}
|
|
232
233
|
],
|
|
233
234
|
true,
|
|
234
|
-
|
|
235
|
+
arg
|
|
235
236
|
)
|
|
236
237
|
);
|
|
237
238
|
|
|
@@ -251,7 +252,7 @@ describe("suffix()", () => {
|
|
|
251
252
|
}
|
|
252
253
|
],
|
|
253
254
|
true,
|
|
254
|
-
|
|
255
|
+
arg
|
|
255
256
|
);
|
|
256
257
|
|
|
257
258
|
|
|
@@ -281,7 +282,7 @@ describe("suffix()", () => {
|
|
|
281
282
|
}
|
|
282
283
|
],
|
|
283
284
|
true,
|
|
284
|
-
|
|
285
|
+
arg
|
|
285
286
|
)
|
|
286
287
|
);
|
|
287
288
|
|
|
@@ -304,7 +305,7 @@ describe("suffix()", () => {
|
|
|
304
305
|
}
|
|
305
306
|
],
|
|
306
307
|
true,
|
|
307
|
-
|
|
308
|
+
arg
|
|
308
309
|
)
|
|
309
310
|
);
|
|
310
311
|
|
|
@@ -325,7 +326,7 @@ describe("suffix()", () => {
|
|
|
325
326
|
}
|
|
326
327
|
],
|
|
327
328
|
true,
|
|
328
|
-
|
|
329
|
+
arg
|
|
329
330
|
);
|
|
330
331
|
|
|
331
332
|
|
|
@@ -357,7 +358,7 @@ describe("suffix()", () => {
|
|
|
357
358
|
}
|
|
358
359
|
],
|
|
359
360
|
true,
|
|
360
|
-
|
|
361
|
+
arg
|
|
361
362
|
);
|
|
362
363
|
|
|
363
364
|
|
|
@@ -386,7 +387,7 @@ describe("suffix()", () => {
|
|
|
386
387
|
}
|
|
387
388
|
],
|
|
388
389
|
true,
|
|
389
|
-
|
|
390
|
+
arg
|
|
390
391
|
)
|
|
391
392
|
);
|
|
392
393
|
|
|
@@ -410,7 +411,7 @@ describe("suffix()", () => {
|
|
|
410
411
|
}
|
|
411
412
|
],
|
|
412
413
|
true,
|
|
413
|
-
|
|
414
|
+
arg
|
|
414
415
|
);
|
|
415
416
|
|
|
416
417
|
|
|
@@ -445,7 +446,7 @@ describe("suffix()", () => {
|
|
|
445
446
|
}
|
|
446
447
|
],
|
|
447
448
|
true,
|
|
448
|
-
|
|
449
|
+
arg
|
|
449
450
|
);
|
|
450
451
|
assert.deepEqual(result.sort(), ["/1", "/2"]);
|
|
451
452
|
});
|
|
@@ -466,7 +467,7 @@ describe("suffix()", () => {
|
|
|
466
467
|
}
|
|
467
468
|
],
|
|
468
469
|
true,
|
|
469
|
-
|
|
470
|
+
arg
|
|
470
471
|
);
|
|
471
472
|
assert.deepEqual(result.sort(), ["/5", "/6", "/7"]);
|
|
472
473
|
});
|
|
@@ -486,7 +487,7 @@ describe("suffix()", () => {
|
|
|
486
487
|
}
|
|
487
488
|
],
|
|
488
489
|
true,
|
|
489
|
-
|
|
490
|
+
arg
|
|
490
491
|
);
|
|
491
492
|
assert.deepEqual(result.sort(), ["/5", "/6", "/7"]);
|
|
492
493
|
});
|
|
@@ -507,7 +508,7 @@ describe("suffix()", () => {
|
|
|
507
508
|
}
|
|
508
509
|
],
|
|
509
510
|
true,
|
|
510
|
-
|
|
511
|
+
arg
|
|
511
512
|
);
|
|
512
513
|
|
|
513
514
|
|
|
@@ -543,7 +544,7 @@ describe("suffix()", () => {
|
|
|
543
544
|
}
|
|
544
545
|
],
|
|
545
546
|
true,
|
|
546
|
-
|
|
547
|
+
arg
|
|
547
548
|
)
|
|
548
549
|
);
|
|
549
550
|
|
|
@@ -570,7 +571,7 @@ describe("suffix()", () => {
|
|
|
570
571
|
}
|
|
571
572
|
],
|
|
572
573
|
true,
|
|
573
|
-
|
|
574
|
+
arg
|
|
574
575
|
)
|
|
575
576
|
);
|
|
576
577
|
|
|
@@ -602,7 +603,7 @@ describe("suffix()", () => {
|
|
|
602
603
|
}
|
|
603
604
|
],
|
|
604
605
|
true,
|
|
605
|
-
|
|
606
|
+
arg
|
|
606
607
|
);
|
|
607
608
|
|
|
608
609
|
|
package/libs/elabase.ts
CHANGED
|
@@ -173,7 +173,7 @@ let batches = {}
|
|
|
173
173
|
// Ported
|
|
174
174
|
export function batchBegin() { // Manual Lifecycle. If you forget to batchSubmit() or batchAbort(), then memory leak due to dangling batch
|
|
175
175
|
const uid = ++BATCH_COUNTER; // Safe. No skew in async ++BATCH_COUNTER as single thread and ++COUNTER is atomic in event loop sense. ⚠️ Only breaks on worker_threads or cluster (multi-process)
|
|
176
|
-
batches[uid] = []
|
|
176
|
+
batches[uid.toString()] = []
|
|
177
177
|
return uid
|
|
178
178
|
}
|
|
179
179
|
// Ported
|
|
@@ -310,6 +310,30 @@ export const operation = async (name, extras) => {
|
|
|
310
310
|
return execute({ operation: name, ...extras })
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
+
type Options = {
|
|
314
|
+
upsert: boolean;
|
|
315
|
+
monotonic: boolean;
|
|
316
|
+
idempotent: boolean;
|
|
317
|
+
$orderby: any;
|
|
318
|
+
$fields: any;
|
|
319
|
+
_id?: string | number;
|
|
320
|
+
}
|
|
321
|
+
type InsertRequest = {
|
|
322
|
+
db: string;
|
|
323
|
+
executor: "native" | "raw";
|
|
324
|
+
collection: string[];
|
|
325
|
+
suffix: string | string[];
|
|
326
|
+
_id?: string | number;
|
|
327
|
+
insert: number | any;
|
|
328
|
+
options: Options;
|
|
329
|
+
syncMode: boolean;
|
|
330
|
+
DIP_URL?: string;
|
|
331
|
+
DIP_DB?: string;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
type QueryRequest = Omit<InsertRequest, "_id" | "insert"> & { query: any }
|
|
335
|
+
type UpdateRequest = Omit<InsertRequest, "insert"> & { query: any, update: any }
|
|
336
|
+
type RemoveRequest = QueryRequest & { delete: true }
|
|
313
337
|
|
|
314
338
|
// Ported
|
|
315
339
|
export const insert = async (meta, collection, value, options?: any, extras?: any) => {
|
|
@@ -322,16 +346,19 @@ export const insert = async (meta, collection, value, options?: any, extras?: an
|
|
|
322
346
|
|
|
323
347
|
const topology_meta = validate_topology(meta, collection, 'in Dip.insert()') // TODO: loop for arrays
|
|
324
348
|
|
|
325
|
-
let arg = {
|
|
326
|
-
collection: collection,
|
|
349
|
+
let arg: InsertRequest = {
|
|
350
|
+
collection: typeof collection === "string" ? [collection] : collection,
|
|
327
351
|
insert: value,
|
|
328
352
|
options: options ? options : {},
|
|
329
353
|
syncMode: meta.syncMode,
|
|
354
|
+
db: extras?.db ?? meta?.db ?? db,
|
|
330
355
|
...(meta.DIP_URL ? {DIP_URL: meta.DIP_URL} : {}),
|
|
331
356
|
...(meta.DIP_DB ? {db: meta.DIP_DB} : {}),
|
|
357
|
+
suffix: [],
|
|
358
|
+
executor: "native",
|
|
332
359
|
}
|
|
333
360
|
arg = Object.assign(arg, extras)
|
|
334
|
-
let suffix_from_policy = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.insert, true, arg)
|
|
361
|
+
let suffix_from_policy = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.insert, true, {db: arg.db, collection: arg.collection})
|
|
335
362
|
let suffix_from_meta = generate_suffix(meta)
|
|
336
363
|
arg.suffix = (suffix_from_meta.length ? suffix_from_meta : [""]).flatMap(meta => (suffix_from_policy.length ? suffix_from_policy : [""]).map(policy => meta + policy))
|
|
337
364
|
arg.executor = meta.executor ?? "native"
|
|
@@ -366,16 +393,19 @@ export const query = async (meta, collection, query, options?: any, extras?: any
|
|
|
366
393
|
|
|
367
394
|
const topology_meta = validate_topology(meta, collection, 'in Dip.query()') // TODO: loop for arrays
|
|
368
395
|
|
|
369
|
-
let arg = {
|
|
370
|
-
collection: collection,
|
|
396
|
+
let arg: QueryRequest = {
|
|
397
|
+
collection: typeof collection === "string" ? [collection] : collection,
|
|
371
398
|
query: query,
|
|
372
399
|
options: options ? options : {},
|
|
373
400
|
syncMode: meta.syncMode,
|
|
401
|
+
db: extras?.db ?? meta?.db ?? db,
|
|
374
402
|
...(meta.DIP_URL ? {DIP_URL: meta.DIP_URL} : {}),
|
|
375
403
|
...(meta.DIP_DB ? {db: meta.DIP_DB} : {}),
|
|
404
|
+
suffix: [],
|
|
405
|
+
executor: "native",
|
|
376
406
|
}
|
|
377
407
|
arg = Object.assign(arg, extras)
|
|
378
|
-
let suffix_from_policy = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.query, false, arg)
|
|
408
|
+
let suffix_from_policy = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.query, false, {db: arg.db, collection: arg.collection})
|
|
379
409
|
let suffix_from_meta = generate_suffix(meta)
|
|
380
410
|
arg.suffix = (suffix_from_meta.length ? suffix_from_meta : [""]).flatMap(meta => (suffix_from_policy.length ? suffix_from_policy : [""]).map(policy => meta + policy))
|
|
381
411
|
arg.executor = meta.executor ?? "native"
|
|
@@ -418,17 +448,20 @@ export const update = async (meta, collection, query, update, options?: any, ext
|
|
|
418
448
|
|
|
419
449
|
const topology_meta = validate_topology(meta, collection, 'in Dip.update()') // TODO: loop for arrays
|
|
420
450
|
|
|
421
|
-
let arg = {
|
|
422
|
-
collection: collection,
|
|
451
|
+
let arg: UpdateRequest = {
|
|
452
|
+
collection: typeof collection === "string" ? [collection] : collection,
|
|
423
453
|
query: query,
|
|
424
454
|
update: update,
|
|
425
455
|
options: options ? options : {},
|
|
426
456
|
syncMode: meta.syncMode,
|
|
457
|
+
db: extras?.db ?? meta?.db ?? db,
|
|
427
458
|
...(meta.DIP_URL ? {DIP_URL: meta.DIP_URL} : {}),
|
|
428
459
|
...(meta.DIP_DB ? {db: meta.DIP_DB} : {}),
|
|
460
|
+
suffix: [],
|
|
461
|
+
executor: "native",
|
|
429
462
|
}
|
|
430
463
|
arg = Object.assign(arg, extras)
|
|
431
|
-
let suffix_from_policy = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.query, false, arg)
|
|
464
|
+
let suffix_from_policy = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.query, false, {db: arg.db, collection: arg.collection})
|
|
432
465
|
let suffix_from_meta = generate_suffix(meta)
|
|
433
466
|
arg.suffix = (suffix_from_meta.length ? suffix_from_meta : [""]).flatMap(meta => (suffix_from_policy.length ? suffix_from_policy : [""]).map(policy => meta + policy))
|
|
434
467
|
arg.executor = meta.executor ?? "native"
|
|
@@ -461,17 +494,20 @@ export const remove = async (meta, collection, query, options?: any, extras?: an
|
|
|
461
494
|
|
|
462
495
|
const topology_meta = validate_topology(meta, collection, 'in Dip.remove()') // TODO: loop for arrays
|
|
463
496
|
|
|
464
|
-
let arg = {
|
|
465
|
-
collection: collection,
|
|
497
|
+
let arg: RemoveRequest = {
|
|
498
|
+
collection: typeof collection === "string" ? [collection] : collection,
|
|
466
499
|
query: query,
|
|
467
500
|
options: options ? options : {},
|
|
468
501
|
delete: true,
|
|
469
502
|
syncMode: meta.syncMode,
|
|
503
|
+
db: extras?.db ?? meta?.db ?? db,
|
|
470
504
|
...(meta.DIP_URL ? {DIP_URL: meta.DIP_URL} : {}),
|
|
471
505
|
...(meta.DIP_DB ? {db: meta.DIP_DB} : {}),
|
|
506
|
+
suffix: [],
|
|
507
|
+
executor: "native",
|
|
472
508
|
}
|
|
473
509
|
arg = Object.assign(arg, extras)
|
|
474
|
-
let suffix_from_policy = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.query, false, arg)
|
|
510
|
+
let suffix_from_policy = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.query, false, {db: arg.db, collection: arg.collection})
|
|
475
511
|
let suffix_from_meta = generate_suffix(meta)
|
|
476
512
|
arg.suffix = (suffix_from_meta.length ? suffix_from_meta : [""]).flatMap(meta => (suffix_from_policy.length ? suffix_from_policy : [""]).map(policy => meta + policy))
|
|
477
513
|
arg.executor = meta.executor ?? "native"
|
|
@@ -657,14 +693,22 @@ function convert_to_array_buffer(value) {
|
|
|
657
693
|
}
|
|
658
694
|
|
|
659
695
|
export function newRaw() {
|
|
660
|
-
|
|
661
|
-
|
|
696
|
+
type BufferItem = {buf: ArrayBuffer | Uint8Array; vlq: Uint8Array}
|
|
697
|
+
type HashItem = {buf: ArrayBuffer | Uint8Array; offset: number}
|
|
698
|
+
type RawManager = {
|
|
699
|
+
buffers: BufferItem[];
|
|
700
|
+
size: number;
|
|
701
|
+
hashes: Map<bigint, HashItem[]>;
|
|
702
|
+
add: (value: ArrayBuffer | string | number | any) => number;
|
|
703
|
+
}
|
|
704
|
+
let obj: RawManager = {buffers: [], size: 0, hashes: new Map()} as RawManager
|
|
705
|
+
obj.add = (value: ArrayBuffer | string | number | any): number => {
|
|
662
706
|
if (value === undefined)
|
|
663
707
|
return undefined
|
|
664
708
|
const buf = (value instanceof ArrayBuffer) ? value : convert_to_array_buffer(value);
|
|
665
709
|
|
|
666
710
|
const hash = Essentials.defaultHash(buf)
|
|
667
|
-
for (
|
|
711
|
+
for (const item of obj.hashes.get(hash) ?? []) { // Conflict. Fallback to verify
|
|
668
712
|
if (Essentials.memcmpEqual(buf, item.buf))
|
|
669
713
|
return item.offset
|
|
670
714
|
}
|
|
@@ -674,8 +718,10 @@ export function newRaw() {
|
|
|
674
718
|
let offset = obj.size
|
|
675
719
|
obj.size += vlq.byteLength + buf.byteLength;
|
|
676
720
|
|
|
677
|
-
|
|
678
|
-
obj.hashes
|
|
721
|
+
|
|
722
|
+
if (!obj.hashes.get(hash))
|
|
723
|
+
obj.hashes.set(hash, [])
|
|
724
|
+
obj.hashes.get(hash).push({offset, buf})
|
|
679
725
|
|
|
680
726
|
return offset
|
|
681
727
|
}
|
|
@@ -695,11 +741,11 @@ function buildHybridRequest(request) {
|
|
|
695
741
|
if (item.update) {
|
|
696
742
|
if ( item._id === undefined && item.options?.upsert && item.query?._id !== undefined)
|
|
697
743
|
item._id = item.query._id
|
|
698
|
-
|
|
744
|
+
item.update = raw.add(item.update)
|
|
699
745
|
} else if (item.insert) {
|
|
700
746
|
if (item._id === undefined && item.insert?._id !== undefined)
|
|
701
747
|
item._id = item.insert._id
|
|
702
|
-
|
|
748
|
+
item.insert = raw.add(item.insert)
|
|
703
749
|
}
|
|
704
750
|
item._id = raw.add(item._id)
|
|
705
751
|
|
package/libs/mobilecodes.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
// Found at https://gist.github.com/ally-commits/9073ff23fc7f96fab1290fdec22775bc
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
type CountryItem = { code: string; label: string; phone: string; phoneLength: number | number[], min?: number, max?: number, suggested?: boolean }
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export const codes: Record<string, CountryItem> = {
|
|
5
8
|
AD: { code: 'AD', label: 'Andorra', phone: '376', phoneLength: 6 },
|
|
6
9
|
AE: {
|
|
7
10
|
code: 'AE',
|
package/libs/utils.ts
CHANGED
|
@@ -406,13 +406,23 @@ export function parseMob(mob: string, code: string): string {
|
|
|
406
406
|
mob = mob.trim().replace(/(,| |-|\+)/g, '') // remove comma, space, hyphen and plus
|
|
407
407
|
mob = mob.startsWith("0") ? mob.replace(/^0*/,'') : mob // remove starting n zeroes
|
|
408
408
|
|
|
409
|
-
|
|
409
|
+
const entry = Mobile.codes[code]
|
|
410
|
+
if (!entry)
|
|
411
|
+
throw new Error('Error: Invalid code in Utils.parseMob()')
|
|
412
|
+
|
|
413
|
+
let prefix = entry.phone
|
|
410
414
|
|
|
411
415
|
if (!mob.startsWith(prefix)) {
|
|
412
416
|
mob = `${prefix}${mob}`
|
|
413
417
|
} else { // mob.startsWith(prefix)
|
|
414
|
-
|
|
415
|
-
|
|
418
|
+
const phoneLengthItems = typeof entry.phoneLength === "number" ? [entry.phoneLength] : entry.phoneLength
|
|
419
|
+
for (const phoneLength of phoneLengthItems) {
|
|
420
|
+
if (mob.length < phoneLength + prefix.length) // mob just happens to start with prefix
|
|
421
|
+
return `${prefix}${mob}`
|
|
422
|
+
else if (mob.length === phoneLength + prefix.length)
|
|
423
|
+
return mob
|
|
424
|
+
}
|
|
425
|
+
throw new Error('Error: Mismatched phoneLength in Utils.parseMob()')
|
|
416
426
|
}
|
|
417
427
|
return mob
|
|
418
428
|
}
|
package/package.json
CHANGED