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