@reykjavik/webtools 0.1.30 → 0.1.32
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/CHANGELOG.md +17 -0
- package/README.md +293 -56
- package/async.d.ts +9 -5
- package/async.js +14 -8
- package/errorhandling.d.ts +99 -0
- package/errorhandling.js +107 -0
- package/esm/async.d.ts +9 -5
- package/esm/async.js +12 -7
- package/esm/errorhandling.d.ts +99 -0
- package/esm/errorhandling.js +102 -0
- package/esm/index.d.ts +1 -0
- package/index.d.ts +1 -0
- package/package.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,23 @@
|
|
|
4
4
|
|
|
5
5
|
- ... <!-- Add new lines here. -->
|
|
6
6
|
|
|
7
|
+
## 0.1.32
|
|
8
|
+
|
|
9
|
+
_2024-10-02_
|
|
10
|
+
|
|
11
|
+
- feat: Add module `@reykjavik/webtools/errorhandling` with `asError` and
|
|
12
|
+
`Result.*` helpers for safe, structured error handling with discriminated
|
|
13
|
+
`[error, result]` tuples.
|
|
14
|
+
|
|
15
|
+
## 0.1.31
|
|
16
|
+
|
|
17
|
+
_2024-09-23_
|
|
18
|
+
|
|
19
|
+
- `@reykjavik/webtools/async`:
|
|
20
|
+
- feat: `maxWait` returns full `PromiseSettledResult` objects
|
|
21
|
+
- fix: `maxWait` result objects should remain stable
|
|
22
|
+
- fix: `maxWait` should distinguish between unresolved and rejected promises
|
|
23
|
+
|
|
7
24
|
## 0.1.30
|
|
8
25
|
|
|
9
26
|
_2024-09-15_
|
package/README.md
CHANGED
|
@@ -24,11 +24,21 @@ bun add @reykjavik/webtools
|
|
|
24
24
|
- [Type `TTLConfig`](#type-ttlconfig)
|
|
25
25
|
- [`toSec` TTL helper](#tosec-ttl-helper)
|
|
26
26
|
- [`toMs` duration helper](#toms-duration-helper)
|
|
27
|
+
- [`@reykjavik/webtools/fixIcelandicLocale`](#reykjavikwebtoolsfixicelandiclocale)
|
|
28
|
+
- [Limitations](#limitations)
|
|
27
29
|
- [`@reykjavik/webtools/async`](#reykjavikwebtoolsasync)
|
|
28
30
|
- [`promiseAllObject`](#promiseallobject)
|
|
29
31
|
- [`maxWait`](#maxwait)
|
|
30
|
-
- [`@reykjavik/webtools/
|
|
31
|
-
- [
|
|
32
|
+
- [`@reykjavik/webtools/errorhandling`](#reykjavikwebtoolserrorhandling)
|
|
33
|
+
- [`asError`](#aserror)
|
|
34
|
+
- [`Result` Singleton](#result-singleton)
|
|
35
|
+
- [Type `ResultTuple`](#type-resulttuple)
|
|
36
|
+
- [Type `ResultTupleObj`](#type-resulttupleobj)
|
|
37
|
+
- [`Result.catch`](#resultcatch)
|
|
38
|
+
- [`Result.map`](#resultmap)
|
|
39
|
+
- [`Result.Success`](#resultsuccess)
|
|
40
|
+
- [`Result.Fail`](#resultfail)
|
|
41
|
+
- [`Result.throw`](#resultthrow)
|
|
32
42
|
- [`@reykjavik/webtools/SiteImprove`](#reykjavikwebtoolssiteimprove)
|
|
33
43
|
- [`SiteImprove` component](#siteimprove-component)
|
|
34
44
|
- [`pingSiteImprove` helper](#pingsiteimprove-helper)
|
|
@@ -236,6 +246,70 @@ const ttlSec = toMs(ttl);
|
|
|
236
246
|
|
|
237
247
|
---
|
|
238
248
|
|
|
249
|
+
## `@reykjavik/webtools/fixIcelandicLocale`
|
|
250
|
+
|
|
251
|
+
As of early 2024, Google Chrome still does not support the Icelandic locale
|
|
252
|
+
`is`/`is-IS` in any way. Meanwhile other browsers have supported it for over a
|
|
253
|
+
decade.
|
|
254
|
+
|
|
255
|
+
This module patches the following methods/classes by substituting the `is`
|
|
256
|
+
locale with `da` (Danish) and apply a few post-hoc fixes to their return
|
|
257
|
+
values.
|
|
258
|
+
|
|
259
|
+
- `Intl.Collator` and `String.prototype.localeCompare` (\*)
|
|
260
|
+
- `Intl.NumberFormat` and `Number.prototype.toLocaleString` (\*)
|
|
261
|
+
- `Intl.DateTimeFormat` and `Date.prototype.toLocaleString`,
|
|
262
|
+
`.toLocaleDateString`, and `.toLocaleTimeString` (\*)
|
|
263
|
+
- `Intl.RelativeDateFormat`
|
|
264
|
+
- `Intl.PluralRules`
|
|
265
|
+
- `Intl.ListFormat`
|
|
266
|
+
|
|
267
|
+
(\*) The results are quite usable, but not entirely perfect. The
|
|
268
|
+
limitations/caveats are listed below.
|
|
269
|
+
|
|
270
|
+
To apply the patch, simply "side-effect import" this module at the top of your
|
|
271
|
+
app's entry point:
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
import '@reykjavik/webtools/fixIcelandicLocale';
|
|
275
|
+
|
|
276
|
+
// Then continue with your day and use `localeCompare` and other Intl.* methods
|
|
277
|
+
// as you normally would. (See "limitations" below.)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
(**NOTE** The patch is only applied in engines that fail a simple feature
|
|
281
|
+
detection test.)
|
|
282
|
+
|
|
283
|
+
### Limitations
|
|
284
|
+
|
|
285
|
+
**`Intl.Collator` and `localeCompare`:**
|
|
286
|
+
|
|
287
|
+
- It sorts initial letters correctly but in the rest of the string, it
|
|
288
|
+
incorrectly treats `ð` and `d` as the same letter (most of the time), and
|
|
289
|
+
lumps the acute-accented characters `á`, `é`, `í`, `ó`, `ú` and `ý` in with
|
|
290
|
+
their non-accented counterparts.
|
|
291
|
+
|
|
292
|
+
**`Intl.NumberFormat` and `toLocaleString`:**
|
|
293
|
+
|
|
294
|
+
- The `style: "unit"` option is not supported and prints units in Danish. (Soo
|
|
295
|
+
many units and unit-variants…)
|
|
296
|
+
- The `currencyDisplay: "name"` option is not supported and prints the
|
|
297
|
+
currency's full name in Danish.
|
|
298
|
+
|
|
299
|
+
**`Intl.DateTimeFormat` and `toLocaleDateString`:**
|
|
300
|
+
|
|
301
|
+
- The `month: 'narrow'` and `weekday: 'narrow'` options are not supported, and
|
|
302
|
+
print the corresponding Danish initials.
|
|
303
|
+
- For `timeZoneName` the values `"long"`, `"shortGeneric"` and `"longGeneric"`
|
|
304
|
+
will appear in Danish.
|
|
305
|
+
- The `timeStyle: 'full'` option prints the timezone names in Danish
|
|
306
|
+
- The `dayPeriod` option has a couple of slight mismatches, at 5 am and 12
|
|
307
|
+
noon.
|
|
308
|
+
|
|
309
|
+
We eagerly accept bugfixes, additions, etc. to this module!
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
239
313
|
## `@reykjavik/webtools/async`
|
|
240
314
|
|
|
241
315
|
Contains a few small helpers for working with async functions and promises.
|
|
@@ -265,23 +339,21 @@ const { user, posts } = await promiseAllObject({
|
|
|
265
339
|
|
|
266
340
|
**Syntax:** `maxWait(timeout: number, promises: Array<any>): Promise<void>`
|
|
267
341
|
**Syntax:**
|
|
268
|
-
`maxWait<T extends PlainObj>(timeout: number, promises: T): Promise<{ [K in keyof T]:
|
|
342
|
+
`maxWait<T extends PlainObj>(timeout: number, promises: T): Promise<{ [K in keyof T]: PromiseSettledResult<T[K]> } | undefined }>`
|
|
269
343
|
|
|
270
|
-
This somewhat esoteric helper resolves soon
|
|
271
|
-
have resolved
|
|
344
|
+
This somewhat esoteric helper resolves soon when all of the passed `promises`
|
|
345
|
+
have settled (resolved or rejected), OR after `timeout` milliseconds —
|
|
346
|
+
whichever comes first.
|
|
272
347
|
|
|
273
348
|
If an object is passed, the resolved value will be an object with the same
|
|
274
|
-
keys,
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
If any of the promises reject, their values become undefined in the returned
|
|
278
|
-
object.
|
|
349
|
+
keys, and any settled values in a `PromiseSettledResult` object, and
|
|
350
|
+
`undefined` for any promises that didn't settle in time.
|
|
279
351
|
|
|
280
352
|
```ts
|
|
281
353
|
import { maxWait } from '@reykjavik/webtools/async';
|
|
282
354
|
|
|
283
|
-
const user = fetchUser();
|
|
284
|
-
const posts = fetchPosts();
|
|
355
|
+
const user = fetchUser(); // Promise<User>
|
|
356
|
+
const posts = fetchPosts(); // Promise<Array<Post>>
|
|
285
357
|
|
|
286
358
|
// Array of promises resolves to void
|
|
287
359
|
await maxWait(500, [user, posts]);
|
|
@@ -291,72 +363,237 @@ const { user, posts } = await maxWait(500, { user, posts });
|
|
|
291
363
|
|
|
292
364
|
console.log(user?.value); // undefined | User
|
|
293
365
|
console.log(posts?.value); // undefined | Array<Post>
|
|
366
|
+
console.log(posts?.status); // 'fulfilled' | 'rejected'
|
|
367
|
+
console.log(posts?.reason); // undefined | unknown
|
|
294
368
|
```
|
|
295
369
|
|
|
296
370
|
---
|
|
297
371
|
|
|
298
|
-
## `@reykjavik/webtools/
|
|
372
|
+
## `@reykjavik/webtools/errorhandling`
|
|
299
373
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
decade.
|
|
374
|
+
A small set of lightweight tools for handling errors and promises in a safer,
|
|
375
|
+
more structured, FP-style way.
|
|
303
376
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
values.
|
|
377
|
+
Errors are always the first return value to promote early, explicit error
|
|
378
|
+
handling.
|
|
307
379
|
|
|
308
|
-
|
|
309
|
-
- `Intl.NumberFormat` and `Number.prototype.toLocaleString` (\*)
|
|
310
|
-
- `Intl.DateTimeFormat` and `Date.prototype.toLocaleString`,
|
|
311
|
-
`.toLocaleDateString`, and `.toLocaleTimeString` (\*)
|
|
312
|
-
- `Intl.RelativeDateFormat`
|
|
313
|
-
- `Intl.PluralRules`
|
|
314
|
-
- `Intl.ListFormat`
|
|
380
|
+
### `asError`
|
|
315
381
|
|
|
316
|
-
(
|
|
317
|
-
limitations/caveats are listed below.
|
|
382
|
+
**Syntax:** `asError(maybeError: unknown): ErrorFromPayload`
|
|
318
383
|
|
|
319
|
-
|
|
320
|
-
|
|
384
|
+
Guarantees that a caught (`catch (e)`) value of `unknown` type, is indeed an
|
|
385
|
+
`Error` instance.
|
|
386
|
+
|
|
387
|
+
If the input is an `Error` instance, it is returned as-is. If the input is
|
|
388
|
+
something else it is wrapped in a new `ErrorFromPayload` instance, and the
|
|
389
|
+
original value is stored in a `payload`
|
|
321
390
|
|
|
322
391
|
```ts
|
|
323
|
-
import '@reykjavik/webtools/
|
|
392
|
+
import { asError, type ErrorFromPayload } from '@reykjavik/webtools/errorhandling';
|
|
393
|
+
|
|
394
|
+
const theError = new Error('Something went wrong');
|
|
395
|
+
try {
|
|
396
|
+
throw theError;
|
|
397
|
+
} catch (err) {
|
|
398
|
+
const error = asError(theError);
|
|
399
|
+
console.error(error === theError); // true
|
|
400
|
+
console.error('patload' in error); // false
|
|
401
|
+
}
|
|
324
402
|
|
|
325
|
-
|
|
326
|
-
|
|
403
|
+
const someObject = ['Something went wrong'];
|
|
404
|
+
try {
|
|
405
|
+
throw someObject;
|
|
406
|
+
} catch (err) {
|
|
407
|
+
const error = asError(someObject);
|
|
408
|
+
console.error(error === someObject); // false
|
|
409
|
+
console.error(error.message === someObject.join(',')); // false
|
|
410
|
+
console.error(error instanceOf ErrorFromPayload); // true
|
|
411
|
+
console.error(error.payload === someObject); // true
|
|
412
|
+
}
|
|
327
413
|
```
|
|
328
414
|
|
|
329
|
-
|
|
330
|
-
detection test.)
|
|
415
|
+
### `Result` Singleton
|
|
331
416
|
|
|
332
|
-
|
|
417
|
+
Singleton object with the following small methods for creating, mapping or
|
|
418
|
+
handling `ResultTupleObj` instances:
|
|
333
419
|
|
|
334
|
-
|
|
420
|
+
- `Result.Success`
|
|
421
|
+
- `Result.Fail`
|
|
422
|
+
- `Result.catch`
|
|
423
|
+
- `Result.map`
|
|
424
|
+
- `Result.throw`
|
|
335
425
|
|
|
336
|
-
|
|
337
|
-
the acute-accented characters `á`, `é`, `í`, `ó`, `ú` and `ý` get lumped in
|
|
338
|
-
with their non-accented counterparts (unless the compared).
|
|
339
|
-
We fix this only for the first letter in the string, but not for the rest of
|
|
340
|
-
it.
|
|
426
|
+
### Type `ResultTuple`
|
|
341
427
|
|
|
342
|
-
|
|
428
|
+
**Syntax:** `ResultTuple<ResultType, OptionalErrorType>`
|
|
343
429
|
|
|
344
|
-
|
|
345
|
-
many units and unit-variants…)
|
|
346
|
-
- The `currencyDisplay: "name"` option is not supported and prints the
|
|
347
|
-
currency's full name in Danish.
|
|
430
|
+
(Also aliased as `Result.Tuple`)
|
|
348
431
|
|
|
349
|
-
|
|
432
|
+
Simple bare-bones discriminated tuple type for a `[error, result]` pair.
|
|
350
433
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
- For `timeZoneName` the values `"long"`, `"shortGeneric"` and `"longGeneric"`
|
|
354
|
-
will appear in Danish.
|
|
355
|
-
- The `timeStyle: 'full'` option prints the timezone names in Danish
|
|
356
|
-
- The `dayPeriod` option has a couple of slight mismatches, at 5 am and 12
|
|
357
|
-
noon.
|
|
434
|
+
```ts
|
|
435
|
+
import { type ResultTuple } from '@reykjavik/webtools/errorhandling';
|
|
358
436
|
|
|
359
|
-
|
|
437
|
+
declare const myResult: ResultTuple<string, Error>;
|
|
438
|
+
|
|
439
|
+
const [error, result] = myResult;
|
|
440
|
+
// Either `error` or `result` will be `undefined`
|
|
441
|
+
|
|
442
|
+
if (error) {
|
|
443
|
+
// Here `error` is an Error instance
|
|
444
|
+
console.error(error.message);
|
|
445
|
+
} else {
|
|
446
|
+
// Here `result` is guaranteed to be a string
|
|
447
|
+
console.log(result);
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Type `ResultTupleObj`
|
|
452
|
+
|
|
453
|
+
**Syntax:** `ResultTupleObj<ResultType, OptionalErrorType>`
|
|
454
|
+
|
|
455
|
+
(Also aliased as `Result.TupleObj`)
|
|
456
|
+
|
|
457
|
+
Discriminated tuple type for a `[error, result]` pair (same as `ResultTuple`)
|
|
458
|
+
but with named properties `error` and `result` attached for dev convenience.
|
|
459
|
+
|
|
460
|
+
```ts
|
|
461
|
+
import { type ResultTuple } from '@reykjavik/webtools/errorhandling';
|
|
462
|
+
|
|
463
|
+
declare const myResult: ResultTuple<string, Error>;
|
|
464
|
+
|
|
465
|
+
const [error, result] = myResult;
|
|
466
|
+
// Either `error` or `result` will be `undefined`
|
|
467
|
+
|
|
468
|
+
if (error) {
|
|
469
|
+
// Here `error` is an Error instance
|
|
470
|
+
console.error(error.message);
|
|
471
|
+
} else {
|
|
472
|
+
// Here `result` is guaranteed to be a string
|
|
473
|
+
console.log(result);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// But `myResults` also has named properties, for convenience
|
|
477
|
+
if (myResult.error) {
|
|
478
|
+
// Here `myResult.error` is an Error instance
|
|
479
|
+
console.error(myResult.error.message);
|
|
480
|
+
} else {
|
|
481
|
+
// Here `myResult.result` is a string
|
|
482
|
+
console.log(myResult.result);
|
|
483
|
+
}
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### `Result.catch`
|
|
487
|
+
|
|
488
|
+
**Syntax:** `Result.catch<T, Err>(callback: () => T): ResultTupleObj<T, Err>`
|
|
489
|
+
**Syntax:**
|
|
490
|
+
`Result.catch<T, Err>(promise: Promise<T>): Promise<ResultTupleObj<T, Err>>`
|
|
491
|
+
|
|
492
|
+
Error handling utility that wraps a promise or a callback function.
|
|
493
|
+
|
|
494
|
+
Catches errors and returns a `ResultTupleObj` — a nice discriminated
|
|
495
|
+
`[error, results]` tuple with the `result` and `error` also attached as named
|
|
496
|
+
properties.
|
|
497
|
+
|
|
498
|
+
Works on both promises and sync callback functions.
|
|
499
|
+
|
|
500
|
+
```ts
|
|
501
|
+
import { Result } from '@reykjavik/webtools/errorhandling';
|
|
502
|
+
|
|
503
|
+
// Callback:
|
|
504
|
+
const [error, fooObject] = Result.catch(() => getFooSyncMayThrow());
|
|
505
|
+
// Promise:
|
|
506
|
+
const [error, fooObject] = await Result.catch(getFooPromiseMayThrow());
|
|
507
|
+
|
|
508
|
+
// Example of object property access:
|
|
509
|
+
const fooQuery = await Result.catch(getFooPromiseMayThrow());
|
|
510
|
+
if (fooQuery.error) {
|
|
511
|
+
console.log(fooQuery.error === fooQuery[0]); // true
|
|
512
|
+
throw fooQuery.error;
|
|
513
|
+
}
|
|
514
|
+
console.log(fooQuery.result === fooQuery[1]); // true
|
|
515
|
+
fooQuery.result; // Guaranteed to be defined
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
This function acts as the inverse of [`Result.throw()`](#resultthrow).
|
|
519
|
+
|
|
520
|
+
### `Result.map`
|
|
521
|
+
|
|
522
|
+
**Syntax:**
|
|
523
|
+
`Result.map<T, T2, E>(result: ResultTuple<T, E>, mapResult: (resultValue: T) => T2): ResultTuple<T2, E>`
|
|
524
|
+
|
|
525
|
+
Helper to map a `ResultTuple`-like object to a new `ResultTupleObj` object,
|
|
526
|
+
applying a transformation function to the result, but retaining the error
|
|
527
|
+
as-is.
|
|
528
|
+
|
|
529
|
+
```ts
|
|
530
|
+
import { Result } from '@reykjavik/webtools/errorhandling';
|
|
531
|
+
|
|
532
|
+
const getStrLength = (str: string) => str.length;
|
|
533
|
+
|
|
534
|
+
const resultTuple =
|
|
535
|
+
Math.random() < 0.5 ? [new Error('Fail')] : [undefined, 'Hello!'];
|
|
536
|
+
|
|
537
|
+
const [error, mappedResult] = Result.map(resultTuple, getStrLength);
|
|
538
|
+
|
|
539
|
+
if (result) {
|
|
540
|
+
console.log(result); // 6
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### `Result.Success`
|
|
545
|
+
|
|
546
|
+
**Syntax:** `Result.Success<T>(result: T): ResultTuple<T>`
|
|
547
|
+
|
|
548
|
+
Factory for creating a successful `ResultTupleObj`.
|
|
549
|
+
|
|
550
|
+
```ts
|
|
551
|
+
import { Result } from '@reykjavik/webtools/errorhandling';
|
|
552
|
+
|
|
553
|
+
const happyResult: Result.SuccessObj<string> =
|
|
554
|
+
Result.Success('My result value');
|
|
555
|
+
|
|
556
|
+
console.log(happyResult.error); // undefined
|
|
557
|
+
console.log(happyResult[0]); // undefined
|
|
558
|
+
console.log(happyResult.result); // 'My result value'
|
|
559
|
+
console.log(happyResult[1]); // 'My result value'
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
### `Result.Fail`
|
|
563
|
+
|
|
564
|
+
**Syntax:** `Result.Fail<E extends Error>(err: T): ResultTuple<unknown, Err>`
|
|
565
|
+
|
|
566
|
+
Factory for creating a failed `ResultTupleObj`.
|
|
567
|
+
|
|
568
|
+
```ts
|
|
569
|
+
import { Result } from '@reykjavik/webtools/errorhandling';
|
|
570
|
+
|
|
571
|
+
const happyResult: Result.FailObj<string> = Result.Fail(new Error('Oh no!'));
|
|
572
|
+
|
|
573
|
+
console.log(happyResult.error.message); // 'Oh no!'
|
|
574
|
+
console.log(happyResult[0].message); // 'Oh no!'
|
|
575
|
+
console.log(happyResult.result); // undefined
|
|
576
|
+
console.log(happyResult[1]); // undefined
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
### `Result.throw`
|
|
580
|
+
|
|
581
|
+
**Syntax:** `Result.throw<T>(result: ResultTuple<T>): T`
|
|
582
|
+
|
|
583
|
+
Unwraps a discriminated `ResultTuple`-like `[error, result]` tuple and throws
|
|
584
|
+
if there's an error, but returns the result otherwise.
|
|
585
|
+
|
|
586
|
+
```ts
|
|
587
|
+
import { Result } from '@reykjavik/webtools/errorhandling';
|
|
588
|
+
|
|
589
|
+
try {
|
|
590
|
+
const fooResults = Result.throw(await getFooResultsTuple());
|
|
591
|
+
} catch (fooError) {
|
|
592
|
+
// error is
|
|
593
|
+
}
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
This function acts as the inverse of [`Result.catch()`](#resultcatch).
|
|
360
597
|
|
|
361
598
|
---
|
|
362
599
|
|
package/async.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EitherObj } from '@reykjavik/hanna-utils';
|
|
1
2
|
type PlainObj = Record<string, unknown>;
|
|
2
3
|
/**
|
|
3
4
|
* Simple sleep function. Returns a promise that resolves after `length`
|
|
@@ -5,16 +6,19 @@ type PlainObj = Record<string, unknown>;
|
|
|
5
6
|
*/
|
|
6
7
|
export declare const sleep: (length: number) => Promise<void>;
|
|
7
8
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
9
|
+
* Returns a function that adds lag/delay to a promise chain,
|
|
10
|
+
* passing the promise payload through.
|
|
11
|
+
*/
|
|
12
|
+
export declare const addLag: (length: number) => <T>(res: T) => Promise<T>;
|
|
13
|
+
/**
|
|
14
|
+
* Resolves as soon as all of the passed `promises` have resolved/settled,
|
|
15
|
+
* or after `timeout` milliseconds — whichever comes first.
|
|
10
16
|
*
|
|
11
17
|
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#maxwait
|
|
12
18
|
*/
|
|
13
19
|
export declare function maxWait(timeout: number, promises: Array<unknown>): Promise<void>;
|
|
14
20
|
export declare function maxWait<PromiseMap extends PlainObj>(timeout: number, promises: PromiseMap): Promise<{
|
|
15
|
-
-readonly [K in keyof PromiseMap]:
|
|
16
|
-
value: Awaited<PromiseMap[K]>;
|
|
17
|
-
} | undefined;
|
|
21
|
+
-readonly [K in keyof PromiseMap]: EitherObj<PromiseFulfilledResult<Awaited<PromiseMap[K]>>, PromiseRejectedResult> | undefined;
|
|
18
22
|
}>;
|
|
19
23
|
/**
|
|
20
24
|
* A variation of `Promise.all()` that accepts an object with named promises
|
package/async.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.promiseAllObject = exports.maxWait = exports.sleep = void 0;
|
|
3
|
+
exports.promiseAllObject = exports.maxWait = exports.addLag = exports.sleep = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Simple sleep function. Returns a promise that resolves after `length`
|
|
6
6
|
* milliseconds.
|
|
7
7
|
*/
|
|
8
8
|
const sleep = (length) => new Promise((resolve) => setTimeout(resolve, length));
|
|
9
9
|
exports.sleep = sleep;
|
|
10
|
+
/**
|
|
11
|
+
* Returns a function that adds lag/delay to a promise chain,
|
|
12
|
+
* passing the promise payload through.
|
|
13
|
+
*/
|
|
14
|
+
const addLag = (length) => (res) => (0, exports.sleep)(length).then(() => res);
|
|
15
|
+
exports.addLag = addLag;
|
|
10
16
|
function maxWait(timeout, promises) {
|
|
11
17
|
if (Array.isArray(promises)) {
|
|
12
18
|
return Promise.race([
|
|
@@ -19,17 +25,17 @@ function maxWait(timeout, promises) {
|
|
|
19
25
|
Object.entries(promises).forEach(([key, value]) => {
|
|
20
26
|
if (value instanceof Promise) {
|
|
21
27
|
retObj[key] = undefined;
|
|
22
|
-
value
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
value.then((value) => {
|
|
29
|
+
retObj[key] = { status: 'fulfilled', value };
|
|
30
|
+
}, (reason) => {
|
|
31
|
+
retObj[key] = { status: 'rejected', reason };
|
|
32
|
+
});
|
|
27
33
|
}
|
|
28
34
|
else {
|
|
29
|
-
retObj[key] = { value };
|
|
35
|
+
retObj[key] = { status: 'fulfilled', value };
|
|
30
36
|
}
|
|
31
37
|
});
|
|
32
|
-
return
|
|
38
|
+
return Promise.resolve().then(() => ({ ...retObj }));
|
|
33
39
|
});
|
|
34
40
|
}
|
|
35
41
|
exports.maxWait = maxWait;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error subclass for thrown values that got cought and turned into an actual
|
|
3
|
+
* Error, with the thrown value as the `payload` property.
|
|
4
|
+
*
|
|
5
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#aserror
|
|
6
|
+
*/
|
|
7
|
+
export declare class ErrorFromPayload extends Error {
|
|
8
|
+
payload?: unknown;
|
|
9
|
+
constructor(payload: unknown);
|
|
10
|
+
name: string;
|
|
11
|
+
}
|
|
12
|
+
/**v
|
|
13
|
+
* Guarantees that a caught (`catch (e)`) value of `unknown` type,
|
|
14
|
+
* is indeed an `Error` instance.
|
|
15
|
+
*
|
|
16
|
+
*If the input is an `Error` instance, it is returned as-is. If the input is
|
|
17
|
+
* something else it is wrapped in a new `ErrorFromPayload` instance, and the
|
|
18
|
+
* original value is stored in a `payload`
|
|
19
|
+
*
|
|
20
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#aserror
|
|
21
|
+
*/
|
|
22
|
+
export declare const asError: (maybeError: unknown) => ErrorFromPayload;
|
|
23
|
+
type SuccessResult<T> = [error: undefined, result: T] & {
|
|
24
|
+
error?: undefined;
|
|
25
|
+
result: T;
|
|
26
|
+
};
|
|
27
|
+
type FailResult<E extends Error> = [error: E] & {
|
|
28
|
+
error: E;
|
|
29
|
+
result?: undefined;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Simple bare-bones discriminated tuple type for a [error, result] pair.
|
|
33
|
+
*
|
|
34
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#type-resulttuple
|
|
35
|
+
*/
|
|
36
|
+
export type ResultTuple<T, E extends Error = Error> = [error: undefined, result: T] | [error: E];
|
|
37
|
+
/**
|
|
38
|
+
* Discriminated tuple type for a `[error, result]` pair (same as `ResultTuple`)
|
|
39
|
+
* but with named properties `error` and `result` attached for dev convenience.
|
|
40
|
+
*
|
|
41
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#type-resulttupleobj
|
|
42
|
+
*/
|
|
43
|
+
export type ResultTupleObj<T, E extends Error = Error> = SuccessResult<T> | FailResult<E>;
|
|
44
|
+
/**
|
|
45
|
+
* Error handling utility that wraps a promise or a callback function.
|
|
46
|
+
*
|
|
47
|
+
* Catches errors and returns a `ResultTupleObj` — a nice discriminated
|
|
48
|
+
* `[error, results]` tuple with the `result` and `error` also attached as
|
|
49
|
+
* named properties.
|
|
50
|
+
*
|
|
51
|
+
* Works on both promises and sync callback functions.
|
|
52
|
+
*
|
|
53
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resultcatch
|
|
54
|
+
*/
|
|
55
|
+
declare function catch_<T, E extends Error = ErrorFromPayload>(promise: Promise<T>): Promise<ResultTupleObj<T, E>>;
|
|
56
|
+
declare function catch_<T, E extends Error = ErrorFromPayload>(callback: () => T): ResultTupleObj<T, E>;
|
|
57
|
+
/**
|
|
58
|
+
* Singleton object with small methods for creating, mapping or handling
|
|
59
|
+
* `ResultTupleObj` instances.
|
|
60
|
+
*
|
|
61
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#result-singleton
|
|
62
|
+
*/
|
|
63
|
+
export declare const Result: {
|
|
64
|
+
/**
|
|
65
|
+
* Factory for creating a successful `Result.TupleObj`.
|
|
66
|
+
*
|
|
67
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resultsuccess
|
|
68
|
+
*/
|
|
69
|
+
Success: <T>(result: T) => SuccessResult<T>;
|
|
70
|
+
/**
|
|
71
|
+
* Factory for creating a failed `Result.TupleObj`.
|
|
72
|
+
*
|
|
73
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resultsfail
|
|
74
|
+
*/
|
|
75
|
+
Fail: <E extends Error = Error>(e: unknown) => FailResult<E>;
|
|
76
|
+
catch: typeof catch_;
|
|
77
|
+
/**
|
|
78
|
+
* Helper to map a `ResultTuple`-like object to a new `ResultTupleObj`
|
|
79
|
+
* object, applying a transformation function to the result, but retaining
|
|
80
|
+
* the error as-is.
|
|
81
|
+
*
|
|
82
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resulmap
|
|
83
|
+
*/
|
|
84
|
+
map: <T_1, T2, E_1 extends Error>(result: ResultTuple<T_1, E_1>, mapFn: (resultValue: T_1) => T2) => ResultTupleObj<T2, E_1>;
|
|
85
|
+
/**
|
|
86
|
+
* Unwraps a discriminated [error, result] `Result.Tuple`-like object
|
|
87
|
+
* and throws if there's an error, but returns the result otherwise.
|
|
88
|
+
*
|
|
89
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resulthrow
|
|
90
|
+
*/
|
|
91
|
+
throw: <T_2>(result: ResultTuple<T_2, Error>) => T_2;
|
|
92
|
+
};
|
|
93
|
+
export declare namespace Result {
|
|
94
|
+
type Tuple<T, E extends Error = Error> = ResultTuple<T, E>;
|
|
95
|
+
type TupleObj<T, E extends Error = Error> = ResultTupleObj<T, E>;
|
|
96
|
+
type SuccessObj<T> = SuccessResult<T>;
|
|
97
|
+
type FailObj<E extends Error> = FailResult<E>;
|
|
98
|
+
}
|
|
99
|
+
export {};
|
package/errorhandling.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Result = exports.asError = exports.ErrorFromPayload = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Error subclass for thrown values that got cought and turned into an actual
|
|
6
|
+
* Error, with the thrown value as the `payload` property.
|
|
7
|
+
*
|
|
8
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#aserror
|
|
9
|
+
*/
|
|
10
|
+
class ErrorFromPayload extends Error {
|
|
11
|
+
constructor(payload) {
|
|
12
|
+
if (process.env.NODE_ENV !== 'production' && payload instanceof Error) {
|
|
13
|
+
throw new Error('Do not pass an Error instance as payload, just use it directly');
|
|
14
|
+
}
|
|
15
|
+
const message = (payload != null ? String(payload) : '') || 'Threw a falsy/empty value';
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = 'ErrorFromPayload';
|
|
18
|
+
this.payload = payload;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.ErrorFromPayload = ErrorFromPayload;
|
|
22
|
+
/**v
|
|
23
|
+
* Guarantees that a caught (`catch (e)`) value of `unknown` type,
|
|
24
|
+
* is indeed an `Error` instance.
|
|
25
|
+
*
|
|
26
|
+
*If the input is an `Error` instance, it is returned as-is. If the input is
|
|
27
|
+
* something else it is wrapped in a new `ErrorFromPayload` instance, and the
|
|
28
|
+
* original value is stored in a `payload`
|
|
29
|
+
*
|
|
30
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#aserror
|
|
31
|
+
*/
|
|
32
|
+
const asError = (maybeError) => {
|
|
33
|
+
if (maybeError instanceof Error) {
|
|
34
|
+
return maybeError;
|
|
35
|
+
}
|
|
36
|
+
return new ErrorFromPayload(maybeError);
|
|
37
|
+
};
|
|
38
|
+
exports.asError = asError;
|
|
39
|
+
const Success = (result) => {
|
|
40
|
+
const tuple = [undefined, result];
|
|
41
|
+
tuple.result = result;
|
|
42
|
+
return tuple;
|
|
43
|
+
};
|
|
44
|
+
const Fail = (e) => {
|
|
45
|
+
const tuple = [(0, exports.asError)(e)];
|
|
46
|
+
tuple.error = tuple[0];
|
|
47
|
+
return tuple;
|
|
48
|
+
};
|
|
49
|
+
function catch_(something) {
|
|
50
|
+
if (something instanceof Promise) {
|
|
51
|
+
return something.then((result) => Success(result), (e) => Fail(e));
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
return Success(something());
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
return Fail(e);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Singleton object with small methods for creating, mapping or handling
|
|
62
|
+
* `ResultTupleObj` instances.
|
|
63
|
+
*
|
|
64
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#result-singleton
|
|
65
|
+
*/
|
|
66
|
+
exports.Result = {
|
|
67
|
+
/**
|
|
68
|
+
* Factory for creating a successful `Result.TupleObj`.
|
|
69
|
+
*
|
|
70
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resultsuccess
|
|
71
|
+
*/
|
|
72
|
+
Success,
|
|
73
|
+
/**
|
|
74
|
+
* Factory for creating a failed `Result.TupleObj`.
|
|
75
|
+
*
|
|
76
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resultsfail
|
|
77
|
+
*/
|
|
78
|
+
Fail,
|
|
79
|
+
// NOTE: The JSDoc must be placed above the `catch_` function above.
|
|
80
|
+
catch: catch_,
|
|
81
|
+
/**
|
|
82
|
+
* Helper to map a `ResultTuple`-like object to a new `ResultTupleObj`
|
|
83
|
+
* object, applying a transformation function to the result, but retaining
|
|
84
|
+
* the error as-is.
|
|
85
|
+
*
|
|
86
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resulmap
|
|
87
|
+
*/
|
|
88
|
+
map: (result, mapFn) => {
|
|
89
|
+
const [error, resultValue] = result;
|
|
90
|
+
if (error) {
|
|
91
|
+
return Fail(error);
|
|
92
|
+
}
|
|
93
|
+
return Success(mapFn(resultValue));
|
|
94
|
+
},
|
|
95
|
+
/**
|
|
96
|
+
* Unwraps a discriminated [error, result] `Result.Tuple`-like object
|
|
97
|
+
* and throws if there's an error, but returns the result otherwise.
|
|
98
|
+
*
|
|
99
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resulthrow
|
|
100
|
+
*/
|
|
101
|
+
throw: (result) => {
|
|
102
|
+
if (result[0]) {
|
|
103
|
+
throw result[0];
|
|
104
|
+
}
|
|
105
|
+
return result[1];
|
|
106
|
+
},
|
|
107
|
+
};
|
package/esm/async.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EitherObj } from '@reykjavik/hanna-utils';
|
|
1
2
|
type PlainObj = Record<string, unknown>;
|
|
2
3
|
/**
|
|
3
4
|
* Simple sleep function. Returns a promise that resolves after `length`
|
|
@@ -5,16 +6,19 @@ type PlainObj = Record<string, unknown>;
|
|
|
5
6
|
*/
|
|
6
7
|
export declare const sleep: (length: number) => Promise<void>;
|
|
7
8
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
9
|
+
* Returns a function that adds lag/delay to a promise chain,
|
|
10
|
+
* passing the promise payload through.
|
|
11
|
+
*/
|
|
12
|
+
export declare const addLag: (length: number) => <T>(res: T) => Promise<T>;
|
|
13
|
+
/**
|
|
14
|
+
* Resolves as soon as all of the passed `promises` have resolved/settled,
|
|
15
|
+
* or after `timeout` milliseconds — whichever comes first.
|
|
10
16
|
*
|
|
11
17
|
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#maxwait
|
|
12
18
|
*/
|
|
13
19
|
export declare function maxWait(timeout: number, promises: Array<unknown>): Promise<void>;
|
|
14
20
|
export declare function maxWait<PromiseMap extends PlainObj>(timeout: number, promises: PromiseMap): Promise<{
|
|
15
|
-
-readonly [K in keyof PromiseMap]:
|
|
16
|
-
value: Awaited<PromiseMap[K]>;
|
|
17
|
-
} | undefined;
|
|
21
|
+
-readonly [K in keyof PromiseMap]: EitherObj<PromiseFulfilledResult<Awaited<PromiseMap[K]>>, PromiseRejectedResult> | undefined;
|
|
18
22
|
}>;
|
|
19
23
|
/**
|
|
20
24
|
* A variation of `Promise.all()` that accepts an object with named promises
|
package/esm/async.js
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
* milliseconds.
|
|
4
4
|
*/
|
|
5
5
|
export const sleep = (length) => new Promise((resolve) => setTimeout(resolve, length));
|
|
6
|
+
/**
|
|
7
|
+
* Returns a function that adds lag/delay to a promise chain,
|
|
8
|
+
* passing the promise payload through.
|
|
9
|
+
*/
|
|
10
|
+
export const addLag = (length) => (res) => sleep(length).then(() => res);
|
|
6
11
|
export function maxWait(timeout, promises) {
|
|
7
12
|
if (Array.isArray(promises)) {
|
|
8
13
|
return Promise.race([
|
|
@@ -15,17 +20,17 @@ export function maxWait(timeout, promises) {
|
|
|
15
20
|
Object.entries(promises).forEach(([key, value]) => {
|
|
16
21
|
if (value instanceof Promise) {
|
|
17
22
|
retObj[key] = undefined;
|
|
18
|
-
value
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
value.then((value) => {
|
|
24
|
+
retObj[key] = { status: 'fulfilled', value };
|
|
25
|
+
}, (reason) => {
|
|
26
|
+
retObj[key] = { status: 'rejected', reason };
|
|
27
|
+
});
|
|
23
28
|
}
|
|
24
29
|
else {
|
|
25
|
-
retObj[key] = { value };
|
|
30
|
+
retObj[key] = { status: 'fulfilled', value };
|
|
26
31
|
}
|
|
27
32
|
});
|
|
28
|
-
return
|
|
33
|
+
return Promise.resolve().then(() => ({ ...retObj }));
|
|
29
34
|
});
|
|
30
35
|
}
|
|
31
36
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error subclass for thrown values that got cought and turned into an actual
|
|
3
|
+
* Error, with the thrown value as the `payload` property.
|
|
4
|
+
*
|
|
5
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#aserror
|
|
6
|
+
*/
|
|
7
|
+
export declare class ErrorFromPayload extends Error {
|
|
8
|
+
payload?: unknown;
|
|
9
|
+
constructor(payload: unknown);
|
|
10
|
+
name: string;
|
|
11
|
+
}
|
|
12
|
+
/**v
|
|
13
|
+
* Guarantees that a caught (`catch (e)`) value of `unknown` type,
|
|
14
|
+
* is indeed an `Error` instance.
|
|
15
|
+
*
|
|
16
|
+
*If the input is an `Error` instance, it is returned as-is. If the input is
|
|
17
|
+
* something else it is wrapped in a new `ErrorFromPayload` instance, and the
|
|
18
|
+
* original value is stored in a `payload`
|
|
19
|
+
*
|
|
20
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#aserror
|
|
21
|
+
*/
|
|
22
|
+
export declare const asError: (maybeError: unknown) => ErrorFromPayload;
|
|
23
|
+
type SuccessResult<T> = [error: undefined, result: T] & {
|
|
24
|
+
error?: undefined;
|
|
25
|
+
result: T;
|
|
26
|
+
};
|
|
27
|
+
type FailResult<E extends Error> = [error: E] & {
|
|
28
|
+
error: E;
|
|
29
|
+
result?: undefined;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Simple bare-bones discriminated tuple type for a [error, result] pair.
|
|
33
|
+
*
|
|
34
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#type-resulttuple
|
|
35
|
+
*/
|
|
36
|
+
export type ResultTuple<T, E extends Error = Error> = [error: undefined, result: T] | [error: E];
|
|
37
|
+
/**
|
|
38
|
+
* Discriminated tuple type for a `[error, result]` pair (same as `ResultTuple`)
|
|
39
|
+
* but with named properties `error` and `result` attached for dev convenience.
|
|
40
|
+
*
|
|
41
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#type-resulttupleobj
|
|
42
|
+
*/
|
|
43
|
+
export type ResultTupleObj<T, E extends Error = Error> = SuccessResult<T> | FailResult<E>;
|
|
44
|
+
/**
|
|
45
|
+
* Error handling utility that wraps a promise or a callback function.
|
|
46
|
+
*
|
|
47
|
+
* Catches errors and returns a `ResultTupleObj` — a nice discriminated
|
|
48
|
+
* `[error, results]` tuple with the `result` and `error` also attached as
|
|
49
|
+
* named properties.
|
|
50
|
+
*
|
|
51
|
+
* Works on both promises and sync callback functions.
|
|
52
|
+
*
|
|
53
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resultcatch
|
|
54
|
+
*/
|
|
55
|
+
declare function catch_<T, E extends Error = ErrorFromPayload>(promise: Promise<T>): Promise<ResultTupleObj<T, E>>;
|
|
56
|
+
declare function catch_<T, E extends Error = ErrorFromPayload>(callback: () => T): ResultTupleObj<T, E>;
|
|
57
|
+
/**
|
|
58
|
+
* Singleton object with small methods for creating, mapping or handling
|
|
59
|
+
* `ResultTupleObj` instances.
|
|
60
|
+
*
|
|
61
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#result-singleton
|
|
62
|
+
*/
|
|
63
|
+
export declare const Result: {
|
|
64
|
+
/**
|
|
65
|
+
* Factory for creating a successful `Result.TupleObj`.
|
|
66
|
+
*
|
|
67
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resultsuccess
|
|
68
|
+
*/
|
|
69
|
+
Success: <T>(result: T) => SuccessResult<T>;
|
|
70
|
+
/**
|
|
71
|
+
* Factory for creating a failed `Result.TupleObj`.
|
|
72
|
+
*
|
|
73
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resultsfail
|
|
74
|
+
*/
|
|
75
|
+
Fail: <E extends Error = Error>(e: unknown) => FailResult<E>;
|
|
76
|
+
catch: typeof catch_;
|
|
77
|
+
/**
|
|
78
|
+
* Helper to map a `ResultTuple`-like object to a new `ResultTupleObj`
|
|
79
|
+
* object, applying a transformation function to the result, but retaining
|
|
80
|
+
* the error as-is.
|
|
81
|
+
*
|
|
82
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resulmap
|
|
83
|
+
*/
|
|
84
|
+
map: <T_1, T2, E_1 extends Error>(result: ResultTuple<T_1, E_1>, mapFn: (resultValue: T_1) => T2) => ResultTupleObj<T2, E_1>;
|
|
85
|
+
/**
|
|
86
|
+
* Unwraps a discriminated [error, result] `Result.Tuple`-like object
|
|
87
|
+
* and throws if there's an error, but returns the result otherwise.
|
|
88
|
+
*
|
|
89
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resulthrow
|
|
90
|
+
*/
|
|
91
|
+
throw: <T_2>(result: ResultTuple<T_2, Error>) => T_2;
|
|
92
|
+
};
|
|
93
|
+
export declare namespace Result {
|
|
94
|
+
type Tuple<T, E extends Error = Error> = ResultTuple<T, E>;
|
|
95
|
+
type TupleObj<T, E extends Error = Error> = ResultTupleObj<T, E>;
|
|
96
|
+
type SuccessObj<T> = SuccessResult<T>;
|
|
97
|
+
type FailObj<E extends Error> = FailResult<E>;
|
|
98
|
+
}
|
|
99
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error subclass for thrown values that got cought and turned into an actual
|
|
3
|
+
* Error, with the thrown value as the `payload` property.
|
|
4
|
+
*
|
|
5
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#aserror
|
|
6
|
+
*/
|
|
7
|
+
export class ErrorFromPayload extends Error {
|
|
8
|
+
constructor(payload) {
|
|
9
|
+
if (process.env.NODE_ENV !== 'production' && payload instanceof Error) {
|
|
10
|
+
throw new Error('Do not pass an Error instance as payload, just use it directly');
|
|
11
|
+
}
|
|
12
|
+
const message = (payload != null ? String(payload) : '') || 'Threw a falsy/empty value';
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'ErrorFromPayload';
|
|
15
|
+
this.payload = payload;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**v
|
|
19
|
+
* Guarantees that a caught (`catch (e)`) value of `unknown` type,
|
|
20
|
+
* is indeed an `Error` instance.
|
|
21
|
+
*
|
|
22
|
+
*If the input is an `Error` instance, it is returned as-is. If the input is
|
|
23
|
+
* something else it is wrapped in a new `ErrorFromPayload` instance, and the
|
|
24
|
+
* original value is stored in a `payload`
|
|
25
|
+
*
|
|
26
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#aserror
|
|
27
|
+
*/
|
|
28
|
+
export const asError = (maybeError) => {
|
|
29
|
+
if (maybeError instanceof Error) {
|
|
30
|
+
return maybeError;
|
|
31
|
+
}
|
|
32
|
+
return new ErrorFromPayload(maybeError);
|
|
33
|
+
};
|
|
34
|
+
const Success = (result) => {
|
|
35
|
+
const tuple = [undefined, result];
|
|
36
|
+
tuple.result = result;
|
|
37
|
+
return tuple;
|
|
38
|
+
};
|
|
39
|
+
const Fail = (e) => {
|
|
40
|
+
const tuple = [asError(e)];
|
|
41
|
+
tuple.error = tuple[0];
|
|
42
|
+
return tuple;
|
|
43
|
+
};
|
|
44
|
+
function catch_(something) {
|
|
45
|
+
if (something instanceof Promise) {
|
|
46
|
+
return something.then((result) => Success(result), (e) => Fail(e));
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
return Success(something());
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
return Fail(e);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Singleton object with small methods for creating, mapping or handling
|
|
57
|
+
* `ResultTupleObj` instances.
|
|
58
|
+
*
|
|
59
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#result-singleton
|
|
60
|
+
*/
|
|
61
|
+
export const Result = {
|
|
62
|
+
/**
|
|
63
|
+
* Factory for creating a successful `Result.TupleObj`.
|
|
64
|
+
*
|
|
65
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resultsuccess
|
|
66
|
+
*/
|
|
67
|
+
Success,
|
|
68
|
+
/**
|
|
69
|
+
* Factory for creating a failed `Result.TupleObj`.
|
|
70
|
+
*
|
|
71
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resultsfail
|
|
72
|
+
*/
|
|
73
|
+
Fail,
|
|
74
|
+
// NOTE: The JSDoc must be placed above the `catch_` function above.
|
|
75
|
+
catch: catch_,
|
|
76
|
+
/**
|
|
77
|
+
* Helper to map a `ResultTuple`-like object to a new `ResultTupleObj`
|
|
78
|
+
* object, applying a transformation function to the result, but retaining
|
|
79
|
+
* the error as-is.
|
|
80
|
+
*
|
|
81
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resulmap
|
|
82
|
+
*/
|
|
83
|
+
map: (result, mapFn) => {
|
|
84
|
+
const [error, resultValue] = result;
|
|
85
|
+
if (error) {
|
|
86
|
+
return Fail(error);
|
|
87
|
+
}
|
|
88
|
+
return Success(mapFn(resultValue));
|
|
89
|
+
},
|
|
90
|
+
/**
|
|
91
|
+
* Unwraps a discriminated [error, result] `Result.Tuple`-like object
|
|
92
|
+
* and throws if there's an error, but returns the result otherwise.
|
|
93
|
+
*
|
|
94
|
+
* @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#resulthrow
|
|
95
|
+
*/
|
|
96
|
+
throw: (result) => {
|
|
97
|
+
if (result[0]) {
|
|
98
|
+
throw result[0];
|
|
99
|
+
}
|
|
100
|
+
return result[1];
|
|
101
|
+
},
|
|
102
|
+
};
|
package/esm/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/// <reference path="./http.d.ts" />
|
|
3
3
|
/// <reference path="./async.d.ts" />
|
|
4
4
|
/// <reference path="./fixIcelandicLocale.d.ts" />
|
|
5
|
+
/// <reference path="./errorhandling.d.ts" />
|
|
5
6
|
/// <reference path="./remix/http.d.ts" />
|
|
6
7
|
/// <reference path="./SiteImprove.d.tsx" />
|
|
7
8
|
/// <reference path="./CookieHubConsent.d.tsx" />
|
package/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/// <reference path="./http.d.ts" />
|
|
3
3
|
/// <reference path="./async.d.ts" />
|
|
4
4
|
/// <reference path="./fixIcelandicLocale.d.ts" />
|
|
5
|
+
/// <reference path="./errorhandling.d.ts" />
|
|
5
6
|
/// <reference path="./remix/http.d.ts" />
|
|
6
7
|
/// <reference path="./SiteImprove.d.tsx" />
|
|
7
8
|
/// <reference path="./CookieHubConsent.d.tsx" />
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reykjavik/webtools",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"description": "Misc. JS/TS helpers used by Reykjavík City's web dev teams.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": "ssh://git@github.com:reykjavikcity/webtools.git",
|
|
@@ -64,6 +64,10 @@
|
|
|
64
64
|
"import": "./esm/fixIcelandicLocale.js",
|
|
65
65
|
"require": "./fixIcelandicLocale.js"
|
|
66
66
|
},
|
|
67
|
+
"./errorhandling": {
|
|
68
|
+
"import": "./esm/errorhandling.js",
|
|
69
|
+
"require": "./errorhandling.js"
|
|
70
|
+
},
|
|
67
71
|
"./remix/http": {
|
|
68
72
|
"import": "./esm/remix/http.js",
|
|
69
73
|
"require": "./remix/http.js"
|