├── .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 |
4 |
5 |
6 | [](https://www.npmjs.com/package/modern-errors-http)
7 | [](https://unpkg.com/modern-errors-http?module)
8 | [](/src/main.d.ts)
9 | [](https://codecov.io/gh/ehmicky/modern-errors-http)
10 | [](https://bundlephobia.com/package/modern-errors-http)
11 | [](https://fosstodon.org/@ehmicky)
12 | [](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 |
--------------------------------------------------------------------------------