@vltpkg/error-cause 0.0.0-2 → 0.0.0-4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -93
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,25 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
# @vltpkg/error-cause
|
|
4
4
|
|
|
5
|
-
Utility functions for `Error` creation to help enforce vlt's
|
|
5
|
+
Utility functions for `Error` creation to help enforce vlt's
|
|
6
|
+
`Error.cause` conventions.
|
|
6
7
|
|
|
7
|
-
**[Usage](#usage)**
|
|
8
|
-
·
|
|
9
|
-
**[Error
|
|
10
|
-
·
|
|
11
|
-
**[Conventions](#conventions)**
|
|
12
|
-
·
|
|
13
|
-
**[Error Types](#error-types)**
|
|
8
|
+
**[Usage](#usage)** ·
|
|
9
|
+
**[Error Reporting](#challenges-of-error-reporting)** ·
|
|
10
|
+
**[Conventions](#conventions)** · **[Error Types](#error-types)**
|
|
14
11
|
|
|
15
12
|
## Why
|
|
16
13
|
|
|
17
|
-
Most node programs have a mishmash of error codes and various
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
Most node programs have a mishmash of error codes and various `Error`
|
|
15
|
+
subtypes, all in different shapes, making error handling and reporting
|
|
16
|
+
more difficult at the top level. This negatively impacts debugging and
|
|
17
|
+
user experience.
|
|
21
18
|
|
|
22
|
-
The JavaScript `Error` constructor has a
|
|
23
|
-
option](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause)
|
|
19
|
+
The JavaScript `Error` constructor has a
|
|
20
|
+
[`cause` option](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause)
|
|
24
21
|
which is supported since Node 16.9. We should use it!
|
|
25
22
|
|
|
26
23
|
This module makes that easy.
|
|
@@ -68,62 +65,60 @@ const checkBar = () => {
|
|
|
68
65
|
}
|
|
69
66
|
```
|
|
70
67
|
|
|
71
|
-
The functions will create an error object with a `cause` property
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
The functions will create an error object with a `cause` property if
|
|
69
|
+
set, and the type checks will ensure that the `cause` object matches
|
|
70
|
+
vlt's conventions.
|
|
74
71
|
|
|
75
72
|
## Challenges of Error Reporting
|
|
76
73
|
|
|
77
74
|
- Provide enough information to be useful. On full inspection, we
|
|
78
|
-
should ideally always get back to not just the initial error
|
|
79
|
-
|
|
80
|
-
|
|
75
|
+
should ideally always get back to not just the initial error that
|
|
76
|
+
was thrown, but also all locations where the error might have been
|
|
77
|
+
caught and handled in some way.
|
|
81
78
|
- Do not provide more information than is useful. Eg,
|
|
82
|
-
`console.error(er)` should not fill the entire scrollback
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
error
|
|
89
|
-
|
|
90
|
-
top-level error handler, so that it can usefully report errors
|
|
91
|
-
and suggest corrections.
|
|
79
|
+
`console.error(er)` should not fill the entire scrollback buffer.
|
|
80
|
+
- New modules and libraries should have minimal friction in creating a
|
|
81
|
+
new style of error when needed. This means, minimize the amount that
|
|
82
|
+
any module needs to know about the errors raised by any other
|
|
83
|
+
module, including especially top-level error handling.
|
|
84
|
+
- _Some_ information about the error must be known to our top-level
|
|
85
|
+
error handler, so that it can usefully report errors and suggest
|
|
86
|
+
corrections.
|
|
92
87
|
|
|
93
88
|
## Solution
|
|
94
89
|
|
|
95
90
|
- A strictly upheld convention of Error object creation using the
|
|
96
91
|
`cause` property.
|
|
97
|
-
- Top level error handler can have special logic where necessary
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
92
|
+
- Top level error handler can have special logic where necessary for
|
|
93
|
+
known error codes, but will still be able to do something more
|
|
94
|
+
useful when an Error object follows our conventions, even if it's
|
|
95
|
+
not a code that it knows.
|
|
101
96
|
|
|
102
97
|
## Conventions
|
|
103
98
|
|
|
104
|
-
The following conventions should be followed for all `Error`
|
|
105
|
-
|
|
99
|
+
The following conventions should be followed for all `Error` creation
|
|
100
|
+
and handling throughout the vlt codebase.
|
|
106
101
|
|
|
107
102
|
- **If you can't help, get out of the way.** Just let throws pass
|
|
108
103
|
through to the top when nothing can be done to assist.
|
|
109
104
|
- **Add information by using thrown error as `cause`.** Use a
|
|
110
105
|
previously-thrown error as the `cause` option.
|
|
111
|
-
- **Add even more info with a double-`cause`.** If more info can
|
|
112
|
-
|
|
106
|
+
- **Add even more info with a double-`cause`.** If more info can be
|
|
107
|
+
added to a prior throw, nest the `cause` properties like
|
|
113
108
|
`{ some, other, info, cause: priorError }`.
|
|
114
109
|
- **Always set `cause`, even if no prior error.** Use a plain-old
|
|
115
110
|
JavaScript object following our field conventions.
|
|
116
111
|
- **Rare exception: synthetic ErrnoException style errors.** If we are
|
|
117
112
|
doing something that is similar to a system operation, it's
|
|
118
113
|
sometimes ok to mimic node's pattern.
|
|
119
|
-
- **Do not subclass `Error`.** Just create a plain old Error, and
|
|
120
|
-
|
|
114
|
+
- **Do not subclass `Error`.** Just create a plain old Error, and set
|
|
115
|
+
the `cause` with additional information.
|
|
121
116
|
|
|
122
117
|
### If you can't help, don't get in the way.
|
|
123
118
|
|
|
124
|
-
Whenever possible, if no remediation or extra information can
|
|
125
|
-
|
|
126
|
-
|
|
119
|
+
Whenever possible, if no remediation or extra information can usefully
|
|
120
|
+
be added, it's best to just not handle errors and let them be raised
|
|
121
|
+
at the higher level. For example, instead of this:
|
|
127
122
|
|
|
128
123
|
```
|
|
129
124
|
let data
|
|
@@ -142,9 +137,9 @@ const data = await readFile(someFile)
|
|
|
142
137
|
|
|
143
138
|
### Add information by using thrown error as `cause`.
|
|
144
139
|
|
|
145
|
-
If we can add information or do something else useful for the
|
|
146
|
-
|
|
147
|
-
|
|
140
|
+
If we can add information or do something else useful for the user in
|
|
141
|
+
understanding the problem, do so by creating a new `Error` and setting
|
|
142
|
+
the original thrown error as the `cause`.
|
|
148
143
|
|
|
149
144
|
```js
|
|
150
145
|
let data
|
|
@@ -158,9 +153,9 @@ try {
|
|
|
158
153
|
|
|
159
154
|
### Add even more info with a double-`cause`.
|
|
160
155
|
|
|
161
|
-
If we can add even more information, that should ideally _not_ be
|
|
162
|
-
|
|
163
|
-
|
|
156
|
+
If we can add even more information, that should ideally _not_ be put
|
|
157
|
+
on the Error we throw, but on a `cause` object. Because `cause`
|
|
158
|
+
objects can nest, we can do something like this:
|
|
164
159
|
|
|
165
160
|
```js
|
|
166
161
|
let data
|
|
@@ -204,27 +199,25 @@ throw error('could not resolve', {
|
|
|
204
199
|
})
|
|
205
200
|
```
|
|
206
201
|
|
|
207
|
-
This makes any big objects easily skipped if we want to just
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
way down the chain.
|
|
202
|
+
This makes any big objects easily skipped if we want to just output
|
|
203
|
+
the error with `console.error()` or something, but still preserves any
|
|
204
|
+
debugging information that might be useful all the way down the chain.
|
|
211
205
|
|
|
212
206
|
### Rare exception: synthetic ErrnoException style errors.
|
|
213
207
|
|
|
214
|
-
In some rare low-level cases, there are operations we perform
|
|
215
|
-
|
|
208
|
+
In some rare low-level cases, there are operations we perform that are
|
|
209
|
+
very similar to a node filesystem operation.
|
|
216
210
|
|
|
217
211
|
For example, the `@vltpkg/which` module raises an error that is
|
|
218
|
-
intentionally similar to node's filesystem `ENOENT` errors,
|
|
219
|
-
|
|
212
|
+
intentionally similar to node's filesystem `ENOENT` errors, because
|
|
213
|
+
that is semantically sensible.
|
|
220
214
|
|
|
221
|
-
In those cases, the error _must_ follow node's conventions as
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
more usefully.
|
|
215
|
+
In those cases, the error _must_ follow node's conventions as close as
|
|
216
|
+
possible. If we feel the need to add additional information beyond a
|
|
217
|
+
known system error code, string path, etc., or if the message isn't
|
|
218
|
+
one that is typically raised by the underlying system, then it's a
|
|
219
|
+
good sign that we ought to be creating an `Error` with a `cause` so
|
|
220
|
+
that it can be reported more usefully.
|
|
228
221
|
|
|
229
222
|
In such cases, this is fine:
|
|
230
223
|
|
|
@@ -246,11 +239,11 @@ throw Object.assign(new Error('could not resolve'), {
|
|
|
246
239
|
})
|
|
247
240
|
```
|
|
248
241
|
|
|
249
|
-
**Do not** copy properties from a lower-level error or cause onto
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
242
|
+
**Do not** copy properties from a lower-level error or cause onto the
|
|
243
|
+
new cause object. That is unnecessary, and obscures the origin of
|
|
244
|
+
problems. Instead, just include the lower-level error as the `cause`
|
|
245
|
+
property. If you already have a low-level error, you don't need to
|
|
246
|
+
invent a synthetic one!
|
|
254
247
|
|
|
255
248
|
For example, do not do this:
|
|
256
249
|
|
|
@@ -280,8 +273,8 @@ try {
|
|
|
280
273
|
### Do not subclass `Error`.
|
|
281
274
|
|
|
282
275
|
Just use the `Error` classes defined in the language. Additional
|
|
283
|
-
information about error causes should be on the `cause` property,
|
|
284
|
-
|
|
276
|
+
information about error causes should be on the `cause` property, not
|
|
277
|
+
implicit in the constructor type.
|
|
285
278
|
|
|
286
279
|
I.e. do not do this:
|
|
287
280
|
|
|
@@ -308,33 +301,33 @@ throw error('Could not version', { version })
|
|
|
308
301
|
All of these are optional. Additional fields may be used where
|
|
309
302
|
appropriate, and should be added to this list over time.
|
|
310
303
|
|
|
311
|
-
- `cause` - The `cause` field within a `cause` object should
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
304
|
+
- `cause` - The `cause` field within a `cause` object should always be
|
|
305
|
+
an `Error` object that was previously thrown. Note that the `cause`
|
|
306
|
+
on an Error itself might _also_ be a previously thrown error, if no
|
|
307
|
+
additional information could be usefully added beyond improving the
|
|
308
|
+
message.
|
|
316
309
|
- `name` - String. The name of something.
|
|
317
310
|
- `offset` - Number. The offset in a Buffer or file where we are
|
|
318
311
|
trying to read or write.
|
|
319
312
|
- `registry` - String or URL. A package registry.
|
|
320
|
-
- `code` - This must be a string if set, and should
|
|
321
|
-
|
|
322
|
-
|
|
313
|
+
- `code` - This must be a string if set, and should only be present if
|
|
314
|
+
it's one of our creation, not a code raised on a system error. Eg,
|
|
315
|
+
`ERESOLVE`, not `ENOENT`.
|
|
323
316
|
- `path` - The target of a file system operation.
|
|
324
317
|
- `target` - path on disk that is being written or extracted to
|
|
325
|
-
- `spec` - a `@vltpkg/spec.Spec` object relevant to the operation
|
|
326
|
-
|
|
327
|
-
- `from` - string. The file path origin of a resolution that
|
|
328
|
-
|
|
329
|
-
- `status` - Number or null. Either the exit code of a process or
|
|
330
|
-
|
|
331
|
-
- `signal` - `NodeJS.Signals` string or null, indicating the
|
|
332
|
-
|
|
318
|
+
- `spec` - a `@vltpkg/spec.Spec` object relevant to the operation that
|
|
319
|
+
failed.
|
|
320
|
+
- `from` - string. The file path origin of a resolution that failed,
|
|
321
|
+
for example in the case of relative `file:` specifiers.
|
|
322
|
+
- `status` - Number or null. Either the exit code of a process or an
|
|
323
|
+
HTTP response status code.
|
|
324
|
+
- `signal` - `NodeJS.Signals` string or null, indicating the signal
|
|
325
|
+
that terminated a process.
|
|
333
326
|
- `validOptions` - Array of valid options when something is not a
|
|
334
327
|
valid option. (For use in `did you mean X?` output.)
|
|
335
|
-
- `todo` - String message indicating what bit of work this might
|
|
336
|
-
|
|
337
|
-
todo: 'nested workspace support' }`.
|
|
328
|
+
- `todo` - String message indicating what bit of work this might be a
|
|
329
|
+
part of, what feature needs to be implemented, etc. Eg,
|
|
330
|
+
`{ todo: 'nested workspace support' }`.
|
|
338
331
|
- `wanted` - A desired value that was not found, or a regular
|
|
339
332
|
expression or other pattern describing it.
|
|
340
333
|
- `found` - The actual value, which was not wanted.
|
|
@@ -354,9 +347,9 @@ todo: 'nested workspace support' }`.
|
|
|
354
347
|
|
|
355
348
|
- If there is a _type_ problem with an argument, for example a
|
|
356
349
|
`string` was expected and a `number` was provided, throw a
|
|
357
|
-
`TypeError`. **Do not** use it for a value that is the correct
|
|
358
|
-
|
|
359
|
-
|
|
350
|
+
`TypeError`. **Do not** use it for a value that is the correct type
|
|
351
|
+
but otherwise invalid, such as a `string` argument that is actually
|
|
352
|
+
a `string` but does not match an expected pattern.
|
|
360
353
|
- If the type is fine, but a parsed string is invalid and not
|
|
361
354
|
parseable, use `SyntaxError`.
|
|
362
355
|
- In all other cases, use `Error`.
|