├── .eslintignore ├── examples ├── nestjs │ ├── pay.ts │ ├── nest-cli.json │ ├── tsconfig.build.json │ ├── pay │ │ ├── pay.module.ts │ │ ├── errors.ts │ │ ├── pay.service.ts │ │ └── pay.controller.ts │ ├── main.ts │ ├── tsconfig.json │ ├── package.json │ └── README.md ├── express │ ├── tsconfig.json │ ├── errors.ts │ ├── package.json │ ├── pay.ts │ ├── README.md │ └── index.ts ├── fastify │ ├── tsconfig.json │ ├── package.json │ ├── errors.ts │ ├── pay.ts │ ├── README.md │ ├── index.ts │ └── package-lock.json └── basic-example │ ├── tsconfig.json │ ├── README.md │ ├── package.json │ ├── types.ts │ ├── pay.ts │ ├── index.ts │ └── package-lock.json ├── .prettierignore ├── .gitignore ├── .prettierrc ├── docs ├── basic-example.gif └── REFERENCE.md ├── .mocharc.json ├── src ├── types │ ├── README.md │ ├── common-result.ts │ ├── result.ts │ └── result-helpers.ts ├── resultify.ts ├── index.ts ├── test │ ├── promise.test.ts │ ├── helper.ts │ ├── combine.test.ts │ ├── resultify.test.ts │ ├── result-from.test.ts │ └── map.test.ts ├── result.ts └── common-result.ts ├── tsconfig.json ├── LICENSE.md ├── .eslintrc.js ├── package.json └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | *.md 2 | -------------------------------------------------------------------------------- /examples/nestjs/pay.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .idea/ -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 2, 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /docs/basic-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wiktor-obrebski/type-safe-errors/HEAD/docs/basic-example.gif -------------------------------------------------------------------------------- /examples/nestjs/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src" 4 | } 5 | -------------------------------------------------------------------------------- /.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": "ts-node/register", 3 | "watch-files": ["./src/**/*.ts"], 4 | "timeout": "10000" 5 | } 6 | -------------------------------------------------------------------------------- /examples/nestjs/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /examples/express/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/fastify/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/types/README.md: -------------------------------------------------------------------------------- 1 | These files are required in TypeScript build output. 2 | As such they MUST NOT have `.d.ts` extension. 3 | 4 | https://github.com/Microsoft/TypeScript/issues/5112 5 | -------------------------------------------------------------------------------- /examples/basic-example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/basic-example/README.md: -------------------------------------------------------------------------------- 1 | # `type-safe-errors` basic example 2 | 3 | The program presents simple function with two domain errors handled as results. 4 | 5 | Run instructions: 6 | ```bash 7 | npm i 8 | npm start 9 | ``` 10 | -------------------------------------------------------------------------------- /examples/basic-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "start": "ts-node index.ts", 4 | "typecheck": "tsc --noEmit" 5 | }, 6 | "dependencies": { 7 | "type-safe-errors": "file:../.." 8 | }, 9 | "devDependencies": { 10 | "ts-node": "^10.9.1", 11 | "typescript": "^4.7.4" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/nestjs/pay/pay.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { PayController } from './pay.controller'; 3 | import { PayService } from './pay.service'; 4 | 5 | @Module({ 6 | imports: [], 7 | controllers: [PayController], 8 | providers: [PayService], 9 | }) 10 | export class PayModule {} 11 | -------------------------------------------------------------------------------- /examples/fastify/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "start": "ts-node index.ts", 4 | "typecheck": "tsc --noEmit" 5 | }, 6 | "dependencies": { 7 | "fastify": "^4.9.2", 8 | "type-safe-errors": "file:../.." 9 | }, 10 | "devDependencies": { 11 | "ts-node": "^10.9.1", 12 | "typescript": "^4.7.4" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/express/errors.ts: -------------------------------------------------------------------------------- 1 | export { InvalidCVCError, UknownProductError, MissingCardNumberError }; 2 | 3 | class InvalidCVCError extends Error { 4 | private __brand!: never; 5 | } 6 | 7 | class UknownProductError extends Error { 8 | private __brand!: never; 9 | } 10 | 11 | class MissingCardNumberError extends Error { 12 | private __brand!: never; 13 | } 14 | -------------------------------------------------------------------------------- /examples/fastify/errors.ts: -------------------------------------------------------------------------------- 1 | export { InvalidCVCError, UknownProductError, MissingCardNumberError }; 2 | 3 | class InvalidCVCError extends Error { 4 | private __brand!: never; 5 | } 6 | 7 | class UknownProductError extends Error { 8 | private __brand!: never; 9 | } 10 | 11 | class MissingCardNumberError extends Error { 12 | private __brand!: never; 13 | } 14 | -------------------------------------------------------------------------------- /examples/nestjs/pay/errors.ts: -------------------------------------------------------------------------------- 1 | export { InvalidCVCError, UknownProductError, MissingCardNumberError }; 2 | 3 | class InvalidCVCError extends Error { 4 | private __brand!: never; 5 | } 6 | 7 | class UknownProductError extends Error { 8 | private __brand!: never; 9 | } 10 | 11 | class MissingCardNumberError extends Error { 12 | private __brand!: never; 13 | } 14 | -------------------------------------------------------------------------------- /src/resultify.ts: -------------------------------------------------------------------------------- 1 | import { Result } from './result'; 2 | 3 | export { resultify }; 4 | 5 | type Callback = ( 6 | ...args: TArgs 7 | ) => TResult | Promise; 8 | 9 | const resultify = 10 | (fn: Callback) => 11 | (...args: TArgs) => 12 | Result.from(() => fn(...args)); 13 | -------------------------------------------------------------------------------- /examples/nestjs/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import morgan from 'morgan'; 3 | 4 | import { PayModule } from './pay/pay.module'; 5 | 6 | bootstrap(); 7 | 8 | async function bootstrap() { 9 | const app = await NestFactory.create(PayModule); 10 | 11 | const logger = morgan('combined'); 12 | app.use(logger); 13 | 14 | await app.listen(3000); 15 | } 16 | -------------------------------------------------------------------------------- /examples/basic-example/types.ts: -------------------------------------------------------------------------------- 1 | export { UserCard, MissingPriceError, InvalidCVCError, Product }; 2 | 3 | interface UserCard { 4 | number: string; 5 | cvc: string; 6 | } 7 | 8 | interface Product { 9 | price: number | null; 10 | } 11 | 12 | class InvalidCVCError extends Error { 13 | private __brand!: never; 14 | } 15 | 16 | class MissingPriceError extends Error { 17 | private __brand!: never; 18 | } 19 | -------------------------------------------------------------------------------- /examples/express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "start": "ts-node index.ts", 4 | "typecheck": "tsc --noEmit" 5 | }, 6 | "dependencies": { 7 | "express": "^4.18.1", 8 | "morgan": "^1.10.0", 9 | "type-safe-errors": "file:../.." 10 | }, 11 | "devDependencies": { 12 | "@types/express": "^4.17.13", 13 | "@types/morgan": "^1.9.3", 14 | "ts-node": "^10.9.1", 15 | "typescript": "^4.7.4" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/nestjs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "es6", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true, 14 | "skipLibCheck": true, 15 | "strict": true, 16 | "esModuleInterop": true, 17 | "experimentalDecorators": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/nestjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "start": "nest start", 4 | "typecheck": "tsc --noEmit" 5 | }, 6 | "dependencies": { 7 | "@nestjs/common": "^9.0.5", 8 | "@nestjs/core": "^9.0.5", 9 | "@nestjs/platform-express": "^9.0.5", 10 | "morgan": "^1.10.0", 11 | "type-safe-errors": "file:../.." 12 | }, 13 | "devDependencies": { 14 | "@nestjs/cli": "^9.0.0", 15 | "@types/express": "^4.17.13", 16 | "@types/morgan": "^1.9.3", 17 | "ts-node": "^10.9.1", 18 | "typescript": "^4.7.4" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // aliasing is to re-export types and factory values on same names 2 | import type { 3 | Result as ResultType, 4 | Ok as OkType, 5 | Err as ErrType, 6 | } from './types/result-helpers'; 7 | 8 | import { Result as ResultVal, Ok as OkVal, Err as ErrVal } from './result'; 9 | 10 | export { UnknownError } from './common-result'; 11 | export { resultify } from './resultify'; 12 | 13 | export const Result = ResultVal; 14 | export type Result = ResultType; 15 | 16 | export const Ok = OkVal; 17 | export type Ok = OkType; 18 | 19 | export const Err = ErrVal; 20 | export type Err = ErrType; 21 | -------------------------------------------------------------------------------- /examples/basic-example/pay.ts: -------------------------------------------------------------------------------- 1 | import { Err, Ok } from 'type-safe-errors'; 2 | import { Product, UserCard, MissingPriceError, InvalidCVCError } from './types'; 3 | 4 | export async function payForProduct(card: UserCard, product: Product) { 5 | // simulate fetching data 6 | await sleep(10); 7 | 8 | if (!card.cvc) { 9 | return Err.of(new InvalidCVCError()); 10 | } 11 | 12 | if (product.price === null) { 13 | return Err.of(new MissingPriceError()); 14 | } 15 | 16 | // payment logic 17 | 18 | return Ok.of(`Success. Payed ${product.price}`); 19 | } 20 | 21 | function sleep(sleepMs: number) { 22 | return new Promise((resolve) => setTimeout(resolve, sleepMs)); 23 | } 24 | -------------------------------------------------------------------------------- /examples/basic-example/index.ts: -------------------------------------------------------------------------------- 1 | import { Result } from 'type-safe-errors'; 2 | import { payForProduct } from './pay'; 3 | import { MissingPriceError, InvalidCVCError } from './types'; 4 | 5 | const userCard = { 6 | number: '1234', 7 | cvc: Math.random() > 0.5 ? '456' : '', 8 | }; 9 | 10 | const product = { 11 | price: Math.random() > 0.5 ? 12.5 : null, 12 | }; 13 | 14 | const paymentResult = Result.from(() => payForProduct(userCard, product)) 15 | .mapErr(InvalidCVCError, () => { 16 | return 'Your card do not have proper CVC code'; 17 | }) 18 | .mapErr(MissingPriceError, () => { 19 | return 'Product do not have price'; 20 | }); 21 | paymentResult.promise().then((resultText) => console.log(resultText)); 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "declaration": true, 5 | "outDir": "./dist", 6 | 7 | "baseUrl": ".", 8 | "esModuleInterop": true, 9 | /* 10 | * intentionally choose to not import helpers 11 | * to avoid `tslib` dependency. 12 | * all non-test code of this lib is designed to not use any 13 | * syntaxt that require this helpers, so the library bundle size 14 | * can stay significantly smaller. 15 | * check https://bundlephobia.com/package/type-safe-errors@0.2.7 16 | * to compare size with and without `tslib` dep 17 | */ 18 | "importHelpers": false, 19 | "module": "commonjs", 20 | "removeComments": false, 21 | "sourceMap": true, 22 | "strict": true, 23 | "lib": ["es2019"], 24 | "moduleResolution": "node", 25 | "types": ["mocha", "chai", "node"] 26 | }, 27 | "include": ["./src/**/*.ts"], 28 | "exclude": ["./dist", "./node_modules"] 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © 2021 Wiktor Obrębski 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /examples/express/pay.ts: -------------------------------------------------------------------------------- 1 | import { Err, Ok } from 'type-safe-errors'; 2 | import { 3 | InvalidCVCError, 4 | MissingCardNumberError, 5 | UknownProductError, 6 | } from './errors'; 7 | 8 | const supportedProductId = '123'; 9 | const supportedCVC = '456'; 10 | 11 | export { payForProduct, getProductPrice }; 12 | 13 | async function payForProduct( 14 | cardNumber: string, 15 | cvc: string, 16 | productPrice: number 17 | ) { 18 | // simulate fetching data 19 | await sleep(10); 20 | 21 | if (cvc !== supportedCVC) { 22 | return Err.of(new InvalidCVCError()); 23 | } 24 | 25 | if (!cardNumber) { 26 | return Err.of(new MissingCardNumberError()); 27 | } 28 | 29 | return Ok.of(`Success. Payed ${productPrice}`); 30 | } 31 | 32 | async function getProductPrice(productId: string) { 33 | // simulate fetching data 34 | await sleep(10); 35 | 36 | return productId === supportedProductId 37 | ? Ok.of(12.5) 38 | : Err.of(new UknownProductError()); 39 | } 40 | 41 | function sleep(sleepMs: number) { 42 | return new Promise((resolve) => setTimeout(resolve, sleepMs)); 43 | } 44 | -------------------------------------------------------------------------------- /examples/fastify/pay.ts: -------------------------------------------------------------------------------- 1 | import { Err, Ok } from 'type-safe-errors'; 2 | import { 3 | InvalidCVCError, 4 | MissingCardNumberError, 5 | UknownProductError, 6 | } from './errors'; 7 | 8 | const supportedProductId = '123'; 9 | const supportedCVC = '456'; 10 | 11 | export { payForProduct, getProductPrice }; 12 | 13 | async function payForProduct( 14 | cardNumber: string, 15 | cvc: string, 16 | productPrice: number 17 | ) { 18 | // simulate fetching data 19 | await sleep(10); 20 | 21 | if (cvc !== supportedCVC) { 22 | return Err.of(new InvalidCVCError()); 23 | } 24 | 25 | if (!cardNumber) { 26 | return Err.of(new MissingCardNumberError()); 27 | } 28 | 29 | return Ok.of(`Success. Payed ${productPrice}`); 30 | } 31 | 32 | async function getProductPrice(productId: string) { 33 | // simulate fetching data 34 | await sleep(10); 35 | 36 | return productId === supportedProductId 37 | ? Ok.of(12.5) 38 | : Err.of(new UknownProductError()); 39 | } 40 | 41 | function sleep(sleepMs: number) { 42 | return new Promise((resolve) => setTimeout(resolve, sleepMs)); 43 | } 44 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: ['prettier'], 3 | 4 | parser: '@typescript-eslint/parser', // Specifies the ESLint parser 5 | parserOptions: { 6 | ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features 7 | sourceType: 'module', // Allows for the use of imports 8 | }, 9 | 10 | extends: [ 11 | 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin 12 | 'prettier', 13 | 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. 14 | ], 15 | 16 | rules: { 17 | '@typescript-eslint/explicit-function-return-type': 'off', 18 | '@typescript-eslint/no-non-null-assertion': 'error', 19 | '@typescript-eslint/explicit-module-boundary-types': 'off', 20 | '@typescript-eslint/no-use-before-define': [ 21 | 'error', 22 | { functions: false, classes: true }, 23 | ], 24 | '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /examples/nestjs/pay/pay.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { Err, Ok } from 'type-safe-errors'; 3 | import { 4 | InvalidCVCError, 5 | UknownProductError, 6 | MissingCardNumberError, 7 | } from './errors'; 8 | 9 | const supportedProductId = '123'; 10 | const supportedCVC = '456'; 11 | 12 | @Injectable() 13 | export class PayService { 14 | async payForProduct(cardNumber: string, cvc: string, productPrice: number) { 15 | // simulate fetching data 16 | await sleep(10); 17 | 18 | if (cvc !== supportedCVC) { 19 | return Err.of(new InvalidCVCError()); 20 | } 21 | 22 | if (!cardNumber) { 23 | return Err.of(new MissingCardNumberError()); 24 | } 25 | 26 | return Ok.of(`Success. Payed ${productPrice}`); 27 | } 28 | 29 | async getProductPrice(productId: string) { 30 | // simulate fetching data 31 | await sleep(10); 32 | 33 | return productId === supportedProductId 34 | ? Ok.of(12.5) 35 | : Err.of(new UknownProductError()); 36 | } 37 | } 38 | 39 | function sleep(sleepMs: number) { 40 | return new Promise((resolve) => setTimeout(resolve, sleepMs)); 41 | } 42 | -------------------------------------------------------------------------------- /src/test/promise.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, assert } from 'chai'; 2 | import { Ok, Err } from '../index'; 3 | 4 | class Error1 extends Error { 5 | private __brand!: never; 6 | } 7 | 8 | test('Result of an Ok value map to a promise of the value', async () => { 9 | const asyncValue: Promise<5> = Ok.of(5 as const).promise(); 10 | const value = await asyncValue; 11 | expect(value).to.equal(5); 12 | }); 13 | 14 | test('Result of an Ok value map to a unsafe promise of the value', async () => { 15 | const asyncValue: Promise<5> = Ok.of(5 as const).unsafePromise(); 16 | const value = await asyncValue; 17 | expect(value).to.equal(5); 18 | }); 19 | 20 | test('Result of an Err value map to an unsafe promise of the value', async () => { 21 | const err = new Error1(); 22 | const asyncValue: Promise = Err.of(err).unsafePromise(); 23 | try { 24 | await asyncValue; 25 | assert.fail('the promise should have been rejected already'); 26 | } catch (err) { 27 | expect(err).to.equal(err); 28 | } 29 | }); 30 | 31 | test('Result of a mixed value map to an unsafe promise of the value', async () => { 32 | const result = Ok.of('test' as const) as Ok<'test'> | Ok<5> | Err; 33 | const asyncValue: Promise<'test' | 5> = result.unsafePromise(); 34 | const value = await asyncValue; 35 | expect(value).to.equal('test'); 36 | }); 37 | -------------------------------------------------------------------------------- /examples/express/README.md: -------------------------------------------------------------------------------- 1 | # `type-safe-errors` fastify example 2 | 3 | The program presents simple [express](https://expressjs.com/) server with one endpoint supported by `type-safe-errors` library. 4 | 5 | Run instructions: 6 | ```bash 7 | npm i 8 | npm start 9 | ``` 10 | 11 | ## Test scenarios 12 | 13 | ### Missing card number 14 | 15 | ```bash 16 | echo '{"productId":"123", "cvc": "456"}' | \ 17 | curl --header "Content-Type: application/json" \ 18 | --request POST \ 19 | --data-binary @- \ 20 | http://localhost:3000/payments 21 | ``` 22 | 23 | ### Invalid cvc 24 | 25 | ```bash 26 | echo '{"productId":"123", "cvc": "999", "cardNumber": "123456789"}' | \ 27 | curl --header "Content-Type: application/json" \ 28 | --request POST \ 29 | --data-binary @- \ 30 | http://localhost:3000/payments 31 | ``` 32 | 33 | ### Product not found 34 | 35 | ```bash 36 | echo '{"productId":"404", "cvc": "456", "cardNumber": "123456789"}' | \ 37 | curl --header "Content-Type: application/json" \ 38 | --request POST \ 39 | --data-binary @- \ 40 | http://localhost:3000/payments 41 | ``` 42 | 43 | ### Payment successful 44 | 45 | ```bash 46 | echo '{"productId":"123", "cvc": "456", "cardNumber": "123456789"}' | \ 47 | curl --header "Content-Type: application/json" \ 48 | --request POST \ 49 | --data-binary @- \ 50 | http://localhost:3000/payments 51 | ``` 52 | -------------------------------------------------------------------------------- /examples/fastify/README.md: -------------------------------------------------------------------------------- 1 | # `type-safe-errors` fastify example 2 | 3 | The program presents simple [fastify](https://www.fastify.io/) server with one endpoint supported by `type-safe-errors` library. 4 | 5 | Run instructions: 6 | ```bash 7 | npm i 8 | npm start 9 | ``` 10 | 11 | ## Test scenarios 12 | 13 | ### Missing card number 14 | 15 | ```bash 16 | echo '{"productId":"123", "cvc": "456"}' | \ 17 | curl --header "Content-Type: application/json" \ 18 | --request POST \ 19 | --data-binary @- \ 20 | http://localhost:3000/payments 21 | ``` 22 | 23 | ### Invalid cvc 24 | 25 | ```bash 26 | echo '{"productId":"123", "cvc": "999", "cardNumber": "123456789"}' | \ 27 | curl --header "Content-Type: application/json" \ 28 | --request POST \ 29 | --data-binary @- \ 30 | http://localhost:3000/payments 31 | ``` 32 | 33 | ### Product not found 34 | 35 | ```bash 36 | echo '{"productId":"404", "cvc": "456", "cardNumber": "123456789"}' | \ 37 | curl --header "Content-Type: application/json" \ 38 | --request POST \ 39 | --data-binary @- \ 40 | http://localhost:3000/payments 41 | ``` 42 | 43 | ### Payment successful 44 | 45 | ```bash 46 | echo '{"productId":"123", "cvc": "456", "cardNumber": "123456789"}' | \ 47 | curl --header "Content-Type: application/json" \ 48 | --request POST \ 49 | --data-binary @- \ 50 | http://localhost:3000/payments 51 | ``` 52 | -------------------------------------------------------------------------------- /examples/nestjs/README.md: -------------------------------------------------------------------------------- 1 | # `type-safe-errors` fastify example 2 | 3 | The program presents simple [express](https://expressjs.com/) server with one endpoint supported by `type-safe-errors` library. 4 | 5 | Run instructions: 6 | ```bash 7 | npm i 8 | npm start 9 | ``` 10 | 11 | ## Test scenarios 12 | 13 | ### Missing card number 14 | 15 | ```bash 16 | echo '{"productId":"123", "cvc": "456"}' | \ 17 | curl --header "Content-Type: application/json" \ 18 | --request POST \ 19 | --data-binary @- \ 20 | http://localhost:3000/payments 21 | ``` 22 | 23 | ### Invalid cvc 24 | 25 | ```bash 26 | echo '{"productId":"123", "cvc": "999", "cardNumber": "123456789"}' | \ 27 | curl --header "Content-Type: application/json" \ 28 | --request POST \ 29 | --data-binary @- \ 30 | http://localhost:3000/payments 31 | ``` 32 | 33 | ### Product not found 34 | 35 | ```bash 36 | echo '{"productId":"404", "cvc": "456", "cardNumber": "123456789"}' | \ 37 | curl --header "Content-Type: application/json" \ 38 | --request POST \ 39 | --data-binary @- \ 40 | http://localhost:3000/payments 41 | ``` 42 | 43 | ### Payment successful 44 | 45 | ```bash 46 | echo '{"productId":"123", "cvc": "456", "cardNumber": "123456789"}' | \ 47 | curl --header "Content-Type: application/json" \ 48 | --request POST \ 49 | --data-binary @- \ 50 | http://localhost:3000/payments 51 | ``` 52 | -------------------------------------------------------------------------------- /examples/fastify/index.ts: -------------------------------------------------------------------------------- 1 | import { fastify } from 'fastify'; 2 | import { Result } from 'type-safe-errors'; 3 | 4 | import { getProductPrice, payForProduct } from './pay'; 5 | import { 6 | InvalidCVCError, 7 | UknownProductError, 8 | MissingCardNumberError, 9 | } from './errors'; 10 | 11 | interface PaymentRequestBody { 12 | cardNumber: string; 13 | cvc: string; 14 | productId: string; 15 | } 16 | 17 | const app = fastify({ logger: true }); 18 | 19 | app.post<{ Body: PaymentRequestBody }>('/payments', async (req, reply) => { 20 | const { cardNumber, cvc, productId } = req.body; 21 | 22 | return Result.from(() => getProductPrice(productId)) 23 | .map((price) => payForProduct(cardNumber, cvc, price)) 24 | .map((successResult) => { 25 | reply.status(200).send({ message: successResult }); 26 | }) 27 | .mapErr(InvalidCVCError, () => { 28 | reply.status(422).send({ message: 'Invalid card CVC' }); 29 | }) 30 | .mapErr(UknownProductError, () => { 31 | reply.status(404).send({ message: `Product '${productId}' not found` }); 32 | }) 33 | .mapErr(MissingCardNumberError, () => { 34 | reply 35 | .status(400) 36 | .send({ message: `Invalid card number: '${cardNumber}'` }); 37 | }) 38 | .promise(); 39 | }); 40 | 41 | // Run the server! 42 | app.listen({ port: 3000 }, (err) => { 43 | if (err) { 44 | app.log.error(err); 45 | process.exit(1); 46 | } 47 | }); 48 | -------------------------------------------------------------------------------- /examples/express/index.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import morgan from 'morgan'; 3 | import { Result } from 'type-safe-errors'; 4 | 5 | import { getProductPrice, payForProduct } from './pay'; 6 | import { 7 | InvalidCVCError, 8 | UknownProductError, 9 | MissingCardNumberError, 10 | } from './errors'; 11 | 12 | interface PaymentRequestBody { 13 | cardNumber: string; 14 | cvc: string; 15 | productId: string; 16 | } 17 | 18 | const app = express(); 19 | const logger = morgan('combined'); 20 | app.use(logger); 21 | app.use(express.json()); 22 | 23 | app.post<{ Body: PaymentRequestBody }>('/payments', async (req, res) => { 24 | const { cardNumber, cvc, productId } = req.body; 25 | 26 | return Result.from(() => getProductPrice(productId)) 27 | .map((price) => payForProduct(cardNumber, cvc, price)) 28 | .map((successResult) => { 29 | res.status(200).send({ message: successResult }); 30 | }) 31 | .mapErr(InvalidCVCError, () => { 32 | res.status(422).send({ message: 'Invalid card CVC' }); 33 | }) 34 | .mapErr(UknownProductError, () => { 35 | res.status(404).send({ message: `Product '${productId}' not found` }); 36 | }) 37 | .mapErr(MissingCardNumberError, () => { 38 | res.status(400).send({ message: `Invalid card number: '${cardNumber}'` }); 39 | }) 40 | .promise(); 41 | }); 42 | 43 | // Run the server! 44 | app.listen(3000, () => { 45 | console.info(`server listening on 3000`); 46 | }); 47 | -------------------------------------------------------------------------------- /examples/nestjs/pay/pay.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Post, Body, Res } from '@nestjs/common'; 2 | import { Response } from 'express'; 3 | import { Result } from 'type-safe-errors'; 4 | 5 | import { 6 | InvalidCVCError, 7 | UknownProductError, 8 | MissingCardNumberError, 9 | } from './errors'; 10 | import { PayService } from './pay.service'; 11 | 12 | class PaymentDto { 13 | cardNumber!: string; 14 | cvc!: string; 15 | productId!: string; 16 | } 17 | 18 | @Controller('/payments') 19 | export class PayController { 20 | constructor(private readonly payService: PayService) {} 21 | 22 | @Post() 23 | createPayment(@Res() res: Response, @Body() payment: PaymentDto) { 24 | const { cardNumber, cvc, productId } = payment; 25 | 26 | return Result.from(() => this.payService.getProductPrice(productId)) 27 | .map((price) => this.payService.payForProduct(cardNumber, cvc, price)) 28 | .map((successResult) => { 29 | res.status(200).send({ message: successResult }); 30 | }) 31 | .mapErr(InvalidCVCError, () => { 32 | res.status(422).send({ message: 'Invalid card CVC' }); 33 | }) 34 | .mapErr(UknownProductError, () => { 35 | res.status(404).send({ message: `Product '${productId}' not found` }); 36 | }) 37 | .mapErr(MissingCardNumberError, () => { 38 | res 39 | .status(400) 40 | .send({ message: `Invalid card number: '${cardNumber}'` }); 41 | }) 42 | .promise(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/result.ts: -------------------------------------------------------------------------------- 1 | import { 2 | commonResultFactory, 3 | resultWrapperFactory, 4 | wrapResultFactory, 5 | isResult, 6 | } from './common-result'; 7 | import { ResultNamespace, OkNamespace, ErrNamespace } from './types/result'; 8 | 9 | export { Ok, OkNamespace, Err, ErrNamespace, Result, ResultNamespace }; 10 | 11 | const Result: ResultNamespace = { 12 | from(factory) { 13 | const resultWrapperPromise = wrapResultFactory(factory); 14 | 15 | return commonResultFactory(resultWrapperPromise) as any; 16 | }, 17 | 18 | combine(results) { 19 | const wrapperPromises = results.map((res) => 20 | Promise.resolve(res).then((v) => 21 | isResult(v) ? v.__value : { isError: false, value: v } 22 | ) 23 | ); 24 | 25 | const resultPromise = Promise.all(wrapperPromises).then((wrappers) => { 26 | const values = []; 27 | for (const wrapper of wrappers) { 28 | if (wrapper.isError) { 29 | return { ...wrapper } as any; 30 | } else { 31 | values.push(wrapper.value); 32 | } 33 | } 34 | return { isError: false, value: values }; 35 | }); 36 | 37 | return commonResultFactory(resultPromise) as any; 38 | }, 39 | }; 40 | 41 | const Ok: OkNamespace = { 42 | of(value) { 43 | const wrappedValue = Promise.resolve(resultWrapperFactory(value, false)); 44 | return commonResultFactory(wrappedValue); 45 | }, 46 | }; 47 | 48 | const Err: ErrNamespace = { 49 | of(error) { 50 | const wrappedValue = Promise.resolve(resultWrapperFactory(error, true)); 51 | return commonResultFactory(wrappedValue) as any; 52 | }, 53 | }; 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "type-safe-errors", 3 | "version": "0.6.0", 4 | "description": "Simple type-safe errors for TypeScript", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "test": "NODE_ENV=test mocha --ui tdd src/**/*.test.ts", 9 | "test:watch": "NODE_ENV=test mocha --ui tdd src/test/*.test.ts --watch", 10 | "typecheck": "tsc --noEmit", 11 | "build": "tsc", 12 | "lint": "eslint ./src --ext ts", 13 | "prepublishOnly": "npm test && npm run build" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/wiktor-obrebski/type-safe-errors.git" 18 | }, 19 | "author": "Wiktor Obrebski", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/wiktor-obrebski/type-safe-errors/issues" 23 | }, 24 | "keywords": [ 25 | "Result", 26 | "Type-safe", 27 | "Promise-like", 28 | "Promise like", 29 | "Strong-typed", 30 | "Strongly typed", 31 | "Strongly typed error", 32 | "Typescript", 33 | "Typescript errors", 34 | "Typed errors", 35 | "Error", 36 | "Exception" 37 | ], 38 | "homepage": "https://github.com/wiktor-obrebski/type-safe-errors#readme", 39 | "devDependencies": { 40 | "@types/chai": "^4.3.1", 41 | "@types/mocha": "^9.1.1", 42 | "@types/node": "^16.11.7", 43 | "@typescript-eslint/eslint-plugin": "^5.30.7", 44 | "@typescript-eslint/parser": "^5.30.7", 45 | "chai": "^4.3.6", 46 | "eslint": "^8.20.0", 47 | "eslint-config-prettier": "^8.5.0", 48 | "eslint-plugin-prettier": "^4.2.1", 49 | "mocha": "^10.0.0", 50 | "prettier": "^2.7.1", 51 | "ts-node": "^10.9.1", 52 | "typescript": "^4.7.4" 53 | }, 54 | "files": [ 55 | "dist/**/*" 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /src/types/common-result.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | Result, 3 | Constructor, 4 | MapOkResult, 5 | InferOk, 6 | ErrMapper, 7 | MapErrResult, 8 | MapAnyErrResult, 9 | InferErr, 10 | Err, 11 | UnknownError, 12 | } from './result-helpers'; 13 | 14 | export type { CommonResult, ResultWrapper }; 15 | 16 | interface CommonResult { 17 | readonly __value: Promise>; 18 | __brand: any; 19 | 20 | map, R>( 21 | this: U, 22 | mapper: (value: InferOk) => R | Promise 23 | ): MapOkResult; 24 | 25 | mapErr< 26 | U extends Result, 27 | R, 28 | E extends InferErr | UnknownError 29 | >( 30 | this: U, 31 | ErrorClass: Constructor, 32 | mapper: (err: E) => R | Promise 33 | ): MapErrResult; 34 | 35 | mapAnyErr, R>( 36 | this: U, 37 | mapper: ErrMapper, R> 38 | ): MapAnyErrResult; 39 | 40 | unsafePromise>( 41 | this: U 42 | ): Promise | never>; 43 | 44 | /** 45 | * Return fulfilled promise of current `Ok` Result value. To use the `promise()` 46 | * function you need first handle all known `Err`` result values. 47 | * It is possible that it return rejected promise for unknown exceptions. 48 | * @returns promise of current Result value - fulfilled if the value is Ok, 49 | * rejected if there was an exception thrown anywhere in the mapping chain 50 | */ 51 | promise>( 52 | this: U 53 | ): Promise | never>; 54 | } 55 | 56 | interface ResultWrapper { 57 | value: TErrorOrValue; 58 | isError: boolean; 59 | } 60 | -------------------------------------------------------------------------------- /src/types/result.ts: -------------------------------------------------------------------------------- 1 | import type { Ok, Err, MapFromResult } from './result-helpers'; 2 | 3 | export type { ResultNamespace, OkNamespace, ErrNamespace }; 4 | 5 | interface ResultNamespace { 6 | from(factory: () => Promise | R): MapFromResult; 7 | 8 | /** 9 | * Combine provided Results list into single Result. If all provided Results 10 | * are Ok Results, returned Result will be Result Ok of array of provided Results values: 11 | * [Ok, Ok] -> Ok<[A, B]> 12 | * If provided Results list have at least one Err Result, returned Result will be 13 | * Result Err of first Err value found in the array. 14 | * @param results list of Ok and/or Err Results 15 | * @returns Result Ok of all input Ok values, or Err Result of one of provided Err values. 16 | */ 17 | combine( 18 | results: [...T] 19 | ): Ok> | ExtractErrTypes[number]; 20 | } 21 | 22 | interface OkNamespace { 23 | /** 24 | * Return Ok Result of the provided value 25 | * @param value the value for which Ok Result should be returned 26 | * @returns Ok Result of provided value 27 | */ 28 | of(value: TValue): Ok; 29 | } 30 | 31 | interface ErrNamespace { 32 | /** 33 | * Return Err Result of the provided value 34 | * @param error the error for which Err Result should be returned 35 | * @returns Err Result of provided error instance 36 | */ 37 | of(error: TError): TError extends unknown ? Err : never; 38 | } 39 | 40 | // Given a list of Results, this infer all the different `Ok` types from that list 41 | type ExtractOkTypes = { 42 | [Key in keyof T]: ExtractOkFromUnion>; 43 | }; 44 | 45 | // Given a list of Results, this infer all the different `Err` types from that list 46 | type ExtractErrTypes = { 47 | [Key in keyof T]: ExtractErrFromUnion>; 48 | }; 49 | 50 | // need to be separated generic type to run it for every element of union T separately 51 | type ExtractOkFromUnion = T extends Err 52 | ? never 53 | : T extends Ok 54 | ? V 55 | : T; 56 | 57 | // need to be separated generic type to run it for every element of union T separately 58 | type ExtractErrFromUnion = T extends Err ? T : never; 59 | -------------------------------------------------------------------------------- /src/common-result.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | Result, 3 | UnknownError as UnknownErrorType, 4 | } from './types/result-helpers'; 5 | import type { CommonResult, ResultWrapper } from './types/common-result'; 6 | 7 | export { 8 | CommonResult, 9 | UnknownError, 10 | isResult, 11 | commonResultFactory, 12 | resultWrapperFactory, 13 | wrapResultFactory, 14 | }; 15 | 16 | class UnknownError extends Error implements UnknownErrorType { 17 | name = '__UnknownError' as const; 18 | 19 | cause?: unknown; 20 | 21 | constructor(cause: unknown) { 22 | super(); 23 | this.cause = cause; 24 | } 25 | } 26 | 27 | const CommonResultPrototype: CommonResult = { 28 | __value: Promise.resolve(resultWrapperFactory(undefined, false)), 29 | __brand: undefined, 30 | 31 | map(mapper) { 32 | const newValWrapperPromise = this.__value.then((wrapper) => { 33 | if (wrapper.isError) { 34 | return wrapper; 35 | } 36 | 37 | return wrapResultFactory(() => mapper(wrapper.value as any)); 38 | }); 39 | return commonResultFactory(newValWrapperPromise) as any; 40 | }, 41 | 42 | mapErr(ErrorClass, mapper) { 43 | const newValWrapperPromise = this.__value.then((wrapper) => { 44 | if (!(wrapper.isError && wrapper.value instanceof ErrorClass)) { 45 | return wrapper; 46 | } 47 | 48 | return wrapResultFactory(() => mapper(wrapper.value as any)); 49 | }); 50 | return commonResultFactory(newValWrapperPromise) as any; 51 | }, 52 | 53 | mapAnyErr(mapper) { 54 | const newValWrapperPromise = this.__value.then((wrapper) => { 55 | if (!wrapper.isError) { 56 | return wrapper; 57 | } 58 | 59 | return wrapResultFactory(() => mapper(wrapper.value as any)); 60 | }); 61 | return commonResultFactory(newValWrapperPromise) as any; 62 | }, 63 | 64 | unsafePromise() { 65 | return this.__value.then((wrapper) => { 66 | if (wrapper.isError) { 67 | const originErr = 68 | wrapper.value instanceof UnknownError 69 | ? wrapper.value.cause 70 | : wrapper.value; 71 | return Promise.reject(originErr); 72 | } 73 | 74 | return Promise.resolve(wrapper.value); 75 | }) as any; 76 | }, 77 | 78 | promise() { 79 | return this.unsafePromise(); 80 | }, 81 | }; 82 | 83 | function commonResultFactory( 84 | value: Promise> 85 | ) { 86 | const commonResult = Object.create(CommonResultPrototype); 87 | commonResult.__value = value; 88 | return commonResult as CommonResult; 89 | } 90 | 91 | function isResult(value: unknown): value is Result { 92 | return CommonResultPrototype.isPrototypeOf(value as any); 93 | } 94 | 95 | function resultWrapperFactory( 96 | value: TErrorOrValue, 97 | isError: boolean 98 | ): ResultWrapper { 99 | return { value, isError }; 100 | } 101 | 102 | function unknownErrorWrapperFactory(err: unknown): ResultWrapper { 103 | return { value: new UnknownError(err), isError: true }; 104 | } 105 | 106 | function wrapResultFactory(valueFactory: () => unknown) { 107 | return Promise.resolve() 108 | .then(valueFactory) 109 | .then((result) => 110 | isResult(result) ? result.__value : resultWrapperFactory(result, false) 111 | ) 112 | .catch(unknownErrorWrapperFactory); 113 | } 114 | -------------------------------------------------------------------------------- /src/test/helper.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | 3 | import { Result } from '../types/result-helpers'; 4 | import { UnknownError } from '../common-result'; 5 | 6 | export { 7 | shouldEventuallyOk, 8 | shouldEventuallyErr, 9 | shouldEventuallyReject, 10 | shouldEventuallyUnknownErr, 11 | shouldBeAssignable, 12 | }; 13 | 14 | async function shouldEventuallyOk( 15 | result: Result, 16 | value: TValue, 17 | done: (err?: any) => void 18 | ): Promise { 19 | const wrapper = await result.__value; 20 | 21 | if (wrapper.isError) { 22 | done(`Ok result expected (${value}), got Err result`); 23 | } else { 24 | try { 25 | expect(wrapper.value).to.deep.equal(value); 26 | done(); 27 | } catch (err) { 28 | done(err); 29 | } 30 | } 31 | } 32 | 33 | async function shouldEventuallyErr( 34 | result: Result, 35 | value: TValue, 36 | done: (err?: any) => void 37 | ): Promise { 38 | const wrapper = await result.__value; 39 | 40 | if (wrapper.isError) { 41 | try { 42 | expect(wrapper.value).to.equal(value); 43 | done(); 44 | } catch (err) { 45 | done(err); 46 | } 47 | } else { 48 | done(`Err result expected (${value}), got Ok result`); 49 | } 50 | } 51 | 52 | async function shouldEventuallyUnknownErr( 53 | result: Result, 54 | err: unknown, 55 | done: (err?: any) => void 56 | ): Promise { 57 | const wrapper = await result.__value; 58 | 59 | if (!wrapper.isError) { 60 | done(`UnknownError Err result expected, got Ok result`); 61 | } 62 | 63 | try { 64 | expect(wrapper.value).instanceOf(UnknownError); 65 | if (wrapper.value instanceof UnknownError) { 66 | expect(wrapper.value.cause).to.equal(err); 67 | return done(); 68 | } 69 | 70 | done('Unexpected tests path, should throw before'); 71 | } catch (err) { 72 | done(err); 73 | } 74 | } 75 | 76 | async function shouldEventuallyReject( 77 | result: Result, 78 | value: TValue, 79 | done: (err?: any) => void 80 | ): Promise { 81 | try { 82 | await result.unsafePromise(); 83 | done(`Reject promise result expected (${value}), but promise resolve`); 84 | } catch (err) { 85 | done(); 86 | } 87 | } 88 | 89 | /** 90 | * Check if two values are compatible, can `value` be assigned to `to`. 91 | * 92 | * Most of the time we can verify the type of an operation by providing 93 | * an explicit expected type, e.g. 94 | * 95 | * ```ts 96 | * const action = () => 1 97 | * const actual: string = action() 98 | * ``` 99 | * 100 | * The test will fail type checks, because `number` cannot be assigned to `string`. 101 | * However, when the expected type is a superset of the returned type, e.g. 102 | * 103 | * ```ts 104 | * const actual: string | number = action() 105 | * ``` 106 | * 107 | * Such a test won't ensure that `action()` returns exactly `string | number`, 108 | * since `number` can be assigned to `string | number`. 109 | * 110 | * Instead we can infer the `actual` value and check if we can assign all 111 | * our expected types to it. 112 | * 113 | * ```ts 114 | * const actual = action() 115 | * 116 | * shouldBeAssignable(actual, 2) // pass 117 | * shouldBeAssignable(actual, "") // fail, string cannot be assigned to number 118 | * ``` 119 | */ 120 | // eslint-disable-next-line @typescript-eslint/no-empty-function 121 | function shouldBeAssignable(_to: T, _value: T) {} 122 | -------------------------------------------------------------------------------- /src/test/combine.test.ts: -------------------------------------------------------------------------------- 1 | import { Result, Ok, Err } from '../index'; 2 | import { 3 | shouldEventuallyOk, 4 | shouldEventuallyErr, 5 | shouldBeAssignable, 6 | } from './helper'; 7 | 8 | class Error1 extends Error { 9 | private __brand!: never; 10 | } 11 | class Error2 extends Error { 12 | private __brand!: never; 13 | } 14 | class Error3 extends Error { 15 | private __brand!: never; 16 | } 17 | 18 | test('Result combine of two Ok values result returns Ok result of array of two values', (done) => { 19 | const mapped: Result<['test-return1', 'test-return2'], never> = 20 | Result.combine([ 21 | Ok.of('test-return1' as const), 22 | Ok.of('test-return2' as const), 23 | ]); 24 | 25 | shouldEventuallyOk(mapped, ['test-return1', 'test-return2'], done); 26 | }); 27 | 28 | test('Result combine of Ok result and Err result value returns Err result with only original Err result value', (done) => { 29 | const err = new Error1(); 30 | const mapped: Result = Result.combine([ 31 | Ok.of('test-return1' as const), 32 | Err.of(err), 33 | ]); 34 | mapped.mapAnyErr((x) => console.log(x)); 35 | 36 | shouldEventuallyErr(mapped, err, done); 37 | }); 38 | 39 | test('Result combine of mixed Result values returns single Err or list of values', (done) => { 40 | const err = new Error1(); 41 | const values = [ 42 | Ok.of(5), 43 | Ok.of('test-return' as const), 44 | Err.of(err), 45 | Err.of(new Error2()), 46 | Err.of(new Error3()), 47 | ]; 48 | const mapped: Result<(number | 'test-return')[], Error1 | Error2 | Error3> = 49 | Result.combine(values); 50 | 51 | shouldEventuallyErr(mapped, err, done); 52 | }); 53 | 54 | test('Result combine of mixed types per Result returns expected result', (done) => { 55 | const mixedResult1 = Ok.of('return-type') as Err | Ok<'return-type'>; 56 | 57 | const mapped: Result<['return-type', number], Error1> = Result.combine([ 58 | mixedResult1, 59 | Ok.of(5), 60 | ]); 61 | 62 | shouldEventuallyOk(mapped, ['return-type', 5], done); 63 | }); 64 | 65 | test('Result combine of mixed types per Result and Result promises returns expected result', (done) => { 66 | const mixedResult1 = Ok.of('return-type') as 67 | | Err 68 | | Ok<'return-type'> 69 | | undefined; 70 | const mixedAsycnResult = Promise.resolve( 71 | Ok.of('return-type2') as Err | Ok<'return-type2'> | undefined 72 | ); 73 | const errLiteralResult = undefined as undefined | Err; 74 | 75 | const mapped: Result< 76 | [ 77 | 'return-type' | undefined, 78 | number, 79 | number, 80 | 'return-type2' | undefined, 81 | undefined 82 | ], 83 | Error1 | Error2 | Error3 84 | > = Result.combine([ 85 | mixedResult1, 86 | Ok.of(5), 87 | Promise.resolve(Ok.of(5)), 88 | mixedAsycnResult, 89 | errLiteralResult, 90 | ]); 91 | 92 | shouldEventuallyOk( 93 | mapped, 94 | ['return-type', 5, 5, 'return-type2', undefined], 95 | done 96 | ); 97 | }); 98 | 99 | test('Result.combine of results with possibly undefined values preserves possibly undefined values', (done) => { 100 | const result = Result.combine([ 101 | Ok.of(undefined), 102 | Ok.of(5), 103 | ]); 104 | 105 | shouldBeAssignable(result, Ok.of<[string, number]>(['hello', 1])); 106 | shouldBeAssignable( 107 | result, 108 | Ok.of<[undefined, undefined]>([undefined, undefined]) 109 | ); 110 | shouldEventuallyOk(result, [undefined, 5], done); 111 | }); 112 | 113 | test('Result.combine of results with nullable values preserves nullable values', (done) => { 114 | const result = Result.combine([ 115 | Ok.of(null), 116 | Ok.of(5), 117 | ]); 118 | 119 | shouldBeAssignable(result, Ok.of<[string, number]>(['hello', 1])); 120 | shouldBeAssignable(result, Ok.of<[null, null]>([null, null])); 121 | shouldEventuallyOk(result, [null, 5], done); 122 | }); 123 | -------------------------------------------------------------------------------- /src/test/resultify.test.ts: -------------------------------------------------------------------------------- 1 | import { Err, Ok } from '../index'; 2 | import { resultify } from '../resultify'; 3 | import { 4 | shouldEventuallyOk, 5 | shouldEventuallyErr, 6 | shouldEventuallyUnknownErr, 7 | } from './helper'; 8 | 9 | class Error2 extends Error { 10 | private __brand!: never; 11 | } 12 | 13 | suite('resultify of a sync function', () => { 14 | test('returns a function that yields `Ok` for a non-`Result` source function', (done) => { 15 | const value = 5; 16 | const nonResultReturningFunction = (val: number) => val * val; 17 | 18 | const resultifiedFunction: (val: number) => Ok = resultify( 19 | nonResultReturningFunction 20 | ); 21 | const resultifiedValue = resultifiedFunction(value); 22 | 23 | shouldEventuallyOk(resultifiedValue, 25, done); 24 | }); 25 | 26 | test('returns a function that yields `Ok` for a throwing source function', (done) => { 27 | const err = new Error('Error but typed'); 28 | const errReturningFunction = () => { 29 | throw err; 30 | }; 31 | 32 | const resultifiedFunction: () => Ok = 33 | resultify(errReturningFunction); 34 | const resultifiedValue = resultifiedFunction(); 35 | 36 | shouldEventuallyUnknownErr(resultifiedValue, err, done); 37 | }); 38 | 39 | test('returns a function that yields `Ok` for an `Ok` source function', (done) => { 40 | const okReturningFunction = (val: number) => Ok.of(val * val); 41 | const value = 4; 42 | 43 | const resultifiedFunction: (val: number) => Ok = 44 | resultify(okReturningFunction); 45 | const resultifiedValue = resultifiedFunction(value); 46 | 47 | shouldEventuallyOk(resultifiedValue, 16, done); 48 | }); 49 | 50 | test('returns a function that yields `Err` for an `Err` source function', (done) => { 51 | const error = new Error2(); 52 | 53 | const errReturningFunction = () => { 54 | return Err.of(error); 55 | }; 56 | 57 | const resultifiedFunction: () => Ok | Err = 58 | resultify(errReturningFunction); 59 | const resultifiedValue = resultifiedFunction(); 60 | 61 | shouldEventuallyErr(resultifiedValue, error, done); 62 | }); 63 | }); 64 | 65 | suite('resultify of an async function', () => { 66 | test('returns a function that yields `Ok` for a non-`Result` source function', (done) => { 67 | const value = 5; 68 | const nonResultReturningFunction = async (val: number) => val * val; 69 | 70 | const resultifiedFunction: (val: number) => Ok = resultify( 71 | nonResultReturningFunction 72 | ); 73 | const resultifiedValue = resultifiedFunction(value); 74 | 75 | shouldEventuallyOk(resultifiedValue, 25, done); 76 | }); 77 | 78 | // TODO: enable after rebase 79 | test('returns a function that yields `Ok` for a throwing source function', (done) => { 80 | const err = new Error('Error but typed'); 81 | const errReturningFunction = async () => { 82 | throw err; 83 | }; 84 | 85 | const resultifiedFunction: () => Ok = 86 | resultify(errReturningFunction); 87 | const resultifiedValue = resultifiedFunction(); 88 | 89 | shouldEventuallyUnknownErr(resultifiedValue, err, done); 90 | }); 91 | 92 | test('returns a function that yields `Ok` for an `Ok` source function', (done) => { 93 | const okReturningFunction = async (val: number) => Ok.of(val * val); 94 | const value = 4; 95 | 96 | const resultifiedFunction: (val: number) => Ok = 97 | resultify(okReturningFunction); 98 | const resultifiedValue = resultifiedFunction(value); 99 | 100 | shouldEventuallyOk(resultifiedValue, 16, done); 101 | }); 102 | 103 | test('returns a function that yields `Err` for an `Err` source function', (done) => { 104 | const error = new Error2(); 105 | 106 | const errReturningFunction = async () => { 107 | return Err.of(error); 108 | }; 109 | 110 | const resultifiedFunction: () => Ok | Err = 111 | resultify(errReturningFunction); 112 | const resultifiedValue = resultifiedFunction(); 113 | 114 | shouldEventuallyErr(resultifiedValue, error, done); 115 | }); 116 | }); 117 | -------------------------------------------------------------------------------- /src/types/result-helpers.ts: -------------------------------------------------------------------------------- 1 | import type { ResultWrapper } from './common-result'; 2 | 3 | export type { 4 | Result, 5 | Constructor, 6 | Ok, 7 | MapFromResult, 8 | MapOkResult, 9 | InferOk, 10 | Err, 11 | ErrMapper, 12 | MapErrResult, 13 | MapAnyErrResult, 14 | InferErr, 15 | UnknownError, 16 | }; 17 | 18 | interface UnknownError extends Error { 19 | name: '__UnknownError'; 20 | 21 | cause?: unknown; 22 | } 23 | 24 | type Result = Ok | Err; 25 | 26 | type Ok = Subresult & { 27 | readonly __value: Promise>; 28 | 29 | __brand: 'ok'; 30 | 31 | /** 32 | * Return fulfilled promise of current `Ok` Result value. To use the `promise()` 33 | * function you need first handle all known Err result values. 34 | * It is possible that it return rejected promise for unknown exceptions. 35 | * @returns promise of current Result value - fulfilled if the value is Ok, 36 | * rejected if there was an exception thrown anywhere in the mapping chain 37 | */ 38 | promise>( 39 | this: U 40 | ): Promise | never>; 41 | }; 42 | 43 | type Err = Subresult & { 44 | readonly __value: Promise>; 45 | __brand: 'err'; 46 | }; 47 | 48 | interface Subresult { 49 | /** 50 | * Map current Result if it's Ok. It's left Err Result unchanged. 51 | * You can use async function for mapping. 52 | * @param mapper the function used to map the current Ok result, can be async 53 | * @returns new Result, mapped if current Result is Ok, left unchanged otherwise 54 | */ 55 | map, R>( 56 | this: U, 57 | mapper: (value: InferOk) => R | Promise 58 | ): MapOkResult; 59 | 60 | /** 61 | * Map current Result if it's a Err of a specific class. 62 | * It's left Ok Result and Err of different class unchanged. 63 | * You can use async function for mapping. 64 | * @param ErrorClass the class of specific error object that should be mapped 65 | * @param mapper the function used to map the current Err result, can be async 66 | * @returns new Result, mapped if current Result is Err of specific class, left unchanged otherwise 67 | */ 68 | mapErr< 69 | U extends Result, 70 | R, 71 | E extends InferErr | UnknownError 72 | >( 73 | this: U, 74 | ErrorClass: Constructor, 75 | mapper: (err: E) => R | Promise 76 | ): MapErrResult; 77 | 78 | /** 79 | * Map current Result if it's Err. It's left Ok Result unchanged. 80 | * You can use async function for mapping. 81 | * @param mapper the function used to map the current Err result, can be async 82 | * @returns new Result, mapped if current Result is Err, left unchanged otherwise 83 | */ 84 | mapAnyErr, R>( 85 | this: U, 86 | mapper: ErrMapper 87 | ): MapAnyErrResult; 88 | 89 | /** 90 | * WARNING: it's not recommended to use this function. consider using `promise()` instead. 91 | * Return promise of current Result value - fulfilled if the value is Ok, 92 | * rejected if the value is Err. The Err type will be lost in the process. 93 | * @returns promise of current Result value - fulfilled if the value is Ok, rejected if the value is Err 94 | */ 95 | unsafePromise>( 96 | this: U 97 | ): Promise | never>; 98 | } 99 | 100 | type InferOk = U extends Ok ? T : never; 101 | 102 | type InferErr = U extends Err ? T : never; 103 | 104 | type ErrMapper, R> = ( 105 | value: InferErr | UnknownError 106 | ) => R | Promise; 107 | 108 | type MapFromResult = ResultOrOk | Ok; 109 | 110 | type MapOkResult = U extends Ok ? ResultOrOk | Ok : U; 111 | 112 | type MapAnyErrResult< 113 | U extends Result, 114 | R 115 | > = U extends Err 116 | ? ResultOrOk>> | Ok 117 | : U; 118 | 119 | type MapErrResult = U extends Err 120 | ? E extends EUnion 121 | ? ResultOrOk | Ok 122 | : U 123 | : U; 124 | 125 | type ResultOrOk = R extends Result ? R : Ok; 126 | 127 | interface Constructor { 128 | new (...args: any[]): C; 129 | } 130 | -------------------------------------------------------------------------------- /src/test/result-from.test.ts: -------------------------------------------------------------------------------- 1 | import { Result, Ok, Err } from '../index'; 2 | import { 3 | shouldEventuallyOk, 4 | shouldEventuallyErr, 5 | shouldEventuallyReject, 6 | shouldEventuallyUnknownErr, 7 | } from './helper'; 8 | 9 | class Error1 extends Error { 10 | private __brand!: never; 11 | } 12 | 13 | suite('Result.from of an result factory', () => { 14 | test('returns mapped result for mapper with plain return', (done) => { 15 | const mapped: Ok<'test-return'> = Result.from(() => { 16 | return 'test-return' as const; 17 | }); 18 | 19 | shouldEventuallyOk(mapped, 'test-return', done); 20 | }); 21 | 22 | test('returns mapped result for mapper with ok result return', (done) => { 23 | const mapped: Ok<'test-return2'> = Result.from(() => { 24 | return Ok.of('test-return2' as const); 25 | }); 26 | 27 | shouldEventuallyOk(mapped, 'test-return2', done); 28 | }); 29 | 30 | test('returns mapped result for mapper with err result return', (done) => { 31 | const err1 = new Error1(); 32 | 33 | const mapped: Err | Ok = Result.from(() => { 34 | return Err.of(err1); 35 | }); 36 | 37 | shouldEventuallyErr(mapped, err1, done); 38 | }); 39 | 40 | test('returns mapped result for mapper with mixed result return', (done) => { 41 | const err1 = new Error1(); 42 | const value = 5; 43 | 44 | const mapped: Result<'test-ok', Error1> = Result.from(() => { 45 | return value > 6 ? Err.of(err1) : Ok.of('test-ok' as const); 46 | }); 47 | 48 | shouldEventuallyOk(mapped, 'test-ok', done); 49 | }); 50 | 51 | test('returns mapped result for mapper with promise of plain return', (done) => { 52 | async function getAsyncOk(value: number) { 53 | return `value of ${value}`; 54 | } 55 | const mapped: Ok = Result.from(async () => { 56 | const res = await getAsyncOk(5); 57 | return res; 58 | }); 59 | 60 | shouldEventuallyOk(mapped, 'value of 5', done); 61 | }); 62 | 63 | test('returns mapped result for mapper with promise of ok result return', (done) => { 64 | async function getAsyncOk(value: number) { 65 | return Ok.of(`ok of ${value}`); 66 | } 67 | const mapped: Ok = Result.from(async () => { 68 | const res = await getAsyncOk(5); 69 | return res; 70 | }); 71 | 72 | shouldEventuallyOk(mapped, 'ok of 5', done); 73 | }); 74 | 75 | test('returns mapped result for mapper with generic result return', (done) => { 76 | const err1 = new Error1(); 77 | 78 | const genericFn = >( 79 | val: TResult 80 | ) => { 81 | const mapped: Ok | Err = Result.from(() => val); 82 | shouldEventuallyErr(mapped, err1, done); 83 | }; 84 | 85 | genericFn(Err.of(err1)); 86 | }); 87 | 88 | test('returns mapped result for mapper with promise of err result return', (done) => { 89 | const err1 = new Error1(); 90 | 91 | async function getAsyncErr(_value: number) { 92 | return Err.of(err1); 93 | } 94 | const mapped: Err | Ok = Result.from(async () => { 95 | const res = await getAsyncErr(5); 96 | return res; 97 | }); 98 | 99 | shouldEventuallyErr(mapped, err1, done); 100 | }); 101 | 102 | test('returns mapped result for mapper with mixed result return', (done) => { 103 | const err1 = new Error1(); 104 | 105 | async function getAsyncResult(value: number) { 106 | return value > 6 ? Err.of(err1) : Ok.of('test-ok' as const); 107 | } 108 | 109 | const mapped: Result<'test-ok', Error1> = Result.from(async () => { 110 | const res = await getAsyncResult(5); 111 | return res; 112 | }); 113 | 114 | shouldEventuallyOk(mapped, 'test-ok', done); 115 | }); 116 | 117 | test('returns rejected result when throwing async mapper', (done) => { 118 | const mapped: Result = Result.from(async () => { 119 | throw new Error('Something goes wrong'); 120 | }); 121 | 122 | shouldEventuallyReject(mapped, 'Something goes wrong', done); 123 | }); 124 | 125 | test('returns rejected result when throwing sync mapper', (done) => { 126 | const mapped: Result = Result.from(() => { 127 | throw new Error('Something goes wrong'); 128 | }); 129 | 130 | shouldEventuallyReject(mapped, 'Something goes wrong', done); 131 | }); 132 | 133 | test('returns mapped value when throwing sync mapper', (done) => { 134 | const err4 = new Error('Something happened'); 135 | 136 | const result = Result.from(() => { 137 | throw err4; 138 | }); 139 | 140 | const mapped: Ok<'mapped-unknown-err-result'> = result.map(() => { 141 | return 'mapped-unknown-err-result' as const; 142 | }); 143 | 144 | shouldEventuallyUnknownErr(mapped, err4, done); 145 | }); 146 | 147 | test('returns mapped value when throwing async mapper', (done) => { 148 | const err4 = new Error('Something happened'); 149 | 150 | const result = Result.from(async () => { 151 | throw err4; 152 | }); 153 | 154 | const mapped: Ok<'mapped-unknown-err-result'> = result.map(() => { 155 | return 'mapped-unknown-err-result' as const; 156 | }); 157 | 158 | shouldEventuallyUnknownErr(mapped, err4, done); 159 | }); 160 | }); 161 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://badgen.net/npm/v/type-safe-errors)](https://badgen.net/npm/v/type-safe-errors) 2 | [![license](https://badgen.net/npm/license/vast-client)](https://badgen.net/npm/license/vast-client) 3 | [![bundlejs](https://deno.bundlejs.com/badge?q=type-safe-errors)](https://deno.bundlejs.com/badge?q=type-safe-errors) 4 | 5 | # Type-safe errors in TypeScript 6 | 7 | ## Overview 8 | `type-safe-errors` provides custom type-safe errors to Typescript. 9 | 10 | The library offers an async promise-like interface and ensures type safety with easy-to-handle errors. 11 | 12 | ## Motivation 13 | The type-safe-errors library was made to solve these problems: 14 | - In TypeScript, when promises are rejected, they lose their error types. It's tough to keep these error types correct, and it gets even harder over time. 15 | - Not all error in the code should lead to throws. Sometimes, it makes more sense to think of an error as just the result of some logic. 16 | For example, if there's a problem connecting to a database, that's a good time to use a throw. But an invalid password should be seen as a result of validation logic, not an exception 17 | 18 | ## Table Of Contents 19 | 20 | * [Installation](#installation) 21 | * [Philosophy](#philosophy) 22 | * [Examples](./examples) 23 | * [API Reference](./docs/REFERENCE.md) 24 | * [Inspiration](#inspiration) 25 | * [Troubleshooting](#troubleshooting) 26 | 27 | ## Installation 28 | 29 | ```sh 30 | npm i type-safe-errors 31 | ``` 32 | 33 | ### Basic example 34 | 35 | In this example, we demonstrate how to use the `type-safe-errors` library to handle user authorization with a simple username and password check. 36 | 37 | ```ts 38 | import { Ok, Err } from 'type-safe-errors'; 39 | 40 | class InvalidCredentialsError extends Error { 41 | private __brand!: never; 42 | } 43 | 44 | // Function to authorize an user based on their username and password 45 | function authorizeUser(username: string, password: string) { 46 | if (username === 'admin' && password === 'admin') { 47 | // Return an Ok result with the user information 48 | return Ok.of({ 49 | name: 'admin', 50 | isAdmin: true, 51 | }); 52 | } else { 53 | // Return an Err result with an InvalidCredentialsError instance 54 | return Err.of(new InvalidCredentialsError()); 55 | } 56 | } 57 | 58 | authorizeUser('admin', 'admin') 59 | // Handle successful authorization 60 | .map((user) => { 61 | console.log('authorized! hello ', user.name); 62 | }) 63 | // Handle error in case of invalid credentials 64 | .mapErr(InvalidCredentialsError, (err) => { 65 | // here `err` is fully typed object of InvalidCredentialsError class 66 | console.log('Invalid credentials!', err); 67 | }) 68 | // Map the result to classic promise 69 | // This is optional, but highly recommended, as it allows TypeScript to detect 70 | // not handled errors, in current code and in the future 71 | .promise(); 72 | ``` 73 | 74 | ### Async basic example 75 | It's common to start a result chain with an async call, such as a call to your database or an external API. 76 | 77 | There are a few ways to handle this, but the simplest is to use the dedicated helper function: [Result.from](./docs/REFERENCE.md#resultfromresultfactory). 78 | 79 | ```ts 80 | import { Ok, Err, Result } from 'type-safe-errors'; 81 | 82 | class InvalidCredentialsError extends Error { 83 | private __brand!: never; 84 | } 85 | 86 | class UserNotFoundError extends Error { 87 | private __brand!: never; 88 | } 89 | 90 | async function authorizeUser(username: string, password: string) { 91 | if (username !== 'admin') { 92 | return Err.of(new UserNotFoundError()); 93 | } 94 | 95 | // simulate async call 96 | const storedPassword = await Promise.resolve('admin'); 97 | if (password !== storedPassword) { 98 | return Err.of(new InvalidCredentialsError()); 99 | } 100 | 101 | return Ok.of({ 102 | name: 'admin', 103 | isAdmin: true, 104 | }); 105 | } 106 | 107 | Result.from(() => authorizeUser('admin', 'admin')) 108 | .map((user) => { 109 | // here `user` type is { name: string, isAdmin: boolean }, from `authorizeUser` return type 110 | console.log('authorized! hello ', user.name); 111 | }) 112 | .mapErr(UserNotFoundError, (err) => { 113 | // here `err` is fully typed object of InvalidCredentialsError class 114 | console.log('Invalid user name!', err); 115 | }) 116 | .mapErr(InvalidCredentialsError, (err) => { 117 | // here `err` is fully typed object of InvalidCredentialsError class 118 | console.log('Invalid credentials!', err); 119 | }); 120 | 121 | ``` 122 | ## Description 123 | If you work with rich business logic, it's common to use exceptions to represent different states of the application. The problem with this solution and TypeScript is that when you catch an exception, you lose information about its types. 124 | 125 | Let consider this simplified example from an [express](https://expressjs.com/) controller: 126 | 127 | ```ts 128 | try { 129 | await payForProduct(userCard, product); 130 | } catch (err) { 131 | res.send(500, "Internal server error"); 132 | } 133 | ``` 134 | 135 | By looking at this code, you cannot determine what kind of exception can happen. 136 | Of course, you can check the `payForProduct` body, but what if it's called by other functions, 137 | which in turn call additional functions? 138 | When dealing with advanced business logic, it becomes difficult to maintain an understanding of all possible scenarios that can lead to errors solely by reading the code. 139 | 140 | That's why it's common to just return `500` in such cases (`express` does it by default). However, there can be many errors that should be handled differently than a `500` status code. For example: 141 | 142 | - Maybe the user did not set any address data yet? 143 | - Maybe the user's cart has expired? 144 | - Maybe the user provided an invalid CVC number? 145 | 146 | In each of these cases, the server should return a different status code along with specific error details. The client app should be informed of the reason, for example, by a `400` status code and error details in the response body. But first, to properly handle the errors, the developer must be aware of what errors can happen. 147 | This is the problem that `type-safe-errors` library is trying to solve. 148 | 149 | ![screen-gif](./docs/basic-example.gif) 150 | 151 | (Full example: [./examples/basic-example](./examples/basic-example)) 152 | 153 | ## Philosophy 154 | 155 | ### Minimal API 156 | Keeping the API minimal reduces the learning curve and makes it easier for developers to understand and use the library effectively. 157 | 158 | It's one of the reasons why the result class is always async (for example, [neverthrow](https://github.com/supermacro/neverthrow) has two different result types, one for synchronous and one for asynchronous results handling). 159 | 160 | The long-term goal is not to handle every possible use case. Instead, it's to do one thing well - providing a way to handle domain exceptions in a strong-typed, future-proof way. 161 | 162 | ### Practical API 163 | Using `type-safe-erros` should be similar in feel to work with traditional js [promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). You can [map](./docs/REFERENCE.md#okmapcallback) any success result (same like you can [then](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) fulfilled promise) or [mapAnyErr](./docs/REFERENCE.md#errmapanyerrcallback) (same as you can [catch](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch) rejected promise). 164 | 165 | You may notice that the `type-safe-error` project is somehow based on [Either](https://github.com/sanctuary-js/sanctuary-either) concept from functional programming. But the goal was not to follow the idea closely but to provide an easy-to-use API in practical TypeScript work, focused on async programming. 166 | 167 | 168 | ## Inspiration 169 | - [Expressive error handling in TypeScript and benefits for domain-driven design](https://medium.com/inato/expressive-error-handling-in-typescript-and-benefits-for-domain-driven-design-70726e061c86) 170 | - [STOP throwing Exceptions! Start being Explicit](https://www.youtube.com/watch?v=4UEanbBaJy4&t=5s) 171 | - [200 OK! Error Handling in GraphQL](https://www.youtube.com/watch?v=A5-H6MtTvqk) 172 | - [neverthrow](https://github.com/supermacro/neverthrow) 173 | - [Khalil Stemmler: Flexible Error Handling w/ the Result Class](https://khalilstemmler.com/articles/enterprise-typescript-nodejs/handling-errors-result-class/) 174 | - [Functional Error Handling with Express.js and DDD](https://khalilstemmler.com/articles/enterprise-typescript-nodejs/functional-error-handling/) 175 | - [Either](https://github.com/sanctuary-js/sanctuary-either) 176 | 177 | ## Troubleshooting 178 | 179 | ### Errors seem not to be caught by `.mapErr(...)` function 180 | 181 | Quick fix: Update your `tsconfig.json` file [`compilerOptions.target`](https://www.typescriptlang.org/tsconfig#target) option to at least `es6`. 182 | 183 | The `type-safe-errors` library depends on `instanceof` standard JS feature. 184 | The `instanceof` feature allows checking if an object is an instance of a particular class or constructor. In the context of `type-safe-errors`, it is used to verify if an error object is an instance of a specific error class. 185 | 186 | However, extending the JavaScript [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) does not work correctly for TypeScript compilation targets `es5` and below. This issue is explained on [TypeScript wiki](https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work). 187 | 188 | One option is to update `tsconfig.json` file [`compilerOptions.target`](https://www.typescriptlang.org/tsconfig#target) to `es6` or a higher version. 189 | An alternative is to abstain from extending by JavaScript [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) class, e.g. 190 | 191 | ```ts 192 | // original 193 | class InvalidCredentialsError extends Error { 194 | private __brand!: never; 195 | } 196 | 197 | // without Error parent 198 | class InvalidCredentialsError { 199 | private __brand!: never; 200 | } 201 | ``` 202 | 203 | This works for most cases, but be aware that sometimes it can result in a rejection of a non-Error JavaScript object (instance of your class). This may interfere with some other tools (for example, Mocha can sometimes show cryptic error messages when a test fails). 204 | -------------------------------------------------------------------------------- /examples/basic-example/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basic-example", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "type-safe-errors": "file:../.." 9 | }, 10 | "devDependencies": { 11 | "ts-node": "^10.9.1", 12 | "typescript": "^4.7.4" 13 | } 14 | }, 15 | "../..": { 16 | "version": "0.2.7", 17 | "license": "MIT", 18 | "devDependencies": { 19 | "@types/chai": "^4.3.1", 20 | "@types/mocha": "^9.1.1", 21 | "@types/node": "^16.11.7", 22 | "@typescript-eslint/eslint-plugin": "^5.30.7", 23 | "@typescript-eslint/parser": "^5.30.7", 24 | "chai": "^4.3.6", 25 | "eslint": "^8.20.0", 26 | "eslint-config-prettier": "^8.5.0", 27 | "eslint-plugin-prettier": "^4.2.1", 28 | "mocha": "^10.0.0", 29 | "prettier": "^2.7.1", 30 | "ts-node": "^10.9.1", 31 | "typescript": "^4.7.4" 32 | } 33 | }, 34 | "node_modules/@cspotcode/source-map-support": { 35 | "version": "0.8.1", 36 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 37 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 38 | "dev": true, 39 | "dependencies": { 40 | "@jridgewell/trace-mapping": "0.3.9" 41 | }, 42 | "engines": { 43 | "node": ">=12" 44 | } 45 | }, 46 | "node_modules/@jridgewell/resolve-uri": { 47 | "version": "3.1.0", 48 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", 49 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", 50 | "dev": true, 51 | "engines": { 52 | "node": ">=6.0.0" 53 | } 54 | }, 55 | "node_modules/@jridgewell/sourcemap-codec": { 56 | "version": "1.4.14", 57 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", 58 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", 59 | "dev": true 60 | }, 61 | "node_modules/@jridgewell/trace-mapping": { 62 | "version": "0.3.9", 63 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 64 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 65 | "dev": true, 66 | "dependencies": { 67 | "@jridgewell/resolve-uri": "^3.0.3", 68 | "@jridgewell/sourcemap-codec": "^1.4.10" 69 | } 70 | }, 71 | "node_modules/@tsconfig/node10": { 72 | "version": "1.0.8", 73 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", 74 | "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", 75 | "dev": true 76 | }, 77 | "node_modules/@tsconfig/node12": { 78 | "version": "1.0.9", 79 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", 80 | "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", 81 | "dev": true 82 | }, 83 | "node_modules/@tsconfig/node14": { 84 | "version": "1.0.1", 85 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", 86 | "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", 87 | "dev": true 88 | }, 89 | "node_modules/@tsconfig/node16": { 90 | "version": "1.0.2", 91 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", 92 | "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", 93 | "dev": true 94 | }, 95 | "node_modules/@types/node": { 96 | "version": "16.4.2", 97 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.4.2.tgz", 98 | "integrity": "sha512-vxyhOzFCm+jC/T5KugbVsYy1DbQM0h3NCFUrVbu0+pYa/nr+heeucpqxpa8j4pUmIGLPYzboY9zIdOF0niFAjQ==", 99 | "dev": true, 100 | "peer": true 101 | }, 102 | "node_modules/acorn": { 103 | "version": "8.8.0", 104 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", 105 | "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", 106 | "dev": true, 107 | "bin": { 108 | "acorn": "bin/acorn" 109 | }, 110 | "engines": { 111 | "node": ">=0.4.0" 112 | } 113 | }, 114 | "node_modules/acorn-walk": { 115 | "version": "8.2.0", 116 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 117 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 118 | "dev": true, 119 | "engines": { 120 | "node": ">=0.4.0" 121 | } 122 | }, 123 | "node_modules/arg": { 124 | "version": "4.1.3", 125 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 126 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 127 | "dev": true 128 | }, 129 | "node_modules/create-require": { 130 | "version": "1.1.1", 131 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 132 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 133 | "dev": true 134 | }, 135 | "node_modules/diff": { 136 | "version": "4.0.2", 137 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 138 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 139 | "dev": true, 140 | "engines": { 141 | "node": ">=0.3.1" 142 | } 143 | }, 144 | "node_modules/make-error": { 145 | "version": "1.3.6", 146 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 147 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 148 | "dev": true 149 | }, 150 | "node_modules/ts-node": { 151 | "version": "10.9.1", 152 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", 153 | "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", 154 | "dev": true, 155 | "dependencies": { 156 | "@cspotcode/source-map-support": "^0.8.0", 157 | "@tsconfig/node10": "^1.0.7", 158 | "@tsconfig/node12": "^1.0.7", 159 | "@tsconfig/node14": "^1.0.0", 160 | "@tsconfig/node16": "^1.0.2", 161 | "acorn": "^8.4.1", 162 | "acorn-walk": "^8.1.1", 163 | "arg": "^4.1.0", 164 | "create-require": "^1.1.0", 165 | "diff": "^4.0.1", 166 | "make-error": "^1.1.1", 167 | "v8-compile-cache-lib": "^3.0.1", 168 | "yn": "3.1.1" 169 | }, 170 | "bin": { 171 | "ts-node": "dist/bin.js", 172 | "ts-node-cwd": "dist/bin-cwd.js", 173 | "ts-node-esm": "dist/bin-esm.js", 174 | "ts-node-script": "dist/bin-script.js", 175 | "ts-node-transpile-only": "dist/bin-transpile.js", 176 | "ts-script": "dist/bin-script-deprecated.js" 177 | }, 178 | "peerDependencies": { 179 | "@swc/core": ">=1.2.50", 180 | "@swc/wasm": ">=1.2.50", 181 | "@types/node": "*", 182 | "typescript": ">=2.7" 183 | }, 184 | "peerDependenciesMeta": { 185 | "@swc/core": { 186 | "optional": true 187 | }, 188 | "@swc/wasm": { 189 | "optional": true 190 | } 191 | } 192 | }, 193 | "node_modules/type-safe-errors": { 194 | "resolved": "../..", 195 | "link": true 196 | }, 197 | "node_modules/typescript": { 198 | "version": "4.7.4", 199 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", 200 | "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", 201 | "dev": true, 202 | "bin": { 203 | "tsc": "bin/tsc", 204 | "tsserver": "bin/tsserver" 205 | }, 206 | "engines": { 207 | "node": ">=4.2.0" 208 | } 209 | }, 210 | "node_modules/v8-compile-cache-lib": { 211 | "version": "3.0.1", 212 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", 213 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", 214 | "dev": true 215 | }, 216 | "node_modules/yn": { 217 | "version": "3.1.1", 218 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 219 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 220 | "dev": true, 221 | "engines": { 222 | "node": ">=6" 223 | } 224 | } 225 | }, 226 | "dependencies": { 227 | "@cspotcode/source-map-support": { 228 | "version": "0.8.1", 229 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 230 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 231 | "dev": true, 232 | "requires": { 233 | "@jridgewell/trace-mapping": "0.3.9" 234 | } 235 | }, 236 | "@jridgewell/resolve-uri": { 237 | "version": "3.1.0", 238 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", 239 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", 240 | "dev": true 241 | }, 242 | "@jridgewell/sourcemap-codec": { 243 | "version": "1.4.14", 244 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", 245 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", 246 | "dev": true 247 | }, 248 | "@jridgewell/trace-mapping": { 249 | "version": "0.3.9", 250 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 251 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 252 | "dev": true, 253 | "requires": { 254 | "@jridgewell/resolve-uri": "^3.0.3", 255 | "@jridgewell/sourcemap-codec": "^1.4.10" 256 | } 257 | }, 258 | "@tsconfig/node10": { 259 | "version": "1.0.8", 260 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", 261 | "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", 262 | "dev": true 263 | }, 264 | "@tsconfig/node12": { 265 | "version": "1.0.9", 266 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", 267 | "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", 268 | "dev": true 269 | }, 270 | "@tsconfig/node14": { 271 | "version": "1.0.1", 272 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", 273 | "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", 274 | "dev": true 275 | }, 276 | "@tsconfig/node16": { 277 | "version": "1.0.2", 278 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", 279 | "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", 280 | "dev": true 281 | }, 282 | "@types/node": { 283 | "version": "16.4.2", 284 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.4.2.tgz", 285 | "integrity": "sha512-vxyhOzFCm+jC/T5KugbVsYy1DbQM0h3NCFUrVbu0+pYa/nr+heeucpqxpa8j4pUmIGLPYzboY9zIdOF0niFAjQ==", 286 | "dev": true, 287 | "peer": true 288 | }, 289 | "acorn": { 290 | "version": "8.8.0", 291 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", 292 | "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", 293 | "dev": true 294 | }, 295 | "acorn-walk": { 296 | "version": "8.2.0", 297 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 298 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 299 | "dev": true 300 | }, 301 | "arg": { 302 | "version": "4.1.3", 303 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 304 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 305 | "dev": true 306 | }, 307 | "create-require": { 308 | "version": "1.1.1", 309 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 310 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 311 | "dev": true 312 | }, 313 | "diff": { 314 | "version": "4.0.2", 315 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 316 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 317 | "dev": true 318 | }, 319 | "make-error": { 320 | "version": "1.3.6", 321 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 322 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 323 | "dev": true 324 | }, 325 | "ts-node": { 326 | "version": "10.9.1", 327 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", 328 | "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", 329 | "dev": true, 330 | "requires": { 331 | "@cspotcode/source-map-support": "^0.8.0", 332 | "@tsconfig/node10": "^1.0.7", 333 | "@tsconfig/node12": "^1.0.7", 334 | "@tsconfig/node14": "^1.0.0", 335 | "@tsconfig/node16": "^1.0.2", 336 | "acorn": "^8.4.1", 337 | "acorn-walk": "^8.1.1", 338 | "arg": "^4.1.0", 339 | "create-require": "^1.1.0", 340 | "diff": "^4.0.1", 341 | "make-error": "^1.1.1", 342 | "v8-compile-cache-lib": "^3.0.1", 343 | "yn": "3.1.1" 344 | } 345 | }, 346 | "type-safe-errors": { 347 | "version": "file:../..", 348 | "requires": { 349 | "@types/chai": "^4.3.1", 350 | "@types/mocha": "^9.1.1", 351 | "@types/node": "^16.11.7", 352 | "@typescript-eslint/eslint-plugin": "^5.30.7", 353 | "@typescript-eslint/parser": "^5.30.7", 354 | "chai": "^4.3.6", 355 | "eslint": "^8.20.0", 356 | "eslint-config-prettier": "^8.5.0", 357 | "eslint-plugin-prettier": "^4.2.1", 358 | "mocha": "^10.0.0", 359 | "prettier": "^2.7.1", 360 | "ts-node": "^10.9.1", 361 | "typescript": "^4.7.4" 362 | } 363 | }, 364 | "typescript": { 365 | "version": "4.7.4", 366 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", 367 | "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", 368 | "dev": true 369 | }, 370 | "v8-compile-cache-lib": { 371 | "version": "3.0.1", 372 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", 373 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", 374 | "dev": true 375 | }, 376 | "yn": { 377 | "version": "3.1.1", 378 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 379 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 380 | "dev": true 381 | } 382 | } 383 | } 384 | -------------------------------------------------------------------------------- /docs/REFERENCE.md: -------------------------------------------------------------------------------- 1 | # API Reference 2 | 3 | `type-safe-errors` exposes three class-like abstractions: [Ok](#ok), [Err](#err), and [Result](#result). 4 | 5 | For an introduction to the type-safe-errors library and its benefits, please refer to the [README](../README.md). For framework-specific examples, please refer to the [Framework Examples](../examples) directory. 6 | ## Ok 7 | An `Ok` object represents a valid result of an action. 8 | 9 | ### Ok.of(...) 10 | 11 | Creates a new `Ok` result. It is a static function, meaning it can only be called on the imported `Ok` namespace and not on `Ok` result instances. 12 | 13 | 14 | The operation is the results version of [Promise.resolve](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve) function. 15 | 16 | **Signature:** 17 | 18 | ```typescript 19 | Ok.of(value: TValue): Ok 20 | ``` 21 | 22 | **Examples:** 23 | 24 | ```typescript 25 | import { Ok } from 'type-safe-errors'; 26 | 27 | const okResult = Ok.of({ 28 | name: 'John', 29 | surname: 'Doe' 30 | }); 31 | 32 | // you can force Ok type by providing first generic argument 33 | const okString = Ok.of('Joe Doe'); 34 | ``` 35 | 36 | --- 37 | 38 | ### ok.map(callback) 39 | 40 | Transforms an `Ok` result into a different result using the provided callback function. This interface is common for both `Ok` and `Err` results: [result.map(callback)](#resultmapcallback) 41 | 42 | The operation is the results version of [Promise.prototype.then](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) function. 43 | 44 | **Examples:** 45 | 46 | ```ts 47 | import { Ok } from 'type-safe-errors'; 48 | 49 | const okResult = Ok.of(5); 50 | // Ok.of(10) 51 | const doubledOkResult = okResult.map(value => value * 2); 52 | ``` 53 | 54 | Note: If an unexpected error is thrown within the callback function, it will be wrapped in an `UnknownError` and passed to the `mapErr` or `mapAnyErr` function if they are in the chain. If not handled, it will result in a rejected promise. 55 | 56 | ```ts 57 | import { Ok, UnknownError } from 'type-safe-errors'; 58 | 59 | Ok.of(5) 60 | .map(val => { 61 | throw new Error('Problem!'); 62 | }) 63 | .mapErr(UnknownError, err => console.error(err.cause)); 64 | ``` 65 | 66 | 67 | 68 | --- 69 | 70 | ### ok.mapErr(ErrorClass, callback) 71 | 72 | No operation is performed for `Ok` results. This interface is common for both `Ok` and `Err` results: [result.mapErr(ErrorClass, callback)](#resultmaperrerrorclass-callback) 73 | 74 | **Examples:** 75 | 76 | ```ts 77 | import { Ok } from 'type-safe-errors'; 78 | import { UserNotFoundError } from './errors'; 79 | 80 | const okResult = Ok.of(5); 81 | // No operation is performed, the original Ok result is returned 82 | const sameOkResult = okResult.mapErr(UserNotFoundError, err => 123); 83 | 84 | ``` 85 | 86 | Note: You can handle unexpected errors that happened before in the Result chain by using `UnknownError` class: 87 | 88 | 89 | ```ts 90 | import { Ok, UnknownError } from 'type-safe-errors'; 91 | 92 | Ok.of(5) 93 | .map(val => { 94 | throw new Error('Problem!'); 95 | }) 96 | .mapErr(UnknownError, err => console.error(err.cause)); 97 | ``` 98 | 99 | 100 | --- 101 | 102 | ### ok.mapAnyErr(callback) 103 | 104 | Do nothing for `Ok` results. 105 | Interface common for both types of results: [result.mapAnyErr(callback)](#resultmapanyerrcallback) 106 | 107 | The operation is the results version of [Promise.prototype.catch](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch) function. 108 | 109 | **Examples:** 110 | 111 | ```ts 112 | import { Ok } from 'type-safe-errors'; 113 | 114 | const okResult = Ok.of(5); 115 | // No operation is performed, the original Ok result is returned 116 | const sameOkResult = okResult.mapAnyErr(err => 123); 117 | 118 | ``` 119 | 120 | Note: If an unexpected error is thrown before in the Result chain, it will be wrapped in an `UnknownError` and passed to this function: 121 | 122 | ```ts 123 | import { Ok, UnknownError } from 'type-safe-errors'; 124 | 125 | Ok.of(5) 126 | .map(val => { 127 | throw new Error('Problem!'); 128 | }) 129 | // error is type `UnknownError` 130 | .mapAnyErr(err => console.error(err.cause)); 131 | ``` 132 | 133 | --- 134 | 135 | ### ok.promise() 136 | 137 | Map an `Ok` result to a [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). The promise will resolve with the original `Ok` result value. 138 | 139 | This function is intentionally unavailable for `Err` results to help detect unhandled `Err` results that might appear in the results chain later on. 140 | 141 | **Signature:** 142 | 143 | ```typescript 144 | Ok.promise(): Promise 145 | ``` 146 | 147 | **Examples:** 148 | 149 | ```typescript 150 | import { Ok, Err } from 'type-safe-errors'; 151 | import { UserNotFoundError } from './errors'; 152 | 153 | async function promiseResolver() { 154 | const number5 = await Ok.of(5).promise(); 155 | 156 | // The line below will not compile, providing quick feedback about the issue 157 | await Err.of(new UserNotFoundError()).promise(); 158 | } 159 | ``` 160 | 161 | --- 162 | 163 | ### ok.unsafePromise() 164 | 165 | Map an `Ok` result to a fulfilled [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). 166 | This is a common interface for both `Ok` and `Err` results: [result.unsafePromise()](#resultunsafePromise). 167 | 168 | **Examples:** 169 | 170 | ```typescript 171 | import { Ok } from 'type-safe-errors'; 172 | 173 | const okResult = Ok.of(5); 174 | const fulfilledPromise = okResult.unsafePromise(); // Promise resolves with value 5 175 | ``` 176 | 177 | --- 178 | 179 | 180 | ## Err 181 | An `Err` object represents an invalid result of an action. 182 | 183 | ### Err.of(...) 184 | 185 | Creates a new `Err` result. It is a static function, meaning it can only be called on the imported `Err` namespace and not on `Err` result instances. 186 | 187 | The operation is the results version of [Promise.reject](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject) function. 188 | 189 | **Signature:** 190 | 191 | ```typescript 192 | Err.of(value: TError): Err 193 | ``` 194 | 195 | **Examples:** 196 | 197 | ```typescript 198 | import { Err } from 'type-safe-errors'; 199 | 200 | class UserNotFoundError extends Error { 201 | private __brand!: never; 202 | } 203 | 204 | const errResult = Err.of(new UserNotFoundError()); 205 | ``` 206 | 207 | Error objects must be branded, by e.g.: 208 | `private __brand!: never;` 209 | 210 | Type branding is a technique used to create nominal (distinct) types in TypeScript. By adding a unique dummy private field to a class, you can differentiate it from other types that share the same structure. Without it, many error classes would be indistinguishable from TypeScript's perspective, which could lead to runtime issues. 211 | 212 | --- 213 | 214 | ### err.map(callback) 215 | 216 | No operation is performed for `Err` results. This interface is common for both `Ok` and `Err` results: [result.map(callback)](#resultmapcallback) 217 | 218 | **Examples:** 219 | 220 | ```ts 221 | import { Ok } from 'type-safe-errors'; 222 | import { UserNotFoundError } from './errors'; 223 | 224 | const errResult = Err.of(new UserNotFoundError()); 225 | // No operation is performed, the original Err result is returned 226 | const sameErrResult = errResult.map(err => 123); 227 | ``` 228 | 229 | --- 230 | 231 | ### err.mapErr(ErrorClass, callback) 232 | 233 | Map the `Err` result of specified class to a different result. 234 | If you don't explicitly return value of `Err` type it will be mapped to `Ok`. 235 | Interface common for both types of results: [result.mapErr(ErrorClass, callback)](#resultmaperrerrorclass-callback) 236 | 237 | **Examples:** 238 | 239 | ```ts 240 | import { Ok } from 'type-safe-errors'; 241 | import { UserNotFoundError, Http404Error } from './errors'; 242 | 243 | const errResult = Err.of(new UserNotFoundError()); 244 | 245 | // Ok.of('User not found') 246 | const okStringResult = errResult.mapErr(UserNotFoundError, err => 'User not found'); 247 | 248 | // Err result of Http404Error is returned 249 | const httpErrResult = errResult.mapErr(UserNotFoundError, err => Err.of(new Http404Error())); 250 | ``` 251 | 252 | --- 253 | 254 | ### err.mapAnyErr(callback) 255 | 256 | Map any `Err` result to a different result. 257 | If you don't explicitly return value of `Err` type it will be mapped to `Ok`. 258 | Interface common for both types of results: [result.mapAnyErr(callback)](#resultmapanyerrcallback) 259 | 260 | **Examples:** 261 | 262 | ```ts 263 | import { Ok } from 'type-safe-errors'; 264 | import { UserNotFoundError, Http404Error } from './errors'; 265 | 266 | const errResult = Err.of(new UserNotFoundError()); 267 | 268 | // Ok.of('User not found') 269 | const okStringResult = errResult.mapAnyErr(err => 'User not found'); 270 | 271 | // Err result of Http404Error is returned 272 | const httpErrResult = errResult.mapAnyErr(err => Err.of(new Http404Error())); 273 | ``` 274 | --- 275 | 276 | ### err.unsafePromise() 277 | 278 | Map an `Err` result to a rejected [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). This is a common interface for both `Ok` and `Err` results: [result.unsafePromise()](#resultunsafePromise). 279 | 280 | **Examples:** 281 | 282 | ```typescript 283 | import { Err } from 'type-safe-errors'; 284 | import { UserNotFoundError } from './errors'; 285 | 286 | const errResult = Err.of(new UserNotFoundError()); 287 | // Promise rejects with value of UserNotFoundError error instance 288 | const rejectedPromise = errResult.unsafePromise(); 289 | 290 | ``` 291 | 292 | --- 293 | 294 | ## Result 295 | 296 | `Result` provides static utility functions to work with multiple results. 297 | 298 | ### Result.combine([result1, result2, ...]) 299 | 300 | Combines a list of provided results into a single result. 301 | The results can be either `Result` instances or promises that resolve to `Result` instances. 302 | If all provided results are `Ok`, the returned result 303 | will be an `Ok` containing an array of values from the provided results: `[Ok, Ok] -> Ok<[A, B]>`. 304 | 305 | If provided results list have at least one `Err` result, 306 | returned result will be `Err` of first `Err` result value found in the array. 307 | 308 | The `Result.combine` operation is the results version of [Promise.all](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) function. 309 | 310 | **Signature:** 311 | 312 | ```typescript 313 | type AsyncResult = Promise> | Result; 314 | 315 | Result.combine(results: [AsyncResult, AsyncResult, ...]): Result<[A, B, ...], Err1 | Err2> 316 | ``` 317 | 318 | **Examples:** 319 | 320 | Successful example 321 | 322 | ```typescript 323 | import { Ok, Result } from 'type-safe-errors'; 324 | 325 | const ok1Result = Ok.of(5); 326 | const ok2ResultFactory = async () => Ok.of(9); 327 | 328 | const okSumResult = Result.combine([ok1Result, ok2ResultFactory()]).map( 329 | ([val1, val2]) => val1 + val2 330 | ) 331 | ``` 332 | 333 | An error example 334 | 335 | ```typescript 336 | import { Ok, Result } from 'type-safe-errors'; 337 | import { UserNotFoundError } from './errors'; 338 | 339 | const ok1Result = Ok.of(5); 340 | const ok2Result = Ok.of(9); 341 | const errResult = Err.of(new UserNotFoundError()); 342 | 343 | const okSumResult = Result.combine([ok1Result, errResult, ok2Result]) 344 | // not called, val2 is `never` type as we already know its an error 345 | .map(([val1, val2, val3]) => val1 + val3) 346 | // called 347 | .mapErr(UserNotFoundError, err => console.log('User not found!')) 348 | ``` 349 | 350 | More complicated example, with dynamic `Result`' types': 351 | 352 | ```typescript 353 | const ok1Result = Ok.of(1); 354 | const ok2Result = Ok.of(2); 355 | 356 | const maybeErr1Result = Ok.of(5).map( 357 | () => Math.random() > 0.5 ? Err.of(new Error1()) : 5 358 | ); 359 | 360 | const maybeErr2ResultFactory = async () => Ok.of(6).map( 361 | () => Math.random() > 0.5 ? Err.of(new Error2()) : 6 362 | ); 363 | 364 | const okSumResult = Result.combine([ 365 | ok1Result, 366 | maybeErr1Result, 367 | ok2Result, 368 | maybeErr2ResultFactory() 369 | ]) 370 | // randomly, when all results all success, map will run 371 | .map(([val1, val5, val2, val6]) => val1 + val5 + val2 + val6) 372 | // if some of results fail, then mapAnyErr is called. `err` is Error1 or Error2 class 373 | .mapAnyErr(err => console.log('something goes wrong')); 374 | 375 | // typeof okSumResult === Result<[1, 5, 2, 6], Error1 | Error2>. 376 | ``` 377 | 378 | --- 379 | 380 | ### Result.from(resultFactory) 381 | 382 | Wrap provided factory function into single result. The function can be async or sync. It is useful to start the result chain. 383 | 384 | All `Err` results returned by the factory function will be mapped to exactly same error result. All other values (`Ok` results and raw JavaScipt structures) will be mapped to `Ok` result. 385 | 386 | **Signature:** 387 | 388 | ```typescript 389 | // sync version 390 | Result.from(factory: () => Result | V): Result | Ok 391 | // async version 392 | Result.from(factory: () => Promise | V>): Result | Ok 393 | ``` 394 | 395 | **Examples:** 396 | 397 | ```typescript 398 | import { Result, Err } from 'type-safe-errors'; 399 | 400 | const fetchOkResult = Result.from(async () => fetchRemoteData()); 401 | 402 | class FetchFailedError extends Error { 403 | private __brand!: never; 404 | } 405 | 406 | const fetchDataOrErrorResult = Result.from(async () => { 407 | const res = fetchRemoteData(); 408 | if (res.ok) { 409 | return res.data; 410 | } else { 411 | return Err.of(new FetchFailedError()); 412 | } 413 | }); 414 | ``` 415 | 416 | Note: If an unexpected error is thrown within the `resultFactory` function, it will be wrapped in an `UnknownError` and passed to the `mapErr` or `mapAnyErr` function if they are in the chain. If not handled, it will result in a rejected promise when using either [ok.promise()](#okpromise) or [result.unsafePromise()](#resultunsafePromise). 417 | 418 | ```ts 419 | import { Result, UnknownError } from 'type-safe-errors'; 420 | 421 | Result.from(() => { 422 | throw new Error('Problem!'); 423 | }) 424 | .mapErr(UnknownError, err => console.error(err.cause)); 425 | 426 | ``` 427 | 428 | --- 429 | 430 | ## resultify 431 | `resultify` is a helper function akin to [Node.js's utils.promisify](https://nodejs.org/api/util.html#utilpromisifyoriginal). 432 | It modifies the return type of any given function, synchronous or asynchronous, to `Result`. 433 | 434 | 435 | **Examples:** 436 | 437 | ```typescript 438 | import { Result, Err, resultify } from 'type-safe-errors'; 439 | 440 | class FetchFailedError extends Error { 441 | private __brand!: never; 442 | } 443 | 444 | async function fetchDataOrErrorResult () { 445 | try { 446 | const res = await fetchRemoteData(); 447 | return res.data; 448 | } catch (err) { 449 | console.log(err); 450 | return Err.of(new FetchFailedError()); 451 | } 452 | }; 453 | 454 | const fetchRemoteDataAsResult = resultify(fetchDataOrErrorResult); 455 | fetchRemoteDataAsResult() 456 | .map(data => data) 457 | .mapErr(FetchFailedError, (err) => console.log(err)); 458 | ``` 459 | 460 | ## Results common interface 461 | `Ok` and `Err` are both considered results and share a common interface. 462 | 463 | ### result.map(callback) 464 | 465 | Map the given `Ok` result to another result. 466 | 467 | **Signature:** 468 | 469 | ```typescript 470 | // if `callback` return a value, the `map` function will return Ok.of(value) 471 | Ok.map(callback: (value: TOk) => TReturn): Ok 472 | 473 | // if `callback` return a `Result`, the `map` function will return given `Result` 474 | Ok.map(callback: (value: TOk) => TReturnResult): TReturnResult 475 | 476 | // called on an `Err` `map` function will return origin `Err` (the `callback` won't be called) 477 | Err.map(callback: (value: never) => never): Err; 478 | ``` 479 | 480 | **Examples:** 481 | 482 | ```typescript 483 | import { Ok, Err } from 'type-safe-errors'; 484 | 485 | class UserNotFoundError extends Error { 486 | private __brand!: never; 487 | } 488 | 489 | const okOfNumber5 = Ok.of(10).map(value => value / 2); 490 | 491 | const errOfUserNotFound = Ok.of(10).map(value => Err.of(new UserNotFoundError())); 492 | 493 | const originErr = Err.of(new UserNotFoundError()); 494 | const sameOriginErr = originErr.map(val => val + 5); 495 | ``` 496 | 497 | --- 498 | 499 | ### result.mapErr(ErrorClass, callback) 500 | 501 | Map the `Err` result of the given class to another result. 502 | 503 | **Signature:** 504 | 505 | ```typescript 506 | // if `Err` result is instance of `classType` and the `callback` return a value, the `mapErr` function will return Ok.of(value) 507 | Err.mapErr(classType: TClass, callback: (value: TErr) => TReturn): Ok 508 | 509 | // if `Err` result is instance of `classType` and the `callback` return a `Result`, the `mapErr` function will return given `Result` 510 | Err.mapErr(classType: TClass, callback: (value: TErr) => TReturnResult): TReturnResult 511 | 512 | // called on an `Ok` result, the `mapErr` function will return origin `Ok` (the `callback` won't be called) 513 | Ok.mapErr(classType: unknown, callback: (value: never) => never): Ok 514 | ``` 515 | 516 | **Examples:** 517 | 518 | ```typescript 519 | import { Ok, Err } from 'type-safe-errors'; 520 | import { UserNotFoundError, Http404Error } from './errors'; 521 | 522 | const defaultUser = { 523 | name: 'John Doe', 524 | } 525 | 526 | const okOfDefaultUser = Err.of(new UserNotFoundError()) 527 | .mapErr(UserNotFoundError, err => defaultUser); 528 | 529 | const errOfHttp404 = Err.of(new UserNotFoundError()) 530 | .mapErr(UserNotFoundError, err => Err.of(new Http404Error())); 531 | 532 | const errOfUserNotFound = Err.of(new UserNotFoundError()) 533 | .mapErr(Http404Error, err => 123); 534 | 535 | const originOk = Ok.of(5); 536 | const okOfNumber5 = originOk.mapErr(UserNotFoundError, err => null); 537 | ``` 538 | 539 | --- 540 | 541 | ### result.mapAnyErr(callback) 542 | 543 | Map any `Err` result to another result. 544 | 545 | **Signature:** 546 | 547 | ```typescript 548 | // if `callback` return a value, the `mapAnyErr` function will return Ok.of(value) 549 | Err.mapAnyErr(callback: (value: TErr) => TReturn): Ok 550 | 551 | // if `callback` return a `Result`, the `mapAnyErr` function will return given `Result` 552 | Err.mapAnyErr(callback: (value: TErr) => TReturnResult): TReturnResult 553 | 554 | // called on an `Ok` result, the `mapAnyErr` function will return origin `Ok` (the `callback` won't be called) 555 | Ok.mapAnyErr(callback: (value: never) => never): Ok 556 | ``` 557 | 558 | **Examples:** 559 | 560 | ```typescript 561 | import { Ok, Err } from 'type-safe-errors'; 562 | import { UserNotFoundError, Http404Error } from './errors'; 563 | 564 | const defaultUser = { 565 | name: 'John Doe', 566 | } 567 | 568 | const okOfDefaultUser = Err.of(new UserNotFoundError()) 569 | .mapAnyErr(err => defaultUser); 570 | 571 | const errOfHttp404 = Err.of(new UserNotFoundError()) 572 | .mapAnyErr(err => Err.of(new Http404Error())); 573 | 574 | const originOk = Ok.of(5); 575 | const okOfNumber5 = originOk.mapAnyErr(err => 123); 576 | ``` 577 | 578 | --- 579 | 580 | ### UnknownError 581 | `UnknownError` is a special error class used to wrap unexpected errors that are thrown within the `map`, `mapErr`, `mapAnyErr`, or `Result.from` context. It has a `cause` property that contains the original error. 582 | 583 | `UnknownError` is the only error that you're not required to handle before utilizing a [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). This is because, even if you invoke [`result.mapErr(ErrorClass, callback)`](#resultmaperrerrorclass-callback) with `UnknownError` as the first parameter, your handler function might still throw an error. Thus, you can never be entirely confident that an unexpected error won't occur, and the library's API acknowledges this reality. 584 | 585 | Examples: 586 | 587 | ```ts 588 | import { Ok, UnknownError } from 'type-safe-errors'; 589 | import { UserNotFoundError } from './errors'; 590 | 591 | Ok.of(5) 592 | .map(val => { 593 | throw new Error('Problem!'); 594 | }) 595 | .mapErr(UnknownError, err => { 596 | // Logs the original Error('Problem!') 597 | console.error(err.cause); 598 | }); 599 | 600 | 601 | Err.of(new UserNotFoundError()) 602 | .mapErr(UserNotFoundError, err => { 603 | throw new Error('Problem!'); 604 | }) 605 | .mapErr(UnknownError, err => { 606 | // Logs the original Error('Problem!') 607 | console.error(err.cause); 608 | }); 609 | 610 | 611 | Err.of(new UserNotFoundError()) 612 | .mapErr(UserNotFoundError, err => { 613 | throw new Error('Problem!'); 614 | }) 615 | .mapAnyErr(err => { 616 | // Logs the original Error('Problem!') 617 | console.error(err.cause); 618 | }); 619 | ``` 620 | 621 | 622 | ### result.unsafePromise() 623 | 624 | Map any result to a [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). 625 | `Ok` result will resolve the promise, `Err` will reject it. 626 | 627 | WARNING: the function is not recommended, as the operation will lost information about types of errors. Consider using of [ok.promise()](#okpromise) instead. 628 | 629 | **Signature:** 630 | 631 | ```typescript 632 | // called on an `Ok` result, the `unsafePromise` function will return promise that will resolve with `Ok` result value 633 | Ok.unsafePromise(): Promise 634 | 635 | // called on an `Err` result, the `unsafePromise` function will return promise that will reject with `Err` result value 636 | Err.unsafePromise(): Promise 637 | 638 | ``` 639 | 640 | **Examples:** 641 | 642 | ```typescript 643 | import { Ok, Err } from 'type-safe-errors'; 644 | import { UserNotFoundError } from './errors'; 645 | 646 | async function promiseResolver() { 647 | const number5 = await Ok.of(5).unsafePromise(); 648 | 649 | // the line below will throw `UserNotFoundError` error, 650 | // as the promise will reject 651 | await Err.of(new UserNotFoundError()).unsafePromise(); 652 | } 653 | ``` 654 | 655 | --- 656 | 657 | -------------------------------------------------------------------------------- /src/test/map.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | 3 | import { Result, Ok, Err, UnknownError } from '../index'; 4 | import { 5 | shouldEventuallyOk, 6 | shouldEventuallyErr, 7 | shouldEventuallyReject, 8 | shouldEventuallyUnknownErr, 9 | } from './helper'; 10 | 11 | class Error1 extends Error { 12 | private __brand!: never; 13 | } 14 | 15 | class Error2 extends Error { 16 | private __brand!: never; 17 | } 18 | 19 | class Error3 extends Error { 20 | private __brand!: never; 21 | } 22 | 23 | /** 24 | * The idea is to directly type specific operation results by expected type. 25 | * Thanks to do this the tests not only fail if runtime code has a bug, but they 26 | * won't compile in case of new bug introduction to lib typings 27 | */ 28 | 29 | suite('Result map of single Ok result', () => { 30 | const result = Ok.of(5); 31 | 32 | test('returns mapped result for mapper with plain return', (done) => { 33 | const mapped: Ok<'test-return'> = result.map((_value: number) => { 34 | return 'test-return' as const; 35 | }); 36 | 37 | shouldEventuallyOk(mapped, 'test-return', done); 38 | }); 39 | 40 | test('returns mapped result for mapper with ok result return', (done) => { 41 | const mapped: Ok<'test-return2'> = result.map((_value: number) => { 42 | return Ok.of('test-return2' as const); 43 | }); 44 | 45 | shouldEventuallyOk(mapped, 'test-return2', done); 46 | }); 47 | 48 | test('returns mapped result for mapper with err result return', (done) => { 49 | const err1 = new Error1(); 50 | 51 | const mapped: Err | Err | Ok = result.map( 52 | (_value: number) => { 53 | return Err.of(err1); 54 | } 55 | ); 56 | 57 | shouldEventuallyErr(mapped, err1, done); 58 | }); 59 | 60 | test('returns mapped result for mapper with mixed result return', (done) => { 61 | const err1 = new Error1(); 62 | 63 | const mapped: Result<'test-ok', Error1> = result.map((value: number) => { 64 | return value > 6 ? Err.of(err1) : Ok.of('test-ok' as const); 65 | }); 66 | 67 | shouldEventuallyOk(mapped, 'test-ok', done); 68 | }); 69 | 70 | test('returns mapped result for mapper with promise of plain return', (done) => { 71 | async function getAsyncOk(value: number) { 72 | return `value of ${value}`; 73 | } 74 | const mapped: Ok = result.map(async (value: number) => { 75 | const res = await getAsyncOk(value); 76 | return res; 77 | }); 78 | 79 | shouldEventuallyOk(mapped, 'value of 5', done); 80 | }); 81 | 82 | test('returns mapped result for mapper with promise of ok result return', (done) => { 83 | async function getAsyncOk(value: number) { 84 | return Ok.of(`ok of ${value}`); 85 | } 86 | const mapped: Ok = result.map(async (value: number) => { 87 | const res = await getAsyncOk(value); 88 | return res; 89 | }); 90 | 91 | shouldEventuallyOk(mapped, 'ok of 5', done); 92 | }); 93 | 94 | test('returns mapped result for mapper with promise of err result return', (done) => { 95 | const err1 = new Error1(); 96 | 97 | async function getAsyncErr(_value: number) { 98 | return Err.of(err1); 99 | } 100 | const mapped: Err | Ok = result.map( 101 | async (value: number) => { 102 | const res = await getAsyncErr(value); 103 | return res; 104 | } 105 | ); 106 | 107 | shouldEventuallyErr(mapped, err1, done); 108 | }); 109 | 110 | test('returns mapped result for mapper with mixed result return', (done) => { 111 | const err1 = new Error1(); 112 | 113 | async function getAsyncOk(value: number) { 114 | return value > 6 ? Err.of(err1) : Ok.of('test-ok' as const); 115 | } 116 | 117 | const mapped: Result<'test-ok', Error1> = result.map( 118 | async (value: number) => { 119 | const res = await getAsyncOk(value); 120 | return res; 121 | } 122 | ); 123 | 124 | shouldEventuallyOk(mapped, 'test-ok', done); 125 | }); 126 | 127 | test('returns mapped UnknownError result if mapper throws an exception', (done) => { 128 | const err4 = new Error2(); 129 | 130 | const mapped: Ok | Err = result.map((_val: number) => { 131 | if (true) { 132 | throw err4; 133 | } 134 | }); 135 | 136 | shouldEventuallyUnknownErr(mapped, err4, done); 137 | }); 138 | 139 | test('returns mapped value for UnknownError err when infering type from throwing result', (done) => { 140 | const err4 = new Error('Something happened'); 141 | 142 | const result = Ok.of(5).map((_val) => { 143 | throw err4; 144 | }); 145 | 146 | const mapped: Ok<'mapped-unknown-err-result'> = result.mapErr( 147 | UnknownError, 148 | (err: UnknownError) => { 149 | expect(err.cause).to.equal(err4); 150 | return 'mapped-unknown-err-result' as const; 151 | } 152 | ); 153 | 154 | shouldEventuallyOk(mapped, 'mapped-unknown-err-result', done); 155 | }); 156 | 157 | test('returns mapped value for UnknownError err when infering type from async throwing result', (done) => { 158 | const err4 = new Error('Something happened'); 159 | 160 | const result = Ok.of(5).map(async (_val) => { 161 | throw err4; 162 | }); 163 | 164 | const mapped: Ok<'mapped-unknown-err-result'> = result.mapErr( 165 | UnknownError, 166 | (err: UnknownError) => { 167 | expect(err.cause).to.equal(err4); 168 | return 'mapped-unknown-err-result' as const; 169 | } 170 | ); 171 | 172 | shouldEventuallyOk(mapped, 'mapped-unknown-err-result', done); 173 | }); 174 | 175 | test('rejects with thrown exception if mapper throws an exception', (done) => { 176 | const err4 = new Error2(); 177 | 178 | const mapped: Ok | Err = result.map((_val: number) => { 179 | if (true) { 180 | throw err4; 181 | } 182 | }); 183 | 184 | shouldEventuallyReject(mapped, err4, done); 185 | }); 186 | }); 187 | 188 | suite('Result map of Err result should not be affected by', () => { 189 | const errInstance = new Error1(); 190 | const result = Err.of(errInstance); 191 | 192 | test('mapper with plain return', (done) => { 193 | const mapped: Err = result.map((_value: never) => { 194 | return 'test-return' as const; 195 | }); 196 | 197 | shouldEventuallyErr(mapped, errInstance, done); 198 | }); 199 | 200 | test('mapper with ok result return', (done) => { 201 | const mapped: Err = result.map((_value: never) => { 202 | return Ok.of('test-return2' as const); 203 | }); 204 | 205 | shouldEventuallyErr(mapped, errInstance, done); 206 | }); 207 | 208 | test('mapper with err result return', (done) => { 209 | const err1 = new Error1(); 210 | 211 | const mapped: Err = result.map((_value: never) => { 212 | return Err.of(err1); 213 | }); 214 | 215 | shouldEventuallyErr(mapped, errInstance, done); 216 | }); 217 | 218 | test('mapper with mixed result return', (done) => { 219 | const err1 = new Error1(); 220 | 221 | const mapped: Err = result.map((value: never) => { 222 | return value > 6 ? Err.of(err1) : Ok.of('test-ok' as const); 223 | }); 224 | 225 | shouldEventuallyErr(mapped, errInstance, done); 226 | }); 227 | 228 | test('mapper with promise of plain return', (done) => { 229 | async function getAsyncOk(value: never) { 230 | return `value of ${value}`; 231 | } 232 | const mapped: Err = result.map(async (value: never) => { 233 | const res = await getAsyncOk(value); 234 | return res; 235 | }); 236 | 237 | shouldEventuallyErr(mapped, errInstance, done); 238 | }); 239 | 240 | test('mapper with promise of ok result return', (done) => { 241 | async function getAsyncOk(value: never) { 242 | return Ok.of(`ok of ${value}`); 243 | } 244 | const mapped: Err = result.map(async (value: never) => { 245 | const res = await getAsyncOk(value); 246 | return res; 247 | }); 248 | 249 | shouldEventuallyErr(mapped, errInstance, done); 250 | }); 251 | 252 | test('mapper with promise of err result return', (done) => { 253 | const err1 = new Error1(); 254 | 255 | async function getAsyncOk(_value: never) { 256 | return Err.of(err1); 257 | } 258 | const mapped: Err = result.map(async (value: never) => { 259 | const res = await getAsyncOk(value); 260 | return res; 261 | }); 262 | 263 | shouldEventuallyErr(mapped, errInstance, done); 264 | }); 265 | 266 | test('mapper with mixed result return', (done) => { 267 | const err1 = new Error1(); 268 | 269 | async function getAsyncOk(value: never) { 270 | return value > 6 ? Err.of(err1) : Ok.of('test-ok' as const); 271 | } 272 | 273 | const mapped: Err = result.map(async (value: never) => { 274 | const res = await getAsyncOk(value); 275 | return res; 276 | }); 277 | 278 | shouldEventuallyErr(mapped, errInstance, done); 279 | }); 280 | 281 | test('mapper with throws an exception', (done) => { 282 | const err1 = new Error1(); 283 | 284 | const mapped: Err = result.map(async (_value: never) => { 285 | throw err1; 286 | }); 287 | 288 | shouldEventuallyErr(mapped, errInstance, done); 289 | }); 290 | }); 291 | 292 | suite('Result map of mixed Ok and Err results', () => { 293 | type MixedResult = Ok | Err; 294 | 295 | const result = Ok.of(5) as MixedResult; 296 | 297 | test('returns mapped result for mapper with plain return', (done) => { 298 | const mapped: MixedResult<'test-return'> = result.map((_value: number) => { 299 | return 'test-return' as const; 300 | }); 301 | 302 | shouldEventuallyOk(mapped, 'test-return', done); 303 | }); 304 | 305 | test('returns mapped result for mapper with ok result return', (done) => { 306 | const mapped: MixedResult<'test-return2'> = result.map((_value: number) => { 307 | return Ok.of('test-return2' as const); 308 | }); 309 | 310 | shouldEventuallyOk(mapped, 'test-return2', done); 311 | }); 312 | 313 | test('returns mapped result for mapper with err result return', (done) => { 314 | const err2 = new Error2(); 315 | 316 | const mapped: Err | Err | Ok = result.map( 317 | (_value: number) => { 318 | return Err.of(err2); 319 | } 320 | ); 321 | 322 | shouldEventuallyErr(mapped, err2, done); 323 | }); 324 | 325 | test('returns mapped result for mapper with mixed result return', (done) => { 326 | const err2 = new Error2(); 327 | 328 | const mapped: Result<'test-ok', Error2 | Error1> = result.map( 329 | (value: number) => { 330 | return value > 6 ? Err.of(err2) : Ok.of('test-ok' as const); 331 | } 332 | ); 333 | 334 | shouldEventuallyOk(mapped, 'test-ok', done); 335 | }); 336 | 337 | test('returns mapped result for mapper with promise of plain return', (done) => { 338 | async function getAsyncOk(value: number) { 339 | return `value of ${value}`; 340 | } 341 | const mapped: MixedResult = result.map(async (value: number) => { 342 | const res = await getAsyncOk(value); 343 | return res; 344 | }); 345 | 346 | shouldEventuallyOk(mapped, 'value of 5', done); 347 | }); 348 | 349 | test('returns mapped result for mapper with promise of ok result return', (done) => { 350 | async function getAsyncOk(value: number) { 351 | return Ok.of(`ok of ${value}`); 352 | } 353 | const mapped: MixedResult = result.map(async (value: number) => { 354 | const res = await getAsyncOk(value); 355 | return res; 356 | }); 357 | 358 | shouldEventuallyOk(mapped, 'ok of 5', done); 359 | }); 360 | 361 | test('returns mapped result for mapper with promise of err result return', (done) => { 362 | const err2 = new Error2(); 363 | 364 | async function getAsyncOk(_value: number) { 365 | return Err.of(err2); 366 | } 367 | const mapped: Err | Err | Ok = result.map( 368 | async (value: number) => { 369 | const res = await getAsyncOk(value); 370 | return res; 371 | } 372 | ); 373 | 374 | shouldEventuallyErr(mapped, err2, done); 375 | }); 376 | 377 | test('returns mapped result for mapper with mixed result return', (done) => { 378 | const err2 = new Error2(); 379 | 380 | async function getAsyncOk(value: number) { 381 | return value > 6 ? Err.of(err2) : Ok.of('test-ok' as const); 382 | } 383 | 384 | const mapped: Result<'test-ok', Error2 | Error1> = result.map( 385 | async (value: number) => { 386 | const res = await getAsyncOk(value); 387 | return res; 388 | } 389 | ); 390 | 391 | shouldEventuallyOk(mapped, 'test-ok', done); 392 | }); 393 | 394 | test('returns mapped UnknownError result if mapper throws an exception', (done) => { 395 | const err4 = new Error2(); 396 | 397 | const mapped: Ok | Err = result.map((_val: number) => { 398 | if (true) { 399 | throw err4; 400 | } 401 | }); 402 | 403 | shouldEventuallyUnknownErr(mapped, err4, done); 404 | }); 405 | 406 | test('rejects with thrown exception if mapper throws an exception', (done) => { 407 | const err4 = new Error2(); 408 | 409 | const mapped: Ok | Err = result.map((_val: number) => { 410 | if (true) { 411 | throw err4; 412 | } 413 | }); 414 | 415 | shouldEventuallyReject(mapped, err4, done); 416 | }); 417 | }); 418 | 419 | suite('Result map of multiple Ok results', () => { 420 | const result = Ok.of(5) as Ok | Ok; 421 | 422 | test('returns mapped result for mapper with plain return', (done) => { 423 | const mapped: Ok<'test-return'> = result.map((_value: number | string) => { 424 | return 'test-return' as const; 425 | }); 426 | 427 | shouldEventuallyOk(mapped, 'test-return', done); 428 | }); 429 | 430 | test('returns mapped result for mapper with ok result return', (done) => { 431 | const mapped: Ok<'test-return2'> = result.map((_value: number | string) => { 432 | return Ok.of('test-return2' as const); 433 | }); 434 | 435 | shouldEventuallyOk(mapped, 'test-return2', done); 436 | }); 437 | 438 | test('returns mapped result for mapper with err result return', (done) => { 439 | const err1 = new Error1(); 440 | 441 | const mapped: Err | Ok = result.map( 442 | (_value: number | string) => { 443 | return Err.of(err1); 444 | } 445 | ); 446 | 447 | shouldEventuallyErr(mapped, err1, done); 448 | }); 449 | 450 | test('returns mapped result for mapper with mixed result return', (done) => { 451 | const err1 = new Error1(); 452 | 453 | const mapped: Result<'test-ok', Error1> = result.map( 454 | (value: number | string) => { 455 | return value > 6 ? Err.of(err1) : Ok.of('test-ok' as const); 456 | } 457 | ); 458 | 459 | shouldEventuallyOk(mapped, 'test-ok', done); 460 | }); 461 | 462 | test('returns mapped result for mapper with promise of plain return', (done) => { 463 | async function getAsyncOk(value: number | string) { 464 | return `value of ${value}`; 465 | } 466 | const mapped: Ok = result.map(async (value: number | string) => { 467 | const res = await getAsyncOk(value); 468 | return res; 469 | }); 470 | 471 | shouldEventuallyOk(mapped, 'value of 5', done); 472 | }); 473 | 474 | test('returns mapped result for mapper with promise of ok result return', (done) => { 475 | async function getAsyncOk(value: number | string) { 476 | return Ok.of(`ok of ${value}`); 477 | } 478 | const mapped: Ok = result.map(async (value: number | string) => { 479 | const res = await getAsyncOk(value); 480 | return res; 481 | }); 482 | 483 | shouldEventuallyOk(mapped, 'ok of 5', done); 484 | }); 485 | 486 | test('returns mapped result for mapper with promise of err result return', (done) => { 487 | const err1 = new Error1(); 488 | 489 | async function getAsyncOk(_value: number | string) { 490 | return Err.of(err1); 491 | } 492 | const mapped: Err | Ok = result.map( 493 | async (value: number | string) => { 494 | const res = await getAsyncOk(value); 495 | return res; 496 | } 497 | ); 498 | 499 | shouldEventuallyErr(mapped, err1, done); 500 | }); 501 | 502 | test('returns mapped result for mapper with mixed result return', (done) => { 503 | const err1 = new Error1(); 504 | 505 | async function getAsyncOk(value: number | string) { 506 | return value > 6 ? Err.of(err1) : Ok.of('test-ok' as const); 507 | } 508 | 509 | const mapped: Result<'test-ok', Error1> = result.map( 510 | async (value: number | string) => { 511 | const res = await getAsyncOk(value); 512 | return res; 513 | } 514 | ); 515 | 516 | shouldEventuallyOk(mapped, 'test-ok', done); 517 | }); 518 | 519 | test('returns mapped UnknownError result if mapper throws an exception', (done) => { 520 | const err4 = new Error2(); 521 | 522 | const mapped: Ok | Err = result.map( 523 | (_val: number | string) => { 524 | if (true) { 525 | throw err4; 526 | } 527 | } 528 | ); 529 | 530 | shouldEventuallyUnknownErr(mapped, err4, done); 531 | }); 532 | 533 | test('rejects with thrown exception if mapper throws an exception', (done) => { 534 | const err4 = new Error2(); 535 | 536 | const mapped: Ok | Err = result.map( 537 | (_val: number | string) => { 538 | if (true) { 539 | throw err4; 540 | } 541 | } 542 | ); 543 | 544 | shouldEventuallyReject(mapped, err4, done); 545 | }); 546 | }); 547 | 548 | suite('Result map of mixed Ok and 2 Err results', () => { 549 | type MixedResult = Ok | Err | Err; 550 | 551 | const result = Ok.of(5) as MixedResult; 552 | 553 | test('returns mapped result for mapper with plain return', (done) => { 554 | const mapped: MixedResult<'test-return'> = result.map((_value: number) => { 555 | return 'test-return' as const; 556 | }); 557 | 558 | shouldEventuallyOk(mapped, 'test-return', done); 559 | }); 560 | 561 | test('returns mapped result for mapper with ok result return', (done) => { 562 | const mapped: MixedResult<'test-return2'> = result.map((_value: number) => { 563 | return Ok.of('test-return2' as const); 564 | }); 565 | 566 | shouldEventuallyOk(mapped, 'test-return2', done); 567 | }); 568 | 569 | test('returns mapped result for mapper with err result return', (done) => { 570 | const err3 = new Error3(); 571 | 572 | const mapped: Err | Err | Err | Ok = 573 | result.map((_value: number) => { 574 | return Err.of(err3); 575 | }); 576 | 577 | shouldEventuallyErr(mapped, err3, done); 578 | }); 579 | 580 | test('returns mapped result for mapper with mixed result return', (done) => { 581 | const err3 = new Error3(); 582 | 583 | const mapped: Result<'test-ok', Error3 | Error1 | Error2> = result.map( 584 | (value: number) => { 585 | return value > 6 ? Err.of(err3) : Ok.of('test-ok' as const); 586 | } 587 | ); 588 | 589 | shouldEventuallyOk(mapped, 'test-ok', done); 590 | }); 591 | 592 | test('returns mapped result for mapper with promise of plain return', (done) => { 593 | async function getAsyncOk(value: number) { 594 | return `value of ${value}`; 595 | } 596 | const mapped: MixedResult = result.map(async (value: number) => { 597 | const res = await getAsyncOk(value); 598 | return res; 599 | }); 600 | 601 | shouldEventuallyOk(mapped, 'value of 5', done); 602 | }); 603 | 604 | test('returns mapped result for mapper with promise of ok result return', (done) => { 605 | async function getAsyncOk(value: number) { 606 | return Ok.of(`ok of ${value}`); 607 | } 608 | const mapped: MixedResult = result.map(async (value: number) => { 609 | const res = await getAsyncOk(value); 610 | return res; 611 | }); 612 | 613 | shouldEventuallyOk(mapped, 'ok of 5', done); 614 | }); 615 | 616 | test('returns mapped result for mapper with promise of err result return', (done) => { 617 | const err3 = new Error3(); 618 | 619 | async function getAsyncOk(_value: number) { 620 | return Err.of(err3); 621 | } 622 | const mapped: Err | Err | Err | Ok = 623 | result.map(async (value: number) => { 624 | const res = await getAsyncOk(value); 625 | return res; 626 | }); 627 | 628 | shouldEventuallyErr(mapped, err3, done); 629 | }); 630 | 631 | test('returns mapped result for mapper with mixed result return', (done) => { 632 | const err3 = new Error3(); 633 | 634 | async function getAsyncOk(value: number) { 635 | return value > 6 ? Err.of(err3) : Ok.of('test-ok' as const); 636 | } 637 | 638 | const mapped: Result<'test-ok', Error3 | Error1 | Error2> = result.map( 639 | async (value: number) => { 640 | const res = await getAsyncOk(value); 641 | return res; 642 | } 643 | ); 644 | 645 | shouldEventuallyOk(mapped, 'test-ok', done); 646 | }); 647 | 648 | test('returns mapped UnknownError result if mapper throws an exception', (done) => { 649 | const err4 = new Error3(); 650 | 651 | const mapped: Ok | Err | Err = result.map( 652 | (_val: number) => { 653 | if (true) { 654 | throw err4; 655 | } 656 | } 657 | ); 658 | 659 | shouldEventuallyUnknownErr(mapped, err4, done); 660 | }); 661 | 662 | test('rejects with thrown exception if mapper throws an exception', (done) => { 663 | const err4 = new Error3(); 664 | 665 | const mapped: Ok | Err | Err = result.map( 666 | (_val: number) => { 667 | if (true) { 668 | throw err4; 669 | } 670 | } 671 | ); 672 | 673 | shouldEventuallyReject(mapped, err4, done); 674 | }); 675 | }); 676 | 677 | suite('Result map of multiple Err results should not be affected by', () => { 678 | const errInstance = new Error1(); 679 | const result = Err.of(errInstance) as Err | Err; 680 | 681 | test('mapper with plain return', (done) => { 682 | const mapped: Err | Err = result.map((_value: never) => { 683 | return 'test-return' as const; 684 | }); 685 | 686 | shouldEventuallyErr(mapped, errInstance, done); 687 | }); 688 | 689 | test('mapper with ok result return', (done) => { 690 | const mapped: Err | Err = result.map((_value: never) => { 691 | return Ok.of('test-return2' as const); 692 | }); 693 | 694 | shouldEventuallyErr(mapped, errInstance, done); 695 | }); 696 | 697 | test('mapper with err result return', (done) => { 698 | const err1 = new Error1(); 699 | 700 | const mapped: Err | Err = result.map((_value: never) => { 701 | return Err.of(err1); 702 | }); 703 | 704 | shouldEventuallyErr(mapped, errInstance, done); 705 | }); 706 | 707 | test('mapper with mixed result return', (done) => { 708 | const err1 = new Error1(); 709 | 710 | const mapped: Err | Err = result.map((value: never) => { 711 | return value > 6 ? Err.of(err1) : Ok.of('test-ok' as const); 712 | }); 713 | 714 | shouldEventuallyErr(mapped, errInstance, done); 715 | }); 716 | 717 | test('mapper with promise of plain return', (done) => { 718 | async function getAsyncOk(value: never) { 719 | return `value of ${value}`; 720 | } 721 | const mapped: Err | Err = result.map( 722 | async (value: never) => { 723 | const res = await getAsyncOk(value); 724 | return res; 725 | } 726 | ); 727 | 728 | shouldEventuallyErr(mapped, errInstance, done); 729 | }); 730 | 731 | test('mapper with promise of ok result return', (done) => { 732 | async function getAsyncOk(value: never) { 733 | return Ok.of(`ok of ${value}`); 734 | } 735 | const mapped: Err | Err = result.map( 736 | async (value: never) => { 737 | const res = await getAsyncOk(value); 738 | return res; 739 | } 740 | ); 741 | 742 | shouldEventuallyErr(mapped, errInstance, done); 743 | }); 744 | 745 | test('mapper with promise of err result return', (done) => { 746 | const err1 = new Error1(); 747 | 748 | async function getAsyncOk(_value: never) { 749 | return Err.of(err1); 750 | } 751 | const mapped: Err | Err = result.map( 752 | async (value: never) => { 753 | const res = await getAsyncOk(value); 754 | return res; 755 | } 756 | ); 757 | 758 | shouldEventuallyErr(mapped, errInstance, done); 759 | }); 760 | 761 | test('mapper with mixed result return', (done) => { 762 | const err1 = new Error1(); 763 | 764 | async function getAsyncOk(value: never) { 765 | return value > 6 ? Err.of(err1) : Ok.of('test-ok' as const); 766 | } 767 | 768 | const mapped: Err | Err = result.map( 769 | async (value: never) => { 770 | const res = await getAsyncOk(value); 771 | return res; 772 | } 773 | ); 774 | 775 | shouldEventuallyErr(mapped, errInstance, done); 776 | }); 777 | 778 | test('mapper with throws an exception', (done) => { 779 | const err1 = new Error1(); 780 | 781 | const mapped: Err | Err = result.map( 782 | async (_value: never) => { 783 | throw err1; 784 | } 785 | ); 786 | 787 | shouldEventuallyErr(mapped, errInstance, done); 788 | }); 789 | }); 790 | 791 | suite('Result map of mixed 2 Ok and 2 Err results', () => { 792 | type MixedResult = Ok | Err | Err; 793 | 794 | const result = Ok.of(5) as Ok | MixedResult; 795 | 796 | test('returns mapped result for mapper with plain return', (done) => { 797 | const mapped: MixedResult<'test-return'> = result.map( 798 | (_value: number | string) => { 799 | return 'test-return' as const; 800 | } 801 | ); 802 | 803 | shouldEventuallyOk(mapped, 'test-return', done); 804 | }); 805 | 806 | test('returns mapped result for mapper with ok result return', (done) => { 807 | const mapped: MixedResult<'test-return2'> = result.map( 808 | (_value: number | string) => { 809 | return Ok.of('test-return2' as const); 810 | } 811 | ); 812 | 813 | shouldEventuallyOk(mapped, 'test-return2', done); 814 | }); 815 | 816 | test('returns mapped result for mapper with err result return', (done) => { 817 | const err3 = new Error3(); 818 | 819 | const mapped: Err | Err | Err | Ok = 820 | result.map((_value: number | string) => { 821 | return Err.of(err3); 822 | }); 823 | 824 | shouldEventuallyErr(mapped, err3, done); 825 | }); 826 | 827 | test('returns mapped result for mapper with mixed result return', (done) => { 828 | const err3 = new Error3(); 829 | 830 | const mapped: Result<'test-ok', Error3 | Error1 | Error2> = result.map( 831 | (value: number | string) => { 832 | return value > 6 ? Err.of(err3) : Ok.of('test-ok' as const); 833 | } 834 | ); 835 | 836 | shouldEventuallyOk(mapped, 'test-ok', done); 837 | }); 838 | 839 | test('returns mapped result for mapper with promise of plain return', (done) => { 840 | async function getAsyncOk(value: number | string) { 841 | return `value of ${value}`; 842 | } 843 | const mapped: MixedResult = result.map( 844 | async (value: number | string) => { 845 | const res = await getAsyncOk(value); 846 | return res; 847 | } 848 | ); 849 | 850 | shouldEventuallyOk(mapped, 'value of 5', done); 851 | }); 852 | 853 | test('returns mapped result for mapper with promise of ok result return', (done) => { 854 | async function getAsyncOk(value: number | string) { 855 | return Ok.of(`ok of ${value}`); 856 | } 857 | const mapped: MixedResult = result.map( 858 | async (value: number | string) => { 859 | const res = await getAsyncOk(value); 860 | return res; 861 | } 862 | ); 863 | 864 | shouldEventuallyOk(mapped, 'ok of 5', done); 865 | }); 866 | 867 | test('returns mapped result for mapper with promise of err result return', (done) => { 868 | const err3 = new Error3(); 869 | 870 | async function getAsyncOk(_value: number | string) { 871 | return Err.of(err3); 872 | } 873 | const mapped: Err | Err | Err | Ok = 874 | result.map(async (value: number | string) => { 875 | const res = await getAsyncOk(value); 876 | return res; 877 | }); 878 | 879 | shouldEventuallyErr(mapped, err3, done); 880 | }); 881 | 882 | test('returns mapped result for mapper with mixed result return', (done) => { 883 | const err3 = new Error3(); 884 | 885 | async function getAsyncOk(value: number | string) { 886 | return value > 6 ? Err.of(err3) : Ok.of('test-ok' as const); 887 | } 888 | 889 | const mapped: Result<'test-ok', Error3 | Error1 | Error2> = result.map( 890 | async (value: number | string) => { 891 | const res = await getAsyncOk(value); 892 | return res; 893 | } 894 | ); 895 | 896 | shouldEventuallyOk(mapped, 'test-ok', done); 897 | }); 898 | 899 | test('rejects with thrown exception if mapper throws an exception', (done) => { 900 | const err4 = new Error2(); 901 | 902 | const result = Ok.of(5) as Err | Ok; 903 | 904 | // the thrown error type is included in result types always, as UnknownError 905 | const mapped: Ok | Err = result.map((_val: number) => { 906 | if (true) { 907 | throw err4; 908 | } 909 | }); 910 | 911 | shouldEventuallyReject(mapped, err4, done); 912 | }); 913 | 914 | test('returns mapped UnknownError result if mapper throws an exception', (done) => { 915 | const err4 = new Error3(); 916 | 917 | const mapped: Ok | Err | Err = result.map( 918 | (_val: string | number) => { 919 | if (true) { 920 | throw err4; 921 | } 922 | } 923 | ); 924 | 925 | shouldEventuallyUnknownErr(mapped, err4, done); 926 | }); 927 | 928 | test('rejects with thrown exception if mapper throws an exception', (done) => { 929 | const err4 = new Error3(); 930 | 931 | const mapped: Ok | Err | Err = result.map( 932 | (_val: number | string) => { 933 | if (true) { 934 | throw err4; 935 | } 936 | } 937 | ); 938 | 939 | shouldEventuallyReject(mapped, err4, done); 940 | }); 941 | }); 942 | 943 | suite('mapAnyErr', () => { 944 | test('returns not changed result for Ok result', (done) => { 945 | const result = Ok.of(5) as Ok | Err | Err; 946 | 947 | const mapped: Ok | Ok<'test-return'> = result.mapAnyErr( 948 | (_value: Error1 | Error2 | UnknownError) => { 949 | return 'test-return' as const; 950 | } 951 | ); 952 | 953 | shouldEventuallyOk(mapped, 5, done); 954 | }); 955 | 956 | test('returns Ok.of mapped value', (done) => { 957 | const err1 = new Error1(); 958 | const result = Err.of(err1) as Err | Ok; 959 | const mapped: Ok | Ok<'test-return'> = result.mapAnyErr( 960 | (_err: Error1 | UnknownError) => { 961 | return 'test-return' as const; 962 | } 963 | ); 964 | 965 | shouldEventuallyOk(mapped, 'test-return', done); 966 | }); 967 | 968 | test('returns mapped value if its an ok result', (done) => { 969 | const err1 = new Error1(); 970 | 971 | const result = Err.of(err1) as Err | Ok; 972 | const mapped: Ok = result.mapAnyErr( 973 | (_err: Error1 | UnknownError) => { 974 | return Ok.of(25); 975 | } 976 | ); 977 | 978 | shouldEventuallyOk(mapped, 25, done); 979 | }); 980 | 981 | test('returns mapped result value if its an err result', (done) => { 982 | const err1 = new Error1(); 983 | const err2 = new Error2(); 984 | 985 | const result = Err.of(err1) as Err | Ok; 986 | const mapped: Ok | Err = result.mapAnyErr( 987 | (_err: Error1 | UnknownError) => { 988 | return Err.of(err2); 989 | } 990 | ); 991 | 992 | shouldEventuallyErr(mapped, err2, done); 993 | }); 994 | 995 | test('returns proxied error value if its an UnknownError result', (done) => { 996 | const err1 = new Error1(); 997 | 998 | const result = Err.of(err1) as Err | Ok; 999 | const mapped: Ok | Err = result.mapAnyErr( 1000 | (err: Error1 | UnknownError) => { 1001 | return Err.of(err); 1002 | } 1003 | ); 1004 | 1005 | shouldEventuallyErr(mapped, err1, done); 1006 | }); 1007 | 1008 | test('spread return error types', (done) => { 1009 | const err1 = new Error1(); 1010 | 1011 | const result = Err.of(err1) as Err | Ok; 1012 | 1013 | const mapped: Ok | Err | Err | Err = 1014 | result.mapAnyErr((err: Error1 | Error3 | UnknownError) => { 1015 | return Err.of(err); 1016 | }); 1017 | 1018 | shouldEventuallyErr(mapped, err1, done); 1019 | }); 1020 | 1021 | test('returns mapped value for UnknownError err when mapper throws', (done) => { 1022 | const err4 = new Error('Something happened'); 1023 | 1024 | const result = Err.of(new Error1()).mapAnyErr(() => { 1025 | throw err4; 1026 | }); 1027 | 1028 | const mapped: Ok<'mapped-unknown-err-result'> = result.mapErr( 1029 | UnknownError, 1030 | (err: UnknownError) => { 1031 | expect(err.cause).to.equal(err4); 1032 | return 'mapped-unknown-err-result' as const; 1033 | } 1034 | ); 1035 | 1036 | shouldEventuallyOk(mapped, 'mapped-unknown-err-result', done); 1037 | }); 1038 | 1039 | test('returns mapped value for UnknownError err when async mapper throws', (done) => { 1040 | const err4 = new Error('Something happened'); 1041 | 1042 | const result = Err.of(new Error1()).mapAnyErr(async () => { 1043 | throw err4; 1044 | }); 1045 | 1046 | const mapped: Ok<'mapped-unknown-err-result'> = result.mapErr( 1047 | UnknownError, 1048 | (err: UnknownError) => { 1049 | expect(err.cause).to.equal(err4); 1050 | return 'mapped-unknown-err-result' as const; 1051 | } 1052 | ); 1053 | 1054 | shouldEventuallyOk(mapped, 'mapped-unknown-err-result', done); 1055 | }); 1056 | 1057 | test('returns mapped UnknownError result if mapper throws an exception', (done) => { 1058 | const err1 = new Error1(); 1059 | const err4 = new Error2(); 1060 | 1061 | const result = Err.of(err1) as Err | Ok; 1062 | 1063 | const mapped: Ok = result.mapAnyErr( 1064 | (_err: Error1 | UnknownError) => { 1065 | if (true) { 1066 | throw err4; 1067 | } 1068 | } 1069 | ); 1070 | 1071 | shouldEventuallyUnknownErr(mapped, err4, done); 1072 | }); 1073 | 1074 | test('rejects with thrown exception if mapper throws an exception', (done) => { 1075 | const err1 = new Error1(); 1076 | const err4 = new Error2(); 1077 | 1078 | const result = Err.of(err1) as Err | Ok; 1079 | 1080 | // the thrown error type is included in result types awalys, as UnknownError 1081 | const mapped: Ok = result.mapAnyErr( 1082 | (_err: Error1 | UnknownError) => { 1083 | if (true) { 1084 | throw err4; 1085 | } 1086 | } 1087 | ); 1088 | 1089 | shouldEventuallyReject(mapped, err4, done); 1090 | }); 1091 | }); 1092 | 1093 | suite('mapErr', () => { 1094 | test('returns not changed result for Ok result', (done) => { 1095 | const result = Ok.of(5) as Ok | Err | Err; 1096 | 1097 | const mapped: Ok | Ok<'test-return'> | Err = result.mapErr( 1098 | Error2, 1099 | (_value: Error2) => { 1100 | return 'test-return' as const; 1101 | } 1102 | ); 1103 | 1104 | shouldEventuallyOk(mapped, 5, done); 1105 | }); 1106 | 1107 | test('returns not changed result for not provided err type result', (done) => { 1108 | const err1 = new Error1(); 1109 | const result = Err.of(err1) as Ok | Err | Err; 1110 | 1111 | const mapped: Ok | Err | Ok<'test-return'> = result.mapErr( 1112 | Error2, 1113 | (_value: Error2) => { 1114 | return 'test-return' as const; 1115 | } 1116 | ); 1117 | 1118 | shouldEventuallyErr(mapped, err1, done); 1119 | }); 1120 | 1121 | test('returns Ok.of mapped value for provided error type', (done) => { 1122 | const err1 = new Error1(); 1123 | const result = Err.of(err1) as Err | Err | Ok; 1124 | const mapped: Ok | Ok<'test-return'> | Err = result.mapErr( 1125 | Error1, 1126 | (_err: Error1) => { 1127 | return 'test-return' as const; 1128 | } 1129 | ); 1130 | 1131 | shouldEventuallyOk(mapped, 'test-return', done); 1132 | }); 1133 | 1134 | test('returns mapped value if its an ok result for provided error type', (done) => { 1135 | const err1 = new Error1(); 1136 | 1137 | const result = Err.of(err1) as Err | Ok; 1138 | const mapped: Ok = result.mapErr(Error1, (_err: Error1) => { 1139 | return Ok.of(25); 1140 | }); 1141 | 1142 | shouldEventuallyOk(mapped, 25, done); 1143 | }); 1144 | 1145 | test('returns mapped value if its an err result for privided error type', (done) => { 1146 | const err1 = new Error1(); 1147 | const err2 = new Error2(); 1148 | 1149 | const result = Err.of(err1) as Err | Ok; 1150 | const mapped: Ok | Err = result.mapErr( 1151 | Error1, 1152 | (_err: Error1) => { 1153 | return Err.of(err2); 1154 | } 1155 | ); 1156 | 1157 | shouldEventuallyErr(mapped, err2, done); 1158 | }); 1159 | 1160 | test( 1161 | 'returns mapped value if its an err result for privided error type ' + 1162 | 'and left other union error types', 1163 | (done) => { 1164 | const err1 = new Error1(); 1165 | const err2 = new Error2(); 1166 | 1167 | const result = Err.of(err1) as Err | Err | Ok; 1168 | 1169 | const mapped: Ok | Ok | Err = result 1170 | .mapErr(Error1, (_err: Error1) => { 1171 | return Err.of(err2); 1172 | }) 1173 | .mapErr(Error3, () => { 1174 | return; 1175 | }); 1176 | shouldEventuallyErr(mapped, err2, done); 1177 | } 1178 | ); 1179 | 1180 | test('returns mapped value for UnknownError err when mapper throws', (done) => { 1181 | const err4 = new Error('Something happened'); 1182 | 1183 | const result = Err.of(new Error1()).mapErr(Error1, () => { 1184 | throw err4; 1185 | }); 1186 | 1187 | const mapped: Ok<'mapped-unknown-err-result'> = result.mapErr( 1188 | UnknownError, 1189 | (err: UnknownError) => { 1190 | expect(err.cause).to.equal(err4); 1191 | return 'mapped-unknown-err-result' as const; 1192 | } 1193 | ); 1194 | 1195 | shouldEventuallyOk(mapped, 'mapped-unknown-err-result', done); 1196 | }); 1197 | 1198 | test('returns mapped value for UnknownError err when async mapper throws', (done) => { 1199 | const err4 = new Error('Something happened'); 1200 | 1201 | const result = Err.of(new Error1()).mapErr(Error1, async () => { 1202 | throw err4; 1203 | }); 1204 | 1205 | const mapped: Ok<'mapped-unknown-err-result'> = result.mapErr( 1206 | UnknownError, 1207 | (err: UnknownError) => { 1208 | expect(err.cause).to.equal(err4); 1209 | return 'mapped-unknown-err-result' as const; 1210 | } 1211 | ); 1212 | 1213 | shouldEventuallyOk(mapped, 'mapped-unknown-err-result', done); 1214 | }); 1215 | 1216 | test('returns mapped value for UnknownError err', (done) => { 1217 | const err4 = new Error('Something happened'); 1218 | 1219 | const result = Result.from(() => { 1220 | throw err4; 1221 | }) as Err | Ok; 1222 | 1223 | const mapped: Ok | Ok<'mapped-unknown-err-result'> | Err = 1224 | result.mapErr(UnknownError, (err: UnknownError) => { 1225 | expect(err.cause).to.equal(err4); 1226 | return 'mapped-unknown-err-result' as const; 1227 | }); 1228 | 1229 | shouldEventuallyOk(mapped, 'mapped-unknown-err-result', done); 1230 | }); 1231 | 1232 | test('returns mapped UnknownError result if mapper throws an exception', (done) => { 1233 | const err1 = new Error1(); 1234 | const err4 = new Error2(); 1235 | 1236 | const result = Err.of(err1) as Err | Ok; 1237 | 1238 | const mapped: Ok = result.mapErr(Error1, (_err: Error1) => { 1239 | if (true) { 1240 | throw err4; 1241 | } 1242 | }); 1243 | 1244 | shouldEventuallyUnknownErr(mapped, err4, done); 1245 | }); 1246 | 1247 | test('rejects with thrown exception if mapper throws an exception', (done) => { 1248 | const err1 = new Error1(); 1249 | const err4 = new Error2(); 1250 | 1251 | const result = Err.of(err1) as Err | Ok; 1252 | 1253 | // the thrown error type is included in result types awalys, as UnknownError 1254 | const mapped: Ok = result.mapErr(Error1, (_err: Error1) => { 1255 | if (true) { 1256 | throw err4; 1257 | } 1258 | }); 1259 | 1260 | shouldEventuallyReject(mapped, err4, done); 1261 | }); 1262 | }); 1263 | -------------------------------------------------------------------------------- /examples/fastify/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fastify", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "fastify": "^4.9.2", 9 | "type-safe-errors": "file:../.." 10 | }, 11 | "devDependencies": { 12 | "ts-node": "^10.9.1", 13 | "typescript": "^4.7.4" 14 | } 15 | }, 16 | "../..": { 17 | "version": "0.2.7", 18 | "license": "MIT", 19 | "devDependencies": { 20 | "@types/chai": "^4.3.1", 21 | "@types/mocha": "^9.1.1", 22 | "@types/node": "^16.11.7", 23 | "@typescript-eslint/eslint-plugin": "^5.30.7", 24 | "@typescript-eslint/parser": "^5.30.7", 25 | "chai": "^4.3.6", 26 | "eslint": "^8.20.0", 27 | "eslint-config-prettier": "^8.5.0", 28 | "eslint-plugin-prettier": "^4.2.1", 29 | "mocha": "^10.0.0", 30 | "prettier": "^2.7.1", 31 | "ts-node": "^10.9.1", 32 | "typescript": "^4.7.4" 33 | } 34 | }, 35 | "node_modules/@cspotcode/source-map-support": { 36 | "version": "0.8.1", 37 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 38 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 39 | "dev": true, 40 | "dependencies": { 41 | "@jridgewell/trace-mapping": "0.3.9" 42 | }, 43 | "engines": { 44 | "node": ">=12" 45 | } 46 | }, 47 | "node_modules/@fastify/ajv-compiler": { 48 | "version": "3.4.0", 49 | "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.4.0.tgz", 50 | "integrity": "sha512-69JnK7Cot+ktn7LD5TikP3b7psBPX55tYpQa8WSumt8r117PCa2zwHnImfBtRWYExreJlI48hr0WZaVrTBGj7w==", 51 | "dependencies": { 52 | "ajv": "^8.11.0", 53 | "ajv-formats": "^2.1.1", 54 | "fast-uri": "^2.0.0" 55 | } 56 | }, 57 | "node_modules/@fastify/deepmerge": { 58 | "version": "1.1.0", 59 | "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-1.1.0.tgz", 60 | "integrity": "sha512-E8Hfdvs1bG6u0N4vN5Nty6JONUfTdOciyD5rn8KnEsLKIenvOVcr210BQR9t34PRkNyjqnMLGk3e0BsaxRdL+g==" 61 | }, 62 | "node_modules/@fastify/error": { 63 | "version": "3.0.0", 64 | "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.0.0.tgz", 65 | "integrity": "sha512-dPRyT40GiHRzSCll3/Jn2nPe25+E1VXc9tDwRAIKwFCxd5Np5wzgz1tmooWG3sV0qKgrBibihVoCna2ru4SEFg==" 66 | }, 67 | "node_modules/@fastify/fast-json-stringify-compiler": { 68 | "version": "4.1.0", 69 | "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.1.0.tgz", 70 | "integrity": "sha512-cTKBV2J9+u6VaKDhX7HepSfPSzw+F+TSd+k0wzifj4rG+4E5PjSFJCk19P8R6tr/72cuzgGd+mbB3jFT6lvAgw==", 71 | "dependencies": { 72 | "fast-json-stringify": "^5.0.0" 73 | } 74 | }, 75 | "node_modules/@jridgewell/resolve-uri": { 76 | "version": "3.1.0", 77 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", 78 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", 79 | "dev": true, 80 | "engines": { 81 | "node": ">=6.0.0" 82 | } 83 | }, 84 | "node_modules/@jridgewell/sourcemap-codec": { 85 | "version": "1.4.14", 86 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", 87 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", 88 | "dev": true 89 | }, 90 | "node_modules/@jridgewell/trace-mapping": { 91 | "version": "0.3.9", 92 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 93 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 94 | "dev": true, 95 | "dependencies": { 96 | "@jridgewell/resolve-uri": "^3.0.3", 97 | "@jridgewell/sourcemap-codec": "^1.4.10" 98 | } 99 | }, 100 | "node_modules/@tsconfig/node10": { 101 | "version": "1.0.8", 102 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", 103 | "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", 104 | "dev": true 105 | }, 106 | "node_modules/@tsconfig/node12": { 107 | "version": "1.0.9", 108 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", 109 | "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", 110 | "dev": true 111 | }, 112 | "node_modules/@tsconfig/node14": { 113 | "version": "1.0.1", 114 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", 115 | "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", 116 | "dev": true 117 | }, 118 | "node_modules/@tsconfig/node16": { 119 | "version": "1.0.2", 120 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", 121 | "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", 122 | "dev": true 123 | }, 124 | "node_modules/@types/node": { 125 | "version": "16.4.7", 126 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.4.7.tgz", 127 | "integrity": "sha512-aDDY54sst8sx47CWT6QQqIZp45yURq4dic0+HCYfYNcY5Ejlb/CLmFnRLfy3wQuYafOeh3lB/DAKaqRKBtcZmA==", 128 | "dev": true, 129 | "peer": true 130 | }, 131 | "node_modules/abort-controller": { 132 | "version": "3.0.0", 133 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 134 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 135 | "dependencies": { 136 | "event-target-shim": "^5.0.0" 137 | }, 138 | "engines": { 139 | "node": ">=6.5" 140 | } 141 | }, 142 | "node_modules/abstract-logging": { 143 | "version": "2.0.1", 144 | "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", 145 | "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" 146 | }, 147 | "node_modules/acorn": { 148 | "version": "8.8.0", 149 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", 150 | "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", 151 | "dev": true, 152 | "bin": { 153 | "acorn": "bin/acorn" 154 | }, 155 | "engines": { 156 | "node": ">=0.4.0" 157 | } 158 | }, 159 | "node_modules/acorn-walk": { 160 | "version": "8.2.0", 161 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 162 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 163 | "dev": true, 164 | "engines": { 165 | "node": ">=0.4.0" 166 | } 167 | }, 168 | "node_modules/ajv": { 169 | "version": "8.11.0", 170 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", 171 | "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", 172 | "dependencies": { 173 | "fast-deep-equal": "^3.1.1", 174 | "json-schema-traverse": "^1.0.0", 175 | "require-from-string": "^2.0.2", 176 | "uri-js": "^4.2.2" 177 | }, 178 | "funding": { 179 | "type": "github", 180 | "url": "https://github.com/sponsors/epoberezkin" 181 | } 182 | }, 183 | "node_modules/ajv-formats": { 184 | "version": "2.1.1", 185 | "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", 186 | "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", 187 | "dependencies": { 188 | "ajv": "^8.0.0" 189 | }, 190 | "peerDependencies": { 191 | "ajv": "^8.0.0" 192 | }, 193 | "peerDependenciesMeta": { 194 | "ajv": { 195 | "optional": true 196 | } 197 | } 198 | }, 199 | "node_modules/archy": { 200 | "version": "1.0.0", 201 | "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", 202 | "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" 203 | }, 204 | "node_modules/arg": { 205 | "version": "4.1.3", 206 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 207 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 208 | "dev": true 209 | }, 210 | "node_modules/atomic-sleep": { 211 | "version": "1.0.0", 212 | "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", 213 | "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", 214 | "engines": { 215 | "node": ">=8.0.0" 216 | } 217 | }, 218 | "node_modules/avvio": { 219 | "version": "8.2.0", 220 | "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.2.0.tgz", 221 | "integrity": "sha512-bbCQdg7bpEv6kGH41RO/3B2/GMMmJSo2iBK+X8AWN9mujtfUipMDfIjsgHCfpnKqoGEQrrmCDKSa5OQ19+fDmg==", 222 | "dependencies": { 223 | "archy": "^1.0.0", 224 | "debug": "^4.0.0", 225 | "fastq": "^1.6.1" 226 | } 227 | }, 228 | "node_modules/base64-js": { 229 | "version": "1.5.1", 230 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 231 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 232 | "funding": [ 233 | { 234 | "type": "github", 235 | "url": "https://github.com/sponsors/feross" 236 | }, 237 | { 238 | "type": "patreon", 239 | "url": "https://www.patreon.com/feross" 240 | }, 241 | { 242 | "type": "consulting", 243 | "url": "https://feross.org/support" 244 | } 245 | ] 246 | }, 247 | "node_modules/buffer": { 248 | "version": "6.0.3", 249 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", 250 | "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", 251 | "funding": [ 252 | { 253 | "type": "github", 254 | "url": "https://github.com/sponsors/feross" 255 | }, 256 | { 257 | "type": "patreon", 258 | "url": "https://www.patreon.com/feross" 259 | }, 260 | { 261 | "type": "consulting", 262 | "url": "https://feross.org/support" 263 | } 264 | ], 265 | "dependencies": { 266 | "base64-js": "^1.3.1", 267 | "ieee754": "^1.2.1" 268 | } 269 | }, 270 | "node_modules/cookie": { 271 | "version": "0.5.0", 272 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", 273 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", 274 | "engines": { 275 | "node": ">= 0.6" 276 | } 277 | }, 278 | "node_modules/create-require": { 279 | "version": "1.1.1", 280 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 281 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 282 | "dev": true 283 | }, 284 | "node_modules/debug": { 285 | "version": "4.3.4", 286 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 287 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 288 | "dependencies": { 289 | "ms": "2.1.2" 290 | }, 291 | "engines": { 292 | "node": ">=6.0" 293 | }, 294 | "peerDependenciesMeta": { 295 | "supports-color": { 296 | "optional": true 297 | } 298 | } 299 | }, 300 | "node_modules/diff": { 301 | "version": "4.0.2", 302 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 303 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 304 | "dev": true, 305 | "engines": { 306 | "node": ">=0.3.1" 307 | } 308 | }, 309 | "node_modules/event-target-shim": { 310 | "version": "5.0.1", 311 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 312 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 313 | "engines": { 314 | "node": ">=6" 315 | } 316 | }, 317 | "node_modules/events": { 318 | "version": "3.3.0", 319 | "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", 320 | "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", 321 | "engines": { 322 | "node": ">=0.8.x" 323 | } 324 | }, 325 | "node_modules/fast-decode-uri-component": { 326 | "version": "1.0.1", 327 | "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", 328 | "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" 329 | }, 330 | "node_modules/fast-deep-equal": { 331 | "version": "3.1.3", 332 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 333 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 334 | }, 335 | "node_modules/fast-json-stringify": { 336 | "version": "5.4.0", 337 | "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.4.0.tgz", 338 | "integrity": "sha512-PIzon53oX/zEGLrGbu4DpfNcYiV4K4rk+JsVrawRPO/G8cNBEMZ3KlIk2BCGqN+m1KCCA4zt5E7Hh3GG9ojRVA==", 339 | "dependencies": { 340 | "@fastify/deepmerge": "^1.0.0", 341 | "ajv": "^8.10.0", 342 | "ajv-formats": "^2.1.1", 343 | "fast-deep-equal": "^3.1.3", 344 | "fast-uri": "^2.1.0", 345 | "rfdc": "^1.2.0" 346 | } 347 | }, 348 | "node_modules/fast-querystring": { 349 | "version": "1.0.0", 350 | "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.0.0.tgz", 351 | "integrity": "sha512-3LQi62IhQoDlmt4ULCYmh17vRO2EtS7hTSsG4WwoKWgV7GLMKBOecEh+aiavASnLx8I2y89OD33AGLo0ccRhzA==", 352 | "dependencies": { 353 | "fast-decode-uri-component": "^1.0.1" 354 | } 355 | }, 356 | "node_modules/fast-redact": { 357 | "version": "3.1.2", 358 | "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.2.tgz", 359 | "integrity": "sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==", 360 | "engines": { 361 | "node": ">=6" 362 | } 363 | }, 364 | "node_modules/fast-uri": { 365 | "version": "2.1.0", 366 | "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.1.0.tgz", 367 | "integrity": "sha512-qKRta6N7BWEFVlyonVY/V+BMLgFqktCUV0QjT259ekAIlbVrMaFnFLxJ4s/JPl4tou56S1BzPufI60bLe29fHA==" 368 | }, 369 | "node_modules/fastify": { 370 | "version": "4.9.2", 371 | "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.9.2.tgz", 372 | "integrity": "sha512-Mk3hv7ZRet2huMYN6IJ8RGy1TAAC7LJsCEjxLf808zafAADNu43xRzbl7FSEIBxKyhntTM0F626Oc34LUNcUxQ==", 373 | "dependencies": { 374 | "@fastify/ajv-compiler": "^3.3.1", 375 | "@fastify/error": "^3.0.0", 376 | "@fastify/fast-json-stringify-compiler": "^4.1.0", 377 | "abstract-logging": "^2.0.1", 378 | "avvio": "^8.2.0", 379 | "find-my-way": "^7.3.0", 380 | "light-my-request": "^5.6.1", 381 | "pino": "^8.5.0", 382 | "process-warning": "^2.0.0", 383 | "proxy-addr": "^2.0.7", 384 | "rfdc": "^1.3.0", 385 | "secure-json-parse": "^2.5.0", 386 | "semver": "^7.3.7", 387 | "tiny-lru": "^9.0.2" 388 | } 389 | }, 390 | "node_modules/fastq": { 391 | "version": "1.13.0", 392 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", 393 | "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", 394 | "dependencies": { 395 | "reusify": "^1.0.4" 396 | } 397 | }, 398 | "node_modules/find-my-way": { 399 | "version": "7.3.1", 400 | "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.3.1.tgz", 401 | "integrity": "sha512-kGvM08SOkqvheLcuQ8GW9t/H901Qb9rZEbcNWbXopzy4jDRoaJpJoObPSKf4MnQLZ20ZTp7rL5MpF6rf+pqmyg==", 402 | "dependencies": { 403 | "fast-deep-equal": "^3.1.3", 404 | "fast-querystring": "^1.0.0", 405 | "safe-regex2": "^2.0.0" 406 | }, 407 | "engines": { 408 | "node": ">=14" 409 | } 410 | }, 411 | "node_modules/forwarded": { 412 | "version": "0.2.0", 413 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 414 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 415 | "engines": { 416 | "node": ">= 0.6" 417 | } 418 | }, 419 | "node_modules/ieee754": { 420 | "version": "1.2.1", 421 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 422 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 423 | "funding": [ 424 | { 425 | "type": "github", 426 | "url": "https://github.com/sponsors/feross" 427 | }, 428 | { 429 | "type": "patreon", 430 | "url": "https://www.patreon.com/feross" 431 | }, 432 | { 433 | "type": "consulting", 434 | "url": "https://feross.org/support" 435 | } 436 | ] 437 | }, 438 | "node_modules/ipaddr.js": { 439 | "version": "1.9.1", 440 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 441 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 442 | "engines": { 443 | "node": ">= 0.10" 444 | } 445 | }, 446 | "node_modules/json-schema-traverse": { 447 | "version": "1.0.0", 448 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 449 | "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" 450 | }, 451 | "node_modules/light-my-request": { 452 | "version": "5.6.1", 453 | "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.6.1.tgz", 454 | "integrity": "sha512-sbJnC1UBRivi9L1kICr3CESb82pNiPNB3TvtdIrZZqW0Qh8uDXvoywMmWKZlihDcmw952CMICCzM+54LDf+E+g==", 455 | "dependencies": { 456 | "cookie": "^0.5.0", 457 | "process-warning": "^2.0.0", 458 | "set-cookie-parser": "^2.4.1" 459 | } 460 | }, 461 | "node_modules/lru-cache": { 462 | "version": "6.0.0", 463 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 464 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 465 | "dependencies": { 466 | "yallist": "^4.0.0" 467 | }, 468 | "engines": { 469 | "node": ">=10" 470 | } 471 | }, 472 | "node_modules/make-error": { 473 | "version": "1.3.6", 474 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 475 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 476 | "dev": true 477 | }, 478 | "node_modules/ms": { 479 | "version": "2.1.2", 480 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 481 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 482 | }, 483 | "node_modules/on-exit-leak-free": { 484 | "version": "2.1.0", 485 | "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", 486 | "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==" 487 | }, 488 | "node_modules/pino": { 489 | "version": "8.7.0", 490 | "resolved": "https://registry.npmjs.org/pino/-/pino-8.7.0.tgz", 491 | "integrity": "sha512-l9sA5uPxmZzwydhMWUcm1gI0YxNnYl8MfSr2h8cwLvOAzQLBLewzF247h/vqHe3/tt6fgtXeG9wdjjoetdI/vA==", 492 | "dependencies": { 493 | "atomic-sleep": "^1.0.0", 494 | "fast-redact": "^3.1.1", 495 | "on-exit-leak-free": "^2.1.0", 496 | "pino-abstract-transport": "v1.0.0", 497 | "pino-std-serializers": "^6.0.0", 498 | "process-warning": "^2.0.0", 499 | "quick-format-unescaped": "^4.0.3", 500 | "real-require": "^0.2.0", 501 | "safe-stable-stringify": "^2.3.1", 502 | "sonic-boom": "^3.1.0", 503 | "thread-stream": "^2.0.0" 504 | }, 505 | "bin": { 506 | "pino": "bin.js" 507 | } 508 | }, 509 | "node_modules/pino-abstract-transport": { 510 | "version": "1.0.0", 511 | "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz", 512 | "integrity": "sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==", 513 | "dependencies": { 514 | "readable-stream": "^4.0.0", 515 | "split2": "^4.0.0" 516 | } 517 | }, 518 | "node_modules/pino-std-serializers": { 519 | "version": "6.0.0", 520 | "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.0.0.tgz", 521 | "integrity": "sha512-mMMOwSKrmyl+Y12Ri2xhH1lbzQxwwpuru9VjyJpgFIH4asSj88F2csdMwN6+M5g1Ll4rmsYghHLQJw81tgZ7LQ==" 522 | }, 523 | "node_modules/process": { 524 | "version": "0.11.10", 525 | "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", 526 | "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", 527 | "engines": { 528 | "node": ">= 0.6.0" 529 | } 530 | }, 531 | "node_modules/process-warning": { 532 | "version": "2.0.0", 533 | "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.0.0.tgz", 534 | "integrity": "sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww==" 535 | }, 536 | "node_modules/proxy-addr": { 537 | "version": "2.0.7", 538 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 539 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 540 | "dependencies": { 541 | "forwarded": "0.2.0", 542 | "ipaddr.js": "1.9.1" 543 | }, 544 | "engines": { 545 | "node": ">= 0.10" 546 | } 547 | }, 548 | "node_modules/punycode": { 549 | "version": "2.1.1", 550 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 551 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 552 | "engines": { 553 | "node": ">=6" 554 | } 555 | }, 556 | "node_modules/quick-format-unescaped": { 557 | "version": "4.0.4", 558 | "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", 559 | "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" 560 | }, 561 | "node_modules/readable-stream": { 562 | "version": "4.2.0", 563 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.2.0.tgz", 564 | "integrity": "sha512-gJrBHsaI3lgBoGMW/jHZsQ/o/TIWiu5ENCJG1BB7fuCKzpFM8GaS2UoBVt9NO+oI+3FcrBNbUkl3ilDe09aY4A==", 565 | "dependencies": { 566 | "abort-controller": "^3.0.0", 567 | "buffer": "^6.0.3", 568 | "events": "^3.3.0", 569 | "process": "^0.11.10" 570 | }, 571 | "engines": { 572 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 573 | } 574 | }, 575 | "node_modules/real-require": { 576 | "version": "0.2.0", 577 | "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", 578 | "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", 579 | "engines": { 580 | "node": ">= 12.13.0" 581 | } 582 | }, 583 | "node_modules/require-from-string": { 584 | "version": "2.0.2", 585 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 586 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", 587 | "engines": { 588 | "node": ">=0.10.0" 589 | } 590 | }, 591 | "node_modules/ret": { 592 | "version": "0.2.2", 593 | "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", 594 | "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", 595 | "engines": { 596 | "node": ">=4" 597 | } 598 | }, 599 | "node_modules/reusify": { 600 | "version": "1.0.4", 601 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 602 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 603 | "engines": { 604 | "iojs": ">=1.0.0", 605 | "node": ">=0.10.0" 606 | } 607 | }, 608 | "node_modules/rfdc": { 609 | "version": "1.3.0", 610 | "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", 611 | "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" 612 | }, 613 | "node_modules/safe-regex2": { 614 | "version": "2.0.0", 615 | "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", 616 | "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", 617 | "dependencies": { 618 | "ret": "~0.2.0" 619 | } 620 | }, 621 | "node_modules/safe-stable-stringify": { 622 | "version": "2.4.1", 623 | "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.1.tgz", 624 | "integrity": "sha512-dVHE6bMtS/bnL2mwualjc6IxEv1F+OCUpA46pKUj6F8uDbUM0jCCulPqRNPSnWwGNKx5etqMjZYdXtrm5KJZGA==", 625 | "engines": { 626 | "node": ">=10" 627 | } 628 | }, 629 | "node_modules/secure-json-parse": { 630 | "version": "2.5.0", 631 | "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.5.0.tgz", 632 | "integrity": "sha512-ZQruFgZnIWH+WyO9t5rWt4ZEGqCKPwhiw+YbzTwpmT9elgLrLcfuyUiSnwwjUiVy9r4VM3urtbNF1xmEh9IL2w==" 633 | }, 634 | "node_modules/semver": { 635 | "version": "7.3.7", 636 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", 637 | "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", 638 | "dependencies": { 639 | "lru-cache": "^6.0.0" 640 | }, 641 | "bin": { 642 | "semver": "bin/semver.js" 643 | }, 644 | "engines": { 645 | "node": ">=10" 646 | } 647 | }, 648 | "node_modules/set-cookie-parser": { 649 | "version": "2.5.1", 650 | "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz", 651 | "integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==" 652 | }, 653 | "node_modules/sonic-boom": { 654 | "version": "3.2.0", 655 | "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.2.0.tgz", 656 | "integrity": "sha512-SbbZ+Kqj/XIunvIAgUZRlqd6CGQYq71tRRbXR92Za8J/R3Yh4Av+TWENiSiEgnlwckYLyP0YZQWVfyNC0dzLaA==", 657 | "dependencies": { 658 | "atomic-sleep": "^1.0.0" 659 | } 660 | }, 661 | "node_modules/split2": { 662 | "version": "4.1.0", 663 | "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", 664 | "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==", 665 | "engines": { 666 | "node": ">= 10.x" 667 | } 668 | }, 669 | "node_modules/thread-stream": { 670 | "version": "2.2.0", 671 | "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.2.0.tgz", 672 | "integrity": "sha512-rUkv4/fnb4rqy/gGy7VuqK6wE1+1DOCOWy4RMeaV69ZHMP11tQKZvZSip1yTgrKCMZzEMcCL/bKfHvSfDHx+iQ==", 673 | "dependencies": { 674 | "real-require": "^0.2.0" 675 | } 676 | }, 677 | "node_modules/tiny-lru": { 678 | "version": "9.0.3", 679 | "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-9.0.3.tgz", 680 | "integrity": "sha512-/i9GruRjXsnDgehxvy6iZ4AFNVxngEFbwzirhdulomMNPGPVV3ECMZOWSw0w4sRMZ9Al9m4jy08GPvRxRUGYlw==", 681 | "engines": { 682 | "node": ">=6" 683 | } 684 | }, 685 | "node_modules/ts-node": { 686 | "version": "10.9.1", 687 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", 688 | "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", 689 | "dev": true, 690 | "dependencies": { 691 | "@cspotcode/source-map-support": "^0.8.0", 692 | "@tsconfig/node10": "^1.0.7", 693 | "@tsconfig/node12": "^1.0.7", 694 | "@tsconfig/node14": "^1.0.0", 695 | "@tsconfig/node16": "^1.0.2", 696 | "acorn": "^8.4.1", 697 | "acorn-walk": "^8.1.1", 698 | "arg": "^4.1.0", 699 | "create-require": "^1.1.0", 700 | "diff": "^4.0.1", 701 | "make-error": "^1.1.1", 702 | "v8-compile-cache-lib": "^3.0.1", 703 | "yn": "3.1.1" 704 | }, 705 | "bin": { 706 | "ts-node": "dist/bin.js", 707 | "ts-node-cwd": "dist/bin-cwd.js", 708 | "ts-node-esm": "dist/bin-esm.js", 709 | "ts-node-script": "dist/bin-script.js", 710 | "ts-node-transpile-only": "dist/bin-transpile.js", 711 | "ts-script": "dist/bin-script-deprecated.js" 712 | }, 713 | "peerDependencies": { 714 | "@swc/core": ">=1.2.50", 715 | "@swc/wasm": ">=1.2.50", 716 | "@types/node": "*", 717 | "typescript": ">=2.7" 718 | }, 719 | "peerDependenciesMeta": { 720 | "@swc/core": { 721 | "optional": true 722 | }, 723 | "@swc/wasm": { 724 | "optional": true 725 | } 726 | } 727 | }, 728 | "node_modules/type-safe-errors": { 729 | "resolved": "../..", 730 | "link": true 731 | }, 732 | "node_modules/typescript": { 733 | "version": "4.7.4", 734 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", 735 | "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", 736 | "dev": true, 737 | "bin": { 738 | "tsc": "bin/tsc", 739 | "tsserver": "bin/tsserver" 740 | }, 741 | "engines": { 742 | "node": ">=4.2.0" 743 | } 744 | }, 745 | "node_modules/uri-js": { 746 | "version": "4.4.1", 747 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 748 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 749 | "dependencies": { 750 | "punycode": "^2.1.0" 751 | } 752 | }, 753 | "node_modules/v8-compile-cache-lib": { 754 | "version": "3.0.1", 755 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", 756 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", 757 | "dev": true 758 | }, 759 | "node_modules/yallist": { 760 | "version": "4.0.0", 761 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 762 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 763 | }, 764 | "node_modules/yn": { 765 | "version": "3.1.1", 766 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 767 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 768 | "dev": true, 769 | "engines": { 770 | "node": ">=6" 771 | } 772 | } 773 | }, 774 | "dependencies": { 775 | "@cspotcode/source-map-support": { 776 | "version": "0.8.1", 777 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 778 | "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 779 | "dev": true, 780 | "requires": { 781 | "@jridgewell/trace-mapping": "0.3.9" 782 | } 783 | }, 784 | "@fastify/ajv-compiler": { 785 | "version": "3.4.0", 786 | "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.4.0.tgz", 787 | "integrity": "sha512-69JnK7Cot+ktn7LD5TikP3b7psBPX55tYpQa8WSumt8r117PCa2zwHnImfBtRWYExreJlI48hr0WZaVrTBGj7w==", 788 | "requires": { 789 | "ajv": "^8.11.0", 790 | "ajv-formats": "^2.1.1", 791 | "fast-uri": "^2.0.0" 792 | } 793 | }, 794 | "@fastify/deepmerge": { 795 | "version": "1.1.0", 796 | "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-1.1.0.tgz", 797 | "integrity": "sha512-E8Hfdvs1bG6u0N4vN5Nty6JONUfTdOciyD5rn8KnEsLKIenvOVcr210BQR9t34PRkNyjqnMLGk3e0BsaxRdL+g==" 798 | }, 799 | "@fastify/error": { 800 | "version": "3.0.0", 801 | "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.0.0.tgz", 802 | "integrity": "sha512-dPRyT40GiHRzSCll3/Jn2nPe25+E1VXc9tDwRAIKwFCxd5Np5wzgz1tmooWG3sV0qKgrBibihVoCna2ru4SEFg==" 803 | }, 804 | "@fastify/fast-json-stringify-compiler": { 805 | "version": "4.1.0", 806 | "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.1.0.tgz", 807 | "integrity": "sha512-cTKBV2J9+u6VaKDhX7HepSfPSzw+F+TSd+k0wzifj4rG+4E5PjSFJCk19P8R6tr/72cuzgGd+mbB3jFT6lvAgw==", 808 | "requires": { 809 | "fast-json-stringify": "^5.0.0" 810 | } 811 | }, 812 | "@jridgewell/resolve-uri": { 813 | "version": "3.1.0", 814 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", 815 | "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", 816 | "dev": true 817 | }, 818 | "@jridgewell/sourcemap-codec": { 819 | "version": "1.4.14", 820 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", 821 | "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", 822 | "dev": true 823 | }, 824 | "@jridgewell/trace-mapping": { 825 | "version": "0.3.9", 826 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 827 | "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 828 | "dev": true, 829 | "requires": { 830 | "@jridgewell/resolve-uri": "^3.0.3", 831 | "@jridgewell/sourcemap-codec": "^1.4.10" 832 | } 833 | }, 834 | "@tsconfig/node10": { 835 | "version": "1.0.8", 836 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", 837 | "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", 838 | "dev": true 839 | }, 840 | "@tsconfig/node12": { 841 | "version": "1.0.9", 842 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", 843 | "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", 844 | "dev": true 845 | }, 846 | "@tsconfig/node14": { 847 | "version": "1.0.1", 848 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", 849 | "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", 850 | "dev": true 851 | }, 852 | "@tsconfig/node16": { 853 | "version": "1.0.2", 854 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", 855 | "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", 856 | "dev": true 857 | }, 858 | "@types/node": { 859 | "version": "16.4.7", 860 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.4.7.tgz", 861 | "integrity": "sha512-aDDY54sst8sx47CWT6QQqIZp45yURq4dic0+HCYfYNcY5Ejlb/CLmFnRLfy3wQuYafOeh3lB/DAKaqRKBtcZmA==", 862 | "dev": true, 863 | "peer": true 864 | }, 865 | "abort-controller": { 866 | "version": "3.0.0", 867 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 868 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 869 | "requires": { 870 | "event-target-shim": "^5.0.0" 871 | } 872 | }, 873 | "abstract-logging": { 874 | "version": "2.0.1", 875 | "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", 876 | "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" 877 | }, 878 | "acorn": { 879 | "version": "8.8.0", 880 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", 881 | "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", 882 | "dev": true 883 | }, 884 | "acorn-walk": { 885 | "version": "8.2.0", 886 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 887 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 888 | "dev": true 889 | }, 890 | "ajv": { 891 | "version": "8.11.0", 892 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", 893 | "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", 894 | "requires": { 895 | "fast-deep-equal": "^3.1.1", 896 | "json-schema-traverse": "^1.0.0", 897 | "require-from-string": "^2.0.2", 898 | "uri-js": "^4.2.2" 899 | } 900 | }, 901 | "ajv-formats": { 902 | "version": "2.1.1", 903 | "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", 904 | "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", 905 | "requires": { 906 | "ajv": "^8.0.0" 907 | } 908 | }, 909 | "archy": { 910 | "version": "1.0.0", 911 | "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", 912 | "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" 913 | }, 914 | "arg": { 915 | "version": "4.1.3", 916 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 917 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 918 | "dev": true 919 | }, 920 | "atomic-sleep": { 921 | "version": "1.0.0", 922 | "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", 923 | "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" 924 | }, 925 | "avvio": { 926 | "version": "8.2.0", 927 | "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.2.0.tgz", 928 | "integrity": "sha512-bbCQdg7bpEv6kGH41RO/3B2/GMMmJSo2iBK+X8AWN9mujtfUipMDfIjsgHCfpnKqoGEQrrmCDKSa5OQ19+fDmg==", 929 | "requires": { 930 | "archy": "^1.0.0", 931 | "debug": "^4.0.0", 932 | "fastq": "^1.6.1" 933 | } 934 | }, 935 | "base64-js": { 936 | "version": "1.5.1", 937 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 938 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 939 | }, 940 | "buffer": { 941 | "version": "6.0.3", 942 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", 943 | "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", 944 | "requires": { 945 | "base64-js": "^1.3.1", 946 | "ieee754": "^1.2.1" 947 | } 948 | }, 949 | "cookie": { 950 | "version": "0.5.0", 951 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", 952 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" 953 | }, 954 | "create-require": { 955 | "version": "1.1.1", 956 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 957 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 958 | "dev": true 959 | }, 960 | "debug": { 961 | "version": "4.3.4", 962 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 963 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 964 | "requires": { 965 | "ms": "2.1.2" 966 | } 967 | }, 968 | "diff": { 969 | "version": "4.0.2", 970 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 971 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 972 | "dev": true 973 | }, 974 | "event-target-shim": { 975 | "version": "5.0.1", 976 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 977 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" 978 | }, 979 | "events": { 980 | "version": "3.3.0", 981 | "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", 982 | "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" 983 | }, 984 | "fast-decode-uri-component": { 985 | "version": "1.0.1", 986 | "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", 987 | "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" 988 | }, 989 | "fast-deep-equal": { 990 | "version": "3.1.3", 991 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 992 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 993 | }, 994 | "fast-json-stringify": { 995 | "version": "5.4.0", 996 | "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.4.0.tgz", 997 | "integrity": "sha512-PIzon53oX/zEGLrGbu4DpfNcYiV4K4rk+JsVrawRPO/G8cNBEMZ3KlIk2BCGqN+m1KCCA4zt5E7Hh3GG9ojRVA==", 998 | "requires": { 999 | "@fastify/deepmerge": "^1.0.0", 1000 | "ajv": "^8.10.0", 1001 | "ajv-formats": "^2.1.1", 1002 | "fast-deep-equal": "^3.1.3", 1003 | "fast-uri": "^2.1.0", 1004 | "rfdc": "^1.2.0" 1005 | } 1006 | }, 1007 | "fast-querystring": { 1008 | "version": "1.0.0", 1009 | "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.0.0.tgz", 1010 | "integrity": "sha512-3LQi62IhQoDlmt4ULCYmh17vRO2EtS7hTSsG4WwoKWgV7GLMKBOecEh+aiavASnLx8I2y89OD33AGLo0ccRhzA==", 1011 | "requires": { 1012 | "fast-decode-uri-component": "^1.0.1" 1013 | } 1014 | }, 1015 | "fast-redact": { 1016 | "version": "3.1.2", 1017 | "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.1.2.tgz", 1018 | "integrity": "sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==" 1019 | }, 1020 | "fast-uri": { 1021 | "version": "2.1.0", 1022 | "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.1.0.tgz", 1023 | "integrity": "sha512-qKRta6N7BWEFVlyonVY/V+BMLgFqktCUV0QjT259ekAIlbVrMaFnFLxJ4s/JPl4tou56S1BzPufI60bLe29fHA==" 1024 | }, 1025 | "fastify": { 1026 | "version": "4.9.2", 1027 | "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.9.2.tgz", 1028 | "integrity": "sha512-Mk3hv7ZRet2huMYN6IJ8RGy1TAAC7LJsCEjxLf808zafAADNu43xRzbl7FSEIBxKyhntTM0F626Oc34LUNcUxQ==", 1029 | "requires": { 1030 | "@fastify/ajv-compiler": "^3.3.1", 1031 | "@fastify/error": "^3.0.0", 1032 | "@fastify/fast-json-stringify-compiler": "^4.1.0", 1033 | "abstract-logging": "^2.0.1", 1034 | "avvio": "^8.2.0", 1035 | "find-my-way": "^7.3.0", 1036 | "light-my-request": "^5.6.1", 1037 | "pino": "^8.5.0", 1038 | "process-warning": "^2.0.0", 1039 | "proxy-addr": "^2.0.7", 1040 | "rfdc": "^1.3.0", 1041 | "secure-json-parse": "^2.5.0", 1042 | "semver": "^7.3.7", 1043 | "tiny-lru": "^9.0.2" 1044 | } 1045 | }, 1046 | "fastq": { 1047 | "version": "1.13.0", 1048 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", 1049 | "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", 1050 | "requires": { 1051 | "reusify": "^1.0.4" 1052 | } 1053 | }, 1054 | "find-my-way": { 1055 | "version": "7.3.1", 1056 | "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.3.1.tgz", 1057 | "integrity": "sha512-kGvM08SOkqvheLcuQ8GW9t/H901Qb9rZEbcNWbXopzy4jDRoaJpJoObPSKf4MnQLZ20ZTp7rL5MpF6rf+pqmyg==", 1058 | "requires": { 1059 | "fast-deep-equal": "^3.1.3", 1060 | "fast-querystring": "^1.0.0", 1061 | "safe-regex2": "^2.0.0" 1062 | } 1063 | }, 1064 | "forwarded": { 1065 | "version": "0.2.0", 1066 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 1067 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" 1068 | }, 1069 | "ieee754": { 1070 | "version": "1.2.1", 1071 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 1072 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" 1073 | }, 1074 | "ipaddr.js": { 1075 | "version": "1.9.1", 1076 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 1077 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 1078 | }, 1079 | "json-schema-traverse": { 1080 | "version": "1.0.0", 1081 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 1082 | "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" 1083 | }, 1084 | "light-my-request": { 1085 | "version": "5.6.1", 1086 | "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.6.1.tgz", 1087 | "integrity": "sha512-sbJnC1UBRivi9L1kICr3CESb82pNiPNB3TvtdIrZZqW0Qh8uDXvoywMmWKZlihDcmw952CMICCzM+54LDf+E+g==", 1088 | "requires": { 1089 | "cookie": "^0.5.0", 1090 | "process-warning": "^2.0.0", 1091 | "set-cookie-parser": "^2.4.1" 1092 | } 1093 | }, 1094 | "lru-cache": { 1095 | "version": "6.0.0", 1096 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 1097 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 1098 | "requires": { 1099 | "yallist": "^4.0.0" 1100 | } 1101 | }, 1102 | "make-error": { 1103 | "version": "1.3.6", 1104 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 1105 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 1106 | "dev": true 1107 | }, 1108 | "ms": { 1109 | "version": "2.1.2", 1110 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1111 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1112 | }, 1113 | "on-exit-leak-free": { 1114 | "version": "2.1.0", 1115 | "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", 1116 | "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==" 1117 | }, 1118 | "pino": { 1119 | "version": "8.7.0", 1120 | "resolved": "https://registry.npmjs.org/pino/-/pino-8.7.0.tgz", 1121 | "integrity": "sha512-l9sA5uPxmZzwydhMWUcm1gI0YxNnYl8MfSr2h8cwLvOAzQLBLewzF247h/vqHe3/tt6fgtXeG9wdjjoetdI/vA==", 1122 | "requires": { 1123 | "atomic-sleep": "^1.0.0", 1124 | "fast-redact": "^3.1.1", 1125 | "on-exit-leak-free": "^2.1.0", 1126 | "pino-abstract-transport": "v1.0.0", 1127 | "pino-std-serializers": "^6.0.0", 1128 | "process-warning": "^2.0.0", 1129 | "quick-format-unescaped": "^4.0.3", 1130 | "real-require": "^0.2.0", 1131 | "safe-stable-stringify": "^2.3.1", 1132 | "sonic-boom": "^3.1.0", 1133 | "thread-stream": "^2.0.0" 1134 | } 1135 | }, 1136 | "pino-abstract-transport": { 1137 | "version": "1.0.0", 1138 | "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz", 1139 | "integrity": "sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==", 1140 | "requires": { 1141 | "readable-stream": "^4.0.0", 1142 | "split2": "^4.0.0" 1143 | } 1144 | }, 1145 | "pino-std-serializers": { 1146 | "version": "6.0.0", 1147 | "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.0.0.tgz", 1148 | "integrity": "sha512-mMMOwSKrmyl+Y12Ri2xhH1lbzQxwwpuru9VjyJpgFIH4asSj88F2csdMwN6+M5g1Ll4rmsYghHLQJw81tgZ7LQ==" 1149 | }, 1150 | "process": { 1151 | "version": "0.11.10", 1152 | "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", 1153 | "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" 1154 | }, 1155 | "process-warning": { 1156 | "version": "2.0.0", 1157 | "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.0.0.tgz", 1158 | "integrity": "sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww==" 1159 | }, 1160 | "proxy-addr": { 1161 | "version": "2.0.7", 1162 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 1163 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 1164 | "requires": { 1165 | "forwarded": "0.2.0", 1166 | "ipaddr.js": "1.9.1" 1167 | } 1168 | }, 1169 | "punycode": { 1170 | "version": "2.1.1", 1171 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1172 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 1173 | }, 1174 | "quick-format-unescaped": { 1175 | "version": "4.0.4", 1176 | "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", 1177 | "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" 1178 | }, 1179 | "readable-stream": { 1180 | "version": "4.2.0", 1181 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.2.0.tgz", 1182 | "integrity": "sha512-gJrBHsaI3lgBoGMW/jHZsQ/o/TIWiu5ENCJG1BB7fuCKzpFM8GaS2UoBVt9NO+oI+3FcrBNbUkl3ilDe09aY4A==", 1183 | "requires": { 1184 | "abort-controller": "^3.0.0", 1185 | "buffer": "^6.0.3", 1186 | "events": "^3.3.0", 1187 | "process": "^0.11.10" 1188 | } 1189 | }, 1190 | "real-require": { 1191 | "version": "0.2.0", 1192 | "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", 1193 | "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==" 1194 | }, 1195 | "require-from-string": { 1196 | "version": "2.0.2", 1197 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 1198 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" 1199 | }, 1200 | "ret": { 1201 | "version": "0.2.2", 1202 | "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", 1203 | "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==" 1204 | }, 1205 | "reusify": { 1206 | "version": "1.0.4", 1207 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 1208 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" 1209 | }, 1210 | "rfdc": { 1211 | "version": "1.3.0", 1212 | "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", 1213 | "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" 1214 | }, 1215 | "safe-regex2": { 1216 | "version": "2.0.0", 1217 | "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", 1218 | "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", 1219 | "requires": { 1220 | "ret": "~0.2.0" 1221 | } 1222 | }, 1223 | "safe-stable-stringify": { 1224 | "version": "2.4.1", 1225 | "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.1.tgz", 1226 | "integrity": "sha512-dVHE6bMtS/bnL2mwualjc6IxEv1F+OCUpA46pKUj6F8uDbUM0jCCulPqRNPSnWwGNKx5etqMjZYdXtrm5KJZGA==" 1227 | }, 1228 | "secure-json-parse": { 1229 | "version": "2.5.0", 1230 | "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.5.0.tgz", 1231 | "integrity": "sha512-ZQruFgZnIWH+WyO9t5rWt4ZEGqCKPwhiw+YbzTwpmT9elgLrLcfuyUiSnwwjUiVy9r4VM3urtbNF1xmEh9IL2w==" 1232 | }, 1233 | "semver": { 1234 | "version": "7.3.7", 1235 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", 1236 | "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", 1237 | "requires": { 1238 | "lru-cache": "^6.0.0" 1239 | } 1240 | }, 1241 | "set-cookie-parser": { 1242 | "version": "2.5.1", 1243 | "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz", 1244 | "integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==" 1245 | }, 1246 | "sonic-boom": { 1247 | "version": "3.2.0", 1248 | "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.2.0.tgz", 1249 | "integrity": "sha512-SbbZ+Kqj/XIunvIAgUZRlqd6CGQYq71tRRbXR92Za8J/R3Yh4Av+TWENiSiEgnlwckYLyP0YZQWVfyNC0dzLaA==", 1250 | "requires": { 1251 | "atomic-sleep": "^1.0.0" 1252 | } 1253 | }, 1254 | "split2": { 1255 | "version": "4.1.0", 1256 | "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", 1257 | "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==" 1258 | }, 1259 | "thread-stream": { 1260 | "version": "2.2.0", 1261 | "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.2.0.tgz", 1262 | "integrity": "sha512-rUkv4/fnb4rqy/gGy7VuqK6wE1+1DOCOWy4RMeaV69ZHMP11tQKZvZSip1yTgrKCMZzEMcCL/bKfHvSfDHx+iQ==", 1263 | "requires": { 1264 | "real-require": "^0.2.0" 1265 | } 1266 | }, 1267 | "tiny-lru": { 1268 | "version": "9.0.3", 1269 | "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-9.0.3.tgz", 1270 | "integrity": "sha512-/i9GruRjXsnDgehxvy6iZ4AFNVxngEFbwzirhdulomMNPGPVV3ECMZOWSw0w4sRMZ9Al9m4jy08GPvRxRUGYlw==" 1271 | }, 1272 | "ts-node": { 1273 | "version": "10.9.1", 1274 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", 1275 | "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", 1276 | "dev": true, 1277 | "requires": { 1278 | "@cspotcode/source-map-support": "^0.8.0", 1279 | "@tsconfig/node10": "^1.0.7", 1280 | "@tsconfig/node12": "^1.0.7", 1281 | "@tsconfig/node14": "^1.0.0", 1282 | "@tsconfig/node16": "^1.0.2", 1283 | "acorn": "^8.4.1", 1284 | "acorn-walk": "^8.1.1", 1285 | "arg": "^4.1.0", 1286 | "create-require": "^1.1.0", 1287 | "diff": "^4.0.1", 1288 | "make-error": "^1.1.1", 1289 | "v8-compile-cache-lib": "^3.0.1", 1290 | "yn": "3.1.1" 1291 | } 1292 | }, 1293 | "type-safe-errors": { 1294 | "version": "file:../..", 1295 | "requires": { 1296 | "@types/chai": "^4.3.1", 1297 | "@types/mocha": "^9.1.1", 1298 | "@types/node": "^16.11.7", 1299 | "@typescript-eslint/eslint-plugin": "^5.30.7", 1300 | "@typescript-eslint/parser": "^5.30.7", 1301 | "chai": "^4.3.6", 1302 | "eslint": "^8.20.0", 1303 | "eslint-config-prettier": "^8.5.0", 1304 | "eslint-plugin-prettier": "^4.2.1", 1305 | "mocha": "^10.0.0", 1306 | "prettier": "^2.7.1", 1307 | "ts-node": "^10.9.1", 1308 | "typescript": "^4.7.4" 1309 | } 1310 | }, 1311 | "typescript": { 1312 | "version": "4.7.4", 1313 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", 1314 | "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", 1315 | "dev": true 1316 | }, 1317 | "uri-js": { 1318 | "version": "4.4.1", 1319 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 1320 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1321 | "requires": { 1322 | "punycode": "^2.1.0" 1323 | } 1324 | }, 1325 | "v8-compile-cache-lib": { 1326 | "version": "3.0.1", 1327 | "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", 1328 | "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", 1329 | "dev": true 1330 | }, 1331 | "yallist": { 1332 | "version": "4.0.0", 1333 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 1334 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 1335 | }, 1336 | "yn": { 1337 | "version": "3.1.1", 1338 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 1339 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 1340 | "dev": true 1341 | } 1342 | } 1343 | } 1344 | --------------------------------------------------------------------------------