@welshman/util 0.1.2 → 0.1.3

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.
Files changed (98) hide show
  1. package/LICENSE +21 -0
  2. package/dist/lib/src/Deferred.d.ts +21 -0
  3. package/dist/lib/src/Deferred.d.ts.map +1 -0
  4. package/dist/lib/src/Deferred.js +21 -0
  5. package/dist/lib/src/Deferred.js.map +1 -0
  6. package/dist/lib/src/Emitter.d.ts +14 -0
  7. package/dist/lib/src/Emitter.d.ts.map +1 -0
  8. package/dist/lib/src/Emitter.js +18 -0
  9. package/dist/lib/src/Emitter.js.map +1 -0
  10. package/dist/lib/src/LRUCache.d.ts +42 -0
  11. package/dist/lib/src/LRUCache.d.ts.map +1 -0
  12. package/dist/lib/src/LRUCache.js +62 -0
  13. package/dist/lib/src/LRUCache.js.map +1 -0
  14. package/dist/lib/src/TaskQueue.d.ts +18 -0
  15. package/dist/lib/src/TaskQueue.d.ts.map +1 -0
  16. package/dist/lib/src/TaskQueue.js +47 -0
  17. package/dist/lib/src/TaskQueue.js.map +1 -0
  18. package/dist/lib/src/Tools.d.ts +630 -0
  19. package/dist/lib/src/Tools.d.ts.map +1 -0
  20. package/dist/lib/src/Tools.js +1061 -0
  21. package/dist/lib/src/Tools.js.map +1 -0
  22. package/dist/lib/src/index.d.ts +7 -0
  23. package/dist/lib/src/index.d.ts.map +1 -0
  24. package/dist/lib/src/index.js +7 -0
  25. package/dist/lib/src/index.js.map +1 -0
  26. package/dist/lib/src/normalize-url/index.d.ts +286 -0
  27. package/dist/lib/src/normalize-url/index.d.ts.map +1 -0
  28. package/dist/lib/src/normalize-url/index.js +252 -0
  29. package/dist/lib/src/normalize-url/index.js.map +1 -0
  30. package/dist/tsconfig.build.tsbuildinfo +1 -0
  31. package/{build → dist/util}/src/Address.d.ts +1 -0
  32. package/dist/util/src/Address.d.ts.map +1 -0
  33. package/dist/util/src/Address.js.map +1 -0
  34. package/{build → dist/util}/src/Encryptable.d.ts +1 -0
  35. package/dist/util/src/Encryptable.d.ts.map +1 -0
  36. package/dist/util/src/Encryptable.js.map +1 -0
  37. package/{build → dist/util}/src/Events.d.ts +4 -3
  38. package/dist/util/src/Events.d.ts.map +1 -0
  39. package/{build → dist/util}/src/Events.js +3 -3
  40. package/dist/util/src/Events.js.map +1 -0
  41. package/{build → dist/util}/src/Filters.d.ts +1 -0
  42. package/dist/util/src/Filters.d.ts.map +1 -0
  43. package/dist/util/src/Filters.js.map +1 -0
  44. package/{build → dist/util}/src/Handler.d.ts +2 -1
  45. package/dist/util/src/Handler.d.ts.map +1 -0
  46. package/dist/util/src/Handler.js.map +1 -0
  47. package/{build → dist/util}/src/Kinds.d.ts +1 -0
  48. package/dist/util/src/Kinds.d.ts.map +1 -0
  49. package/dist/util/src/Kinds.js.map +1 -0
  50. package/{build → dist/util}/src/Links.d.ts +1 -0
  51. package/dist/util/src/Links.d.ts.map +1 -0
  52. package/dist/util/src/Links.js.map +1 -0
  53. package/{build → dist/util}/src/List.d.ts +1 -0
  54. package/dist/util/src/List.d.ts.map +1 -0
  55. package/dist/util/src/List.js.map +1 -0
  56. package/{build → dist/util}/src/Profile.d.ts +1 -0
  57. package/dist/util/src/Profile.d.ts.map +1 -0
  58. package/dist/util/src/Profile.js.map +1 -0
  59. package/{build → dist/util}/src/Relay.d.ts +1 -0
  60. package/dist/util/src/Relay.d.ts.map +1 -0
  61. package/dist/util/src/Relay.js.map +1 -0
  62. package/{build → dist/util}/src/Tags.d.ts +1 -0
  63. package/dist/util/src/Tags.d.ts.map +1 -0
  64. package/dist/util/src/Tags.js.map +1 -0
  65. package/{build → dist/util}/src/Zaps.d.ts +1 -0
  66. package/dist/util/src/Zaps.d.ts.map +1 -0
  67. package/dist/util/src/Zaps.js.map +1 -0
  68. package/{build → dist/util}/src/index.d.ts +1 -0
  69. package/dist/util/src/index.d.ts.map +1 -0
  70. package/dist/util/src/index.js.map +1 -0
  71. package/package.json +16 -22
  72. package/README.md +0 -15
  73. package/build/src/Address.js.map +0 -1
  74. package/build/src/Encryptable.js.map +0 -1
  75. package/build/src/Events.js.map +0 -1
  76. package/build/src/Filters.js.map +0 -1
  77. package/build/src/Handler.js.map +0 -1
  78. package/build/src/Kinds.js.map +0 -1
  79. package/build/src/Links.js.map +0 -1
  80. package/build/src/List.js.map +0 -1
  81. package/build/src/Profile.js.map +0 -1
  82. package/build/src/Relay.js.map +0 -1
  83. package/build/src/Tags.js.map +0 -1
  84. package/build/src/Zaps.js.map +0 -1
  85. package/build/src/index.js.map +0 -1
  86. package/build/tsconfig.tsbuildinfo +0 -1
  87. /package/{build → dist/util}/src/Address.js +0 -0
  88. /package/{build → dist/util}/src/Encryptable.js +0 -0
  89. /package/{build → dist/util}/src/Filters.js +0 -0
  90. /package/{build → dist/util}/src/Handler.js +0 -0
  91. /package/{build → dist/util}/src/Kinds.js +0 -0
  92. /package/{build → dist/util}/src/Links.js +0 -0
  93. /package/{build → dist/util}/src/List.js +0 -0
  94. /package/{build → dist/util}/src/Profile.js +0 -0
  95. /package/{build → dist/util}/src/Relay.js +0 -0
  96. /package/{build → dist/util}/src/Tags.js +0 -0
  97. /package/{build → dist/util}/src/Zaps.js +0 -0
  98. /package/{build → dist/util}/src/index.js +0 -0
