├── .editorconfig ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── main.yml │ └── pull_request.yml ├── .gitignore ├── .npmignore ├── .npmrc ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── examples ├── handle-errors.js └── prototypes.js ├── package.json ├── src └── index.js └── test └── index.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | max_line_length = 100 13 | indent_brace_style = 1TBS 14 | spaces_around_operators = true 15 | quote_type = auto 16 | 17 | [package.json] 18 | indent_style = space 19 | indent_size = 2 20 | 21 | [*.md] 22 | trim_trailing_whitespace = false 23 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: '/' 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: 'github-actions' 8 | directory: '/' 9 | schedule: 10 | # Check for updates to GitHub Actions every weekday 11 | interval: 'daily' 12 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: main 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | contributors: 10 | if: "${{ github.event.head_commit.message != 'build: contributors' }}" 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | token: ${{ secrets.GITHUB_TOKEN }} 18 | - name: Setup Node.js 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version: lts/* 22 | - name: Contributors 23 | run: | 24 | git config --global user.email ${{ secrets.GIT_EMAIL }} 25 | git config --global user.name ${{ secrets.GIT_USERNAME }} 26 | npm run contributors 27 | - name: Push changes 28 | run: | 29 | git push origin ${{ github.head_ref }} 30 | 31 | release: 32 | if: | 33 | !startsWith(github.event.head_commit.message, 'chore(release):') && 34 | !startsWith(github.event.head_commit.message, 'docs:') && 35 | !startsWith(github.event.head_commit.message, 'ci:') 36 | needs: [contributors] 37 | runs-on: ubuntu-latest 38 | steps: 39 | - name: Checkout 40 | uses: actions/checkout@v4 41 | with: 42 | fetch-depth: 2 43 | token: ${{ secrets.GITHUB_TOKEN }} 44 | - name: Setup Node.js 45 | uses: actions/setup-node@v4 46 | with: 47 | node-version: lts/* 48 | - name: Setup PNPM 49 | uses: pnpm/action-setup@v4 50 | with: 51 | version: latest 52 | run_install: true 53 | - name: Test 54 | run: npm test 55 | - name: Report 56 | run: npx c8 report --reporter=text-lcov > coverage/lcov.info 57 | - name: Coverage 58 | uses: coverallsapp/github-action@main 59 | with: 60 | github-token: ${{ secrets.GITHUB_TOKEN }} 61 | - name: Release 62 | env: 63 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 64 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 65 | run: | 66 | git config --global user.email ${{ secrets.GIT_EMAIL }} 67 | git config --global user.name ${{ secrets.GIT_USERNAME }} 68 | git pull origin master 69 | npm run release 70 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: pull_request 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | test: 13 | if: github.ref != 'refs/heads/master' 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | with: 19 | token: ${{ secrets.GITHUB_TOKEN }} 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: lts/* 24 | - name: Setup PNPM 25 | uses: pnpm/action-setup@v4 26 | with: 27 | version: latest 28 | run_install: true 29 | - name: Test 30 | run: npm test 31 | - name: Report 32 | run: npx c8 report --reporter=text-lcov > coverage/lcov.info 33 | - name: Coverage 34 | uses: coverallsapp/github-action@main 35 | with: 36 | github-token: ${{ secrets.GITHUB_TOKEN }} 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ############################ 2 | # npm 3 | ############################ 4 | node_modules 5 | npm-debug.log 6 | 7 | 8 | ############################ 9 | # tmp, editor & OS files 10 | ############################ 11 | .tmp 12 | *.swo 13 | *.swp 14 | *.swn 15 | *.swm 16 | .DS_STORE 17 | *# 18 | *~ 19 | .idea 20 | nbproject 21 | 22 | 23 | ############################ 24 | # Tests 25 | ############################ 26 | testApp 27 | coverage 28 | .nyc_output 29 | 30 | ############################ 31 | # Other 32 | ############################ 33 | .node_history 34 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .project 3 | *.sublime-* 4 | .DS_Store 5 | *.seed 6 | *.log 7 | *.csv 8 | *.dat 9 | *.out 10 | *.pid 11 | *.swp 12 | *.swo 13 | node_modules 14 | coverage 15 | *.tgz 16 | *.xml 17 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | unsafe-perm=true 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### 5.0.1 (2025-01-28) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * merging props ([#47](https://github.com/kikobeats/whoops/issues/47)) ([e9e24d6](https://github.com/kikobeats/whoops/commit/e9e24d6c1ad2d9cd178c056bfd09889216af0d7f)) 11 | 12 | ## 5.0.0 (2025-01-28) 13 | 14 | 15 | ### ⚠ BREAKING CHANGES 16 | 17 | * simplify implementation (#46) 18 | 19 | ### Features 20 | 21 | * simplify implementation ([#46](https://github.com/kikobeats/whoops/issues/46)) ([86f8d7a](https://github.com/kikobeats/whoops/commit/86f8d7a124c343cee2c790af7f5a51fd73b0a171)) 22 | 23 | ### 4.1.8 (2025-01-27) 24 | 25 | ### 4.1.7 (2023-10-29) 26 | 27 | ### 4.1.6 (2023-10-24) 28 | 29 | ### 4.1.5 (2023-09-05) 30 | 31 | ### 4.1.4 (2023-04-14) 32 | 33 | ### 4.1.3 (2023-04-14) 34 | 35 | 36 | ### Bug Fixes 37 | 38 | * **extend-error:** handle undefined error#stack ([#42](https://github.com/kikobeats/whoops/issues/42)) ([2dc0cd1](https://github.com/kikobeats/whoops/commit/2dc0cd156d71d6b5e51958627f766a3b6ca9048b)) 39 | 40 | ### 4.1.2 (2022-07-20) 41 | 42 | ### 4.1.1 (2022-05-27) 43 | 44 | 45 | ### Bug Fixes 46 | 47 | * add nano-staged config ([f741480](https://github.com/kikobeats/whoops/commit/f741480cd7fb486651fbe01b911289149f10b030)) 48 | 49 | ## 4.1.0 (2019-12-15) 50 | 51 | * build: update dependencies ([af7e392](https://github.com/kikobeats/whoops/commit/af7e392)) 52 | * fix(package): update clean-stack to version 2.1.0 ([3275e3c](https://github.com/kikobeats/whoops/commit/3275e3c)) 53 | * fix(package): update mimic-fn to version 2.1.0 ([db95daa](https://github.com/kikobeats/whoops/commit/db95daa)) 54 | * fix(package): update mimic-fn to version 3.0.0 ([e1cc912](https://github.com/kikobeats/whoops/commit/e1cc912)) 55 | 56 | 57 | 58 | 59 | ## 4.0.2 (2019-03-08) 60 | 61 | * fix(package): update clean-stack to version 2.0.0 ([24952ae](https://github.com/kikobeats/whoops/commit/24952ae)) 62 | * fix(package): update mimic-fn to version 1.2.0 ([719fc26](https://github.com/kikobeats/whoops/commit/719fc26)) 63 | * fix(package): update mimic-fn to version 2.0.0 ([d5c78a1](https://github.com/kikobeats/whoops/commit/d5c78a1)) 64 | * Refactor ([64f751b](https://github.com/kikobeats/whoops/commit/64f751b)) 65 | * Update compositor.json ([e26b0fd](https://github.com/kikobeats/whoops/commit/e26b0fd)) 66 | * Update compositor.json ([da98ea7](https://github.com/kikobeats/whoops/commit/da98ea7)) 67 | * Update README.md ([0228c84](https://github.com/kikobeats/whoops/commit/0228c84)) 68 | * docs(readme): add Greenkeeper badge ([4e1a125](https://github.com/kikobeats/whoops/commit/4e1a125)) 69 | 70 | 71 | 72 | 73 | ## 4.0.1 (2017-08-08) 74 | 75 | * Reducing library size ([ce26a2d](https://github.com/kikobeats/whoops/commit/ce26a2d)) 76 | * Update README.md ([2e71ec2](https://github.com/kikobeats/whoops/commit/2e71ec2)) 77 | 78 | 79 | 80 | 81 | # 4.0.0 (2017-08-08) 82 | 83 | * refactor ([ca6e60e](https://github.com/kikobeats/whoops/commit/ca6e60e)) 84 | * Remove unnecessasry ([6ec4220](https://github.com/kikobeats/whoops/commit/6ec4220)) 85 | * Support attach props into constructor ([8dd67b9](https://github.com/kikobeats/whoops/commit/8dd67b9)) 86 | * Update builds ([6e6275c](https://github.com/kikobeats/whoops/commit/6e6275c)) 87 | * Update docs ([515c498](https://github.com/kikobeats/whoops/commit/515c498)) 88 | * Update README.md ([a0dc9b0](https://github.com/kikobeats/whoops/commit/a0dc9b0)) 89 | * Update README.md ([b289826](https://github.com/kikobeats/whoops/commit/b289826)) 90 | * Update README.md ([bf87a1f](https://github.com/kikobeats/whoops/commit/bf87a1f)) 91 | 92 | 93 | 94 | 95 | ## 3.1.1 (2017-07-18) 96 | 97 | * Update README.md ([4561a8b](https://github.com/kikobeats/whoops/commit/4561a8b)) 98 | * docs(readme): add Greenkeeper badge ([9b93e2c](https://github.com/kikobeats/whoops/commit/9b93e2c)) 99 | * chore(package): update clean-stack to version 1.2.0 ([b2cb039](https://github.com/kikobeats/whoops/commit/b2cb039)) 100 | * chore(package): update clean-stack to version 1.3.0 ([12fb684](https://github.com/kikobeats/whoops/commit/12fb684)) 101 | 102 | 103 | 104 | 105 | # 3.1.0 (2016-12-19) 106 | 107 | * Add description field ([6b3b308](https://github.com/kikobeats/whoops/commit/6b3b308)) 108 | * Drop node 5 from testing ([c223c7f](https://github.com/kikobeats/whoops/commit/c223c7f)) 109 | 110 | 111 | 112 | 113 | ## 3.0.3 (2016-12-19) 114 | 115 | * 80 → 100 lines ([a117bb8](https://github.com/kikobeats/whoops/commit/a117bb8)) 116 | * Avoid inline regexp declaration ([2f91173](https://github.com/kikobeats/whoops/commit/2f91173)) 117 | * Better docs ([591d743](https://github.com/kikobeats/whoops/commit/591d743)) 118 | * Test refactor ([58fb3dc](https://github.com/kikobeats/whoops/commit/58fb3dc)) 119 | * Update deps ([c0d5a49](https://github.com/kikobeats/whoops/commit/c0d5a49)) 120 | * Update README.md ([c9c32f8](https://github.com/kikobeats/whoops/commit/c9c32f8)) 121 | 122 | 123 | 124 | 125 | ## 3.0.2 (2016-09-13) 126 | 127 | * chore(package): update clean-stack to version 1.0.0 ([ff0f4ce](https://github.com/kikobeats/whoops/commit/ff0f4ce)) 128 | * Update ([ce66183](https://github.com/kikobeats/whoops/commit/ce66183)) 129 | 130 | 131 | 132 | 133 | ## 3.0.1 (2016-08-01) 134 | 135 | * Avoid eval for setup function name ([1ecde20](https://github.com/kikobeats/whoops/commit/1ecde20)) 136 | * Remove interface string façade ([14c5932](https://github.com/kikobeats/whoops/commit/14c5932)) 137 | * Remove unncessary check ([6a4d469](https://github.com/kikobeats/whoops/commit/6a4d469)) 138 | * Rename extend-error → create-extend-error ([fd3726e](https://github.com/kikobeats/whoops/commit/fd3726e)) 139 | * Setup Error name in constructor ([4ac076d](https://github.com/kikobeats/whoops/commit/4ac076d)) 140 | 141 | 142 | 143 | 144 | # 3.0.0 (2016-07-30) 145 | 146 | * Add clean-stack dep ([7f32385](https://github.com/kikobeats/whoops/commit/7f32385)) 147 | * Drop < 4 node support ([185ae66](https://github.com/kikobeats/whoops/commit/185ae66)) 148 | * Fix dep ([a87c1ea](https://github.com/kikobeats/whoops/commit/a87c1ea)) 149 | * Refactor fn template ([942a189](https://github.com/kikobeats/whoops/commit/942a189)) 150 | * Remove capture-stack-trace dep ([5fdae23](https://github.com/kikobeats/whoops/commit/5fdae23)) 151 | * Remove string interface with 3 params ([5439d61](https://github.com/kikobeats/whoops/commit/5439d61)) 152 | * Remove unnecessary dep ([78d6dac](https://github.com/kikobeats/whoops/commit/78d6dac)) 153 | * Update docs ([34f5e8c](https://github.com/kikobeats/whoops/commit/34f5e8c)) 154 | 155 | 156 | 157 | 158 | # 2.2.0 (2016-07-30) 159 | 160 | * Add coverage ([1547649](https://github.com/kikobeats/whoops/commit/1547649)) 161 | * Move pretty on different pkg ([3640943](https://github.com/kikobeats/whoops/commit/3640943)) 162 | * Refactor scaffold ([5907391](https://github.com/kikobeats/whoops/commit/5907391)) 163 | * Remove browser build ([e0e3621](https://github.com/kikobeats/whoops/commit/e0e3621)) 164 | 165 | 166 | 167 | 168 | # 2.1.0 (2016-05-19) 169 | 170 | * Add missing dependency ([b832bf0](https://github.com/kikobeats/whoops/commit/b832bf0)) 171 | * Add pretty output ([a1c6e4c](https://github.com/kikobeats/whoops/commit/a1c6e4c)) 172 | * Update docs ([1b6c6a8](https://github.com/kikobeats/whoops/commit/1b6c6a8)) 173 | * Update README.md ([30224fe](https://github.com/kikobeats/whoops/commit/30224fe)) 174 | * Update travis builds ([44e69b5](https://github.com/kikobeats/whoops/commit/44e69b5)) 175 | 176 | 177 | 178 | 179 | ## 2.0.1 (2016-02-07) 180 | 181 | 182 | * lock dependency ([9d36dd5](https://github.com/kikobeats/whoops/commit/9d36dd5)) 183 | 184 | 185 | 186 | 187 | # 2.0.0 (2016-02-03) 188 | 189 | 190 | * 2.0.0 releases ([c7ba44a](https://github.com/kikobeats/whoops/commit/c7ba44a)) 191 | * Little refactor ([4fb84a1](https://github.com/kikobeats/whoops/commit/4fb84a1)) 192 | * Refactor tests ([5fc36b1](https://github.com/kikobeats/whoops/commit/5fc36b1)) 193 | * Setup correctly instanceof ([6436f3e](https://github.com/kikobeats/whoops/commit/6436f3e)) 194 | * YEAH ([2c8b56b](https://github.com/kikobeats/whoops/commit/2c8b56b)) 195 | 196 | 197 | 198 | 199 | ## 1.1.1 (2016-01-28) 200 | 201 | 202 | * 1.1.1 releases ([60cb701](https://github.com/kikobeats/whoops/commit/60cb701)) 203 | * Add standard as devDependency ([313a136](https://github.com/kikobeats/whoops/commit/313a136)) 204 | * Remove lodash.forEach dep ([f5f96ee](https://github.com/kikobeats/whoops/commit/f5f96ee)) 205 | * setup correctly bumped ([75d190b](https://github.com/kikobeats/whoops/commit/75d190b)) 206 | * Update README.md ([ec8d39c](https://github.com/kikobeats/whoops/commit/ec8d39c)) 207 | * Update README.md ([ff486b4](https://github.com/kikobeats/whoops/commit/ff486b4)) 208 | * Update README.md ([23527e7](https://github.com/kikobeats/whoops/commit/23527e7)) 209 | 210 | 211 | 212 | 213 | # 1.1.0 (2016-01-14) 214 | 215 | 216 | * releases ([5fff532](https://github.com/kikobeats/whoops/commit/5fff532)) 217 | * Add .create method ([1315768](https://github.com/kikobeats/whoops/commit/1315768)) 218 | * Add coffe as devDependency ([ce75ae9](https://github.com/kikobeats/whoops/commit/ce75ae9)) 219 | * Add custom message function in object factory ([470414c](https://github.com/kikobeats/whoops/commit/470414c)) 220 | * bye coffee ([6a3a1d4](https://github.com/kikobeats/whoops/commit/6a3a1d4)) 221 | * bye config ([e920003](https://github.com/kikobeats/whoops/commit/e920003)) 222 | * Little tests refactor ([d3abb53](https://github.com/kikobeats/whoops/commit/d3abb53)) 223 | * Update description and documentation ([0ad3cba](https://github.com/kikobeats/whoops/commit/0ad3cba)) 224 | * Update scripts ([29240d2](https://github.com/kikobeats/whoops/commit/29240d2)) 225 | 226 | 227 | 228 | 229 | ## 1.0.1 (2015-11-23) 230 | 231 | 232 | * 1.0.1 releases ([56e26f2](https://github.com/kikobeats/whoops/commit/56e26f2)) 233 | * Fix typos in README.md ([e9063f2](https://github.com/kikobeats/whoops/commit/e9063f2)) 234 | * Merge branch 'master' of github.com:kikobeats/whoops ([a363134](https://github.com/kikobeats/whoops/commit/a363134)) 235 | * Merge pull request #4 from jorrit/patch-1 ([d9156f6](https://github.com/kikobeats/whoops/commit/d9156f6)) 236 | * rewrite to avoid new ([23e586b](https://github.com/kikobeats/whoops/commit/23e586b)) 237 | * update to upgrade bower.json ([b18589d](https://github.com/kikobeats/whoops/commit/b18589d)) 238 | 239 | 240 | 241 | 242 | # 1.0.0 (2015-11-20) 243 | 244 | 245 | * 1.0.0 releases ([447ae1e](https://github.com/kikobeats/whoops/commit/447ae1e)) 246 | * refactored and renamed ([cf2b458](https://github.com/kikobeats/whoops/commit/cf2b458)) 247 | * removed extra spaces ([931245d](https://github.com/kikobeats/whoops/commit/931245d)) 248 | * updated ([9bf0fde](https://github.com/kikobeats/whoops/commit/9bf0fde)) 249 | * updated bumped config ([4583c59](https://github.com/kikobeats/whoops/commit/4583c59)) 250 | * updated dependencies ([3d7ec1b](https://github.com/kikobeats/whoops/commit/3d7ec1b)) 251 | * updated documentation ([6ea514b](https://github.com/kikobeats/whoops/commit/6ea514b)) 252 | * updated renaming ([4b9306c](https://github.com/kikobeats/whoops/commit/4b9306c)) 253 | * updated travis builds ([a330eae](https://github.com/kikobeats/whoops/commit/a330eae)) 254 | 255 | 256 | 257 | 258 | # 0.2.0 (2015-10-01) 259 | 260 | 261 | * 0.2.0 releases ([aff1849](https://github.com/kikobeats/whoops/commit/aff1849)) 262 | * Added format for string constructor ([d584188](https://github.com/kikobeats/whoops/commit/d584188)) 263 | * improve example ([aa2d318](https://github.com/kikobeats/whoops/commit/aa2d318)) 264 | * little suite of tests 😁 ([5e11def](https://github.com/kikobeats/whoops/commit/5e11def)) 265 | * Update package.json ([1120535](https://github.com/kikobeats/whoops/commit/1120535)) 266 | * updated ([d5fc2d9](https://github.com/kikobeats/whoops/commit/d5fc2d9)) 267 | 268 | 269 | 270 | 271 | ## 0.1.3 (2015-07-28) 272 | 273 | 274 | * 0.1.3 releases ([e0de222](https://github.com/kikobeats/whoops/commit/e0de222)) 275 | * fixed devDependencies ([bccbf41](https://github.com/kikobeats/whoops/commit/bccbf41)) 276 | * updated bumped settings ([67ff23e](https://github.com/kikobeats/whoops/commit/67ff23e)) 277 | 278 | 279 | 280 | 281 | ## 0.1.2 (2015-07-28) 282 | 283 | 284 | * 0.1.2 releases ([7273df6](https://github.com/kikobeats/whoops/commit/7273df6)) 285 | 286 | 287 | 288 | 289 | ## 0.1.1 (2015-07-28) 290 | 291 | 292 | * 0.1.1 release ([6fcf6d7](https://github.com/kikobeats/whoops/commit/6fcf6d7)) 293 | * 0.1.1 releases ([15d607b](https://github.com/kikobeats/whoops/commit/15d607b)) 294 | * Update README.md ([d5e55b8](https://github.com/kikobeats/whoops/commit/d5e55b8)) 295 | * Update README.md ([869be0a](https://github.com/kikobeats/whoops/commit/869be0a)) 296 | * Update README.md ([8344dab](https://github.com/kikobeats/whoops/commit/8344dab)) 297 | * updated dependencies ([afe3a10](https://github.com/kikobeats/whoops/commit/afe3a10)) 298 | 299 | 300 | 301 | 302 | # 0.1.0 (2015-07-26) 303 | 304 | 305 | * 0.1.0 releases ([594c110](https://github.com/kikobeats/whoops/commit/594c110)) 306 | * added documentation and more examples ([3e68abb](https://github.com/kikobeats/whoops/commit/3e68abb)) 307 | * first approach ([c52341b](https://github.com/kikobeats/whoops/commit/c52341b)) 308 | * Update README.md ([e7172ab](https://github.com/kikobeats/whoops/commit/e7172ab)) 309 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2015 Kiko Beats 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # whoops 2 | 3 | ![Last version](https://img.shields.io/github/tag/Kikobeats/whoops.svg?style=flat-square) 4 | [![Coverage Status](https://img.shields.io/coveralls/Kikobeats/whoops.svg?style=flat-square)](https://coveralls.io/github/Kikobeats/whoops) 5 | [![NPM Status](http://img.shields.io/npm/dm/whoops.svg?style=flat-square)](https://www.npmjs.org/package/whoops) 6 | 7 | > It makes simple throw qualified errors. Inspired in [errno](https://github.com/rvagg/node-errno), [create-error-class](https://github.com/floatdrop/create-error-class) and [fault](https://github.com/wooorm/fault). 8 | 9 | ## Why 10 | 11 | - An easy way to create qualified errors. 12 | - Using the standard `Error` interface in browser and NodeJS. 13 | - Attach extra information, being flexible with whatever user case. 14 | - Less than 50 lines (~500 bytes) 15 | 16 | This library is a compromise to provide a clean API for use `Error` native class. 17 | 18 | ## Install 19 | 20 | ```bash 21 | npm install whoops --save 22 | ``` 23 | 24 | Basically it turns: 25 | 26 | ```js 27 | const error = Error('Something is wrong') 28 | error.name = 'DAMNError' 29 | throw error // => 'DAMNError: ENOFILE, Something is wrong' 30 | ``` 31 | 32 | Into a one line more productive declaration: 33 | 34 | ```js 35 | const whoops = require('whoops') 36 | const userError = whoops('UserError') 37 | 38 | throw userError('User not found') // => 'UserError: User not found' 39 | ``` 40 | 41 | ## Creating Qualified Errors 42 | 43 | Call `whoops` to get a constructor function. Every time you call the constructor, you get an `Error` instance: 44 | 45 | ```js 46 | const whoops = require('whoops') 47 | const myError = whoops() 48 | throw myError() 49 | ``` 50 | 51 | Create domain specific errors providing a `className` as first argument: 52 | 53 | ```js 54 | const whoops = require('whoops') 55 | const userError = whoops('userError') 56 | throw userError() 57 | ``` 58 | 59 | The qualified error will be extends from `Error`: 60 | 61 | ```js 62 | const whoops = require('whoops') 63 | const userError = whoops('userError') 64 | const error = userError() 65 | console.log(error instanceof Error); // => true 66 | ``` 67 | 68 | Attach extra information passing a `props` as second argument: 69 | 70 | ```js 71 | const whoops = require('whoops') 72 | const userError = whoops('userError', {code: 'ENOVALID'}) 73 | const err = userError() 74 | console.log(`My error code is ${err.code}`) // => My error code is ENOVALID 75 | ``` 76 | 77 | You can associate dynamic `props` as well: 78 | 79 | ```js 80 | const whoops = require('whoops') 81 | const userError = whoops('userError', { 82 | code: 'ENOVALID', 83 | message: props => `User '${props.username}' not found` 84 | }) 85 | 86 | const err = userError({username: 'kiko'}) 87 | console.log(err.message) // => User 'kiko' not found 88 | ``` 89 | 90 | ## Error Types 91 | 92 | By default you will get `Error` instances calling whoops, but you can get different errors calling the properly method: 93 | 94 | | Name | Method | 95 | |----------------|------------------| 96 | | [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) | whoops | 97 | | [TypeError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError) | whoops.type | 98 | | [RangeError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError) | whoops.range | 99 | | [EvalError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/EvalError) | whoops.eval | 100 | | [SyntaxError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError) | whoops.syntax | 101 | | [ReferenceError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError) | whoops.reference | 102 | | [URIError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/URIError) | whoops.uri | 103 | 104 | ## Extra: Always throw/return an Error! 105 | 106 | If you code implementation is 107 | 108 | - **synchronous**, throws `Error`. If you just return the `Error` nothings happens!. 109 | - **asynchronous**, returns `Error` in the first argument of the callback (or using promises). 110 | 111 | About asynchronous code, is correct return a `Object` that is not a `Error` in the first argument of the callback to express unexpected behavior, but the `Object` doesn't have a type and definitely can't follow a error interface for determinate a special behavior: 112 | 113 | ```js 114 | callback('LOL something was wrong') // poor 115 | callback({message: 'LOL something was wrong' } // poor, but better 116 | callback(whoops('LOL, something was wrong') // BEST! 117 | ``` 118 | 119 | Passing always an `Error` you can can associated different type of error with different behavior: 120 | 121 | ```js 122 | switch (err.name) { 123 | case 'JSONError': 124 | console.log('your error logic here') 125 | break 126 | default: 127 | console.log('undefined code') 128 | break 129 | }; 130 | ``` 131 | 132 | ## Related 133 | 134 | - [create-error-class](https://github.com/floatdrop/create-error-class) – Create error class. 135 | - [fault](https://github.com/wooorm/fault) – Functional errors with formatted output. 136 | 137 | 138 | ## License 139 | 140 | MIT © [Kiko Beats](http://www.kikobeats.com) 141 | -------------------------------------------------------------------------------- /examples/handle-errors.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs') 4 | const Whoops = require('..') 5 | 6 | console.log('\n[ Standard NodeJS errors ]\n') 7 | 8 | try { 9 | fs.readFileSync('filename') 10 | } catch (err) { 11 | console.log(' message :', err.message) 12 | console.log(' name\t :', err.name) 13 | console.log(' code\t :', err.code) 14 | console.log(' path\t :', err.path) 15 | console.log(' errno\t :', err.errno) 16 | 17 | // { [Error: ENOENT, open 'filename'] 18 | // errno: 34, 19 | // code: 'ENOENT', 20 | // path: 'filename' } 21 | } 22 | 23 | console.log('\n[ Whoops Object Constructor ]\n') 24 | 25 | const errObjt = new Whoops({ 26 | message: 'The format of the JSON is invalid', 27 | name: 'JSONError', 28 | code: 'NotValidJSON', 29 | path: 'filename', 30 | errno: 127, 31 | foo: 'bar' 32 | }) 33 | 34 | console.log(' message :', errObjt.message) 35 | console.log(' name\t :', errObjt.name) 36 | console.log(' code\t :', errObjt.code) 37 | console.log(' path\t :', errObjt.path) 38 | console.log(' errno\t :', errObjt.errno) 39 | console.log(' foo\t :', errObjt.foo) 40 | 41 | console.log('\n[ Whoops String constructor ]\n') 42 | 43 | const errString = new Whoops('JSONError', 'NotValidJSON', 'The format of the JSON is invalid') 44 | 45 | console.log(' message :', errString.message) 46 | console.log(' name\t :', errString.name) 47 | console.log(' code\t :', errString.code) 48 | 49 | console.log('\n[ constructor comparation ]\n') 50 | 51 | console.log('same message?\t', errString.message === errObjt.message) 52 | console.log('same name?\t', errString.name === errObjt.name) 53 | console.log('same typeof?\t', typeof errString === typeof errObjt) 54 | 55 | console.log('\n[ handling err codes ]\n') 56 | 57 | switch (errObjt.name) { 58 | case 'JSONError': 59 | console.log('your error logic here') 60 | break 61 | default: 62 | console.log('Standard Error name') 63 | break 64 | } 65 | -------------------------------------------------------------------------------- /examples/prototypes.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const whoops = require('..') 4 | const DAMNError = whoops.create('DAMNError') 5 | const err = DAMNError('Something is wrong') 6 | 7 | console.log('static ::') 8 | console.log(DAMNError) 9 | console.log('DAMNError instanceof Error?', DAMNError instanceof Error) 10 | console.log() 11 | console.log('instance ::') 12 | console.log(err) 13 | console.log('err instanceof Error?', err instanceof Error) 14 | console.log('err instanceof DAMNError?', err instanceof Error) 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "whoops", 3 | "description": "It makes simple throw qualified errors.", 4 | "homepage": "https://github.com/Kikobeats/whoops", 5 | "version": "5.0.1", 6 | "main": "src/index.js", 7 | "author": { 8 | "email": "josefrancisco.verdu@gmail.com", 9 | "name": "Kiko Beats", 10 | "url": "https://github.com/Kikobeats" 11 | }, 12 | "contributors": [ 13 | { 14 | "name": "Jorrit Schippers", 15 | "email": "jorrit@ncode.nl" 16 | }, 17 | { 18 | "name": "Prasad Nayak", 19 | "email": "prasadnayak1006@gmail.com" 20 | } 21 | ], 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/kikobeats/whoops.git" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/Kikobeats/whoops/issues" 28 | }, 29 | "keywords": [ 30 | "constructor", 31 | "custom", 32 | "error", 33 | "simple", 34 | "throw" 35 | ], 36 | "devDependencies": { 37 | "@commitlint/cli": "latest", 38 | "@commitlint/config-conventional": "latest", 39 | "@ksmithut/prettier-standard": "latest", 40 | "c8": "latest", 41 | "ci-publish": "latest", 42 | "finepack": "latest", 43 | "git-authors-cli": "latest", 44 | "github-generate-release": "latest", 45 | "nano-staged": "latest", 46 | "should": "latest", 47 | "simple-git-hooks": "latest", 48 | "standard": "latest", 49 | "standard-markdown": "latest", 50 | "standard-version": "latest" 51 | }, 52 | "engines": { 53 | "node": ">= 8" 54 | }, 55 | "files": [ 56 | "src" 57 | ], 58 | "scripts": { 59 | "clean": "rm -rf node_modules", 60 | "contributors": "(npx git-authors-cli && npx finepack && git add package.json && git commit -m 'build: contributors' --no-verify) || true", 61 | "coveralls": "nyc report --reporter=text-lcov | coveralls", 62 | "lint": "standard", 63 | "postrelease": "npm run release:tags && npm run release:github && (ci-publish || npm publish --access=public)", 64 | "pretest": "npm run lint", 65 | "release": "standard-version -a", 66 | "release:github": "github-generate-release", 67 | "release:tags": "git push --follow-tags origin HEAD:master", 68 | "test": "c8 node --test" 69 | }, 70 | "license": "MIT", 71 | "commitlint": { 72 | "extends": [ 73 | "@commitlint/config-conventional" 74 | ], 75 | "rules": { 76 | "body-max-line-length": [ 77 | 0 78 | ] 79 | } 80 | }, 81 | "nano-staged": { 82 | "*.js": [ 83 | "prettier-standard", 84 | "standard --fix" 85 | ], 86 | "*.md": [ 87 | "standard-markdown" 88 | ], 89 | "package.json": [ 90 | "finepack" 91 | ] 92 | }, 93 | "simple-git-hooks": { 94 | "commit-msg": "npx commitlint --edit", 95 | "pre-commit": "npx nano-staged" 96 | }, 97 | "standard": { 98 | "globals": [ 99 | "describe", 100 | "it" 101 | ] 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function createErrorClass (ErrorClass) { 4 | return (name, defaults) => { 5 | class CustomError extends ErrorClass { 6 | constructor (raw = {}) { 7 | super(raw) 8 | const { message, ...props } = Object.assign( 9 | {}, 10 | defaults, 11 | typeof raw === 'string' ? { message: raw } : raw 12 | ) 13 | Object.keys(props).forEach(key => (this[key] = props[key])) 14 | if (message) this.description = typeof message === 'function' ? message(props) : message 15 | this.message = this.code ? `${this.code}, ${this.description}` : this.description 16 | this.name = name || ErrorClass.name 17 | } 18 | } 19 | 20 | // Function to create an instance, allowing use without `new` 21 | function CustomErrorFactory (props) { 22 | return new CustomError(props) 23 | } 24 | 25 | // Ensure the function's name matches the class name for consistency 26 | Object.defineProperty(CustomErrorFactory, 'name', { 27 | value: name || ErrorClass.name, 28 | writable: false 29 | }) 30 | 31 | // Make `instanceof` checks work with both the class and factory 32 | CustomErrorFactory.prototype = CustomError.prototype 33 | 34 | return CustomErrorFactory 35 | } 36 | } 37 | 38 | module.exports = createErrorClass(Error) 39 | module.exports.type = createErrorClass(TypeError) 40 | module.exports.range = createErrorClass(RangeError) 41 | module.exports.eval = createErrorClass(EvalError) 42 | module.exports.syntax = createErrorClass(SyntaxError) 43 | module.exports.reference = createErrorClass(ReferenceError) 44 | module.exports.uri = createErrorClass(URIError) 45 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { describe, it } = require('node:test') 4 | 5 | const whoops = require('..') 6 | 7 | describe('constructor', () => { 8 | ;[ 9 | [Error, whoops], 10 | [TypeError, whoops.type], 11 | [RangeError, whoops.range], 12 | [EvalError, whoops.eval], 13 | [SyntaxError, whoops.syntax], 14 | [ReferenceError, whoops.reference], 15 | [URIError, whoops.uri] 16 | ].forEach(([ErrorClass, whoops]) => { 17 | describe(ErrorClass.name, () => { 18 | ;[ 19 | { name: 'without providing a class name', input: undefined }, 20 | { name: 'providing a class name', input: 'UserError' } 21 | ].forEach(({ name, input }) => { 22 | it(name, t => { 23 | const userError = whoops(input) 24 | t.assert.equal(typeof userError, 'function') 25 | t.assert.equal(userError.name, input || ErrorClass.name) 26 | t.assert.equal(userError().name, input || ErrorClass.name) 27 | t.assert.equal(userError().message, undefined) 28 | t.assert.equal(userError().description, undefined) 29 | t.assert.equal(userError().code, undefined) 30 | t.assert.equal(userError() instanceof userError, true) 31 | t.assert.equal(userError() instanceof ErrorClass, true) 32 | }) 33 | }) 34 | }) 35 | }) 36 | 37 | it('attach props', t => { 38 | const userError = whoops('UserError', { code: 'ENOVALID' }) 39 | t.assert.equal(typeof userError, 'function') 40 | t.assert.equal(userError.name, 'UserError') 41 | const error = userError({ message: 'user not found' }) 42 | t.assert.equal(error.message, 'ENOVALID, user not found') 43 | t.assert.equal(error.code, 'ENOVALID') 44 | t.assert.equal(error.description, 'user not found') 45 | }) 46 | 47 | describe('instance', () => { 48 | describe('passing message prop', () => { 49 | it('as string', t => { 50 | const userError = whoops('UserError') 51 | const error = userError('user not found') 52 | t.assert.equal(error instanceof userError, true) 53 | t.assert.equal(error instanceof Error, true) 54 | t.assert.equal(error.message, 'user not found') 55 | t.assert.equal(error.description, 'user not found') 56 | }) 57 | 58 | it('as object', t => { 59 | const userError = whoops('UserError') 60 | const error = userError({ message: 'user not found' }) 61 | t.assert.equal(error instanceof userError, true) 62 | t.assert.equal(error instanceof Error, true) 63 | t.assert.equal(error.message, 'user not found') 64 | t.assert.equal(error.description, 'user not found') 65 | }) 66 | }) 67 | 68 | it('passing message & code props', t => { 69 | const userError = whoops('UserError', { 70 | message: ({ username }) => `user '${username}' not found`, 71 | code: 'ENOVALID' 72 | }) 73 | 74 | const error = userError({ username: 'kikobeats' }) 75 | 76 | t.assert.equal(error.message, "ENOVALID, user 'kikobeats' not found") 77 | t.assert.equal(error.code, 'ENOVALID') 78 | t.assert.equal(error.description, "user 'kikobeats' not found") 79 | 80 | t.assert.equal(error instanceof userError, true) 81 | t.assert.equal(error instanceof Error, true) 82 | }) 83 | 84 | it('passing message, code & extra props', t => { 85 | const userError = whoops('UserError') 86 | const error = userError({ 87 | username: 'kikobeats', 88 | message: props => `user '${props.username}' not found`, 89 | code: 'ENOVALID' 90 | }) 91 | t.assert.equal(error instanceof userError, true) 92 | t.assert.equal(error instanceof Error, true) 93 | t.assert.equal(error.message, "ENOVALID, user 'kikobeats' not found") 94 | t.assert.equal(error.code, 'ENOVALID') 95 | t.assert.equal(error.description, "user 'kikobeats' not found") 96 | t.assert.equal(error.username, 'kikobeats') 97 | }) 98 | }) 99 | }) 100 | --------------------------------------------------------------------------------