@naturalcycles/js-lib 15.93.4 → 15.94.0
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/decorators/createPromiseDecorator.d.ts +2 -1
- package/dist/decorators/createPromiseDecorator.js +8 -7
- package/dist/object/keySortedMap.d.ts +1 -1
- package/dist/object/keySortedMap.js +12 -6
- package/dist/object/lazyKeySortedMap.d.ts +1 -1
- package/dist/object/lazyKeySortedMap.js +14 -13
- package/package.json +3 -3
- package/src/decorators/createPromiseDecorator.ts +10 -11
- package/src/object/keySortedMap.ts +11 -6
- package/src/object/lazyKeySortedMap.ts +14 -13
|
@@ -19,10 +19,11 @@ export interface PromiseDecoratorCfg<RES = any, PARAMS = any> {
|
|
|
19
19
|
* which will most likely be "unhandledRejection" (unless there's another higher-level @Decorator that will catch it).
|
|
20
20
|
* If `catchFn` is present - it's responsible for handling or re-throwing the error.
|
|
21
21
|
* Whatever `catchFn` returns - passed to the original output.
|
|
22
|
+
* If Promise is returned - it will be awaited.
|
|
22
23
|
*/
|
|
23
24
|
catchFn?: (r: PromiseDecoratorResp<PARAMS> & {
|
|
24
25
|
err: any;
|
|
25
|
-
}) => RES
|
|
26
|
+
}) => RES | Promise<RES>;
|
|
26
27
|
/**
|
|
27
28
|
* Fires AFTER thenFn / catchFn, like a usual Promise.finally().
|
|
28
29
|
* Doesn't have access to neither res nor err (same as Promise.finally).
|
|
@@ -63,17 +63,18 @@ export function _createPromiseDecorator(cfg, decoratorParams = {}) {
|
|
|
63
63
|
decoratorName,
|
|
64
64
|
started,
|
|
65
65
|
};
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
if (!cfg.catchFn) {
|
|
67
|
+
cfg.finallyFn?.(resp);
|
|
68
|
+
throw err; // rethrow
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
return await cfg.catchFn({
|
|
69
72
|
...resp,
|
|
70
73
|
err,
|
|
71
74
|
});
|
|
72
|
-
handled = true;
|
|
73
75
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
throw err; // rethrow
|
|
76
|
+
finally {
|
|
77
|
+
cfg.finallyFn?.(resp);
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
80
|
};
|
|
@@ -32,12 +32,17 @@ export class KeySortedMap {
|
|
|
32
32
|
* Allows to set multiple key-value pairs at once.
|
|
33
33
|
*/
|
|
34
34
|
setMany(obj) {
|
|
35
|
+
let hasNewKeys = false;
|
|
35
36
|
for (const [k, v] of Object.entries(obj)) {
|
|
37
|
+
if (!this.map.has(k)) {
|
|
38
|
+
this.#sortedKeys.push(k);
|
|
39
|
+
hasNewKeys = true;
|
|
40
|
+
}
|
|
36
41
|
this.map.set(k, v);
|
|
37
|
-
this.#sortedKeys.push(k);
|
|
38
42
|
}
|
|
39
43
|
// Resort all at once
|
|
40
|
-
|
|
44
|
+
if (hasNewKeys)
|
|
45
|
+
this.sortKeys();
|
|
41
46
|
return this;
|
|
42
47
|
}
|
|
43
48
|
/**
|
|
@@ -101,8 +106,7 @@ export class KeySortedMap {
|
|
|
101
106
|
return this.entries();
|
|
102
107
|
}
|
|
103
108
|
toString() {
|
|
104
|
-
|
|
105
|
-
return 'abc';
|
|
109
|
+
return `KeySortedMap(${this.size}) ${JSON.stringify(this.toObject())}`;
|
|
106
110
|
}
|
|
107
111
|
[Symbol.toStringTag] = 'KeySortedMap';
|
|
108
112
|
/**
|
|
@@ -179,15 +183,17 @@ export class KeySortedMap {
|
|
|
179
183
|
return new Map(this.entries());
|
|
180
184
|
}
|
|
181
185
|
/**
|
|
182
|
-
* lowerBound: first index i s.t. keys[i] >= target
|
|
186
|
+
* lowerBound: first index i s.t. keys[i] >= target, in comparator order
|
|
183
187
|
*/
|
|
184
188
|
lowerBound(target) {
|
|
189
|
+
const comparator = this.#comparator;
|
|
185
190
|
let lo = 0;
|
|
186
191
|
let hi = this.#sortedKeys.length;
|
|
187
192
|
while (lo < hi) {
|
|
188
193
|
// oxlint-disable-next-line no-bitwise
|
|
189
194
|
const mid = (lo + hi) >>> 1;
|
|
190
|
-
|
|
195
|
+
const key = this.#sortedKeys[mid];
|
|
196
|
+
if (comparator ? comparator(key, target) < 0 : key < target) {
|
|
191
197
|
lo = mid + 1;
|
|
192
198
|
}
|
|
193
199
|
else {
|
|
@@ -50,7 +50,7 @@ export declare class LazyKeySortedMap<K, V> {
|
|
|
50
50
|
values(): MapIterator<V>;
|
|
51
51
|
entries(): MapIterator<[K, V]>;
|
|
52
52
|
[Symbol.iterator](): MapIterator<[K, V]>;
|
|
53
|
-
readonly [Symbol.toStringTag] = "
|
|
53
|
+
readonly [Symbol.toStringTag] = "LazyKeySortedMap";
|
|
54
54
|
/**
|
|
55
55
|
* Zero-allocation callbacks over sorted data (faster than spreading to arrays).
|
|
56
56
|
*/
|
|
@@ -34,10 +34,12 @@ export class LazyKeySortedMap {
|
|
|
34
34
|
*/
|
|
35
35
|
setMany(obj) {
|
|
36
36
|
for (const [k, v] of Object.entries(obj)) {
|
|
37
|
+
if (!this.map.has(k)) {
|
|
38
|
+
this.maybeSortedKeys.push(k);
|
|
39
|
+
this.keysAreSorted = false;
|
|
40
|
+
}
|
|
37
41
|
this.map.set(k, v);
|
|
38
|
-
this.maybeSortedKeys.push(k);
|
|
39
42
|
}
|
|
40
|
-
this.keysAreSorted = false;
|
|
41
43
|
return this;
|
|
42
44
|
}
|
|
43
45
|
/**
|
|
@@ -86,7 +88,7 @@ export class LazyKeySortedMap {
|
|
|
86
88
|
[Symbol.iterator]() {
|
|
87
89
|
return this.entries();
|
|
88
90
|
}
|
|
89
|
-
[Symbol.toStringTag] = '
|
|
91
|
+
[Symbol.toStringTag] = 'LazyKeySortedMap';
|
|
90
92
|
/**
|
|
91
93
|
* Zero-allocation callbacks over sorted data (faster than spreading to arrays).
|
|
92
94
|
*/
|
|
@@ -110,9 +112,9 @@ export class LazyKeySortedMap {
|
|
|
110
112
|
return keys[keys.length - 1];
|
|
111
113
|
}
|
|
112
114
|
lastKey() {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return
|
|
115
|
+
_assert(this.maybeSortedKeys.length, 'Map.lastKey called on empty map');
|
|
116
|
+
const keys = this.getSortedKeys();
|
|
117
|
+
return keys[keys.length - 1];
|
|
116
118
|
}
|
|
117
119
|
firstValueOrUndefined() {
|
|
118
120
|
if (!this.maybeSortedKeys.length)
|
|
@@ -120,9 +122,8 @@ export class LazyKeySortedMap {
|
|
|
120
122
|
return this.map.get(this.getSortedKeys()[0]);
|
|
121
123
|
}
|
|
122
124
|
firstValue() {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
return v;
|
|
125
|
+
_assert(this.maybeSortedKeys.length, 'Map.firstValue called on empty map');
|
|
126
|
+
return this.map.get(this.getSortedKeys()[0]);
|
|
126
127
|
}
|
|
127
128
|
lastValueOrUndefined() {
|
|
128
129
|
if (!this.maybeSortedKeys.length)
|
|
@@ -131,9 +132,9 @@ export class LazyKeySortedMap {
|
|
|
131
132
|
return this.map.get(keys[keys.length - 1]);
|
|
132
133
|
}
|
|
133
134
|
lastValue() {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
return
|
|
135
|
+
_assert(this.maybeSortedKeys.length, 'Map.lastValue called on empty map');
|
|
136
|
+
const keys = this.getSortedKeys();
|
|
137
|
+
return this.map.get(keys[keys.length - 1]);
|
|
137
138
|
}
|
|
138
139
|
firstEntryOrUndefined() {
|
|
139
140
|
if (!this.maybeSortedKeys.length)
|
|
@@ -154,7 +155,7 @@ export class LazyKeySortedMap {
|
|
|
154
155
|
return [k, this.map.get(k)];
|
|
155
156
|
}
|
|
156
157
|
lastEntry() {
|
|
157
|
-
const e = this.
|
|
158
|
+
const e = this.lastEntryOrUndefined();
|
|
158
159
|
_assert(e, 'Map.lastEntry called on empty map');
|
|
159
160
|
return e;
|
|
160
161
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/js-lib",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "15.
|
|
4
|
+
"version": "15.94.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"tslib": "^2"
|
|
7
7
|
},
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@naturalcycles/dev-lib": "
|
|
17
|
+
"@naturalcycles/dev-lib": "20.66.0",
|
|
18
18
|
"@types/node": "^26",
|
|
19
19
|
"@types/semver": "^7",
|
|
20
20
|
"typescript": "^7",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"types": "dist/index.d.ts",
|
|
64
64
|
"sideEffects": false,
|
|
65
65
|
"engines": {
|
|
66
|
-
"node": ">=
|
|
66
|
+
"node": ">=26.12.1"
|
|
67
67
|
},
|
|
68
68
|
"publishConfig": {
|
|
69
69
|
"provenance": true,
|
|
@@ -22,8 +22,9 @@ export interface PromiseDecoratorCfg<RES = any, PARAMS = any> {
|
|
|
22
22
|
* which will most likely be "unhandledRejection" (unless there's another higher-level @Decorator that will catch it).
|
|
23
23
|
* If `catchFn` is present - it's responsible for handling or re-throwing the error.
|
|
24
24
|
* Whatever `catchFn` returns - passed to the original output.
|
|
25
|
+
* If Promise is returned - it will be awaited.
|
|
25
26
|
*/
|
|
26
|
-
catchFn?: (r: PromiseDecoratorResp<PARAMS> & { err: any }) => RES
|
|
27
|
+
catchFn?: (r: PromiseDecoratorResp<PARAMS> & { err: any }) => RES | Promise<RES>
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Fires AFTER thenFn / catchFn, like a usual Promise.finally().
|
|
@@ -122,20 +123,18 @@ export function _createPromiseDecorator<RES = any, PARAMS = any>(
|
|
|
122
123
|
started,
|
|
123
124
|
}
|
|
124
125
|
|
|
125
|
-
|
|
126
|
+
if (!cfg.catchFn) {
|
|
127
|
+
cfg.finallyFn?.(resp)
|
|
128
|
+
throw err // rethrow
|
|
129
|
+
}
|
|
126
130
|
|
|
127
|
-
|
|
128
|
-
cfg.catchFn({
|
|
131
|
+
try {
|
|
132
|
+
return await cfg.catchFn({
|
|
129
133
|
...resp,
|
|
130
134
|
err,
|
|
131
135
|
})
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
cfg.finallyFn?.(resp)
|
|
136
|
-
|
|
137
|
-
if (!handled) {
|
|
138
|
-
throw err // rethrow
|
|
136
|
+
} finally {
|
|
137
|
+
cfg.finallyFn?.(resp)
|
|
139
138
|
}
|
|
140
139
|
}
|
|
141
140
|
}
|
|
@@ -66,12 +66,16 @@ export class KeySortedMap<K, V> {
|
|
|
66
66
|
* Allows to set multiple key-value pairs at once.
|
|
67
67
|
*/
|
|
68
68
|
setMany(obj: Record<any, V>): this {
|
|
69
|
+
let hasNewKeys = false
|
|
69
70
|
for (const [k, v] of Object.entries(obj)) {
|
|
71
|
+
if (!this.map.has(k as K)) {
|
|
72
|
+
this.#sortedKeys.push(k as K)
|
|
73
|
+
hasNewKeys = true
|
|
74
|
+
}
|
|
70
75
|
this.map.set(k as K, v)
|
|
71
|
-
this.#sortedKeys.push(k as K)
|
|
72
76
|
}
|
|
73
77
|
// Resort all at once
|
|
74
|
-
this.sortKeys()
|
|
78
|
+
if (hasNewKeys) this.sortKeys()
|
|
75
79
|
return this
|
|
76
80
|
}
|
|
77
81
|
|
|
@@ -139,8 +143,7 @@ export class KeySortedMap<K, V> {
|
|
|
139
143
|
}
|
|
140
144
|
|
|
141
145
|
toString(): string {
|
|
142
|
-
|
|
143
|
-
return 'abc'
|
|
146
|
+
return `KeySortedMap(${this.size}) ${JSON.stringify(this.toObject())}`
|
|
144
147
|
}
|
|
145
148
|
|
|
146
149
|
readonly [Symbol.toStringTag] = 'KeySortedMap'
|
|
@@ -233,15 +236,17 @@ export class KeySortedMap<K, V> {
|
|
|
233
236
|
}
|
|
234
237
|
|
|
235
238
|
/**
|
|
236
|
-
* lowerBound: first index i s.t. keys[i] >= target
|
|
239
|
+
* lowerBound: first index i s.t. keys[i] >= target, in comparator order
|
|
237
240
|
*/
|
|
238
241
|
private lowerBound(target: K): number {
|
|
242
|
+
const comparator = this.#comparator
|
|
239
243
|
let lo = 0
|
|
240
244
|
let hi = this.#sortedKeys.length
|
|
241
245
|
while (lo < hi) {
|
|
242
246
|
// oxlint-disable-next-line no-bitwise
|
|
243
247
|
const mid = (lo + hi) >>> 1
|
|
244
|
-
|
|
248
|
+
const key = this.#sortedKeys[mid]!
|
|
249
|
+
if (comparator ? comparator(key, target) < 0 : key < target) {
|
|
245
250
|
lo = mid + 1
|
|
246
251
|
} else {
|
|
247
252
|
hi = mid
|
|
@@ -63,10 +63,12 @@ export class LazyKeySortedMap<K, V> {
|
|
|
63
63
|
*/
|
|
64
64
|
setMany(obj: Record<any, V>): this {
|
|
65
65
|
for (const [k, v] of Object.entries(obj)) {
|
|
66
|
+
if (!this.map.has(k as K)) {
|
|
67
|
+
this.maybeSortedKeys.push(k as K)
|
|
68
|
+
this.keysAreSorted = false
|
|
69
|
+
}
|
|
66
70
|
this.map.set(k as K, v)
|
|
67
|
-
this.maybeSortedKeys.push(k as K)
|
|
68
71
|
}
|
|
69
|
-
this.keysAreSorted = false
|
|
70
72
|
return this
|
|
71
73
|
}
|
|
72
74
|
|
|
@@ -120,7 +122,7 @@ export class LazyKeySortedMap<K, V> {
|
|
|
120
122
|
return this.entries()
|
|
121
123
|
}
|
|
122
124
|
|
|
123
|
-
readonly [Symbol.toStringTag] = '
|
|
125
|
+
readonly [Symbol.toStringTag] = 'LazyKeySortedMap'
|
|
124
126
|
|
|
125
127
|
/**
|
|
126
128
|
* Zero-allocation callbacks over sorted data (faster than spreading to arrays).
|
|
@@ -148,9 +150,9 @@ export class LazyKeySortedMap<K, V> {
|
|
|
148
150
|
}
|
|
149
151
|
|
|
150
152
|
lastKey(): K {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
return
|
|
153
|
+
_assert(this.maybeSortedKeys.length, 'Map.lastKey called on empty map')
|
|
154
|
+
const keys = this.getSortedKeys()
|
|
155
|
+
return keys[keys.length - 1]!
|
|
154
156
|
}
|
|
155
157
|
|
|
156
158
|
firstValueOrUndefined(): V | undefined {
|
|
@@ -159,9 +161,8 @@ export class LazyKeySortedMap<K, V> {
|
|
|
159
161
|
}
|
|
160
162
|
|
|
161
163
|
firstValue(): V {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return v
|
|
164
|
+
_assert(this.maybeSortedKeys.length, 'Map.firstValue called on empty map')
|
|
165
|
+
return this.map.get(this.getSortedKeys()[0]!)!
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
lastValueOrUndefined(): V | undefined {
|
|
@@ -171,9 +172,9 @@ export class LazyKeySortedMap<K, V> {
|
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
lastValue(): V {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
return
|
|
175
|
+
_assert(this.maybeSortedKeys.length, 'Map.lastValue called on empty map')
|
|
176
|
+
const keys = this.getSortedKeys()
|
|
177
|
+
return this.map.get(keys[keys.length - 1]!)!
|
|
177
178
|
}
|
|
178
179
|
|
|
179
180
|
firstEntryOrUndefined(): [K, V] | undefined {
|
|
@@ -196,7 +197,7 @@ export class LazyKeySortedMap<K, V> {
|
|
|
196
197
|
}
|
|
197
198
|
|
|
198
199
|
lastEntry(): [K, V] {
|
|
199
|
-
const e = this.
|
|
200
|
+
const e = this.lastEntryOrUndefined()
|
|
200
201
|
_assert(e, 'Map.lastEntry called on empty map')
|
|
201
202
|
return e
|
|
202
203
|
}
|