@@ -0,0 +1,1061 @@
1
+ import { bech32, utf8 } from "@scure/base";
2
+ // ----------------------------------------------------------------------------
3
+ // Basic functional programming utilities
4
+ // ----------------------------------------------------------------------------
5
+ /** Function that does nothing and returns undefined */
6
+ export const noop = (...args) => undefined;
7
+ /**
8
+ * Returns the input value unchanged
9
+ * @param x - Any value
10
+ * @returns The same value
11
+ */
12
+ export const identity = (x, ...args) => x;
13
+ /**
14
+ * Creates a function that always returns the same value
15
+ * @param x - Value to return
16
+ * @returns Function that returns x
17
+ */
18
+ export const always = (x, ...args) => () => x;
19
+ /**
20
+ * Returns the logical NOT of a value
21
+ * @param x - Value to negate
22
+ * @returns !x
23
+ */
24
+ export const not = (x, ...args) => !x;
25
+ /**
26
+ * Deep equality comparison
27
+ * @param a - First value
28
+ * @param b - Second value
29
+ * @returns True if values are deeply equal
30
+ */
31
+ export const equals = (a, b) => {
32
+ if (a === b)
33
+ return true;
34
+ if (a instanceof Set && b instanceof Set) {
35
+ a = Array.from(a);
36
+ b = Array.from(b);
37
+ }
38
+ if (a instanceof Set) {
39
+ if (!(b instanceof Set) || a.size !== b.size) {
40
+ return false;
41
+ }
42
+ return Array.from(a).every(x => b.has(x));
43
+ }
44
+ if (Array.isArray(a)) {
45
+ if (!Array.isArray(b) || a.length !== b.length) {
46
+ return false;
47
+ }
48
+ for (let i = 0; i < a.length; i++) {
49
+ if (!equals(a[i], b[i])) {
50
+ return false;
51
+ }
52
+ }
53
+ return true;
54
+ }
55
+ if (isPojo(a)) {
56
+ if (!isPojo(b)) {
57
+ return false;
58
+ }
59
+ const aKeys = Object.keys(a);
60
+ const bKeys = Object.keys(b);
61
+ if (aKeys.length !== bKeys.length) {
62
+ return false;
63
+ }
64
+ for (const k of aKeys) {
65
+ if (!equals(a[k], b[k])) {
66
+ return false;
67
+ }
68
+ }
69
+ return true;
70
+ }
71
+ return false;
72
+ };
73
+ // ----------------------------------------------------------------------------
74
+ // Numbers
75
+ // ----------------------------------------------------------------------------
76
+ /** Converts string or number to number */
77
+ export const ensureNumber = (x) => parseFloat(x);
78
+ /** Converts a `number | undefined` to a number, defaulting to 0 */
79
+ export const num = (x) => x || 0;
80
+ /** Adds two numbers, handling undefined values */
81
+ export const add = (x, y) => num(x) + num(y);
82
+ /** Subtracts two numbers, handling undefined values */
83
+ export const sub = (x, y) => num(x) - num(y);
84
+ /** Multiplies two numbers, handling undefined values */
85
+ export const mul = (x, y) => num(x) * num(y);
86
+ /** Divides two numbers, handling undefined values */
87
+ export const div = (x, y) => num(x) / y;
88
+ /** Increments a number by 1, handling undefined values */
89
+ export const inc = (x) => add(x, 1);
90
+ /** Decrements a number by 1, handling undefined values */
91
+ export const dec = (x) => sub(x, 1);
92
+ /** Less than comparison, handling undefined values */
93
+ export const lt = (x, y) => num(x) < num(y);
94
+ /** Less than or equal comparison, handling undefined values */
95
+ export const lte = (x, y) => num(x) <= num(y);
96
+ /** Greater than comparison, handling undefined values */
97
+ export const gt = (x, y) => num(x) > num(y);
98
+ /** Greater than or equal comparison, handling undefined values */
99
+ export const gte = (x, y) => num(x) >= num(y);
100
+ /** Returns maximum value in array, handling undefined values */
101
+ export const max = (xs) => xs.reduce((a, b) => Math.max(num(a), num(b)), 0);
102
+ /** Returns minimum value in array, handling undefined values */
103
+ export const min = (xs) => {
104
+ const [head, ...tail] = xs.filter(x => x !== undefined);
105
+ if (tail.length === 0)
106
+ return head || 0;
107
+ return tail.reduce((a, b) => Math.min(a, b), head);
108
+ };
109
+ /** Returns sum of array values, handling undefined values */
110
+ export const sum = (xs) => xs.reduce((a, b) => add(a, b), 0);
111
+ /** Returns average of array values, handling undefined values */
112
+ export const avg = (xs) => sum(xs) / xs.length;
113
+ /**
114
+ * Checks if a number is between two values (exclusive)
115
+ * @param bounds - Lower and upper bounds
116
+ * @param n - Number to check
117
+ * @returns True if n is between low and high
118
+ */
119
+ export const between = ([low, high], n) => n > low && n < high;
120
+ /**
121
+ * Checks if a number is between two values (inclusive)
122
+ * @param bounds - Lower and upper bounds
123
+ * @param n - Number to check
124
+ * @returns True if n is between low and high
125
+ */
126
+ export const within = ([low, high], n) => n >= low && n <= high;
127
+ /**
128
+ * Constrains number between min and max values
129
+ * @param bounds - Minimum and maximum allowed values
130
+ * @param n - Number to clamp
131
+ * @returns Clamped value
132
+ */
133
+ export const clamp = ([min, max], n) => Math.min(max, Math.max(min, n));
134
+ /**
135
+ * Round a number to the nearest float precision
136
+ * @param precision - Number of decimal places
137
+ * @param x - Number to round
138
+ * @returns Formatted number
139
+ */
140
+ export const round = (precision, x) => Math.round(x * Math.pow(10, precision)) / Math.pow(10, precision);
141
+ // ----------------------------------------------------------------------------
142
+ // Timestamps
143
+ // ----------------------------------------------------------------------------
144
+ /** One minute in seconds */
145
+ export const MINUTE = 60;
146
+ /** One hour in seconds */
147
+ export const HOUR = 60 * MINUTE;
148
+ /** One day in seconds */
149
+ export const DAY = 24 * HOUR;
150
+ /** One week in seconds */
151
+ export const WEEK = 7 * DAY;
152
+ /** One month in seconds (approximate) */
153
+ export const MONTH = 30 * DAY;
154
+ /** One quarter in seconds (approximate) */
155
+ export const QUARTER = 90 * DAY;
156
+ /** One year in seconds (approximate) */
157
+ export const YEAR = 365 * DAY;
158
+ /**
159
+ * Multiplies time unit by count
160
+ * @param unit - Time unit in seconds
161
+ * @param count - Number of units
162
+ * @returns Total seconds
163
+ */
164
+ export const int = (unit, count = 1) => unit * count;
165
+ /** Returns current Unix timestamp in seconds */
166
+ export const now = () => Math.round(Date.now() / 1000);
167
+ /**
168
+ * Returns Unix timestamp from specified time ago
169
+ * @param unit - Time unit in seconds
170
+ * @param count - Number of units
171
+ * @returns Timestamp in seconds
172
+ */
173
+ export const ago = (unit, count = 1) => now() - int(unit, count);
174
+ /**
175
+ * Converts seconds to milliseconds
176
+ * @param seconds - Time in seconds
177
+ * @returns Time in milliseconds
178
+ */
179
+ export const ms = (seconds) => seconds * 1000;
180
+ // ----------------------------------------------------------------------------
181
+ // Sequences
182
+ // ----------------------------------------------------------------------------
183
+ /**
184
+ * Returns the first element of an array
185
+ * @param xs - The array
186
+ * @returns First element or undefined
187
+ */
188
+ export const first = (xs, ...args) => {
189
+ for (const x of xs) {
190
+ return x;
191
+ }
192
+ };
193
+ /**
194
+ * Returns the first element of the first array in a nested array
195
+ * @param xs - Array of arrays
196
+ * @returns First element of first array or undefined
197
+ */
198
+ export const ffirst = (xs, ...args) => {
199
+ for (const chunk of xs) {
200
+ for (const x of chunk) {
201
+ return x;
202
+ }
203
+ }
204
+ };
205
+ /**
206
+ * Returns the last element of an array
207
+ * @param xs - The array
208
+ * @returns Last element or undefined
209
+ */
210
+ export const last = (xs, ...args) => {
211
+ const a = Array.from(xs);
212
+ return a[a.length - 1];
213
+ };
214
+ /**
215
+ * Returns array with first n elements removed
216
+ * @param n - Number of elements to drop
217
+ * @param xs - Input array
218
+ * @returns Array with first n elements removed
219
+ */
220
+ export const drop = (n, xs) => Array.from(xs).slice(n);
221
+ /**
222
+ * Returns first n elements of array
223
+ * @param n - Number of elements to take
224
+ * @param xs - Input array
225
+ * @returns Array of first n elements
226
+ */
227
+ export const take = (n, xs) => Array.from(xs).slice(0, n);
228
+ /**
229
+ * Concatenates multiple arrays, filtering out null/undefined
230
+ * @param xs - Arrays to concatenate
231
+ * @returns Combined array
232
+ */
233
+ export const concat = (...xs) => xs.flatMap(x => (x === undefined ? [] : x));
234
+ /**
235
+ * Appends element to array
236
+ * @param x - Element to append
237
+ * @param xs - Array to append to
238
+ * @returns New array with element appended
239
+ */
240
+ export const append = (x, xs) => concat(xs, [x]);
241
+ /**
242
+ * Creates union of two arrays
243
+ * @param a - First array
244
+ * @param b - Second array
245
+ * @returns Array containing unique elements from both arrays
246
+ */
247
+ export const union = (a, b) => uniq([...a, ...b]);
248
+ /**
249
+ * Returns elements common to both arrays
250
+ * @param a - First array
251
+ * @param b - Second array
252
+ * @returns Array of elements present in both inputs
253
+ */
254
+ export const intersection = (a, b) => {
255
+ const s = new Set(b);
256
+ return a.filter(x => s.has(x));
257
+ };
258
+ /**
259
+ * Returns elements in first array not present in second
260
+ * @param a - Source array
261
+ * @param b - Array of elements to exclude
262
+ * @returns Array containing elements unique to first array
263
+ */
264
+ export const difference = (a, b) => {
265
+ const s = new Set(b);
266
+ return a.filter(x => !s.has(x));
267
+ };
268
+ /**
269
+ * Removes all instances of an element from array
270
+ * @param a - Element to remove
271
+ * @param xs - Source array
272
+ * @returns New array with element removed
273
+ */
274
+ export const remove = (a, xs) => xs.filter(x => x !== a);
275
+ /**
276
+ * Returns elements from second array not present in first
277
+ * @param a - Array of elements to exclude
278
+ * @param b - Source array
279
+ * @returns Filtered array
280
+ */
281
+ export const without = (a, b) => b.filter(x => !a.includes(x));
282
+ /**
283
+ * Toggles presence of element in array
284
+ * @param x - Element to toggle
285
+ * @param xs - Source array
286
+ * @returns New array with element added or removed
287
+ */
288
+ export const toggle = (x, xs) => (xs.includes(x) ? remove(x, xs) : append(x, xs));
289
+ /**
290
+ * Generates sequence of numbers from a to b
291
+ * @param a - Start number (inclusive)
292
+ * @param b - End number (exclusive)
293
+ * @param step - Increment between numbers
294
+ * @yields Numbers in sequence
295
+ */
296
+ export function* range(a, b, step = 1) {
297
+ for (let i = a; i < b; i += step) {
298
+ yield i;
299
+ }
300
+ }
301
+ /**
302
+ * Yields indexed items
303
+ * @param items - A collection of items
304
+ * @yields tuples of [index, item]
305
+ */
306
+ export function* enumerate(items) {
307
+ for (let i = 0; i < items.length; i += 1) {
308
+ yield [i, items[i]];
309
+ }
310
+ }
311
+ /** Returns a function that gets property value from object */
312
+ export const pluck = (k, xs) => xs.map(x => x[k]);
313
+ /**
314
+ * Creates object from array of key-value pairs
315
+ * @param pairs - Array of [key, value] tuples
316
+ * @returns Object with keys and values from pairs
317
+ */
318
+ export const fromPairs = (pairs) => {
319
+ const r = {};
320
+ for (const [k, v] of pairs) {
321
+ if (k && v) {
322
+ r[k] = v;
323
+ }
324
+ }
325
+ return r;
326
+ };
327
+ /**
328
+ * Flattens array of arrays into single array
329
+ * @param xs - Array of arrays to flatten
330
+ * @returns Flattened array
331
+ */
332
+ export const flatten = (xs) => xs.flatMap(identity);
333
+ /**
334
+ * Splits array into two arrays based on predicate
335
+ * @param f - Function to test elements
336
+ * @param xs - Array to partition
337
+ * @returns Tuple of [matching, non-matching] arrays
338
+ */
339
+ export const partition = (f, xs) => {
340
+ const a = [];
341
+ const b = [];
342
+ for (const x of xs) {
343
+ if (f(x)) {
344
+ a.push(x);
345
+ }
346
+ else {
347
+ b.push(x);
348
+ }
349
+ }
350
+ return [a, b];
351
+ };
352
+ /**
353
+ * Returns array with duplicate elements removed
354
+ * @param xs - Array with possible duplicates
355
+ * @returns Array with unique elements
356
+ */
357
+ export const uniq = (xs) => Array.from(new Set(xs));
358
+ /**
359
+ * Returns array with elements unique by key function
360
+ * @param f - Function to generate key for each element
361
+ * @param xs - Input array
362
+ * @returns Array with elements unique by key
363
+ */
364
+ export const uniqBy = (f, xs) => {
365
+ const s = new Set();
366
+ const r = [];
367
+ for (const x of xs) {
368
+ const k = f(x);
369
+ if (s.has(k)) {
370
+ continue;
371
+ }
372
+ s.add(k);
373
+ r.push(x);
374
+ }
375
+ return r;
376
+ };
377
+ /**
378
+ * Returns sorted copy of array
379
+ * @param xs - Array to sort
380
+ * @returns New sorted array
381
+ */
382
+ export const sort = (xs) => [...xs].sort();
383
+ /**
384
+ * Returns array sorted by key function
385
+ * @param f - Function to generate sort key
386
+ * @param xs - Array to sort
387
+ * @returns Sorted array
388
+ */
389
+ export const sortBy = (f, xs) => [...xs].sort((a, b) => {
390
+ const x = f(a);
391
+ const y = f(b);
392
+ return x < y ? -1 : x > y ? 1 : 0;
393
+ });
394
+ /**
395
+ * Groups array elements by key function
396
+ * @param f - Function to generate group key
397
+ * @param xs - Array to group
398
+ * @returns Map of groups
399
+ */
400
+ export const groupBy = (f, xs) => {
401
+ const r = new Map();
402
+ for (const x of xs) {
403
+ const k = f(x);
404
+ let v = r.get(k);
405
+ if (!v) {
406
+ v = [];
407
+ r.set(k, v);
408
+ }
409
+ v.push(x);
410
+ }
411
+ return r;
412
+ };
413
+ /**
414
+ * Creates map from array using key function
415
+ * @param f - Function to generate key
416
+ * @param xs - Array to index
417
+ * @returns Map of values by key
418
+ */
419
+ export const indexBy = (f, xs) => {
420
+ const r = new Map();
421
+ for (const x of xs) {
422
+ r.set(f(x), x);
423
+ }
424
+ return r;
425
+ };
426
+ /**
427
+ * Creates array of specified length using generator function
428
+ * @param n - Length of array
429
+ * @param f - Function to generate each element
430
+ * @returns Generated array
431
+ */
432
+ export const initArray = (n, f) => {
433
+ const result = [];
434
+ for (let i = 0; i < n; i++) {
435
+ result.push(f());
436
+ }
437
+ return result;
438
+ };
439
+ /**
440
+ * Splits array into chunks of specified length
441
+ * @param chunkLength - Maximum length of each chunk
442
+ * @param xs - Array to split
443
+ * @returns Array of chunks
444
+ */
445
+ export const chunk = (chunkLength, xs) => {
446
+ const result = [];
447
+ const current = [];
448
+ for (const item of xs) {
449
+ if (current.length < chunkLength) {
450
+ current.push(item);
451
+ }
452
+ else {
453
+ result.push(current.splice(0));
454
+ current.push(item);
455
+ }
456
+ }
457
+ if (current.length > 0) {
458
+ result.push(current);
459
+ }
460
+ return result;
461
+ };
462
+ /**
463
+ * Splits array into specified number of chunks
464
+ * @param n - Number of chunks
465
+ * @param xs - Array to split
466
+ * @returns Array of n chunks
467
+ */
468
+ export const chunks = (n, xs) => {
469
+ const result = initArray(n, () => []);
470
+ for (let i = 0; i < xs.length; i++) {
471
+ result[i % n].push(xs[i]);
472
+ }
473
+ return result;
474
+ };
475
+ /** Splits array into two parts at index */
476
+ export const splitAt = (n, xs) => [xs.slice(0, n), xs.slice(n)];
477
+ /** Inserts element into array at index */
478
+ export const insert = (n, x, xs) => [...xs.slice(0, n), x, ...xs.slice(n)];
479
+ /** Returns random element from array */
480
+ export const choice = (xs) => xs[Math.floor(xs.length * Math.random())];
481
+ /** Returns shuffled copy of iterable */
482
+ export const shuffle = (xs) => Array.from(xs).sort(() => (Math.random() > 0.5 ? 1 : -1));
483
+ /** Returns n random elements from array */
484
+ export const sample = (n, xs) => shuffle(xs).slice(0, n);
485
+ /** Checks if value is iterable */
486
+ export const isIterable = (x) => Symbol.iterator in Object(x);
487
+ /** Ensures value is iterable by wrapping in array if needed */
488
+ export const toIterable = (x) => (isIterable(x) ? x : [x]);
489
+ /** Ensures value is array by wrapping if needed */
490
+ export const ensurePlural = (x) => (x instanceof Array ? x : [x]);
491
+ // ----------------------------------------------------------------------------
492
+ // Objects
493
+ // ----------------------------------------------------------------------------
494
+ /**
495
+ * Checks if value is a plain object
496
+ * @param obj - Value to check
497
+ * @returns True if value is a plain object
498
+ */
499
+ export const isPojo = (obj) => {
500
+ if (obj === null || typeof obj !== "object") {
501
+ return false;
502
+ }
503
+ return Object.getPrototypeOf(obj) === Object.prototype;
504
+ };
505
+ /**
506
+ * Creates new object with only specified keys
507
+ * @param ks - Keys to keep
508
+ * @param x - Source object
509
+ * @returns New object with only specified keys
510
+ */
511
+ export const pick = (ks, x) => {
512
+ const r = { ...x };
513
+ for (const k of Object.keys(x)) {
514
+ if (!ks.includes(k)) {
515
+ delete r[k];
516
+ }
517
+ }
518
+ return r;
519
+ };
520
+ /**
521
+ * Creates new object with specified keys removed
522
+ * @param ks - Keys to remove
523
+ * @param x - Source object
524
+ * @returns New object without specified keys
525
+ */
526
+ export const omit = (ks, x) => {
527
+ const r = { ...x };
528
+ for (const k of ks) {
529
+ delete r[k];
530
+ }
531
+ return r;
532
+ };
533
+ /**
534
+ * Creates new object excluding entries with specified values
535
+ * @param xs - Values to exclude
536
+ * @param x - Source object
537
+ * @returns New object without entries containing specified values
538
+ */
539
+ export const omitVals = (xs, x) => {
540
+ const r = {};
541
+ for (const [k, v] of Object.entries(x)) {
542
+ if (!xs.includes(v)) {
543
+ r[k] = v;
544
+ }
545
+ }
546
+ return r;
547
+ };
548
+ /**
549
+ * Filters object values based on predicate
550
+ * @param f - Function to test values
551
+ * @param x - Object to filter
552
+ * @returns Object with only values that pass predicate
553
+ */
554
+ export const filterVals = (f, x) => {
555
+ const r = {};
556
+ for (const k in x) {
557
+ if (f(x[k])) {
558
+ r[k] = x[k];
559
+ }
560
+ }
561
+ return r;
562
+ };
563
+ /**
564
+ * Creates new object with transformed keys
565
+ * @param f - Function to transform keys
566
+ * @param x - Source object
567
+ * @returns Object with transformed keys
568
+ */
569
+ export const mapKeys = (f, x) => {
570
+ const r = {};
571
+ for (const [k, v] of Object.entries(x)) {
572
+ r[f(k)] = v;
573
+ }
574
+ return r;
575
+ };
576
+ /**
577
+ * Creates new object with transformed values
578
+ * @param f - Function to transform values
579
+ * @param x - Source object
580
+ * @returns Object with transformed values
581
+ */
582
+ export const mapVals = (f, x) => {
583
+ const r = {};
584
+ for (const [k, v] of Object.entries(x)) {
585
+ r[k] = f(v);
586
+ }
587
+ return r;
588
+ };
589
+ /**
590
+ * Merges two objects, with left object taking precedence
591
+ * @param a - Left object
592
+ * @param b - Right object
593
+ * @returns Merged object with a"s properties overriding b"s
594
+ */
595
+ export const mergeLeft = (a, b) => ({
596
+ ...b,
597
+ ...a,
598
+ });
599
+ /**
600
+ * Merges two objects, with right object taking precedence
601
+ * @param a - Left object
602
+ * @param b - Right object
603
+ * @returns Merged object with b"s properties overriding a"s
604
+ */
605
+ export const mergeRight = (a, b) => ({
606
+ ...a,
607
+ ...b,
608
+ });
609
+ /** Deep merge two objects, prioritizing the first argument. */
610
+ export const deepMergeLeft = (a, b) => deepMergeRight(b, a);
611
+ /** Deep merge two objects, prioritizing the second argument. */
612
+ export const deepMergeRight = (a, b) => {
613
+ a = { ...a };
614
+ for (const [k, v] of Object.entries(b)) {
615
+ if (isPojo(v) && isPojo(a[k])) {
616
+ a[k] = deepMergeRight(a[k], v);
617
+ }
618
+ else {
619
+ a[k] = v;
620
+ }
621
+ }
622
+ return a;
623
+ };
624
+ /**
625
+ * Switches on key in object, with default fallback
626
+ * @param k - Key to look up
627
+ * @param m - Object with values and optional default
628
+ * @returns Value at key or default value
629
+ */
630
+ export const switcher = (k, m) => m[k] === undefined ? m.default : m[k];
631
+ // ----------------------------------------------------------------------------
632
+ // Combinators
633
+ // ----------------------------------------------------------------------------
634
+ /** Returns a function that returns the boolean negation of the given function */
635
+ export const complement = (f) => (...args) => !f(...args);
636
+ /**
637
+ * Safely executes function and handles errors
638
+ * @param f - Function to execute
639
+ * @param onError - Optional error handler
640
+ * @returns Function result or undefined if error
641
+ */
642
+ export const tryCatch = (f, onError) => {
643
+ try {
644
+ const r = f();
645
+ if (r instanceof Promise) {
646
+ r.catch(e => onError?.(e));
647
+ }
648
+ return r;
649
+ }
650
+ catch (e) {
651
+ onError?.(e);
652
+ }
653
+ return undefined;
654
+ };
655
+ /**
656
+ * Creates function that only executes once
657
+ * @param f - Function to wrap
658
+ * @returns Function that executes f only on first call
659
+ */
660
+ export const once = (f) => {
661
+ let called = false;
662
+ return (...args) => {
663
+ if (!called) {
664
+ called = true;
665
+ f(...args);
666
+ }
667
+ };
668
+ };
669
+ /**
670
+ * Calls a function
671
+ * @param f - Function to call
672
+ * @returns Whatever f returns
673
+ */
674
+ export const call = (f, ...args) => f();
675
+ /**
676
+ * Memoizes function results based on arguments
677
+ * @param f - Function to memoize
678
+ * @returns Memoized function
679
+ */
680
+ export const memoize = (f) => {
681
+ let prevArgs;
682
+ let result;
683
+ return (...args) => {
684
+ if (!equals(prevArgs, args)) {
685
+ prevArgs = args;
686
+ result = f(...args);
687
+ }
688
+ return result;
689
+ };
690
+ };
691
+ /**
692
+ * Executes a function if the value is defined
693
+ * @param x - The value to check
694
+ * @param f - Function to execute if x is defined
695
+ * @returns Result of f(x) if x is defined, undefined otherwise
696
+ */
697
+ export const ifLet = (x, f) => x === undefined ? undefined : f(x);
698
+ // ----------------------------------------------------------------------------
699
+ // Randomness
700
+ // ----------------------------------------------------------------------------
701
+ /**
702
+ * Generates random integer between min and max (inclusive)
703
+ * @param min - Minimum value
704
+ * @param max - Maximum value
705
+ * @returns Random integer
706
+ */
707
+ export const randomInt = (min = 0, max = 9) => min + Math.round(Math.random() * (max - min));
708
+ /**
709
+ * Generates random string ID
710
+ * @returns Random string suitable for use as an ID
711
+ */
712
+ export const randomId = () => Math.random().toString().slice(2);
713
+ // ----------------------------------------------------------------------------
714
+ // Async
715
+ // ----------------------------------------------------------------------------
716
+ /**
717
+ * Creates a promise that resolves after specified time
718
+ * @param t - Time in milliseconds
719
+ * @returns Promise that resolves after t milliseconds
720
+ */
721
+ export const sleep = (t) => new Promise(resolve => setTimeout(resolve, t));
722
+ /**
723
+ * Creates a microtask that yields to other tasks in the event loop
724
+ * @returns Promise that resolves after yielding
725
+ */
726
+ export const yieldThread = () => {
727
+ if (typeof window !== "undefined" &&
728
+ "scheduler" in window &&
729
+ "yield" in window.scheduler) {
730
+ return window.scheduler.yield();
731
+ }
732
+ return new Promise(resolve => {
733
+ setTimeout(resolve, 0);
734
+ });
735
+ };
736
+ /**
737
+ * Creates throttled version of function
738
+ * @param ms - Minimum time between calls
739
+ * @param f - Function to throttle
740
+ * @returns Throttled function
741
+ */
742
+ export const throttle = (ms, f) => {
743
+ if (ms === 0) {
744
+ return f;
745
+ }
746
+ let paused = false;
747
+ let nextArgs;
748
+ const unpause = () => {
749
+ if (nextArgs) {
750
+ f(...nextArgs);
751
+ nextArgs = undefined;
752
+ }
753
+ paused = false;
754
+ };
755
+ return (...thisArgs) => {
756
+ if (!paused) {
757
+ f(...thisArgs);
758
+ paused = true;
759
+ setTimeout(unpause, ms);
760
+ }
761
+ else {
762
+ nextArgs = thisArgs;
763
+ }
764
+ };
765
+ };
766
+ /**
767
+ * Creates throttled function that returns cached value
768
+ * @param ms - Minimum time between updates
769
+ * @param f - Function to throttle
770
+ * @returns Function returning latest value
771
+ */
772
+ export const throttleWithValue = (ms, f) => {
773
+ let value;
774
+ const update = throttle(ms, () => {
775
+ value = f();
776
+ });
777
+ return () => {
778
+ update();
779
+ return value;
780
+ };
781
+ };
782
+ /**
783
+ * Creates batching function that collects items
784
+ * this function does not delay execution, if a series of items is passed in sequence
785
+ * the first item will be processed immediately, and the rest will be batched
786
+ * @param t - Time window for batching
787
+ * @param f - Function to process batch
788
+ * @returns Function that adds items to batch
789
+ */
790
+ export const batch = (t, f) => {
791
+ const xs = [];
792
+ const cb = throttle(t, () => xs.length > 0 && f(xs.splice(0)));
793
+ return (x) => {
794
+ xs.push(x);
795
+ cb();
796
+ };
797
+ };
798
+ /**
799
+ * Creates batching function that returns results
800
+ * @param t - Time window for batching
801
+ * @param execute - Function to process batch
802
+ * @returns Function that returns promise of result
803
+ */
804
+ export const batcher = (t, execute) => {
805
+ const queue = [];
806
+ const _execute = async () => {
807
+ const items = queue.splice(0);
808
+ const results = await execute(items.map(item => item.request));
809
+ results.forEach(async (r, i) => {
810
+ if (results.length === items.length) {
811
+ items[i].resolve(await r);
812
+ }
813
+ else {
814
+ items[i].reject("Execute must return a result for each request");
815
+ }
816
+ });
817
+ };
818
+ return (request) => new Promise((resolve, reject) => {
819
+ if (queue.length === 0) {
820
+ setTimeout(_execute, t);
821
+ }
822
+ queue.push({ request, resolve, reject });
823
+ });
824
+ };
825
+ // ----------------------------------------------------------------------------
826
+ // URLs
827
+ // ----------------------------------------------------------------------------
828
+ /**
829
+ * Removes protocol (http://, https://, etc) from URL
830
+ * @param url - URL to process
831
+ * @returns URL without protocol
832
+ */
833
+ export const stripProtocol = (url) => url.replace(/.*:\/\//, "");
834
+ /**
835
+ * Formats URL for display by removing protocol, www, and trailing slash
836
+ * @param url - URL to format
837
+ * @returns Formatted URL
838
+ */
839
+ export const displayUrl = (url) => stripProtocol(url)
840
+ .replace(/^(www\.)?/i, "")
841
+ .replace(/\/$/, "");
842
+ /**
843
+ * Extracts and formats domain from URL
844
+ * @param url - URL to process
845
+ * @returns Formatted domain name
846
+ */
847
+ export const displayDomain = (url) => displayUrl(first(url.split(/[\/\?]/)) || "");
848
+ // ----------------------------------------------------------------------------
849
+ // JSON, localStorage, fetch, event emitters, etc
850
+ // ----------------------------------------------------------------------------
851
+ /**
852
+ * Safely parses JSON string
853
+ * @param json - JSON string to parse
854
+ * @returns Parsed object or null if invalid
855
+ */
856
+ export const parseJson = (json) => {
857
+ if (!json)
858
+ return undefined;
859
+ try {
860
+ return JSON.parse(json);
861
+ }
862
+ catch (e) {
863
+ return undefined;
864
+ }
865
+ };
866
+ /**
867
+ * Gets and parses JSON from localStorage
868
+ * @param k - Storage key
869
+ * @returns Parsed value or undefined if invalid/missing
870
+ */
871
+ export const getJson = (k) => parseJson(localStorage.getItem(k) || "");
872
+ /**
873
+ * Stringifies and stores value in localStorage
874
+ * @param k - Storage key
875
+ * @param v - Value to store
876
+ */
877
+ export const setJson = (k, v) => localStorage.setItem(k, JSON.stringify(v));
878
+ /**
879
+ * Fetches JSON from URL with options
880
+ * @param url - URL to fetch from
881
+ * @param opts - Fetch options
882
+ * @returns Promise of parsed JSON response
883
+ */
884
+ export const fetchJson = async (url, opts = {}) => {
885
+ if (!opts.headers) {
886
+ opts.headers = {};
887
+ }
888
+ if (!opts.headers["Accept"]) {
889
+ opts.headers["Accept"] = "application/json";
890
+ }
891
+ const res = await fetch(url, opts);
892
+ const json = await res.json();
893
+ return json;
894
+ };
895
+ /**
896
+ * Posts JSON data to URL
897
+ * @param url - URL to post to
898
+ * @param data - Data to send
899
+ * @param opts - Additional fetch options
900
+ * @returns Promise of parsed JSON response
901
+ */
902
+ export const postJson = async (url, data, opts = {}) => {
903
+ if (!opts.method) {
904
+ opts.method = "POST";
905
+ }
906
+ if (!opts.headers) {
907
+ opts.headers = {};
908
+ }
909
+ opts.headers["Content-Type"] = "application/json";
910
+ opts.body = JSON.stringify(data);
911
+ return fetchJson(url, opts);
912
+ };
913
+ /**
914
+ * Uploads file to URL
915
+ * @param url - Upload URL
916
+ * @param file - File to upload
917
+ * @returns Promise of parsed JSON response
918
+ */
919
+ export const uploadFile = (url, file) => {
920
+ const body = new FormData();
921
+ body.append("file", file);
922
+ return fetchJson(url, { method: "POST", body });
923
+ };
924
+ /**
925
+ * A generic type-safe event listener function that works with event emitters.
926
+ *
927
+ * @param target - The event target object with add/remove listener methods
928
+ * @param eventName - The name of the event to listen for
929
+ * @param callback - The callback function to execute when the event occurs
930
+ * @returns A function that removes the event listener when called
931
+ */
932
+ export const on = (target, eventName, callback) => {
933
+ target.on(eventName, callback);
934
+ return () => {
935
+ target.off(eventName, callback);
936
+ };
937
+ };
938
+ // ----------------------------------------------------------------------------
939
+ // Strings
940
+ // ----------------------------------------------------------------------------
941
+ /**
942
+ * Truncates string to length, breaking at word boundaries
943
+ * @param s - String to truncate
944
+ * @param l - Maximum length
945
+ * @param suffix - String to append if truncated
946
+ * @returns Truncated string
947
+ */
948
+ export const ellipsize = (s, l, suffix = "...") => {
949
+ if (s.length < l * 1.1) {
950
+ return s;
951
+ }
952
+ while (s.length > l && s.includes(" ")) {
953
+ s = s.split(" ").slice(0, -1).join(" ");
954
+ }
955
+ return s + suffix;
956
+ };
957
+ /** Generates a hash string from input string */
958
+ export const hash = (s) => Math.abs(s.split("").reduce((a, b) => ((a << 5) - a + b.charCodeAt(0)) | 0, 0)).toString();
959
+ // ----------------------------------------------------------------------------
960
+ // Curried utilities for working with collections
961
+ // ----------------------------------------------------------------------------
962
+ /** Returns a function that gets the nth element of an array */
963
+ export const nth = (i) => (xs, ...args) => xs[i];
964
+ /** Returns a function that checks if nth element equals value */
965
+ export const nthEq = (i, v) => (xs, ...args) => xs[i] === v;
966
+ /** Returns a function that checks if nth element does not equal value */
967
+ export const nthNe = (i, v) => (xs, ...args) => xs[i] !== v;
968
+ /** Returns a function that checks if key/value pairs of x match all pairs in spec */
969
+ export const spec = (values) => (x, ...args) => {
970
+ if (Array.isArray(values)) {
971
+ for (let i = 0; i < values.length; i++) {
972
+ if (x[i] !== values[i]) {
973
+ return false;
974
+ }
975
+ }
976
+ }
977
+ else {
978
+ for (const [k, v] of Object.entries(values)) {
979
+ if (x[k] !== v)
980
+ return false;
981
+ }
982
+ }
983
+ return true;
984
+ };
985
+ /** Returns a function that checks equality with value */
986
+ export const eq = (v) => (x) => x === v;
987
+ /** Returns a function that checks inequality with value */
988
+ export const ne = (v) => (x) => x !== v;
989
+ /** Returns a function that gets property value from object */
990
+ export const prop = (k) => (x) => x[k];
991
+ /** Returns a function that adds/updates a property on object */
992
+ export const assoc = (k, v) => (o) => ({ ...o, [k]: v });
993
+ /** Returns a function that removes a property on object */
994
+ export const dissoc = (k) => (o) => omit([k], o);
995
+ // ----------------------------------------------------------------------------
996
+ // Sets
997
+ // ----------------------------------------------------------------------------
998
+ /**
999
+ * Adds value to Set at key in object
1000
+ * @param m - Object mapping keys to Sets
1001
+ * @param k - Key to add to
1002
+ * @param v - Value to add
1003
+ */
1004
+ export const addToKey = (m, k, v) => {
1005
+ const s = m[k] || new Set();
1006
+ s.add(v);
1007
+ m[k] = s;
1008
+ };
1009
+ /**
1010
+ * Pushes value to array at key in object
1011
+ * @param m - Object mapping keys to arrays
1012
+ * @param k - Key to push to
1013
+ * @param v - Value to push
1014
+ */
1015
+ export const pushToKey = (m, k, v) => {
1016
+ const a = m[k] || [];
1017
+ a.push(v);
1018
+ m[k] = a;
1019
+ };
1020
+ // ----------------------------------------------------------------------------
1021
+ // Maps
1022
+ // ----------------------------------------------------------------------------
1023
+ /**
1024
+ * Adds value to Set at key in Map
1025
+ * @param m - Map of Sets
1026
+ * @param k - Key to add to
1027
+ * @param v - Value to add
1028
+ */
1029
+ export const addToMapKey = (m, k, v) => {
1030
+ const s = m.get(k) || new Set();
1031
+ s.add(v);
1032
+ m.set(k, s);
1033
+ };
1034
+ /**
1035
+ * Pushes value to array at key in Map
1036
+ * @param m - Map of arrays
1037
+ * @param k - Key to push to
1038
+ * @param v - Value to push
1039
+ */
1040
+ export const pushToMapKey = (m, k, v) => {
1041
+ const a = m.get(k) || [];
1042
+ a.push(v);
1043
+ m.set(k, a);
1044
+ };
1045
+ // ----------------------------------------------------------------------------
1046
+ // Bech32 <-> hex encoding
1047
+ // ----------------------------------------------------------------------------
1048
+ /**
1049
+ * Converts hex string to bech32 format
1050
+ * @param prefix - Bech32 prefix
1051
+ * @param hex - Hex string to convert
1052
+ * @returns Bech32 encoded string
1053
+ */
1054
+ export const hexToBech32 = (prefix, hex) => bech32.encode(prefix, bech32.toWords(utf8.decode(hex)), false);
1055
+ /**
1056
+ * Converts bech32 string to hex format
1057
+ * @param b32 - Bech32 string to convert
1058
+ * @returns Hex encoded string
1059
+ */
1060
+ export const bech32ToHex = (b32) => utf8.encode(bech32.fromWords(bech32.decode(b32, false).words));
1061
+ //# sourceMappingURL=Tools.js.map