├── .all-contributorsrc ├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── bug_types.yml │ └── feature_request.yml ├── pull_request_template.md └── workflows │ └── workflow.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── ava.config.js ├── eslint.config.js ├── gulpfile.js ├── package-lock.json ├── package.json ├── prettier.config.js ├── src ├── main.d.ts ├── main.js ├── main.test-d.ts └── main.test.js └── tsconfig.json /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "modern-errors-http", 3 | "projectOwner": "ehmicky", 4 | "repoType": "github", 5 | "repoHost": "https://github.com", 6 | "files": [ 7 | "README.md" 8 | ], 9 | "imageSize": 100, 10 | "commit": true, 11 | "linkToUsage": false, 12 | "contributors": [ 13 | { 14 | "login": "ehmicky", 15 | "name": "ehmicky", 16 | "avatar_url": "https://avatars2.githubusercontent.com/u/8136211?v=4", 17 | "profile": "https://fosstodon.org/@ehmicky", 18 | "contributions": [ 19 | "code", 20 | "design", 21 | "ideas", 22 | "doc" 23 | ] 24 | } 25 | ], 26 | "contributorsPerLine": 7, 27 | "skipCi": true, 28 | "commitConvention": "none" 29 | } 30 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | max_line_length = 80 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: Report a bug 3 | title: Please replace with a clear and descriptive title 4 | labels: [bug] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: Thanks for reporting this bug! 9 | - type: checkboxes 10 | attributes: 11 | label: Guidelines 12 | options: 13 | - label: 14 | Please search other issues to make sure this bug has not already 15 | been reported. 16 | required: true 17 | - label: 18 | If this is related to a typo or the documentation being unclear, 19 | please click on the relevant page's `Edit` button (pencil icon) and 20 | suggest a correction instead. 21 | required: true 22 | - type: textarea 23 | attributes: 24 | label: Describe the bug 25 | placeholder: A clear and concise description of what the bug is. 26 | validations: 27 | required: true 28 | - type: textarea 29 | attributes: 30 | label: Steps to reproduce 31 | placeholder: | 32 | Step-by-step instructions on how to reproduce the behavior. 33 | Example: 34 | 1. Type the following command: [...] 35 | 2. etc. 36 | validations: 37 | required: true 38 | - type: textarea 39 | attributes: 40 | label: Configuration 41 | placeholder: Command line options and/or configuration file, if any. 42 | validations: 43 | required: true 44 | - type: textarea 45 | attributes: 46 | label: Environment 47 | description: | 48 | Enter the following command in a terminal and copy/paste its output: 49 | ```bash 50 | npx envinfo --system --binaries --browsers --npmPackages modern-errors-http 51 | ``` 52 | validations: 53 | required: true 54 | - type: checkboxes 55 | attributes: 56 | label: Pull request (optional) 57 | description: 58 | Pull requests are welcome! If you would like to help us fix this bug, 59 | please check our [contributions 60 | guidelines](../blob/main/CONTRIBUTING.md). 61 | options: 62 | - label: I can submit a pull request. 63 | required: false 64 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_types.yml: -------------------------------------------------------------------------------- 1 | name: Bug report (TypeScript types) 2 | description: Report a bug about TypeScript types 3 | title: Please replace with a clear and descriptive title 4 | labels: [bug] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: Thanks for reporting this bug! 9 | - type: checkboxes 10 | attributes: 11 | label: Guidelines 12 | options: 13 | - label: 14 | Please search other issues to make sure this bug has not already 15 | been reported. 16 | required: true 17 | - type: textarea 18 | attributes: 19 | label: Describe the bug 20 | placeholder: A clear and concise description of what the bug is. 21 | validations: 22 | required: true 23 | - type: textarea 24 | attributes: 25 | label: Steps to reproduce 26 | description: | 27 | Please reproduce the bug using the [TypeScript playground](https://www.typescriptlang.org/play) or [Bug workbench](https://www.typescriptlang.org/dev/bug-workbench), then paste the URL here. 28 | validations: 29 | required: true 30 | - type: textarea 31 | attributes: 32 | label: Environment 33 | description: | 34 | Enter the following command in a terminal and copy/paste its output: 35 | ```bash 36 | npx envinfo --system --binaries --browsers --npmPackages modern-errors-http,typescript --npmGlobalPackages typescript 37 | ``` 38 | validations: 39 | required: true 40 | - type: checkboxes 41 | attributes: 42 | label: Pull request (optional) 43 | description: 44 | Pull requests are welcome! If you would like to help us fix this bug, 45 | please check our [contributions 46 | guidelines](../blob/main/CONTRIBUTING.md). 47 | options: 48 | - label: I can submit a pull request. 49 | required: false 50 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Suggest an idea for this project 3 | title: Please replace with a clear and descriptive title 4 | labels: [enhancement] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: Thanks for suggesting a new feature! 9 | - type: checkboxes 10 | attributes: 11 | label: Guidelines 12 | options: 13 | - label: 14 | Please search other issues to make sure this feature has not already 15 | been requested. 16 | required: true 17 | - type: textarea 18 | attributes: 19 | label: Which problem is this feature request solving? 20 | placeholder: I'm always frustrated when [...] 21 | validations: 22 | required: true 23 | - type: textarea 24 | attributes: 25 | label: Describe the solution you'd like 26 | placeholder: This could be fixed by [...] 27 | validations: 28 | required: true 29 | - type: checkboxes 30 | attributes: 31 | label: Pull request (optional) 32 | description: 33 | Pull requests are welcome! If you would like to help us fix this bug, 34 | please check our [contributions 35 | guidelines](../blob/main/CONTRIBUTING.md). 36 | options: 37 | - label: I can submit a pull request. 38 | required: false 39 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 🎉 Thanks for sending this pull request! 🎉 2 | 3 | Please make sure the title is clear and descriptive. 4 | 5 | If you are fixing a typo or documentation, please skip these instructions. 6 | 7 | Otherwise please fill in the sections below. 8 | 9 | **Which problem is this pull request solving?** 10 | 11 | Example: I'm always frustrated when [...] 12 | 13 | **List other issues or pull requests related to this problem** 14 | 15 | Example: This fixes #5012 16 | 17 | **Describe the solution you've chosen** 18 | 19 | Example: I've fixed this by [...] 20 | 21 | **Describe alternatives you've considered** 22 | 23 | Example: Another solution would be [...] 24 | 25 | **Checklist** 26 | 27 | Please add a `x` inside each checkbox: 28 | 29 | - [ ] I have read the [contribution guidelines](../blob/main/CONTRIBUTING.md). 30 | - [ ] I have added tests (we are enforcing 100% test coverage). 31 | - [ ] I have added documentation in the `README.md`, the `docs` directory (if 32 | any) 33 | - [ ] The status checks are successful (continuous integration). Those can be 34 | seen below. 35 | -------------------------------------------------------------------------------- /.github/workflows/workflow.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [push, pull_request] 3 | jobs: 4 | combinations: 5 | uses: ehmicky/dev-tasks/.github/workflows/build.yml@main 6 | secrets: inherit 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | npm-debug.log 4 | node_modules 5 | /core 6 | .eslintcache 7 | .lycheecache 8 | .npmrc 9 | .yarn-error.log 10 | !.github/ 11 | /coverage 12 | /build 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 5.0.1 2 | 3 | ## Documentation 4 | 5 | - Improve documentation in `README.md` 6 | 7 | # 5.0.0 8 | 9 | ## Breaking changes 10 | 11 | - Minimal supported Node.js version is now `18.18.0` 12 | 13 | # 4.0.0 14 | 15 | ## Breaking changes 16 | 17 | - Minimal supported `modern-errors` version is now `6.0.0` 18 | 19 | # 3.0.0 20 | 21 | ## Breaking changes 22 | 23 | - Minimal supported Node.js version is now `16.17.0` 24 | 25 | # 2.3.0 26 | 27 | ## Features 28 | 29 | - Split core logic to a separate module 30 | [`error-http-response`](https://github.com/ehmicky/error-http-response) 31 | 32 | # 2.2.0 33 | 34 | ## Features 35 | 36 | - `error.httpResponse()` has been renamed to 37 | [`BaseError.httpResponse(error)`](README.md#baseerrorhttpresponseerror). 38 | `error.httpResponse()` is deprecated but still supported. 39 | 40 | # 2.1.0 41 | 42 | ## Features 43 | 44 | - Upgrade to the latest version of `modern-errors` 45 | 46 | # 2.0.0 47 | 48 | ## Breaking changes 49 | 50 | - [`modern-errors@5`](https://github.com/ehmicky/modern-errors/releases/tag/5.0.0) 51 | is now required 52 | 53 | # 1.5.0 54 | 55 | ## Features 56 | 57 | - Improve tree-shaking support 58 | 59 | # 1.4.0 60 | 61 | ## Features 62 | 63 | - Add browser support 64 | 65 | # 1.3.0 66 | 67 | ## Features 68 | 69 | - Decrease package size 70 | 71 | # 1.2.0 72 | 73 | ## Features 74 | 75 | - Upgrade to 76 | [`modern-errors@v4`](https://github.com/ehmicky/modern-errors/releases/tag/4.0.0) 77 | 78 | # 1.1.0 79 | 80 | ## Documentation 81 | 82 | Improve `README`. 83 | 84 | # 1.0.0 85 | 86 | Initial release. 87 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | This text is available in 4 | [many other languages](https://www.contributor-covenant.org/translations). 5 | 6 | ## Our Pledge 7 | 8 | We as members, contributors, and leaders pledge to make participation in our 9 | community a harassment-free experience for everyone, regardless of age, body 10 | size, visible or invisible disability, ethnicity, sex characteristics, gender 11 | identity and expression, level of experience, education, socio-economic status, 12 | nationality, personal appearance, race, religion, or sexual identity and 13 | orientation. 14 | 15 | We pledge to act and interact in ways that contribute to an open, welcoming, 16 | diverse, inclusive, and healthy community. 17 | 18 | ## Our Standards 19 | 20 | Examples of behavior that contributes to a positive environment for our 21 | community include: 22 | 23 | - Demonstrating empathy and kindness toward other people 24 | - Being respectful of differing opinions, viewpoints, and experiences 25 | - Giving and gracefully accepting constructive feedback 26 | - Accepting responsibility and apologizing to those affected by our mistakes, 27 | and learning from the experience 28 | - Focusing on what is best not just for us as individuals, but for the overall 29 | community 30 | 31 | Examples of unacceptable behavior include: 32 | 33 | - The use of sexualized language or imagery, and sexual attention or advances of 34 | any kind 35 | - Trolling, insulting or derogatory comments, and personal or political attacks 36 | - Public or private harassment 37 | - Publishing others' private information, such as a physical or email address, 38 | without their explicit permission 39 | - Other conduct which could reasonably be considered inappropriate in a 40 | professional setting 41 | 42 | ## Enforcement Responsibilities 43 | 44 | Community leaders are responsible for clarifying and enforcing our standards of 45 | acceptable behavior and will take appropriate and fair corrective action in 46 | response to any behavior that they deem inappropriate, threatening, offensive, 47 | or harmful. 48 | 49 | Community leaders have the right and responsibility to remove, edit, or reject 50 | comments, commits, code, wiki edits, issues, and other contributions that are 51 | not aligned to this Code of Conduct, and will communicate reasons for moderation 52 | decisions when appropriate. 53 | 54 | ## Scope 55 | 56 | This Code of Conduct applies within all community spaces, and also applies when 57 | an individual is officially representing the community in public spaces. 58 | Examples of representing our community include using an official e-mail address, 59 | posting via an official social media account, or acting as an appointed 60 | representative at an online or offline event. 61 | 62 | ## Enforcement 63 | 64 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 65 | reported to the community leaders responsible for enforcement at 66 | ehmicky+report@gmail.com All complaints will be reviewed and investigated 67 | promptly and fairly. 68 | 69 | All community leaders are obligated to respect the privacy and security of the 70 | reporter of any incident. 71 | 72 | ## Enforcement Guidelines 73 | 74 | Community leaders will follow these Community Impact Guidelines in determining 75 | the consequences for any action they deem in violation of this Code of Conduct: 76 | 77 | ### 1. Correction 78 | 79 | **Community Impact**: Use of inappropriate language or other behavior deemed 80 | unprofessional or unwelcome in the community. 81 | 82 | **Consequence**: A private, written warning from community leaders, providing 83 | clarity around the nature of the violation and an explanation of why the 84 | behavior was inappropriate. A public apology may be requested. 85 | 86 | ### 2. Warning 87 | 88 | **Community Impact**: A violation through a single incident or series of 89 | actions. 90 | 91 | **Consequence**: A warning with consequences for continued behavior. No 92 | interaction with the people involved, including unsolicited interaction with 93 | those enforcing the Code of Conduct, for a specified period of time. This 94 | includes avoiding interactions in community spaces as well as external channels 95 | like social media. Violating these terms may lead to a temporary or permanent 96 | ban. 97 | 98 | ### 3. Temporary Ban 99 | 100 | **Community Impact**: A serious violation of community standards, including 101 | sustained inappropriate behavior. 102 | 103 | **Consequence**: A temporary ban from any sort of interaction or public 104 | communication with the community for a specified period of time. No public or 105 | private interaction with the people involved, including unsolicited interaction 106 | with those enforcing the Code of Conduct, is allowed during this period. 107 | Violating these terms may lead to a permanent ban. 108 | 109 | ### 4. Permanent Ban 110 | 111 | **Community Impact**: Demonstrating a pattern of violation of community 112 | standards, including sustained inappropriate behavior, harassment of an 113 | individual, or aggression toward or disparagement of classes of individuals. 114 | 115 | **Consequence**: A permanent ban from any sort of public interaction within the 116 | community. 117 | 118 | ## Attribution 119 | 120 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 121 | version 2.0, available at 122 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 123 | 124 | Community Impact Guidelines were inspired by 125 | [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). 126 | 127 | [homepage]: https://www.contributor-covenant.org 128 | 129 | For answers to common questions about this code of conduct, see the FAQ at 130 | https://www.contributor-covenant.org/faq. Translations are available at 131 | https://www.contributor-covenant.org/translations. 132 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributions 2 | 3 | 🎉 Thanks for considering contributing to this project! 🎉 4 | 5 | These guidelines will help you send a pull request. 6 | 7 | If you're submitting an issue instead, please skip this document. 8 | 9 | If your pull request is related to a typo or the documentation being unclear, 10 | please click on the relevant page's `Edit` button (pencil icon) and directly 11 | suggest a correction instead. 12 | 13 | This project was made with ❤️. The simplest way to give back is by starring and 14 | sharing it online. 15 | 16 | Everyone is welcome regardless of personal background. We enforce a 17 | [Code of conduct](CODE_OF_CONDUCT.md) in order to promote a positive and 18 | inclusive environment. 19 | 20 | # Development process 21 | 22 | First fork and clone the repository. If you're not sure how to do this, please 23 | watch 24 | [these videos](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github). 25 | 26 | Run: 27 | 28 | ```bash 29 | npm install 30 | ``` 31 | 32 | Make sure everything is correctly setup with: 33 | 34 | ```bash 35 | npm test 36 | ``` 37 | 38 | We use Gulp tasks to lint, test and build this project. Please check 39 | [dev-tasks](https://github.com/ehmicky/dev-tasks/blob/main/README.md) to learn 40 | how to use them. You don't need to know Gulp to use these tasks. 41 | 42 | # Requirements 43 | 44 | Our coding style is documented 45 | [here](https://github.com/ehmicky/eslint-config#coding-style). Linting and 46 | formatting should automatically handle it though. 47 | 48 | After submitting the pull request, please make sure the Continuous Integration 49 | checks are passing. 50 | 51 | We enforce 100% test coverage: each line of code must be tested. 52 | 53 | New options, methods, properties, configuration and behavior must be documented 54 | in all of these: 55 | 56 | - the `README.md` 57 | - the `docs` directory (if any) 58 | 59 | Please use the same style as the rest of the documentation and examples. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2025 ehmicky 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the “Software”), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | modern-errors logo 4 | 5 | 6 | [![Node](https://img.shields.io/badge/-Node.js-808080?logo=node.js&colorA=404040&logoColor=66cc33)](https://www.npmjs.com/package/modern-errors-http) 7 | [![Browsers](https://img.shields.io/badge/-Browsers-808080?logo=firefox&colorA=404040)](https://unpkg.com/modern-errors-http?module) 8 | [![TypeScript](https://img.shields.io/badge/-Typed-808080?logo=typescript&colorA=404040&logoColor=0096ff)](/src/main.d.ts) 9 | [![Codecov](https://img.shields.io/badge/-Tested%20100%25-808080?logo=codecov&colorA=404040)](https://codecov.io/gh/ehmicky/modern-errors-http) 10 | [![Minified size](https://img.shields.io/bundlephobia/minzip/modern-errors-http?label&colorA=404040&colorB=808080&logo=webpack)](https://bundlephobia.com/package/modern-errors-http) 11 | [![Mastodon](https://img.shields.io/badge/-Mastodon-808080.svg?logo=mastodon&colorA=404040&logoColor=9590F9)](https://fosstodon.org/@ehmicky) 12 | [![Medium](https://img.shields.io/badge/-Medium-808080.svg?logo=medium&colorA=404040)](https://medium.com/@ehmicky) 13 | 14 | [`modern-errors`](https://github.com/ehmicky/modern-errors) 15 | [plugin](https://github.com/ehmicky/modern-errors#-plugins) to create HTTP error 16 | responses. 17 | 18 | This adds [`BaseError.httpResponse(error)`](#baseerrorhttpresponseerror) which 19 | converts `error` to a plain object 20 | ([RFC 7807](https://www.rfc-editor.org/rfc/rfc7807), "problem details") to use 21 | in an HTTP response. 22 | 23 | # Example 24 | 25 | [Adding the plugin](https://github.com/ehmicky/modern-errors#adding-plugins) to 26 | [`modern-errors`](https://github.com/ehmicky/modern-errors). 27 | 28 | ```js 29 | import ModernError from 'modern-errors' 30 | 31 | import modernErrorsHttp from 'modern-errors-http' 32 | 33 | export const BaseError = ModernError.subclass('BaseError', { 34 | plugins: [modernErrorsHttp], 35 | }) 36 | ``` 37 | 38 | [Configuring](#configuration) error fields. 39 | 40 | ```js 41 | export const AuthError = BaseError.subclass('AuthError', { 42 | http: { 43 | type: 'https://example.com/probs/auth', 44 | status: 401, 45 | }, 46 | }) 47 | ``` 48 | 49 | [Creating](#baseerrorhttpresponseerror) an HTTP error response. 50 | 51 | ```js 52 | const error = new AuthError('Could not authenticate.', { 53 | http: { 54 | instance: '/users/62', 55 | extra: { userId: 62 }, 56 | }, 57 | }) 58 | const object = BaseError.httpResponse(error) 59 | // { 60 | // type: 'https://example.com/probs/auth', 61 | // status: 401, 62 | // title: 'AuthError', 63 | // detail: 'Could not authenticate.', 64 | // instance: '/users/62', 65 | // stack: `AuthError: Could not authenticate. 66 | // at ...`, 67 | // extra: { userId: 62 }, 68 | // } 69 | ``` 70 | 71 | # Install 72 | 73 | ```bash 74 | npm install modern-errors-http 75 | ``` 76 | 77 | This package works in both Node.js >=18.18.0 and 78 | [browsers](https://raw.githubusercontent.com/ehmicky/dev-tasks/main/src/browserslist). 79 | 80 | This is an ES module. It must be loaded using 81 | [an `import` or `import()` statement](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c), 82 | not `require()`. If TypeScript is used, it must be configured to 83 | [output ES modules](https://www.typescriptlang.org/docs/handbook/esm-node.html), 84 | not CommonJS. 85 | 86 | # API 87 | 88 | ## modernErrorsHttp 89 | 90 | _Type_: `Plugin` 91 | 92 | Plugin object to pass to the 93 | [`plugins` option](https://github.com/ehmicky/modern-errors#adding-plugins) of 94 | `ErrorClass.subclass()`. 95 | 96 | ## BaseError.httpResponse(error) 97 | 98 | `error`: `Error`\ 99 | _Return value_: `HttpResponse` 100 | 101 | Converts `error` to a plain object to use in an HTTP response. Its shape follows 102 | [RFC 7807](https://www.rfc-editor.org/rfc/rfc7807) ("problem details"). 103 | 104 | ## Options 105 | 106 | _Type_: `object` 107 | 108 | ### type 109 | 110 | _Type_: `urlString`\ 111 | _Default_: `undefined` 112 | 113 | URI identifying and documenting the error class. Ideally, each error class 114 | [should set one](#configuration). 115 | 116 | ### status 117 | 118 | _Type_: `integer`\ 119 | _Default_: `undefined` 120 | 121 | HTTP status code. 122 | 123 | ### title 124 | 125 | _Type_: `string`\ 126 | _Default_: `error.name` 127 | 128 | Error class name. 129 | 130 | ### detail 131 | 132 | _Type_: `string`\ 133 | _Default_: `error.message` 134 | 135 | Error description. 136 | 137 | ### instance 138 | 139 | _Type_: `urlString`\ 140 | _Default_: `undefined` 141 | 142 | URI identifying the value which errored. 143 | 144 | ### stack 145 | 146 | _Type_: `string`\ 147 | _Default_: `error.stack` 148 | 149 | Error stack trace. Can be set to an empty string. 150 | 151 | ### extra 152 | 153 | _Type_: `object`\ 154 | _Default_: any additional `error` properties 155 | 156 | Additional information. This is always 157 | [safe to serialize as JSON](https://github.com/ehmicky/safe-json-value). Can be 158 | set to an empty object. 159 | 160 | ## Configuration 161 | 162 | [Options](#options) can apply to (in priority order): 163 | 164 | - Any error: second argument to 165 | [`ModernError.subclass()`](https://github.com/ehmicky/modern-errors#options-1) 166 | 167 | ```js 168 | export const BaseError = ModernError.subclass('BaseError', { 169 | plugins: [modernErrorsHttp], 170 | http: options, 171 | }) 172 | ``` 173 | 174 | - Any error of a specific class (and its subclasses): second argument to 175 | [`ErrorClass.subclass()`](https://github.com/ehmicky/modern-errors#options-1) 176 | 177 | ```js 178 | export const AuthError = BaseError.subclass('AuthError', { http: options }) 179 | ``` 180 | 181 | - A specific error: second argument to 182 | [`new ErrorClass()`](https://github.com/ehmicky/modern-errors#options-3) 183 | 184 | ```js 185 | throw new AuthError('...', { http: options }) 186 | ``` 187 | 188 | - A specific [`BaseError.httpResponse(error)`](#baseerrorhttpresponseerror) call 189 | 190 | ```js 191 | BaseError.httpResponse(error, options) 192 | ``` 193 | 194 | # Related projects 195 | 196 | - [`error-http-response`](https://github.com/ehmicky/error-http-response): 197 | Create HTTP error responses 198 | - [`safe-json-value`](https://github.com/ehmicky/safe-json-value): ⛑️ JSON 199 | serialization should never fail 200 | - [`modern-errors`](https://github.com/ehmicky/modern-errors): Handle errors in 201 | a simple, stable, consistent way 202 | - [`modern-errors-cli`](https://github.com/ehmicky/modern-errors-cli): Handle 203 | errors in CLI modules 204 | - [`modern-errors-beautiful`](https://github.com/ehmicky/modern-errors-beautiful): 205 | Prettify errors messages and stacks 206 | - [`modern-errors-process`](https://github.com/ehmicky/modern-errors-process): 207 | Handle process errors 208 | - [`modern-errors-bugs`](https://github.com/ehmicky/modern-errors-bugs): Print 209 | where to report bugs 210 | - [`modern-errors-serialize`](https://github.com/ehmicky/modern-errors-serialize): 211 | Serialize/parse errors 212 | - [`modern-errors-clean`](https://github.com/ehmicky/modern-errors-clean): Clean 213 | stack traces 214 | - [`modern-errors-winston`](https://github.com/ehmicky/modern-errors-winston): 215 | Log errors with Winston 216 | - [`modern-errors-switch`](https://github.com/ehmicky/modern-errors-switch): 217 | Execute class-specific logic 218 | 219 | # Support 220 | 221 | For any question, _don't hesitate_ to [submit an issue on GitHub](../../issues). 222 | 223 | Everyone is welcome regardless of personal background. We enforce a 224 | [Code of conduct](CODE_OF_CONDUCT.md) in order to promote a positive and 225 | inclusive environment. 226 | 227 | # Contributing 228 | 229 | This project was made with ❤️. The simplest way to give back is by starring and 230 | sharing it online. 231 | 232 | If the documentation is unclear or has a typo, please click on the page's `Edit` 233 | button (pencil icon) and suggest a correction. 234 | 235 | If you would like to help us fix a bug or add a new feature, please check our 236 | [guidelines](CONTRIBUTING.md). Pull requests are welcome! 237 | 238 | 239 | 240 | 241 | 242 | 245 | 246 | -------------------------------------------------------------------------------- /ava.config.js: -------------------------------------------------------------------------------- 1 | export { default } from '@ehmicky/dev-tasks/ava.config.js' 2 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | export { default } from '@ehmicky/eslint-config' 2 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | export * from '@ehmicky/dev-tasks' 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "modern-errors-http", 3 | "version": "5.0.1", 4 | "type": "module", 5 | "exports": { 6 | "types": "./build/src/main.d.ts", 7 | "default": "./build/src/main.js" 8 | }, 9 | "main": "./build/src/main.js", 10 | "types": "./build/src/main.d.ts", 11 | "files": [ 12 | "build/src/**/*.{js,json,d.ts}", 13 | "!build/src/**/*.test.js", 14 | "!build/src/{helpers,fixtures}" 15 | ], 16 | "sideEffects": false, 17 | "scripts": { 18 | "test": "gulp test" 19 | }, 20 | "description": "`modern-errors` plugin to create HTTP error responses", 21 | "keywords": [ 22 | "browser", 23 | "error", 24 | "error-handler", 25 | "error-handling", 26 | "error-monitoring", 27 | "http", 28 | "javascript", 29 | "library", 30 | "modern-errors", 31 | "modern-errors-plugin", 32 | "nodejs", 33 | "plugins", 34 | "problem-details", 35 | "rest", 36 | "rest-api", 37 | "rfc", 38 | "rfc-7807", 39 | "stack", 40 | "stacktrace", 41 | "typescript" 42 | ], 43 | "license": "MIT", 44 | "homepage": "https://www.github.com/ehmicky/modern-errors-http", 45 | "repository": { 46 | "type": "git", 47 | "url": "git+https://github.com/ehmicky/modern-errors-http.git" 48 | }, 49 | "bugs": { 50 | "url": "https://github.com/ehmicky/modern-errors-http/issues" 51 | }, 52 | "author": "ehmicky (https://github.com/ehmicky)", 53 | "directories": { 54 | "lib": "src" 55 | }, 56 | "dependencies": { 57 | "error-http-response": "^3.0.1" 58 | }, 59 | "devDependencies": { 60 | "@ehmicky/dev-tasks": "^3.0.34", 61 | "@ehmicky/eslint-config": "^20.0.32", 62 | "@ehmicky/prettier-config": "^1.0.6" 63 | }, 64 | "peerDependencies": { 65 | "modern-errors": "^7.1.2" 66 | }, 67 | "engines": { 68 | "node": ">=18.18.0" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | export { default } from '@ehmicky/prettier-config' 2 | -------------------------------------------------------------------------------- /src/main.d.ts: -------------------------------------------------------------------------------- 1 | import type { HttpResponse, Options } from 'error-http-response' 2 | import type { Info } from 'modern-errors' 3 | 4 | export type { HttpResponse, Options } 5 | 6 | /** 7 | * `modern-errors-http` plugin 8 | */ 9 | declare const plugin: { 10 | name: 'http' 11 | getOptions: (input: Options) => Options 12 | instanceMethods: { 13 | /** 14 | * Converts `error` to a plain object to use in an HTTP response. 15 | * Its shape follows [RFC 7807](https://www.rfc-editor.org/rfc/rfc7807) 16 | * ("problem details"). 17 | * 18 | * @example 19 | * ```js 20 | * const object = BaseError.httpResponse(error) 21 | * // { 22 | * // type: 'https://example.com/probs/auth', 23 | * // status: 401, 24 | * // title: 'AuthError', 25 | * // detail: 'Could not authenticate.', 26 | * // instance: '/users/62', 27 | * // stack: `AuthError: Could not authenticate. 28 | * // at ...`, 29 | * // extra: { userId: 62 }, 30 | * // } 31 | * ``` 32 | */ 33 | httpResponse: (info: Info['staticMethods']) => HttpResponse 34 | } 35 | } 36 | export default plugin 37 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import errorHttpResponse from 'error-http-response' 2 | 3 | // Validate options 4 | const getOptions = (options) => { 5 | errorHttpResponse('', options) 6 | return options 7 | } 8 | 9 | // Turn `error` into a RFC 7807 problem details object 10 | const httpResponse = ({ error, options }) => errorHttpResponse(error, options) 11 | 12 | export default { 13 | name: 'http', 14 | getOptions, 15 | instanceMethods: { httpResponse }, 16 | } 17 | -------------------------------------------------------------------------------- /src/main.test-d.ts: -------------------------------------------------------------------------------- 1 | import ModernError from 'modern-errors' 2 | import { expectAssignable, expectNotAssignable, expectType } from 'tsd' 3 | 4 | import plugin, { type HttpResponse, type Options } from 'modern-errors-http' 5 | 6 | const BaseError = ModernError.subclass('BaseError', { plugins: [plugin] }) 7 | const error = new BaseError('') 8 | const httpResponse = BaseError.httpResponse(error) 9 | 10 | ModernError.subclass('TestError', { plugins: [plugin], http: {} }) 11 | BaseError.httpResponse(error, {}) 12 | expectAssignable({}) 13 | // @ts-expect-error 14 | BaseError.httpResponse(error, undefined) 15 | expectNotAssignable(undefined) 16 | // @ts-expect-error 17 | ModernError.subclass('TestError', { plugins: [plugin], http: true }) 18 | // @ts-expect-error 19 | BaseError.httpResponse(error, true) 20 | expectNotAssignable(true) 21 | ModernError.subclass('TestError', { 22 | plugins: [plugin], 23 | // @ts-expect-error 24 | http: { unknown: true }, 25 | }) 26 | // @ts-expect-error 27 | BaseError.httpResponse(error, { unknown: true }) 28 | expectNotAssignable({ unknown: true }) 29 | 30 | ModernError.subclass('TestError', { plugins: [plugin], http: { title: '' } }) 31 | BaseError.httpResponse(error, { title: '' }) 32 | expectAssignable({ title: '' }) 33 | ModernError.subclass('TestError', { 34 | plugins: [plugin], 35 | // @ts-expect-error 36 | http: { title: true }, 37 | }) 38 | // @ts-expect-error 39 | BaseError.httpResponse(error, { title: true }) 40 | expectNotAssignable({ title: true }) 41 | 42 | expectType(httpResponse) 43 | expectType(httpResponse.title) 44 | 45 | expectType(error.httpResponse()) 46 | // @ts-expect-error 47 | error.httpResponse(true) 48 | -------------------------------------------------------------------------------- /src/main.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import ModernError from 'modern-errors' 3 | 4 | import modernErrorsHttp from 'modern-errors-http' 5 | 6 | export const BaseError = ModernError.subclass('BaseError', { 7 | plugins: [modernErrorsHttp], 8 | }) 9 | export const baseError = new BaseError('test') 10 | 11 | test('Options are validated', (t) => { 12 | t.throws(BaseError.httpResponse.bind(undefined, baseError, { title: true })) 13 | }) 14 | 15 | test('Valid options are kept', (t) => { 16 | t.is( 17 | BaseError.httpResponse(baseError, { title: 'testTitle' }).title, 18 | 'testTitle', 19 | ) 20 | }) 21 | 22 | test('Assign default options', (t) => { 23 | t.deepEqual(BaseError.httpResponse(baseError), { 24 | title: baseError.name, 25 | detail: baseError.message, 26 | stack: baseError.stack, 27 | }) 28 | }) 29 | 30 | test('Can be called as error.httpResponse()', (t) => { 31 | t.is(BaseError.httpResponse(baseError).title, baseError.name) 32 | }) 33 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@ehmicky/dev-tasks/tsconfig.json" 3 | } 4 | --------------------------------------------------------------------------------