|
1 | | -# EID Runtime Exceptions and Utilities for Javascript |
| 1 | +# EID Runtime Exceptions and Utilities for JavaScript/TypeScript |
2 | 2 |
|
3 | | -[](https://travis-ci.org/wavesoftware/javascript-eid-exceptions) [](https://coveralls.io/github/wavesoftware/javascript-eid-exceptions?branch=master) [](https://www.npmjs.com/package/eid.js) []() |
| 3 | +[](https://github.com/wavesoftware/javascript-eid-exceptions/actions/workflows/ci.yml) [](https://www.npmjs.com/package/eid.js) |
4 | 4 |
|
5 | | -This small library holds a set of exceptions and utilities that implements idea of fast, reusable, error codes that can be simply thrown fast in case of unpredictable and unrecoverable application failure. It is meant to be used for application bugs. |
| 5 | +A small, zero-dependency library for [defensive programming](https://en.wikipedia.org/wiki/Defensive_programming) in JavaScript and TypeScript. It provides reusable Exception IDs (EIDs), precondition checks, and standardized error handling for fail-fast detection of programming errors. |
6 | 6 |
|
7 | | -## Idea |
| 7 | +Instead of writing ad-hoc validation with generic `Error` messages, you assign a unique, permanent ID to each check site. When a bug reaches production, the ID links the error page (or log entry) directly to the line of code that failed. |
8 | 8 |
|
9 | | -The idea is to use a set of simple runtime exceptions. They should always take the Exception ID (Eid) object in the making. This eid object will then be reported when displaying or logging that exception. It can also be viewed on the professional fatal error window of the application as a bug reference. EidRuntimeExceptions contains also additional unique ID to distinguish each single exception from others with same Eid. This approach simplifies the management of exceptions in the application and allows developers to focus on functionalities rather than coming up with the correct statement for the exception. |
| 9 | +## How it works |
10 | 10 |
|
11 | | -This approach is best to use with tools and plugins like: |
| 11 | +Every exception takes an `Eid` object that combines two identifiers: |
12 | 12 |
|
13 | | - * [EidGenerator for Netbeans IDE](http://plugins.netbeans.org/plugin/53137/exception-id-eid-generator) |
14 | | - * [Generating Exception Id number in Intellij IDEA with Live Templates](https://github.com/wavesoftware/java-eid-exceptions/wiki/Generating%20Exception%20Id%20number%20in%20Intellij%20IDEA%20with%20Live%20Templates) |
| 13 | +- A **static ID** (e.g. `"20260714:120000"`) -- a permanent label inserted by the developer at the throw site. |
| 14 | +- A **unique ID** (e.g. `<a3f7x1>`) -- generated at runtime to distinguish individual occurrences. |
15 | 15 |
|
16 | | -Example: |
| 16 | +When the exception is logged or displayed on an error page, both IDs appear together. Support teams search by the static ID to find the code path, and by the unique ID to pinpoint the exact occurrence. |
17 | 17 |
|
18 | | -```js |
19 | | -var cause = 'A extra message'; |
20 | | -throw new EidIllegalStateException("20150721:100554", cause); |
| 18 | +```typescript |
| 19 | +import { EidIllegalStateException } from "eid.js"; |
| 20 | + |
| 21 | +throw new EidIllegalStateException("20260714:120000", "An extra message"); |
21 | 22 | ``` |
22 | 23 |
|
23 | | -Example log: |
| 24 | +Output: |
24 | 25 |
|
25 | 26 | ``` |
26 | | -ERROR 2016-01-11T22:48:42.445 EidIllegalStateException: [20150721:100554]<rww5y3> A extra message - Error |
27 | | - at http://localhost:3000/browser/toplevel/eid.min.js:1:3959 |
28 | | - at Object.i.3.../eid (http://localhost:3000/browser/toplevel/eid.min.js:1:4211) |
29 | | - at r (http://localhost:3000/browser/toplevel/eid.min.js:1:254) |
30 | | - at http://localhost:3000/browser/toplevel/eid.min.js:1:305 |
31 | | - at http://localhost:3000/browser/toplevel/eid.min.js:1:536 |
32 | | - at Object.i.1.../../lib/eid (http://localhost:3000/browser/toplevel/eid.min.js:1:974) |
33 | | - at r (http://localhost:3000/browser/toplevel/eid.min.js:1:254) |
34 | | - at t (http://localhost:3000/browser/toplevel/eid.min.js:1:421) |
35 | | - at http://localhost:3000/browser/toplevel/eid.min.js:1:438 |
| 27 | +EidIllegalStateException: [20260714:120000]<a3f7x1> An extra message |
| 28 | + at Object.<anonymous> (/app/src/service.ts:42:11) |
36 | 29 | ``` |
37 | 30 |
|
| 31 | +Static IDs can be generated using [Live Templates in IntelliJ IDEA](https://github.com/wavesoftware/java-eid-exceptions/wiki/Generating%20Exception%20Id%20number%20in%20Intellij%20IDEA%20with%20Live%20Templates). |
38 | 32 |
|
39 | | -## Caution |
| 33 | +## Intended use |
40 | 34 |
|
41 | | -This classes shouldn't be used in any public API or library. It is designed to be used for in-house development of end user applications which will report bugs in standardized error pages or post them to issue tracker. |
| 35 | +This library is not intended for public APIs or shared libraries. It is designed for in-house, end-user applications that report bugs through standardized error pages or issue trackers. |
42 | 36 |
|
43 | | -## NPM |
| 37 | +## Installation |
44 | 38 |
|
45 | 39 | ```bash |
46 | | -npm install eid.js --save |
| 40 | +npm install eid.js |
47 | 41 | ``` |
48 | 42 |
|
49 | | -## Bower |
| 43 | +### ESM (recommended) |
50 | 44 |
|
51 | | -```bash |
52 | | -bower install eid.js --save |
| 45 | +```typescript |
| 46 | +import { Eid, EidPreconditions, EidIllegalStateException } from "eid.js"; |
53 | 47 | ``` |
54 | 48 |
|
55 | | -### `EidPreconditions` class |
| 49 | +### CommonJS |
| 50 | + |
| 51 | +```javascript |
| 52 | +const { Eid, EidPreconditions, EidIllegalStateException } = require("eid.js"); |
| 53 | +``` |
56 | 54 |
|
57 | | -#### General use |
| 55 | +### Browser (IIFE) |
58 | 56 |
|
59 | | -`EidPreconditions` class consists static methods that help to use Eid in a method or constructor. This is solely for convenience purposes. Use them to check whether method or constructor was invoked correctly (whether its preconditions have been met). These methods generally accept a `boolean` expression which is expected to be `true` (or in the case of `checkNotNull`, an object reference which is expected to be non-null). When `false` (or `null`) is passed instead, the `EidPreconditions` method throws an unchecked exception, which helps the calling method communicate to its caller that that caller has made a mistake. |
| 57 | +```html |
| 58 | +<script src="https://unpkg.com/eid.js/dist/index.global.js"></script> |
| 59 | +<script> |
| 60 | + const { Eid, EidPreconditions } = window.Eid; |
| 61 | +</script> |
| 62 | +``` |
60 | 63 |
|
61 | | -Each method accepts a EID string or Eid object, which is designed to ease of use and provide strict ID for given exception usage. This approach speed up development of large application and helps support teams by giving both static and random ID for each possible bug that could occur. |
| 64 | +## Error cause support |
62 | 65 |
|
63 | | -Each example uses static import: |
| 66 | +All Eid exceptions support the standard ES2022 `Error.cause` property, preserving the original error and its full stack trace: |
64 | 67 |
|
65 | | -```js |
66 | | -// nodejs |
67 | | -var EidPreconditions = require('eid.js').preconditions; |
| 68 | +```typescript |
| 69 | +import { EidRuntimeException } from "eid.js"; |
68 | 70 |
|
69 | | -// browser using bower using toplevel version |
70 | | -var EidPreconditions = window.Eid.preconditions; |
| 71 | +try { |
| 72 | + riskyOperation(); |
| 73 | +} catch (err) { |
| 74 | + throw new EidRuntimeException("20260714:120000", "operation failed", err); |
| 75 | + // The original error is available via .cause |
| 76 | +} |
71 | 77 | ``` |
72 | 78 |
|
73 | | -#### `checkArgument` method |
| 79 | +`tryToExecute` chains the caught error as `cause` automatically: |
74 | 80 |
|
75 | | -`checkArgument` method should be used to check argument of the method, and validate it in technical terms (not business terms). |
| 81 | +```typescript |
| 82 | +import { EidPreconditions } from "eid.js"; |
76 | 83 |
|
77 | | -Example: |
78 | | - |
79 | | -```js |
80 | | -// [..] |
81 | | -function sqrt(value) { |
82 | | - checkArgument(value >= 0.0, "20150718:012333"); |
83 | | - // if ok, calculate the square root |
84 | | -} |
| 84 | +const result = EidPreconditions.tryToExecute(() => { |
| 85 | + return JSON.parse(untrustedInput); |
| 86 | +}, "20260714:120100"); |
| 87 | +// If JSON.parse throws, the SyntaxError is available via .cause |
85 | 88 | ``` |
86 | 89 |
|
87 | | -In this example, `checkArgument` throws an `EidIllegalArgumentException` to indicate that developer made an error in its call to `sqrt`. |
| 90 | +## `EidPreconditions` |
88 | 91 |
|
89 | | -#### `checkState` method |
| 92 | +A collection of static methods for validating preconditions. Each method accepts an EID string (or an `Eid` object) and throws a typed Eid exception when the check fails. |
90 | 93 |
|
91 | | -`checkState` method should be used to check state of the class in given moment, and validate it in technical terms (not business terms). |
| 94 | +### `checkArgument` |
92 | 95 |
|
93 | | -Example: |
| 96 | +Validates a method argument in technical terms (not business rules). |
94 | 97 |
|
95 | | -```js |
96 | | -checkState(a >= 3.14 && b < 0., "20150721:115016"); |
| 98 | +```typescript |
| 99 | +import { EidPreconditions } from "eid.js"; |
| 100 | + |
| 101 | +function sqrt(value: number): number { |
| 102 | + EidPreconditions.checkArgument(value >= 0.0, "20150718:012333"); |
| 103 | + return Math.sqrt(value); |
| 104 | +} |
97 | 105 | ``` |
98 | 106 |
|
99 | | -#### `checkNotNull` method |
| 107 | +Throws `EidIllegalArgumentException` if `value` is negative. |
100 | 108 |
|
101 | | -`checkNotNull` method should be used to check if given non null argument is actually `null` |
| 109 | +### `checkState` |
102 | 110 |
|
103 | | -Example: |
| 111 | +Validates the internal state of an object at a given moment. |
104 | 112 |
|
105 | | -```js |
106 | | -var nonNullUserName = checkNotNull(userName, "20150721:115515"); |
| 113 | +```typescript |
| 114 | +EidPreconditions.checkState(a >= 3.14 && b < 0, "20150721:115016"); |
107 | 115 | ``` |
108 | 116 |
|
109 | | -#### `checkElementIndex` method |
| 117 | +### `checkNotNull` |
110 | 118 |
|
111 | | -`checkElementIndex` method can be used to test parameters of an array, before being used |
| 119 | +Ensures that a reference is not `null`. |
112 | 120 |
|
113 | | -```js |
114 | | -checkElementIndex(index, list.length, "20150721:115749"); |
| 121 | +```typescript |
| 122 | +const nonNullUserName = EidPreconditions.checkNotNull(userName, "20150721:115515"); |
115 | 123 | ``` |
116 | 124 |
|
117 | | -#### Formatted message support |
| 125 | +### `checkElementIndex` |
| 126 | + |
| 127 | +Validates that an index is within bounds of an array or list. |
| 128 | + |
| 129 | +```typescript |
| 130 | +EidPreconditions.checkElementIndex(index, list.length, "20150721:115749"); |
| 131 | +``` |
118 | 132 |
|
119 | | -There have been added additional `message` to method descriptors for `checkArgument`, `checkState`, `checkNotNull` and `checkElementIndex` method. Those method's parameter can sometimes be used to pass additional information to exceptions that will be displayed in log files. |
| 133 | +### Optional message |
120 | 134 |
|
121 | | -For example: |
| 135 | +All check methods accept an optional `message` parameter with additional context for the exception: |
122 | 136 |
|
123 | | -```js |
124 | | -checkState(transation.isValid(), "20151119:120238", "Invalid transaction: " + transaction); |
| 137 | +```typescript |
| 138 | +EidPreconditions.checkState( |
| 139 | + transaction.isValid(), |
| 140 | + "20151119:120238", |
| 141 | + `Invalid transaction: ${transaction}` |
| 142 | +); |
125 | 143 | ``` |
126 | 144 |
|
127 | | -Will produce output similar to; |
| 145 | +Output: |
128 | 146 |
|
129 | 147 | ``` |
130 | | -EidIllegalStateException: [20151119:120238]<xf4j1l> => Invalid transaction: <Transaction id=null, buyer=null, products=[]> |
| 148 | +EidIllegalStateException: [20151119:120238]<xf4j1l> Invalid transaction: <Transaction id=null, buyer=null, products=[]> |
131 | 149 | ``` |
132 | 150 |
|
133 | | -#### Functional try to execute blocks |
| 151 | +### `tryToExecute` |
134 | 152 |
|
135 | | -You can use functional blocks to handle operations, that are intended to operate properly. This approach simplify the code and makes it more readable. It's also good way to deal with untested, uncovered `catch` blocks. It's easy and gives developers nice way of dealing with countless operations that suppose to work as intended. |
| 153 | +Wraps a block of code that is expected to succeed. If it throws, the error is re-thrown as an `EidRuntimeException` with the original error preserved as `cause`. |
136 | 154 |
|
137 | | -Example: |
| 155 | +```typescript |
| 156 | +import { EidPreconditions } from "eid.js"; |
| 157 | +import { readFileSync } from "node:fs"; |
138 | 158 |
|
139 | | -```js |
140 | | -var content = EidPreconditions.tryToExecute(function() { |
141 | | - var fs = require('fs'); |
142 | | - return fs.readFileSync('project.properties'); |
| 159 | +const content = EidPreconditions.tryToExecute(() => { |
| 160 | + return readFileSync("project.properties", "utf-8"); |
143 | 161 | }, "20150718:121521"); |
144 | 162 | ``` |
145 | 163 |
|
146 | | -#### Logging |
| 164 | +### Logging |
| 165 | + |
| 166 | +The `Eid` object provides a `makeLogMessage` method for structured log output. Use `%s` as a format specifier: |
147 | 167 |
|
148 | | -Eid object can also be useful in logging. That are `makeLogMessage` method provided to do that. Message formatting is done using equivalent of Java's `String.format(String, Object[])` method. |
149 | | -For example: |
| 168 | +```typescript |
| 169 | +import { Eid } from "eid.js"; |
150 | 170 |
|
151 | | -```js |
152 | 171 | log.debug(new Eid("20151119:121814").makeLogMessage("REST request received: %s", request)); |
153 | 172 | ``` |
154 | 173 |
|
155 | | -will unfold to something similar to: |
| 174 | +Output: |
156 | 175 |
|
157 | 176 | ``` |
158 | | -2017-01-08T16:45:34,334 DEBUG [20151119:121814]<d1afca> REST request received: <RestRequest user=<User id=345> flow=ShowLastTransactions step=Confirm> |
| 177 | +[20151119:121814]<d1afca> => REST request received: <RestRequest user=<User id=345>> |
159 | 178 | ``` |
160 | 179 |
|
161 | | -###Contributing |
| 180 | +## Contributing |
162 | 181 |
|
163 | 182 | Contributions are welcome! |
164 | 183 |
|
165 | | -To contribute, follow the standard [git flow](http://danielkummer.github.io/git-flow-cheatsheet/) of: |
| 184 | +1. Fork the repository |
| 185 | +2. Create a feature branch (`git checkout -b feature/my-new-feature`) |
| 186 | +3. Commit your changes (`git commit -am 'Add some feature'`) |
| 187 | +4. Push to the branch (`git push origin feature/my-new-feature`) |
| 188 | +5. Open a Pull Request |
166 | 189 |
|
167 | | -1. Fork it |
168 | | -1. Create your feature branch (`git checkout -b feature/my-new-feature`) |
169 | | -1. Commit your changes (`git commit -am 'Add some feature'`) |
170 | | -1. Push to the branch (`git push origin feature/my-new-feature`) |
171 | | -1. Create new Pull Request |
172 | | - |
173 | | -Even if you can't contribute code, if you have an idea for an improvement please open an [issue](https://github.com/wavesoftware/javascript-eid-exceptions/issues). |
| 190 | +If you have an idea for an improvement, please open an [issue](https://github.com/wavesoftware/javascript-eid-exceptions/issues). |
174 | 191 |
|
175 | 192 | ## Requirements |
176 | 193 |
|
177 | | -* NodeJS >= 0.10 |
178 | | -* Any modern browser, supported by browserify. |
179 | | - |
| 194 | +* Node.js >= 20 |
| 195 | +* TypeScript >= 5.0 (optional, types are included) |
180 | 196 |
|
181 | | -### Releases |
| 197 | +## Releases |
182 | 198 |
|
| 199 | +- 2.0.0 |
| 200 | + - Full rewrite to TypeScript |
| 201 | + - ESM + CommonJS + IIFE browser bundle |
| 202 | + - ES2022 `Error.cause` support |
| 203 | + - Vitest test suite, Biome linting, GitHub Actions CI |
| 204 | + - Dropped: Bower, Gulp, Travis CI, Node < 20 |
183 | 205 | - 1.0.0 |
184 | | - - initial release |
185 | | - - library ported from Java version |
| 206 | + - Initial release |
| 207 | + - Library ported from Java version |
0 commit comments