@isopodlabs/utilities 1.9.0 → 1.10.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/README.md CHANGED
@@ -1,27 +1,346 @@
1
- # @isopodlabs/utilities
2
- [![npm version](https://img.shields.io/npm/v/@isopodlabs/utilities.svg)](https://www.npmjs.com/package/@isopodlabs/utilities)
3
- [![GitHub stars](https://img.shields.io/github/stars/adrianstephens/ts-utilities.svg?style=social)](https://github.com/adrianstephens/ts-utilities)
4
- [![License](https://img.shields.io/npm/l/@isopodlabs/utilities.svg)](LICENSE.txt)
5
-
6
- This package provides a set of utilities for TypeScript.
7
-
8
- ##Support My Work
9
- If you use this package, consider [buying me a cup of tea](https://coff.ee/adrianstephens) to support future updates!
10
-
11
- ## Usage
12
-
13
- Here is a basic example of how to use the package:
14
-
15
- ```typescript
16
- import * as utils from '@isopodlabs/utilities';
17
- ````
18
-
19
- **Interfaces**:
20
-
21
-
22
- **Functions**:
23
-
24
-
25
- ## License
26
-
27
- This project is licensed under the MIT License.
1
+ # @isopodlabs/utilities
2
+ [![npm version](https://img.shields.io/npm/v/@isopodlabs/utilities.svg)](https://www.npmjs.com/package/@isopodlabs/utilities)
3
+ [![GitHub stars](https://img.shields.io/github/stars/adrianstephens/ts-utilities.svg?style=social)](https://github.com/adrianstephens/ts-utilities)
4
+ [![License](https://img.shields.io/npm/l/@isopodlabs/utilities.svg)](LICENSE.txt)
5
+
6
+ A comprehensive collection of TypeScript utilities for common programming tasks, including array/object manipulation, async operations, bit manipulation, string processing, and more.
7
+
8
+ ## Support My Work
9
+ If you use this package, consider [buying me a cup of tea](https://coff.ee/adrianstephens) to support future updates!
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @isopodlabs/utilities
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```typescript
20
+ import * as utils from '@isopodlabs/utilities';
21
+ import { DeferredPromise, regex } from '@isopodlabs/utilities';
22
+ import * as glob from '@isopodlabs/utilities/glob';
23
+ import * as insensitive from '@isopodlabs/utilities/insensitive';
24
+ ```
25
+
26
+ ## Core Features
27
+
28
+ ### Async Utilities (`async`)
29
+
30
+ Utilities for working with async operations on iterables:
31
+
32
+ ```typescript
33
+ import { async } from '@isopodlabs/utilities';
34
+
35
+ // Parallel async map
36
+ const results = await async.map([1, 2, 3], async (n) => n * 2);
37
+
38
+ // Serial async map (one at a time)
39
+ const results = await async.mapSerial([1, 2, 3], async (n) => n * 2);
40
+
41
+ // Async reduce
42
+ const sum = await async.reduce([1, 2, 3], async (acc, n) => acc + n, 0);
43
+
44
+ // Async filter
45
+ const evens = await async.filter([1, 2, 3, 4], async (n) => n % 2 === 0);
46
+
47
+ // Async map object
48
+ const mapped = await async.mapObject(
49
+ { a: 1, b: 2 },
50
+ async ([k, v]) => [k, v * 2]
51
+ );
52
+ ```
53
+
54
+ ### Array Utilities (`array`)
55
+
56
+ ```typescript
57
+ import { array } from '@isopodlabs/utilities';
58
+
59
+ // Remove element from array
60
+ const arr = [1, 2, 3, 2];
61
+ array.remove(arr, 2); // returns true, arr is now [1, 3, 2]
62
+
63
+ // Compare arrays
64
+ array.compare([1, 2], [1, 3]); // returns -1
65
+
66
+ // Check equality
67
+ array.equal([1, 2, 3], [1, 2, 3]); // returns true
68
+
69
+ // Reverse in place
70
+ array.reverse(arr, 0, arr.length - 1);
71
+
72
+ // Rotate elements
73
+ array.rotate(arr, 0, arr.length, 1);
74
+
75
+ // Create array of instances
76
+ const instances = array.make(5, MyClass);
77
+
78
+ // Lazy slice generator
79
+ for (const item of array.lazySlice([1, 2, 3, 4, 5], 1, 4)) {
80
+ console.log(item);
81
+ }
82
+ ```
83
+
84
+ ### Algorithm Utilities
85
+
86
+ ```typescript
87
+ import { partition, lowerBound, min, max, argmin, argmax } from '@isopodlabs/utilities';
88
+
89
+ // Partition by predicate
90
+ const { true: evens, false: odds } = partition([1, 2, 3, 4], n => n % 2 === 0);
91
+
92
+ // Binary search lower bound
93
+ const index = lowerBound([1, 2, 4, 5], 3);
94
+
95
+ // Find minimum/maximum
96
+ const minimum = min([3, 1, 4, 1, 5]);
97
+ const maximum = max([3, 1, 4, 1, 5]);
98
+
99
+ // Find index of min/max
100
+ const minIndex = argmin([3, 1, 4, 1, 5]);
101
+ const maxIndex = argmax([3, 1, 4, 1, 5]);
102
+ ```
103
+
104
+ ### Iterator Utilities
105
+
106
+ ```typescript
107
+ import { map, filter, reduce, find, forEach } from '@isopodlabs/utilities';
108
+
109
+ // Map over any iterable
110
+ const doubled = map([1, 2, 3], n => n * 2);
111
+
112
+ // Filter iterable
113
+ const evens = filter([1, 2, 3, 4], n => n % 2 === 0);
114
+
115
+ // Reduce iterable
116
+ const sum = reduce([1, 2, 3], (acc, n) => acc + n, 0);
117
+
118
+ // Find first match
119
+ const first = find([1, 2, 3], n => n > 1); // returns 2
120
+
121
+ // ForEach with index
122
+ forEach([1, 2, 3], (n, i) => console.log(`${i}: ${n}`));
123
+ ```
124
+
125
+ ### Object Utilities
126
+
127
+ ```typescript
128
+ import { obj } from '@isopodlabs/utilities';
129
+
130
+ // Map object values
131
+ const doubled = obj.map({ a: 1, b: 2 }, ([k, v]) => [k, v * 2]);
132
+
133
+ // Filter object entries
134
+ const filtered = obj.filter({ a: 1, b: 2, c: 3 }, ([k, v]) => v > 1);
135
+
136
+ // Deep merge objects
137
+ const merged = obj.merge({ a: 1, b: { c: 2 } }, { b: { d: 3 } });
138
+
139
+ // Clone object with prototype
140
+ const copy = obj.clone(original);
141
+ ```
142
+
143
+
144
+ ### String Utilities
145
+
146
+ ```typescript
147
+ import { replace, async_replace, splitFirstOf, splitLastOf, StringParser } from '@isopodlabs/utilities';
148
+
149
+ // Advanced replace with callback
150
+ const result = replace('hello world', /\w+/g, m => m[0].toUpperCase());
151
+
152
+ // Async replace
153
+ const result = await async_replace('hello world', /\w+/g, async m => m[0].toUpperCase());
154
+
155
+ // Split on first/last occurrence
156
+ const [before, after] = splitFirstOf('a.b.c', '.');
157
+ const [before, after] = splitLastOf('a.b.c', '.');
158
+
159
+ // String parser
160
+ const parser = new StringParser('hello world');
161
+ parser.skip('hello '); // returns true
162
+ parser.match(/\w+/); // returns 'world'
163
+ ```
164
+
165
+ ### Bit Manipulation (`bits`)
166
+
167
+ ```typescript
168
+ import { bits } from '@isopodlabs/utilities';
169
+
170
+ // Find lowest set bit index
171
+ const index = bits.lowestSet32(0b1010); // returns 1
172
+
173
+ // Find highest set bit index
174
+ const index = bits.highestSet32(0b1010); // returns 4
175
+
176
+ // Count set bits
177
+ const count = bits.countSet32(0b1011); // returns 3
178
+
179
+ // Find nth set bit
180
+ const index = bits.nthSet32(0b10101, 2); // finds 3rd set bit
181
+
182
+ // Works with BigInt too
183
+ const high = bits.highestSet(12345678901234567890n);
184
+ const count = bits.countSet(12345678901234567890n);
185
+ ```
186
+
187
+ ### Glob Patterns (`glob`)
188
+
189
+ Convert glob patterns to regular expressions:
190
+
191
+ ```typescript
192
+ import { glob } from '@isopodlabs/utilities';
193
+
194
+ // Parse glob to regex string
195
+ const pattern = glob.parse('src/**/*.ts');
196
+
197
+ // Create regex from glob
198
+ const re = glob.toRe('*.js');
199
+
200
+ // Multiple globs
201
+ const re = glob.toReMulti(['*.js', '*.ts']);
202
+ ```
203
+
204
+ ### Case-Insensitive Utilities (`insensitive`)
205
+
206
+ ```typescript
207
+ import { insensitive } from '@isopodlabs/utilities';
208
+
209
+ // Case-insensitive string
210
+ const str = insensitive.String('Hello');
211
+ str.includes('HELLO'); // true
212
+
213
+ // Case-insensitive map
214
+ const map = new insensitive.Map([['Key', 'value']]);
215
+ map.get('key'); // 'value'
216
+ map.get('KEY'); // 'value'
217
+
218
+ // Case-insensitive set
219
+ const set = new insensitive.Set(['Hello', 'World']);
220
+ set.has('hello'); // true
221
+
222
+ // Case-insensitive record
223
+ const rec = insensitive.Record({ Name: 'John' });
224
+ rec['name']; // 'John'
225
+
226
+ // Preserve original case
227
+ const rec2 = insensitive.Record2({ Name: 'John' });
228
+ Object.keys(rec2); // ['Name']
229
+ ```
230
+
231
+ ### Additional Utilities
232
+
233
+ #### `Lazy`
234
+
235
+ ```typescript
236
+ import { Lazy, CallCombiner, makeCache } from '@isopodlabs/utilities';
237
+
238
+ // Lazy evaluation
239
+ const lazy = new Lazy(() => expensiveComputation());
240
+ const value = lazy.value; // only computed once
241
+
242
+ // Combine rapid calls
243
+ const combiner = new CallCombiner(() => console.log('done'), 100);
244
+ combiner.trigger(); // will only call after 100ms of no triggers
245
+
246
+ ```
247
+
248
+ #### `Simple cache`
249
+
250
+ ```typescript
251
+ import { makeCache } from '@isopodlabs/utilities';
252
+
253
+ // Simple cache
254
+ const cache = makeCache(key => loadExpensiveData(key));
255
+ const data = cache.get('mykey');
256
+ cache.remove('mykey');
257
+ cache.clear();
258
+
259
+ // Set operations
260
+ const united = union(new Set([1, 2]), new Set([2, 3]));
261
+ const [remaining, removed] = difference(new Set([1, 2, 3]), new Set([2, 4]));
262
+ ```
263
+
264
+ #### `DeferredPromise`
265
+
266
+ A promise that can be resolved/rejected externally:
267
+
268
+ ```typescript
269
+ import { DeferredPromise } from '@isopodlabs/utilities';
270
+
271
+ const deferred = new DeferredPromise<number>();
272
+ deferred.then(value => console.log(value));
273
+
274
+ // Later...
275
+ deferred.resolve(42);
276
+
277
+ // Reset and reuse
278
+ deferred.reset();
279
+ ```
280
+
281
+ #### Regex Template Tag
282
+
283
+ Create readable regular expressions:
284
+
285
+ ```typescript
286
+ import { regex } from '@isopodlabs/utilities';
287
+
288
+ // Multi-line regex with comments
289
+ const re = regex`
290
+ \d+ # one or more digits
291
+ \. # decimal point
292
+ \d{2} # exactly 2 digits
293
+ `;
294
+
295
+ // With custom flags
296
+ const re = regex('i')`hello|world`;
297
+ ```
298
+
299
+ #### Parallel/Serial Execution
300
+
301
+ ```typescript
302
+ import { parallel, serial } from '@isopodlabs/utilities';
303
+
304
+ // Run functions in parallel
305
+ const results = await parallel(
306
+ () => fetchA(),
307
+ () => fetchB(),
308
+ () => fetchC()
309
+ );
310
+
311
+ // Run functions serially
312
+ const results = await serial(
313
+ () => fetchA(),
314
+ () => fetchB(),
315
+ () => fetchC()
316
+ );
317
+ ```
318
+
319
+ ## API Reference
320
+
321
+ ### Exported Namespaces
322
+ - `obj` - Object utilities
323
+ - `glob` - Glob pattern utilities
324
+ - `bits` - Bit manipulation utilities
325
+ - `array` - Array utilities
326
+ - `async` - Async iterator utilities
327
+ - `insensitive` - Case-insensitive string/collection utilities
328
+
329
+ ### Exported Functions
330
+ - Generic: `compare`, `reverse`, `union`, `difference`
331
+ - Algorithm: `partition`, `lowerBound`, `min`, `max`, `argmin`, `argmax`
332
+ - Iterator: `map`, `filter`, `reduce`, `find`, `forEach`
333
+ - Object: `map`, `filter`, `merge`, `clone`,
334
+ - String: `replace`, `async_replace`, `splitFirstOf`, `splitLastOf`, `trim0`, `previousChar`, `tag`
335
+ - Execution: `parallel`, `serial`
336
+ - Regex: `regex`, `reDup`
337
+
338
+ ### Exported Classes
339
+ - `DeferredPromise` - Externally resolvable promise
340
+ - `Lazy` - Lazy evaluation wrapper
341
+ - `CallCombiner` - Debounce/combine rapid calls
342
+ - `StringParser` - String parsing helper
343
+
344
+ ## License
345
+
346
+ This project is licensed under the MIT License.
package/dist/array.d.ts CHANGED
@@ -5,3 +5,8 @@ export declare function reverse<T>(array: T[], start: number, end: number): void
5
5
  export declare function rotate<T>(array: T[], start: number, end: number, shift: number): void;
6
6
  export declare function make<T>(n: number, constructor: new () => T): T[];
7
7
  export declare function lazySlice<T>(array: T[], start?: number, end?: number): Generator<T>;
8
+ export declare function lowerBound<T>(array: T[], value: T, func?: (a: T, b: T, i: number) => boolean): number;
9
+ export declare function argmin<T>(array: T[], func?: (a: T, b: T) => number): number;
10
+ export declare function min<T>(array: T[], func?: (a: T, b: T) => number): T;
11
+ export declare function argmax<T>(array: T[], func?: (a: T, b: T) => number): number;
12
+ export declare function max<T>(array: T[], func?: (a: T, b: T) => number): T;
package/dist/array.js CHANGED
@@ -7,7 +7,12 @@ exports.reverse = reverse;
7
7
  exports.rotate = rotate;
8
8
  exports.make = make;
9
9
  exports.lazySlice = lazySlice;
10
- const object_1 = require("./object");
10
+ exports.lowerBound = lowerBound;
11
+ exports.argmin = argmin;
12
+ exports.min = min;
13
+ exports.argmax = argmax;
14
+ exports.max = max;
15
+ const index_1 = require("./index");
11
16
  //-----------------------------------------------------------------------------
12
17
  // array
13
18
  //-----------------------------------------------------------------------------
@@ -22,7 +27,7 @@ function remove(array, item) {
22
27
  function compare(arr1, arr2) {
23
28
  const length = Math.min(arr1.length, arr2.length);
24
29
  for (let i = 0; i < length; i++) {
25
- const r = (0, object_1.compare)(arr1[i], arr2[i]);
30
+ const r = (0, index_1.compare)(arr1[i], arr2[i]);
26
31
  if (r)
27
32
  return r;
28
33
  }
@@ -54,7 +59,7 @@ function rotate(array, start, end, shift) {
54
59
  }
55
60
  }
56
61
  function make(n, constructor) {
57
- return Array.from({ length: n }, () => new constructor);
62
+ return Array.from({ length: n }, () => new constructor());
58
63
  }
59
64
  function* lazySlice(array, start, end) {
60
65
  const len = array.length;
@@ -67,10 +72,45 @@ function* lazySlice(array, start, end) {
67
72
  if (end === undefined)
68
73
  end = len;
69
74
  else if (end < 0)
70
- end = Math.max(len + start, 0);
75
+ end = Math.max(len + end, 0);
71
76
  else
72
77
  end = Math.min(end, len);
73
78
  for (let i = start; i < end; i++)
74
79
  yield array[i];
75
80
  }
81
+ function lowerBound(array, value, func = index_1.less) {
82
+ let i = 0;
83
+ for (let n = array.length; n; n >>= 1) {
84
+ const mid = i + (n >> 1);
85
+ if (func(array[mid], value, mid)) {
86
+ i = mid + 1;
87
+ --n;
88
+ }
89
+ }
90
+ return i;
91
+ }
92
+ function argmin(array, func = index_1.compare) {
93
+ let mini = 0;
94
+ for (let i = 1; i < array.length; i++) {
95
+ const v = func(array[i], array[mini]);
96
+ if (v < 0)
97
+ mini = i;
98
+ }
99
+ return mini;
100
+ }
101
+ function min(array, func = index_1.compare) {
102
+ return array[argmin(array, func)];
103
+ }
104
+ function argmax(array, func = index_1.compare) {
105
+ let maxi = 0;
106
+ for (let i = 1; i < array.length; i++) {
107
+ const v = func(array[i], array[maxi]);
108
+ if (v > 0)
109
+ maxi = i;
110
+ }
111
+ return maxi;
112
+ }
113
+ function max(array, func = index_1.compare) {
114
+ return array[argmax(array, func)];
115
+ }
76
116
  //# sourceMappingURL=array.js.map
package/dist/bits.js CHANGED
@@ -249,8 +249,9 @@ function nthSet(x, i) {
249
249
  return n;
250
250
  }
251
251
  function reverse(x) {
252
- if (typeof x === 'number')
252
+ if (x < 0x100000000)
253
253
  return reverse32(Number(x));
254
+ x = BigInt(x);
254
255
  let k = 5;
255
256
  for (let t = x >> 32n; t;)
256
257
  t >>= BigInt(1 << k++);
package/dist/index.d.ts CHANGED
@@ -1,14 +1,21 @@
1
- export * from './object';
2
1
  export * from './iterator';
3
2
  export * from './string';
4
- export * from './algorithm';
3
+ export { lowerBound, argmin, min, argmax, max } from './array';
4
+ export { partition } from './iterator';
5
+ export * as obj from './obj';
5
6
  export * as glob from './glob';
6
7
  export * as bits from './bits';
7
8
  export * as array from './array';
8
9
  export * as async from './async';
9
10
  export * as insensitive from './insensitive';
11
+ export declare function compare<T>(a: T, b: T): number;
12
+ export declare function less<T>(a: T, b: T): boolean;
13
+ export declare function reverse_compare<T>(a: T, b: T): number;
14
+ export declare function reverse<T, R>(func: (a: T, b: T) => R): (a: T, b: T) => R;
10
15
  export declare function parallel(...fns: (() => any)[]): Promise<any[]>;
11
16
  export declare function serial(...fns: (() => any)[]): Promise<any[]>;
17
+ export declare function union<T>(a: Set<T>, b: Set<T>): Set<T>;
18
+ export declare function difference<T>(a: Set<T>, b: Set<T>): [Set<T>, Set<T>];
12
19
  export declare function regex(strings: TemplateStringsArray, ...args: any[]): RegExp;
13
20
  export declare function regex(flags: string): (strings: TemplateStringsArray, ...args: any[]) => RegExp;
14
21
  export declare function reDup(re: RegExp): RegExp;
@@ -22,3 +29,27 @@ export declare class DeferredPromise<T> {
22
29
  then<T2 = void>(onfulfilled?: (t: T) => T2 | PromiseLike<T2>): Promise<T2>;
23
30
  reject(error: any): void;
24
31
  }
32
+ export declare class Lazy<T> {
33
+ private factory;
34
+ private _value;
35
+ constructor(factory: () => T);
36
+ get value(): T;
37
+ then<U>(this: T extends Promise<infer _R> ? Lazy<T> : never, onFulfilled: (value: T extends Promise<infer R> ? R : never) => U): Promise<U>;
38
+ }
39
+ export declare function Memoize<T>(func: () => T): () => T;
40
+ export declare class CallCombiner0 {
41
+ private timeout?;
42
+ combine(delay: number, func: () => void): void;
43
+ pending(): boolean;
44
+ }
45
+ export declare class CallCombiner extends CallCombiner0 {
46
+ private func;
47
+ private delay;
48
+ constructor(func: () => void, delay: number);
49
+ trigger(): void;
50
+ }
51
+ export declare function makeCache<T>(load: (key: string) => T): {
52
+ get: (key: string) => T;
53
+ remove: (key: string) => boolean;
54
+ clear: () => void;
55
+ };
package/dist/index.js CHANGED
@@ -36,27 +36,72 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
36
36
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.DeferredPromise = exports.insensitive = exports.async = exports.array = exports.bits = exports.glob = void 0;
39
+ exports.CallCombiner = exports.CallCombiner0 = exports.Lazy = exports.DeferredPromise = exports.insensitive = exports.async = exports.array = exports.bits = exports.glob = exports.obj = exports.partition = exports.max = exports.argmax = exports.min = exports.argmin = exports.lowerBound = void 0;
40
+ exports.compare = compare;
41
+ exports.less = less;
42
+ exports.reverse_compare = reverse_compare;
43
+ exports.reverse = reverse;
40
44
  exports.parallel = parallel;
41
45
  exports.serial = serial;
46
+ exports.union = union;
47
+ exports.difference = difference;
42
48
  exports.regex = regex;
43
49
  exports.reDup = reDup;
50
+ exports.Memoize = Memoize;
51
+ exports.makeCache = makeCache;
44
52
  const async = __importStar(require("./async"));
45
- __exportStar(require("./object"), exports);
46
53
  __exportStar(require("./iterator"), exports);
47
54
  __exportStar(require("./string"), exports);
48
- __exportStar(require("./algorithm"), exports);
55
+ var array_1 = require("./array");
56
+ Object.defineProperty(exports, "lowerBound", { enumerable: true, get: function () { return array_1.lowerBound; } });
57
+ Object.defineProperty(exports, "argmin", { enumerable: true, get: function () { return array_1.argmin; } });
58
+ Object.defineProperty(exports, "min", { enumerable: true, get: function () { return array_1.min; } });
59
+ Object.defineProperty(exports, "argmax", { enumerable: true, get: function () { return array_1.argmax; } });
60
+ Object.defineProperty(exports, "max", { enumerable: true, get: function () { return array_1.max; } });
61
+ var iterator_1 = require("./iterator");
62
+ Object.defineProperty(exports, "partition", { enumerable: true, get: function () { return iterator_1.partition; } });
63
+ exports.obj = __importStar(require("./obj"));
49
64
  exports.glob = __importStar(require("./glob"));
50
65
  exports.bits = __importStar(require("./bits"));
51
66
  exports.array = __importStar(require("./array"));
52
67
  exports.async = __importStar(require("./async"));
53
68
  exports.insensitive = __importStar(require("./insensitive"));
69
+ //-----------------------------------------------------------------------------
70
+ // handy generic functions
71
+ //-----------------------------------------------------------------------------
72
+ function compare(a, b) {
73
+ return a < b ? -1 : a > b ? 1 : 0;
74
+ }
75
+ function less(a, b) {
76
+ return a < b;
77
+ }
78
+ function reverse_compare(a, b) {
79
+ return compare(b, a);
80
+ }
81
+ function reverse(func) {
82
+ return (a, b) => func(b, a);
83
+ }
54
84
  async function parallel(...fns) {
55
85
  return async.map(fns, f => f());
56
86
  }
57
87
  async function serial(...fns) {
58
88
  return async.mapSerial(fns, f => f());
59
89
  }
90
+ //-----------------------------------------------------------------------------
91
+ // set helpers
92
+ //-----------------------------------------------------------------------------
93
+ function union(a, b) {
94
+ return new Set([...a, ...b]);
95
+ }
96
+ function difference(a, b) {
97
+ const remaining = new Set(a);
98
+ const removed = new Set();
99
+ for (const item of b) {
100
+ if (remaining.delete(item))
101
+ removed.add(item);
102
+ }
103
+ return [remaining, removed];
104
+ }
60
105
  function regex(param, ...args) {
61
106
  const flags = typeof param === 'string' ? param : 'g';
62
107
  if (typeof param === 'string')
@@ -70,6 +115,9 @@ function regex(param, ...args) {
70
115
  ;
71
116
  }
72
117
  function reDup(re) { return new RegExp(re.source, re.flags); }
118
+ //-----------------------------------------------------------------------------
119
+ // handy classes and functions
120
+ //-----------------------------------------------------------------------------
73
121
  class DeferredPromise {
74
122
  promise;
75
123
  resolver;
@@ -101,4 +149,65 @@ class DeferredPromise {
101
149
  }
102
150
  }
103
151
  exports.DeferredPromise = DeferredPromise;
152
+ class Lazy {
153
+ factory;
154
+ _value;
155
+ constructor(factory) {
156
+ this.factory = factory;
157
+ }
158
+ get value() {
159
+ if (this._value === undefined)
160
+ this._value = this.factory();
161
+ return this._value;
162
+ }
163
+ // Add 'then' method only when T is a Promise
164
+ then(onFulfilled) {
165
+ return this.value.then(onFulfilled);
166
+ }
167
+ }
168
+ exports.Lazy = Lazy;
169
+ function Memoize(func) {
170
+ const lazy = new Lazy(func);
171
+ return () => lazy.value;
172
+ }
173
+ class CallCombiner0 {
174
+ timeout;
175
+ combine(delay, func) {
176
+ if (this.timeout)
177
+ clearTimeout(this.timeout);
178
+ this.timeout = setTimeout(() => {
179
+ this.timeout = undefined;
180
+ func();
181
+ }, delay);
182
+ }
183
+ pending() {
184
+ return !!this.timeout;
185
+ }
186
+ }
187
+ exports.CallCombiner0 = CallCombiner0;
188
+ class CallCombiner extends CallCombiner0 {
189
+ func;
190
+ delay;
191
+ constructor(func, delay) {
192
+ super();
193
+ this.func = func;
194
+ this.delay = delay;
195
+ }
196
+ trigger() {
197
+ super.combine(this.delay, this.func);
198
+ }
199
+ }
200
+ exports.CallCombiner = CallCombiner;
201
+ function makeCache(load) {
202
+ const cache = {};
203
+ return {
204
+ get: (key) => {
205
+ if (!cache[key])
206
+ cache[key] = load(key);
207
+ return cache[key];
208
+ },
209
+ remove: (key) => delete cache[key],
210
+ clear: () => Object.keys(cache).forEach(k => delete cache[k]),
211
+ };
212
+ }
104
213
  //# sourceMappingURL=index.js.map
@@ -1,25 +1,26 @@
1
- declare class CaseInsensitiveString {
1
+ type string2 = string | istring;
2
+ declare class istring {
2
3
  private value;
3
4
  constructor(value: string);
4
5
  get length(): number;
5
6
  toString(): string;
6
7
  toUpperCase(): string;
7
8
  toLowerCase(): string;
8
- includes(searchString: string, position?: number): boolean;
9
- startsWith(searchString: string, position?: number): boolean;
10
- endsWith(searchString: string, position?: number): boolean;
11
- indexOf(searchString: string, position?: number): number;
12
- lastIndexOf(searchString: string, position?: number): number;
13
- compare(other: string | CaseInsensitiveString): 0 | 1 | -1;
9
+ includes(searchString: string2, position?: number): boolean;
10
+ startsWith(searchString: string2, position?: number): boolean;
11
+ endsWith(searchString: string2, position?: number): boolean;
12
+ indexOf(searchString: string2, position?: number): number;
13
+ lastIndexOf(searchString: string2, position?: number): number;
14
+ compare(other: string2): 0 | 1 | -1;
14
15
  }
15
- declare class CaseInsensitiveString2 extends CaseInsensitiveString {
16
+ declare class istring2 extends istring {
16
17
  private orig;
17
18
  constructor(orig: string);
18
19
  toString(): string;
19
20
  }
20
- export declare function String(value: string): CaseInsensitiveString;
21
- export declare function String2(value: string): CaseInsensitiveString2;
22
- export declare function compare(a: string | CaseInsensitiveString, b: string | CaseInsensitiveString): 0 | 1 | -1;
21
+ export declare function String(value: string): istring;
22
+ export declare function String2(value: string): istring2;
23
+ export declare function compare(a: string2, b: string2): 0 | 1 | -1;
23
24
  export declare function Record<T>(obj: Record<string, T>): Record<string, T>;
24
25
  export declare function Record2<T>(obj: Record<string, T>): Record<string, T>;
25
26
  export declare class Map<T> extends globalThis.Map<string, T> {
@@ -6,7 +6,7 @@ exports.String2 = String2;
6
6
  exports.compare = compare;
7
7
  exports.Record = Record;
8
8
  exports.Record2 = Record2;
9
- class CaseInsensitiveString {
9
+ class istring {
10
10
  value;
11
11
  constructor(value) { this.value = value.toUpperCase(); }
12
12
  get length() { return this.value.length; }
@@ -21,7 +21,7 @@ class CaseInsensitiveString {
21
21
  compare(other) { const bi = other.toUpperCase(); return this.value < bi ? -1 : this.value > bi ? 1 : 0; }
22
22
  }
23
23
  // keeps original string
24
- class CaseInsensitiveString2 extends CaseInsensitiveString {
24
+ class istring2 extends istring {
25
25
  orig;
26
26
  constructor(orig) {
27
27
  super(orig);
@@ -30,10 +30,10 @@ class CaseInsensitiveString2 extends CaseInsensitiveString {
30
30
  toString() { return this.orig; }
31
31
  }
32
32
  function String(value) {
33
- return new CaseInsensitiveString(value);
33
+ return new istring(value);
34
34
  }
35
35
  function String2(value) {
36
- return new CaseInsensitiveString2(value);
36
+ return new istring2(value);
37
37
  }
38
38
  function compare(a, b) {
39
39
  const ai = a.toUpperCase(), bi = b.toUpperCase();
@@ -5,3 +5,6 @@ export declare function find<T>(iterable: Iterable<T> | undefined, func: (v: T)
5
5
  export declare function map<T, U>(iterable: Iterable<T> | undefined, func: (v: T, i: number) => U): U[];
6
6
  export declare function reduce<T, U>(iterable: Iterable<T>, func: (acc: U, v: T, i: number, iterable: Iterable<T>) => U, initialValue: U): U;
7
7
  export declare function filter<T>(iterable: Iterable<T>, func: (v: T, i: number) => unknown): T[];
8
+ type PartitionIndex<U> = U extends boolean ? 'true' | 'false' : U;
9
+ export declare function partition<T, U extends keyof any | boolean>(array: Iterable<T>, func: (v: T) => U): Record<PartitionIndex<U>, T[]>;
10
+ export {};
package/dist/iterator.js CHANGED
@@ -39,6 +39,7 @@ exports.find = find;
39
39
  exports.map = map;
40
40
  exports.reduce = reduce;
41
41
  exports.filter = filter;
42
+ exports.partition = partition;
42
43
  const array = __importStar(require("./array"));
43
44
  function remove(iterable, item) {
44
45
  return array.remove(Array.from(iterable), item);
@@ -76,4 +77,10 @@ function filter(iterable, func) {
76
77
  array.push(v);
77
78
  return array;
78
79
  }
80
+ function partition(array, func) {
81
+ const partitions = {};
82
+ for (const i of array)
83
+ (partitions[func(i)] ??= []).push(i);
84
+ return partitions;
85
+ }
79
86
  //# sourceMappingURL=iterator.js.map
package/dist/obj.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export declare function map<T, U>(obj: Record<string, T>, func: (x: [k: string, v: T]) => [k: string, v: U]): Record<string, U>;
2
+ export declare function filter<T>(obj: Record<string, T>, func: (x: [k: string, v: T]) => boolean): Record<string, T>;
3
+ export declare function merge(...list: Record<string, any>[]): Record<string, any>;
4
+ export declare function isEmpty(obj: object): boolean;
5
+ export declare function clone<T extends object>(obj: T): T;
package/dist/obj.js ADDED
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.map = map;
4
+ exports.filter = filter;
5
+ exports.merge = merge;
6
+ exports.isEmpty = isEmpty;
7
+ exports.clone = clone;
8
+ function map(obj, func) {
9
+ return Object.fromEntries(Object.entries(obj).map(x => func(x)));
10
+ }
11
+ function filter(obj, func) {
12
+ return Object.fromEntries(Object.entries(obj).filter(x => func(x)));
13
+ }
14
+ function merge(...list) {
15
+ function isObject(value) {
16
+ return typeof value === 'object' && value !== null;
17
+ }
18
+ function recurse(target, source) {
19
+ for (const key in source) {
20
+ if (isObject(source[key]) && isObject(target[key]))
21
+ recurse(target[key], source[key]);
22
+ else
23
+ target[key] = source[key];
24
+ }
25
+ return target;
26
+ }
27
+ return list.reduce((merged, r) => recurse(merged, r), {});
28
+ }
29
+ function isEmpty(obj) {
30
+ return Object.keys(obj).length === 0;
31
+ }
32
+ function clone(obj) {
33
+ return Object.assign(Object.create(Object.getPrototypeOf(obj)), obj);
34
+ }
35
+ //# sourceMappingURL=obj.js.map
package/dist/string.d.ts CHANGED
@@ -2,13 +2,13 @@ export declare function firstOf(value: string, find: string): number;
2
2
  export declare function lastOf(value: string, find: string): number;
3
3
  export declare function splitFirstOf(value: string, find: string): (string | undefined)[];
4
4
  export declare function splitLastOf(value: string, find: string): (string | undefined)[];
5
+ export declare function splitEvery(s: string, n: number): string[];
5
6
  export declare function trim0(value: string): string;
6
7
  export declare function replace(value: string, re: RegExp, process: (match: RegExpExecArray) => string): string;
7
8
  export declare function async_replace(value: string, re: RegExp, process: (match: RegExpExecArray) => Promise<string>): Promise<string>;
8
9
  export declare function async_replace_async(value: string, re: RegExp, process: (match: RegExpExecArray) => Promise<string>): Promise<string>;
9
10
  export declare function replace_back(value: string, re: RegExp, process: (match: RegExpExecArray, right: string) => string): string;
10
11
  export declare function async_replace_back(value: string, re: RegExp, process: (match: RegExpExecArray, right: string) => Promise<string>): Promise<string>;
11
- export declare function splitEvery(s: string, n: number): string[];
12
12
  export declare function tag(strings: TemplateStringsArray, ...keys: any[]): (...values: any[]) => string;
13
13
  export declare function previousChar(str: string, pos: number): string;
14
14
  export declare function hasCustomToString(value: any): boolean;
@@ -24,6 +24,6 @@ export declare class StringParser {
24
24
  get(n: number): string;
25
25
  skip(c: string): boolean;
26
26
  expect(c: string): void;
27
- match(re: RegExp): string | undefined;
28
27
  exec(re: RegExp): RegExpExecArray | undefined;
28
+ match(re: RegExp): string | undefined;
29
29
  }
package/dist/string.js CHANGED
@@ -8,13 +8,13 @@ exports.firstOf = firstOf;
8
8
  exports.lastOf = lastOf;
9
9
  exports.splitFirstOf = splitFirstOf;
10
10
  exports.splitLastOf = splitLastOf;
11
+ exports.splitEvery = splitEvery;
11
12
  exports.trim0 = trim0;
12
13
  exports.replace = replace;
13
14
  exports.async_replace = async_replace;
14
15
  exports.async_replace_async = async_replace_async;
15
16
  exports.replace_back = replace_back;
16
17
  exports.async_replace_back = async_replace_back;
17
- exports.splitEvery = splitEvery;
18
18
  exports.tag = tag;
19
19
  exports.previousChar = previousChar;
20
20
  exports.hasCustomToString = hasCustomToString;
@@ -41,6 +41,9 @@ function splitLastOf(value, find) {
41
41
  const index = lastOf(value, find);
42
42
  return index === -1 ? [value, undefined] : [value.substring(0, index), value.substring(index + 1)];
43
43
  }
44
+ function splitEvery(s, n) {
45
+ return Array.from({ length: Math.ceil(s.length / n) }, (_, i) => s.slice(i * n, (i + 1) * n));
46
+ }
44
47
  function trim0(value) {
45
48
  const index = value.indexOf('\0');
46
49
  return index === -1 ? value : value.substring(0, index);
@@ -107,9 +110,6 @@ async function async_replace_back(value, re, process) {
107
110
  re.lastIndex = value.length;
108
111
  return value.substring(start);
109
112
  }
110
- function splitEvery(s, n) {
111
- return Array.from({ length: Math.ceil(s.length / n) }, (_, i) => s.slice(i * n, (i + 1) * n));
112
- }
113
113
  function tag(strings, ...keys) {
114
114
  return (...values) => {
115
115
  const dict = values.at(-1) || {};
@@ -158,13 +158,6 @@ class StringParser {
158
158
  if (!this.skip(c))
159
159
  throw new Error(`Expected '${c}'`);
160
160
  }
161
- match(re) {
162
- const m = re.exec(this.remainder());
163
- if (m) {
164
- this.pos += m.index + m[0].length;
165
- return m[0];
166
- }
167
- }
168
161
  exec(re) {
169
162
  const m = re.exec(this.remainder());
170
163
  if (m) {
@@ -172,6 +165,9 @@ class StringParser {
172
165
  return m;
173
166
  }
174
167
  }
168
+ match(re) {
169
+ return this.exec(re)?.[0];
170
+ }
175
171
  }
176
172
  exports.StringParser = StringParser;
177
173
  //# sourceMappingURL=string.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isopodlabs/utilities",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "Various typescript helpers.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,9 +0,0 @@
1
- type PartitionIndex<U> = U extends boolean ? 'true' | 'false' : U;
2
- export declare function partition<T, U extends keyof any | boolean>(array: Iterable<T>, func: (v: T) => U): Record<PartitionIndex<U>, T[]>;
3
- export declare function less<T>(a: T, b: T): boolean;
4
- export declare function lowerBound<T>(array: T[], value: T, func?: (a: T, b: T, i: number) => boolean): number;
5
- export declare function argmin<T>(array: T[], func?: (a: T, b: T) => number): number;
6
- export declare function min<T>(array: T[], func?: (a: T, b: T) => number): T;
7
- export declare function argmax<T>(array: T[], func?: (a: T, b: T) => number): number;
8
- export declare function max<T>(array: T[], func?: (a: T, b: T) => number): T;
9
- export {};
package/dist/algorithm.js DELETED
@@ -1,55 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.partition = partition;
4
- exports.less = less;
5
- exports.lowerBound = lowerBound;
6
- exports.argmin = argmin;
7
- exports.min = min;
8
- exports.argmax = argmax;
9
- exports.max = max;
10
- const object_1 = require("./object");
11
- function partition(array, func) {
12
- const partitions = {};
13
- for (const i of array)
14
- (partitions[func(i)] ??= []).push(i);
15
- return partitions;
16
- }
17
- function less(a, b) {
18
- return a < b;
19
- }
20
- function lowerBound(array, value, func = less) {
21
- let i = 0;
22
- for (let n = array.length; n; n >>= 1) {
23
- const mid = i + (n >> 1);
24
- if (func(array[mid], value, mid)) {
25
- i = mid + 1;
26
- --n;
27
- }
28
- }
29
- return i;
30
- }
31
- function argmin(array, func = object_1.compare) {
32
- let mini = 0;
33
- for (let i = 1; i < array.length; i++) {
34
- const v = func(array[i], array[mini]);
35
- if (v < 0)
36
- mini = i;
37
- }
38
- return mini;
39
- }
40
- function min(array, func = object_1.compare) {
41
- return array[argmin(array, func)];
42
- }
43
- function argmax(array, func = object_1.compare) {
44
- let maxi = 0;
45
- for (let i = 1; i < array.length; i++) {
46
- const v = func(array[i], array[maxi]);
47
- if (v > 0)
48
- maxi = i;
49
- }
50
- return maxi;
51
- }
52
- function max(array, func = object_1.compare) {
53
- return array[argmax(array, func)];
54
- }
55
- //# sourceMappingURL=algorithm.js.map
package/dist/object.d.ts DELETED
@@ -1,34 +0,0 @@
1
- export declare function mapObject<T, U>(obj: Record<string, T>, func: (x: [k: string, v: T]) => [k: string, v: U]): Record<string, U>;
2
- export declare function filterObject<T>(obj: Record<string, T>, func: (x: [k: string, v: T]) => boolean): Record<string, T>;
3
- export declare function merge(...list: Record<string, any>[]): Record<string, any>;
4
- export declare function isEmpty(obj: object): boolean;
5
- export declare function clone<T extends object>(obj: T): T;
6
- export declare function compare<T>(a: T, b: T): number;
7
- export declare function reverse_compare<T>(a: T, b: T): number;
8
- export declare function reverse<T, R>(func: (a: T, b: T) => R): (a: T, b: T) => R;
9
- export declare class Lazy<T> {
10
- private factory;
11
- private _value;
12
- constructor(factory: () => T);
13
- get value(): T;
14
- then<U>(this: T extends Promise<infer _R> ? Lazy<T> : never, onFulfilled: (value: T extends Promise<infer R> ? R : never) => U): Promise<U>;
15
- }
16
- export declare function Memoize<T>(func: () => T): () => T;
17
- export declare class CallCombiner0 {
18
- private timeout?;
19
- combine(delay: number, func: () => void): void;
20
- pending(): boolean;
21
- }
22
- export declare class CallCombiner extends CallCombiner0 {
23
- private func;
24
- private delay;
25
- constructor(func: () => void, delay: number);
26
- trigger(): void;
27
- }
28
- export declare function makeCache<T>(load: (key: string) => T): {
29
- get: (fullpath: string) => T;
30
- remove: (fullpath: string) => boolean;
31
- clear: () => void;
32
- };
33
- export declare function union<T>(a: Set<T>, b: Set<T>): Set<T>;
34
- export declare function difference<T>(a: Set<T>, b: Set<T>): [Set<T>, Set<T>];
package/dist/object.js DELETED
@@ -1,125 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CallCombiner = exports.CallCombiner0 = exports.Lazy = void 0;
4
- exports.mapObject = mapObject;
5
- exports.filterObject = filterObject;
6
- exports.merge = merge;
7
- exports.isEmpty = isEmpty;
8
- exports.clone = clone;
9
- exports.compare = compare;
10
- exports.reverse_compare = reverse_compare;
11
- exports.reverse = reverse;
12
- exports.Memoize = Memoize;
13
- exports.makeCache = makeCache;
14
- exports.union = union;
15
- exports.difference = difference;
16
- function mapObject(obj, func) {
17
- return Object.fromEntries(Object.entries(obj).map(x => func(x)));
18
- }
19
- function filterObject(obj, func) {
20
- return Object.fromEntries(Object.entries(obj).filter(x => func(x)));
21
- }
22
- function merge(...list) {
23
- function isObject(value) {
24
- return typeof value === 'object' && value !== null;
25
- }
26
- function recurse(target, source) {
27
- for (const key in source) {
28
- if (isObject(source[key]) && isObject(target[key]))
29
- recurse(target[key], source[key]);
30
- else
31
- target[key] = source[key];
32
- }
33
- return target;
34
- }
35
- return list.reduce((merged, r) => recurse(merged, r), {});
36
- }
37
- function isEmpty(obj) {
38
- return Object.keys(obj).length === 0;
39
- }
40
- function clone(obj) {
41
- return Object.assign(Object.create(Object.getPrototypeOf(obj)), obj);
42
- }
43
- function compare(a, b) {
44
- return a < b ? -1 : a > b ? 1 : 0;
45
- }
46
- function reverse_compare(a, b) {
47
- return compare(b, a);
48
- }
49
- function reverse(func) {
50
- return (a, b) => func(b, a);
51
- }
52
- class Lazy {
53
- factory;
54
- _value;
55
- constructor(factory) {
56
- this.factory = factory;
57
- }
58
- get value() {
59
- if (this._value === undefined)
60
- this._value = this.factory();
61
- return this._value;
62
- }
63
- // Add 'then' method only when T is a Promise
64
- then(onFulfilled) {
65
- return this.value.then(onFulfilled);
66
- }
67
- }
68
- exports.Lazy = Lazy;
69
- function Memoize(func) {
70
- const lazy = new Lazy(func);
71
- return () => lazy.value;
72
- }
73
- class CallCombiner0 {
74
- timeout;
75
- combine(delay, func) {
76
- if (this.timeout)
77
- clearTimeout(this.timeout);
78
- this.timeout = setTimeout(() => {
79
- this.timeout = undefined;
80
- func();
81
- }, delay);
82
- }
83
- pending() {
84
- return !!this.timeout;
85
- }
86
- }
87
- exports.CallCombiner0 = CallCombiner0;
88
- class CallCombiner extends CallCombiner0 {
89
- func;
90
- delay;
91
- constructor(func, delay) {
92
- super();
93
- this.func = func;
94
- this.delay = delay;
95
- }
96
- trigger() {
97
- super.combine(this.delay, this.func);
98
- }
99
- }
100
- exports.CallCombiner = CallCombiner;
101
- function makeCache(load) {
102
- const cache = {};
103
- return {
104
- get: (fullpath) => {
105
- if (!cache[fullpath])
106
- cache[fullpath] = load(fullpath);
107
- return cache[fullpath];
108
- },
109
- remove: (fullpath) => delete cache[fullpath],
110
- clear: () => Object.keys(cache).forEach(k => delete cache[k]),
111
- };
112
- }
113
- function union(a, b) {
114
- return new Set([...a, ...b]);
115
- }
116
- function difference(a, b) {
117
- const remaining = new Set(a);
118
- const removed = new Set();
119
- for (const item of b) {
120
- if (remaining.delete(item))
121
- removed.add(item);
122
- }
123
- return [remaining, removed];
124
- }
125
- //# sourceMappingURL=object.js.map