@reykjavik/webtools 0.1.31 → 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 +8 -0
- package/README.md +283 -45
- package/errorhandling.d.ts +99 -0
- package/errorhandling.js +107 -0
- 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,14 @@
|
|
|
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
|
+
|
|
7
15
|
## 0.1.31
|
|
8
16
|
|
|
9
17
|
_2024-09-23_
|
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.
|
|
@@ -295,67 +369,231 @@ console.log(posts?.reason); // undefined | unknown
|
|
|
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
|
-
incorrectly treats `ð` and `d` as the same letter (most of the time), and
|
|
338
|
-
lumps the acute-accented characters `á`, `é`, `í`, `ó`, `ú` and `ý` in with
|
|
339
|
-
their non-accented counterparts.
|
|
426
|
+
### Type `ResultTuple`
|
|
340
427
|
|
|
341
|
-
|
|
428
|
+
**Syntax:** `ResultTuple<ResultType, OptionalErrorType>`
|
|
342
429
|
|
|
343
|
-
|
|
344
|
-
many units and unit-variants…)
|
|
345
|
-
- The `currencyDisplay: "name"` option is not supported and prints the
|
|
346
|
-
currency's full name in Danish.
|
|
430
|
+
(Also aliased as `Result.Tuple`)
|
|
347
431
|
|
|
348
|
-
|
|
432
|
+
Simple bare-bones discriminated tuple type for a `[error, result]` pair.
|
|
349
433
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
- For `timeZoneName` the values `"long"`, `"shortGeneric"` and `"longGeneric"`
|
|
353
|
-
will appear in Danish.
|
|
354
|
-
- The `timeStyle: 'full'` option prints the timezone names in Danish
|
|
355
|
-
- The `dayPeriod` option has a couple of slight mismatches, at 5 am and 12
|
|
356
|
-
noon.
|
|
434
|
+
```ts
|
|
435
|
+
import { type ResultTuple } from '@reykjavik/webtools/errorhandling';
|
|
357
436
|
|
|
358
|
-
|
|
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).
|
|
359
597
|
|
|
360
598
|
---
|
|
361
599
|
|
|
@@ -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
|
+
};
|
|
@@ -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"
|