@owlmeans/error 0.1.0
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/LICENSE +21 -0
- package/README.md +390 -0
- package/build/.gitkeep +0 -0
- package/build/consts.d.ts +3 -0
- package/build/consts.d.ts.map +1 -0
- package/build/consts.js +3 -0
- package/build/consts.js.map +1 -0
- package/build/helper.d.ts +4 -0
- package/build/helper.d.ts.map +1 -0
- package/build/helper.js +4 -0
- package/build/helper.js.map +1 -0
- package/build/i18n/en.json +4 -0
- package/build/i18n.d.ts +2 -0
- package/build/i18n.d.ts.map +1 -0
- package/build/i18n.js +4 -0
- package/build/i18n.js.map +1 -0
- package/build/index.d.ts +6 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +6 -0
- package/build/index.js.map +1 -0
- package/build/resilient.d.ts +15 -0
- package/build/resilient.d.ts.map +1 -0
- package/build/resilient.js +63 -0
- package/build/resilient.js.map +1 -0
- package/build/types.d.ts +14 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/build/utils.d.ts +5 -0
- package/build/utils.d.ts.map +1 -0
- package/build/utils.js +26 -0
- package/build/utils.js.map +1 -0
- package/package.json +34 -0
- package/src/consts.ts +4 -0
- package/src/helper.ts +7 -0
- package/src/i18n/en.json +4 -0
- package/src/i18n.ts +5 -0
- package/src/index.ts +6 -0
- package/src/resilient.ts +81 -0
- package/src/types.ts +16 -0
- package/src/utils.ts +35 -0
- package/tsconfig.json +14 -0
- package/tsconfig.tsbuildinfo +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 OwlMeans Common — Fullstack typescript framework
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
# @owlmeans/error
|
|
2
|
+
|
|
3
|
+
A fully typed error system for seamless error handling between backend and frontend, allowing errors to be marshaled/unmarshaled while preserving type information across network boundaries.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `@owlmeans/error` package provides a robust error handling system that enables type-safe error propagation between server and client applications. The core concept revolves around the `ResilientError` class, which can be:
|
|
8
|
+
|
|
9
|
+
- **Marshaled** (serialized) on the server side
|
|
10
|
+
- **Transmitted** over the network as regular Error objects
|
|
11
|
+
- **Unmarshaled** (deserialized) on the client side back to the same typed error class
|
|
12
|
+
|
|
13
|
+
This ensures that error handling remains consistent and type-safe across the entire application stack.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @owlmeans/error
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Core Concepts
|
|
22
|
+
|
|
23
|
+
### ResilientError Class
|
|
24
|
+
|
|
25
|
+
The `ResilientError` class is the foundation of the error system. It extends the standard JavaScript `Error` class and provides additional functionality for marshaling, unmarshaling, and type conversion.
|
|
26
|
+
|
|
27
|
+
### Error Conversion System
|
|
28
|
+
|
|
29
|
+
The package includes a flexible converter system that allows registration of custom error types and their conversion logic through the `Converter` interface.
|
|
30
|
+
|
|
31
|
+
### Marshaling & Unmarshaling
|
|
32
|
+
|
|
33
|
+
- **Marshaling**: Converting a ResilientError into a transferable format
|
|
34
|
+
- **Unmarshaling**: Reconstructing a ResilientError from its marshaled form
|
|
35
|
+
|
|
36
|
+
## API Reference
|
|
37
|
+
|
|
38
|
+
### ResilientError Class
|
|
39
|
+
|
|
40
|
+
#### Properties
|
|
41
|
+
|
|
42
|
+
- `type: string` - The error type identifier
|
|
43
|
+
- `oiriginalStack?: string` - The original stack trace when the error was created
|
|
44
|
+
|
|
45
|
+
#### Static Properties
|
|
46
|
+
|
|
47
|
+
- `separator: string` - The separator used in marshaled error messages (default: `'|||'`)
|
|
48
|
+
- `typeName: string` - The default type name for ResilientError instances (default: `'ResilientError'`)
|
|
49
|
+
- `converters: Converter[]` - Array of registered error converters
|
|
50
|
+
|
|
51
|
+
#### Static Methods
|
|
52
|
+
|
|
53
|
+
##### `registerErrorClass(resilientErrorClass, errorClass?)`
|
|
54
|
+
|
|
55
|
+
Registers a custom error class with the converter system.
|
|
56
|
+
|
|
57
|
+
**Parameters:**
|
|
58
|
+
- `resilientErrorClass: ResilientErrorConstructor` - The ResilientError subclass to register
|
|
59
|
+
- `errorClass?: ErrorConstructor` - Optional native Error class to convert from
|
|
60
|
+
|
|
61
|
+
**Returns:** `Converter` - The created converter instance
|
|
62
|
+
|
|
63
|
+
**Example:**
|
|
64
|
+
```typescript
|
|
65
|
+
class CustomError extends ResilientError {
|
|
66
|
+
static typeName = 'CustomError'
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
ResilientError.registerErrorClass(CustomError, TypeError)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
##### `ensure(err, throwOnUnknown?)`
|
|
73
|
+
|
|
74
|
+
Ensures an error is converted to a ResilientError instance.
|
|
75
|
+
|
|
76
|
+
**Parameters:**
|
|
77
|
+
- `err: Error | string` - The error to convert
|
|
78
|
+
- `throwOnUnknown?: boolean` - Whether to throw on unknown error types (default: false)
|
|
79
|
+
|
|
80
|
+
**Returns:** `ResilientError` - The converted error
|
|
81
|
+
|
|
82
|
+
**Example:**
|
|
83
|
+
```typescript
|
|
84
|
+
const resilientError = ResilientError.ensure(new Error('Something went wrong'))
|
|
85
|
+
const typedError = ResilientError.ensure('Error message')
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
##### `marshal(err)`
|
|
89
|
+
|
|
90
|
+
Marshals an error for network transmission.
|
|
91
|
+
|
|
92
|
+
**Parameters:**
|
|
93
|
+
- `err: Error` - The error to marshal
|
|
94
|
+
|
|
95
|
+
**Returns:** `Error` - A marshaled error object
|
|
96
|
+
|
|
97
|
+
**Example:**
|
|
98
|
+
```typescript
|
|
99
|
+
const original = new ResilientError('CustomError', 'Something failed')
|
|
100
|
+
const marshaled = ResilientError.marshal(original)
|
|
101
|
+
// marshaled.message contains: "CustomError|||Something failed|||[stack trace]"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
#### Instance Methods
|
|
105
|
+
|
|
106
|
+
##### `marshal()`
|
|
107
|
+
|
|
108
|
+
Marshals the current error instance.
|
|
109
|
+
|
|
110
|
+
**Returns:** `Error` - A marshaled error object
|
|
111
|
+
|
|
112
|
+
**Example:**
|
|
113
|
+
```typescript
|
|
114
|
+
const error = new ResilientError('MyError', 'Description')
|
|
115
|
+
const marshaled = error.marshal()
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
##### `finalizeUnmarshal()`
|
|
119
|
+
|
|
120
|
+
Called after unmarshaling to perform any post-processing. Override in subclasses for custom behavior.
|
|
121
|
+
|
|
122
|
+
**Returns:** `void`
|
|
123
|
+
|
|
124
|
+
#### Constructor
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
constructor(type: string, message: string, stack?: string)
|
|
128
|
+
constructor(message: string, stack?: string)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Parameters:**
|
|
132
|
+
- `type: string` - The error type identifier
|
|
133
|
+
- `message: string` - The error message
|
|
134
|
+
- `stack?: string` - Optional stack trace
|
|
135
|
+
|
|
136
|
+
**Example:**
|
|
137
|
+
```typescript
|
|
138
|
+
const error1 = new ResilientError('ValidationError', 'Invalid input')
|
|
139
|
+
const error2 = new ResilientError('Network error') // uses default type
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Helper Functions
|
|
143
|
+
|
|
144
|
+
#### `enuserError<T>(err, throwOnUnknown?)`
|
|
145
|
+
|
|
146
|
+
Convenience function that ensures an error is a ResilientError instance.
|
|
147
|
+
|
|
148
|
+
**Parameters:**
|
|
149
|
+
- `err: Error | string` - The error to ensure
|
|
150
|
+
- `throwOnUnknown?: boolean` - Whether to throw on unknown error types
|
|
151
|
+
|
|
152
|
+
**Returns:** `T extends ResilientError` - The ensured error
|
|
153
|
+
|
|
154
|
+
**Example:**
|
|
155
|
+
```typescript
|
|
156
|
+
import { enuserError } from '@owlmeans/error'
|
|
157
|
+
|
|
158
|
+
const resilientError = enuserError(new Error('Something went wrong'))
|
|
159
|
+
const typedError = enuserError<MyCustomError>(someError)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### `marshalError(err)`
|
|
163
|
+
|
|
164
|
+
Convenience function that marshals an error after ensuring it's a ResilientError.
|
|
165
|
+
|
|
166
|
+
**Parameters:**
|
|
167
|
+
- `err: Error | string` - The error to marshal
|
|
168
|
+
|
|
169
|
+
**Returns:** `Error` - The marshaled error
|
|
170
|
+
|
|
171
|
+
**Example:**
|
|
172
|
+
```typescript
|
|
173
|
+
import { marshalError } from '@owlmeans/error'
|
|
174
|
+
|
|
175
|
+
const marshaled = marshalError(new Error('Server error'))
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Utility Functions
|
|
179
|
+
|
|
180
|
+
#### `createErrorConverter(resilientErrorClass, errorClass?)`
|
|
181
|
+
|
|
182
|
+
Creates a converter for transforming native errors to ResilientError instances.
|
|
183
|
+
|
|
184
|
+
**Parameters:**
|
|
185
|
+
- `resilientErrorClass: ResilientErrorConstructor` - The target ResilientError class
|
|
186
|
+
- `errorClass?: ErrorConstructor` - Optional source Error class to match against
|
|
187
|
+
|
|
188
|
+
**Returns:** `Converter` - The created converter
|
|
189
|
+
|
|
190
|
+
**Example:**
|
|
191
|
+
```typescript
|
|
192
|
+
import { createErrorConverter } from '@owlmeans/error'
|
|
193
|
+
|
|
194
|
+
const converter = createErrorConverter(MyResilientError, TypeError)
|
|
195
|
+
ResilientError.converters.push(converter)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### `unmarshal<T>(errorClass)`
|
|
199
|
+
|
|
200
|
+
Creates an unmarshaling function for a specific error class.
|
|
201
|
+
|
|
202
|
+
**Parameters:**
|
|
203
|
+
- `errorClass: ResilientErrorConstructor` - The error class to unmarshal to
|
|
204
|
+
|
|
205
|
+
**Returns:** `(err: Error) => T` - Function that unmarshals errors to the specified type
|
|
206
|
+
|
|
207
|
+
**Example:**
|
|
208
|
+
```typescript
|
|
209
|
+
import { unmarshal } from '@owlmeans/error'
|
|
210
|
+
|
|
211
|
+
const unmarshalMyError = unmarshal(MyResilientError)
|
|
212
|
+
const restored = unmarshalMyError(marshaledError)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Type Definitions
|
|
216
|
+
|
|
217
|
+
#### `ValueOrError<T>`
|
|
218
|
+
|
|
219
|
+
A utility type that represents either a value or a ResilientError.
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
type ValueOrError<T> = T | ResilientError
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Example:**
|
|
226
|
+
```typescript
|
|
227
|
+
function processData(): ValueOrError<string> {
|
|
228
|
+
if (someCondition) {
|
|
229
|
+
return "success"
|
|
230
|
+
}
|
|
231
|
+
return new ResilientError('ProcessingError', 'Failed to process')
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
#### `Converter`
|
|
236
|
+
|
|
237
|
+
Interface for error conversion logic.
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
interface Converter {
|
|
241
|
+
match: (err: Error) => boolean
|
|
242
|
+
convert: (err: Error) => ResilientError
|
|
243
|
+
isMarshaled: (err: Error) => boolean
|
|
244
|
+
unmarshal: (err: Error) => ResilientError
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Methods:**
|
|
249
|
+
- `match(err)` - Determines if the converter can handle the error
|
|
250
|
+
- `convert(err)` - Converts the error to a ResilientError
|
|
251
|
+
- `isMarshaled(err)` - Checks if the error is in marshaled form
|
|
252
|
+
- `unmarshal(err)` - Unmarshals the error back to ResilientError
|
|
253
|
+
|
|
254
|
+
#### `ResilientErrorConstructor<T>`
|
|
255
|
+
|
|
256
|
+
Constructor interface for ResilientError classes.
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
interface ResilientErrorConstructor<T extends ResilientError = ResilientError> {
|
|
260
|
+
new (type: string, message: string, stack?: string): T
|
|
261
|
+
new (message: string, stack?: string): T
|
|
262
|
+
typeName: string
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Constants
|
|
267
|
+
|
|
268
|
+
#### `SEPARATOR`
|
|
269
|
+
|
|
270
|
+
The default separator used in marshaled error messages.
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
const SEPARATOR = '|||'
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
#### `RESILENT_ERROR`
|
|
277
|
+
|
|
278
|
+
The default type name for ResilientError instances.
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
const RESILENT_ERROR = 'ResilientError'
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Usage Examples
|
|
285
|
+
|
|
286
|
+
### Basic Error Handling
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
import { ResilientError, enuserError } from '@owlmeans/error'
|
|
290
|
+
|
|
291
|
+
// Create a resilient error
|
|
292
|
+
const error = new ResilientError('ValidationError', 'Invalid email format')
|
|
293
|
+
|
|
294
|
+
// Ensure any error is resilient
|
|
295
|
+
const resilientError = enuserError(new Error('Something went wrong'))
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Custom Error Types
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
import { ResilientError } from '@owlmeans/error'
|
|
302
|
+
|
|
303
|
+
class ValidationError extends ResilientError {
|
|
304
|
+
static typeName = 'ValidationError'
|
|
305
|
+
|
|
306
|
+
constructor(field: string, message: string) {
|
|
307
|
+
super(ValidationError.typeName, `${field}: ${message}`)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Register the custom error type
|
|
312
|
+
ResilientError.registerErrorClass(ValidationError)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Network Error Transmission
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
import { ResilientError, marshalError, enuserError } from '@owlmeans/error'
|
|
319
|
+
|
|
320
|
+
// Server side - marshal error for transmission
|
|
321
|
+
function handleServerError(error: Error) {
|
|
322
|
+
const marshaled = marshalError(error)
|
|
323
|
+
return { error: marshaled.message }
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Client side - unmarshal received error
|
|
327
|
+
function handleClientError(errorMessage: string) {
|
|
328
|
+
const error = new Error(errorMessage)
|
|
329
|
+
const resilientError = enuserError(error)
|
|
330
|
+
return resilientError
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Error Conversion System
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
import { ResilientError, createErrorConverter } from '@owlmeans/error'
|
|
338
|
+
|
|
339
|
+
class NetworkError extends ResilientError {
|
|
340
|
+
static typeName = 'NetworkError'
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Register converter for fetch errors
|
|
344
|
+
const converter = createErrorConverter(NetworkError, TypeError)
|
|
345
|
+
ResilientError.converters.push(converter)
|
|
346
|
+
|
|
347
|
+
// Now TypeError instances will be automatically converted
|
|
348
|
+
const converted = ResilientError.ensure(new TypeError('Network failure'))
|
|
349
|
+
// converted will be a NetworkError instance
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Working with ValueOrError
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
import { ValueOrError, ResilientError } from '@owlmeans/error'
|
|
356
|
+
|
|
357
|
+
function fetchUserData(id: string): ValueOrError<User> {
|
|
358
|
+
try {
|
|
359
|
+
// Simulate API call
|
|
360
|
+
return { id, name: 'John Doe' }
|
|
361
|
+
} catch (error) {
|
|
362
|
+
return new ResilientError('FetchError', 'Failed to fetch user')
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const result = fetchUserData('123')
|
|
367
|
+
if (result instanceof ResilientError) {
|
|
368
|
+
console.error('Error:', result.message)
|
|
369
|
+
} else {
|
|
370
|
+
console.log('User:', result.name)
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## Best Practices
|
|
375
|
+
|
|
376
|
+
1. **Define Custom Error Types**: Create specific error classes for different error categories
|
|
377
|
+
2. **Register Error Classes**: Use `registerErrorClass()` to enable automatic conversion
|
|
378
|
+
3. **Use Helper Functions**: Leverage `enuserError()` and `marshalError()` for common operations
|
|
379
|
+
4. **Implement finalizeUnmarshal()**: Override in custom error classes for post-processing
|
|
380
|
+
5. **Type Safety**: Use `ValueOrError<T>` type for functions that may return errors
|
|
381
|
+
|
|
382
|
+
## Integration with OwlMeans Common
|
|
383
|
+
|
|
384
|
+
This package integrates with the broader OwlMeans Common library ecosystem, following the established patterns for:
|
|
385
|
+
- **Types**: Error-related type definitions
|
|
386
|
+
- **Helpers**: Utility functions for error handling
|
|
387
|
+
- **Service**: Domain-specific error handling logic
|
|
388
|
+
- **i18n**: Internationalization support for error messages
|
|
389
|
+
|
|
390
|
+
The error system is designed to work seamlessly across different OwlMeans packages including server, client, and web implementations.
|
package/build/.gitkeep
ADDED
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,SAAS,QAAQ,CAAA;AAE9B,eAAO,MAAM,cAAc,mBAAmB,CAAA"}
|
package/build/consts.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,CAAA;AAE9B,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,CAAA"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ResilientError } from './resilient.js';
|
|
2
|
+
export declare const enuserError: <T extends ResilientError = ResilientError>(err: Error | string, throwOnUnknown?: boolean) => T;
|
|
3
|
+
export declare const marshalError: (err: Error | string) => Error;
|
|
4
|
+
//# sourceMappingURL=helper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAE/C,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,cAAc,wBAAwB,KAAK,GAAG,MAAM,mBAAmB,OAAO,KAAG,CACtE,CAAA;AAEjD,eAAO,MAAM,YAAY,QAAS,KAAK,GAAG,MAAM,KAAG,KACC,CAAA"}
|
package/build/helper.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ResilientError } from './resilient.js';
|
|
2
|
+
export const enuserError = (err, throwOnUnknown) => ResilientError.ensure(err, throwOnUnknown);
|
|
3
|
+
export const marshalError = (err) => ResilientError.marshal(ResilientError.ensure(err));
|
|
4
|
+
//# sourceMappingURL=helper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAE/C,MAAM,CAAC,MAAM,WAAW,GAAG,CAA4C,GAAmB,EAAE,cAAwB,EAAK,EAAE,CACzH,cAAc,CAAC,MAAM,CAAC,GAAG,EAAE,cAAc,CAAM,CAAA;AAEjD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAmB,EAAS,EAAE,CACzD,cAAc,CAAC,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA"}
|
package/build/i18n.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":""}
|
package/build/i18n.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAE3C,OAAO,EAAE,MAAM,gBAAgB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AAErD,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAA"}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Converter, ResilientErrorConstructor } from './types.js';
|
|
2
|
+
export declare class ResilientError extends Error {
|
|
3
|
+
static separator: string;
|
|
4
|
+
static typeName: string;
|
|
5
|
+
static converters: Converter[];
|
|
6
|
+
static registerErrorClass(resilientErrorClass: ResilientErrorConstructor, errorClass?: ErrorConstructor): Converter;
|
|
7
|
+
static ensure(err: Error | string, throwOnUnknown?: boolean): ResilientError;
|
|
8
|
+
static marshal(err: Error): Error;
|
|
9
|
+
type: string;
|
|
10
|
+
oiriginalStack?: string;
|
|
11
|
+
constructor(type: string, message: string, stack?: string);
|
|
12
|
+
marshal(): Error;
|
|
13
|
+
finalizeUnmarshal(): void;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=resilient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resilient.d.ts","sourceRoot":"","sources":["../src/resilient.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AAGtE,qBAAa,cAAe,SAAQ,KAAK;IACvC,OAAc,SAAS,EAAE,MAAM,CAAY;IAE3C,OAAc,QAAQ,EAAE,MAAM,CAAiB;IAE/C,OAAc,UAAU,EAAE,SAAS,EAAE,CAAK;WAE5B,kBAAkB,CAAC,mBAAmB,EAAE,yBAAyB,EAAE,UAAU,CAAC,EAAE,gBAAgB,GAAG,SAAS;WAO5G,MAAM,CAAC,GAAG,EAAE,KAAK,GAAG,MAAM,EAAE,cAAc,CAAC,EAAE,OAAO,GAAG,cAAc;WA8BrE,OAAO,CAAC,GAAG,EAAE,KAAK,GAAG,KAAK;IAQjC,IAAI,EAAE,MAAM,CAAiB;IAE7B,cAAc,CAAC,EAAE,MAAM,CAAA;gBAElB,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;IAUzD,OAAO,IAAI,KAAK;IAIhB,iBAAiB,IAAI,IAAI;CAC1B"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { RESILENT_ERROR, SEPARATOR } from './consts.js';
|
|
2
|
+
import { createErrorConverter } from './utils.js';
|
|
3
|
+
export class ResilientError extends Error {
|
|
4
|
+
static separator = SEPARATOR;
|
|
5
|
+
static typeName = RESILENT_ERROR;
|
|
6
|
+
static converters = [];
|
|
7
|
+
static registerErrorClass(resilientErrorClass, errorClass) {
|
|
8
|
+
const converter = createErrorConverter(resilientErrorClass, errorClass);
|
|
9
|
+
this.converters.push(converter);
|
|
10
|
+
return converter;
|
|
11
|
+
}
|
|
12
|
+
static ensure(err, throwOnUnknown) {
|
|
13
|
+
err = typeof err === 'string' ? new Error(err) : err;
|
|
14
|
+
if (err instanceof ResilientError) {
|
|
15
|
+
return err;
|
|
16
|
+
}
|
|
17
|
+
// We don't proceed SyntaxError - system should crash in this case
|
|
18
|
+
if (err instanceof SyntaxError) {
|
|
19
|
+
throw err;
|
|
20
|
+
}
|
|
21
|
+
// Umarshal marhalled error that is wrapepd to ordinary error
|
|
22
|
+
const unmarhaller = this.converters.toReversed().find(converter => converter.isMarshaled(err));
|
|
23
|
+
if (unmarhaller != null) {
|
|
24
|
+
return unmarhaller.unmarshal(err);
|
|
25
|
+
}
|
|
26
|
+
// Convert object of Error subtypes to ResilientError subtype
|
|
27
|
+
const converter = this.converters.find(converter => converter.match(err));
|
|
28
|
+
if (converter != null) {
|
|
29
|
+
return converter.convert(err);
|
|
30
|
+
}
|
|
31
|
+
if (throwOnUnknown === true) {
|
|
32
|
+
throw err;
|
|
33
|
+
}
|
|
34
|
+
return new ResilientError(this.typeName, err.message, err.stack);
|
|
35
|
+
}
|
|
36
|
+
static marshal(err) {
|
|
37
|
+
if (err instanceof ResilientError) {
|
|
38
|
+
return new Error([err.type, err.message, err.oiriginalStack].join(this.separator));
|
|
39
|
+
}
|
|
40
|
+
return new Error([this.typeName, err.message, err.stack].join(this.separator));
|
|
41
|
+
}
|
|
42
|
+
type = RESILENT_ERROR;
|
|
43
|
+
oiriginalStack;
|
|
44
|
+
constructor(type, message, stack) {
|
|
45
|
+
super(message);
|
|
46
|
+
this.type = type;
|
|
47
|
+
if (stack != null) {
|
|
48
|
+
this.oiriginalStack = stack;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.oiriginalStack = this.stack;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
marshal() {
|
|
55
|
+
return ResilientError.marshal(this);
|
|
56
|
+
}
|
|
57
|
+
finalizeUnmarshal() { }
|
|
58
|
+
}
|
|
59
|
+
ResilientError.converters.push({
|
|
60
|
+
...createErrorConverter(ResilientError),
|
|
61
|
+
match: () => true
|
|
62
|
+
});
|
|
63
|
+
//# sourceMappingURL=resilient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resilient.js","sourceRoot":"","sources":["../src/resilient.ts"],"names":[],"mappings":"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"}
|
package/build/types.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ResilientError } from './resilient.js';
|
|
2
|
+
export type ValueOrError<T> = T | ResilientError;
|
|
3
|
+
export interface Converter {
|
|
4
|
+
match: (err: Error) => boolean;
|
|
5
|
+
convert: (err: Error) => ResilientError;
|
|
6
|
+
isMarshaled: (err: Error) => boolean;
|
|
7
|
+
unmarshal: (err: Error) => ResilientError;
|
|
8
|
+
}
|
|
9
|
+
export interface ResilientErrorConstructor<T extends ResilientError = ResilientError> {
|
|
10
|
+
new (type: string, message: string, stack?: string): T;
|
|
11
|
+
new (message: string, stack?: string): T;
|
|
12
|
+
typeName: string;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEpD,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,cAAc,CAAA;AAEhD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,OAAO,CAAA;IAC9B,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,cAAc,CAAA;IACvC,WAAW,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,OAAO,CAAA;IACpC,SAAS,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,cAAc,CAAA;CAC1C;AAED,MAAM,WAAW,yBAAyB,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc;IAClF,KAAK,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,CAAC,CAAA;IACtD,KAAK,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,CAAC,CAAA;IACxC,QAAQ,EAAE,MAAM,CAAA;CACjB"}
|
package/build/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/build/utils.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ResilientError } from './resilient.js';
|
|
2
|
+
import type { Converter, ResilientErrorConstructor } from './types.js';
|
|
3
|
+
export declare const createErrorConverter: (resilientErrorClass: ResilientErrorConstructor, errorClass?: ErrorConstructor) => Converter;
|
|
4
|
+
export declare const unmarshal: <T extends ResilientError = ResilientError>(errorClass: ResilientErrorConstructor) => (err: Error) => T;
|
|
5
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AAEtE,eAAO,MAAM,oBAAoB,wBACV,yBAAyB,eACjC,gBAAgB,KAC5B,SAQF,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,cAAc,+BAA+B,yBAAyB,WAClG,KAAK,KAAG,CAiBb,CAAA"}
|
package/build/utils.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ResilientError } from './resilient.js';
|
|
2
|
+
export const createErrorConverter = (resilientErrorClass, errorClass) => {
|
|
3
|
+
return {
|
|
4
|
+
match: err => errorClass != null && err instanceof errorClass,
|
|
5
|
+
convert: err => new resilientErrorClass(err.message, err.stack),
|
|
6
|
+
isMarshaled: err => err.message.startsWith(resilientErrorClass.typeName + ResilientError.separator),
|
|
7
|
+
unmarshal: unmarshal(resilientErrorClass)
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export const unmarshal = (errorClass) => (err) => {
|
|
11
|
+
if (err instanceof errorClass) {
|
|
12
|
+
return err;
|
|
13
|
+
}
|
|
14
|
+
const args = err.message.split(ResilientError.separator, 3);
|
|
15
|
+
if (args.length < 2) {
|
|
16
|
+
throw SyntaxError('Invalid marshaled error');
|
|
17
|
+
}
|
|
18
|
+
if (args[0] === errorClass.typeName) {
|
|
19
|
+
args.shift();
|
|
20
|
+
}
|
|
21
|
+
const error = new errorClass(...args);
|
|
22
|
+
error.message = args[0];
|
|
23
|
+
error.finalizeUnmarshal();
|
|
24
|
+
return error;
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAG/C,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,mBAA8C,EAC9C,UAA6B,EAClB,EAAE;IACb,OAAO;QACL,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,UAAU,IAAI,IAAI,IAAI,GAAG,YAAY,UAAU;QAC7D,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI,mBAAmB,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC;QAC/D,WAAW,EAAE,GAAG,CAAC,EAAE,CACjB,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,mBAAmB,CAAC,QAAQ,GAAG,cAAc,CAAC,SAAS,CAAC;QACjF,SAAS,EAAE,SAAS,CAAC,mBAAmB,CAAC;KAC1C,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,CAA4C,UAAqC,EAAE,EAAE,CAC5G,CAAC,GAAU,EAAK,EAAE;IAChB,IAAI,GAAG,YAAY,UAAU,EAAE,CAAC;QAC9B,OAAO,GAAQ,CAAA;IACjB,CAAC;IACD,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAA8B,CAAA;IACxF,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,WAAW,CAAC,yBAAyB,CAAC,CAAA;IAC9C,CAAC;IAED,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,QAAQ,EAAE,CAAC;QACpC,IAAI,CAAC,KAAK,EAAE,CAAA;IACd,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,GAAG,IAAI,CAAC,CAAA;IACrC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;IACvB,KAAK,CAAC,iBAAiB,EAAE,CAAA;IACzB,OAAO,KAAU,CAAA;AACnB,CAAC,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@owlmeans/error",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "tsc -b",
|
|
7
|
+
"dev": "sleep 156 && nodemon -e ts,tsx,json --watch src --exec \"tsc -p ./tsconfig.json\"",
|
|
8
|
+
"watch": "tsc -b -w --preserveWatchOutput --pretty"
|
|
9
|
+
},
|
|
10
|
+
"main": "build/index.js",
|
|
11
|
+
"module": "build/index.js",
|
|
12
|
+
"types": "build/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": "./build/index.js",
|
|
16
|
+
"require": "./build/index.js",
|
|
17
|
+
"default": "./build/index.js",
|
|
18
|
+
"module": "./build/index.js",
|
|
19
|
+
"types": "./build/index.d.ts"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@owlmeans/i18n": "^0.1.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"nodemon": "^3.1.7",
|
|
27
|
+
"npm-check": "^6.0.1",
|
|
28
|
+
"typescript": "^5.6.3"
|
|
29
|
+
},
|
|
30
|
+
"private": false,
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/consts.ts
ADDED
package/src/helper.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ResilientError } from './resilient.js'
|
|
2
|
+
|
|
3
|
+
export const enuserError = <T extends ResilientError = ResilientError>(err: Error | string, throwOnUnknown?: boolean): T =>
|
|
4
|
+
ResilientError.ensure(err, throwOnUnknown) as T
|
|
5
|
+
|
|
6
|
+
export const marshalError = (err: Error | string): Error =>
|
|
7
|
+
ResilientError.marshal(ResilientError.ensure(err))
|
package/src/i18n/en.json
ADDED
package/src/i18n.ts
ADDED
package/src/index.ts
ADDED
package/src/resilient.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { RESILENT_ERROR, SEPARATOR } from './consts.js'
|
|
2
|
+
import type { Converter, ResilientErrorConstructor } from './types.js'
|
|
3
|
+
import { createErrorConverter } from './utils.js'
|
|
4
|
+
|
|
5
|
+
export class ResilientError extends Error {
|
|
6
|
+
public static separator: string = SEPARATOR
|
|
7
|
+
|
|
8
|
+
public static typeName: string = RESILENT_ERROR
|
|
9
|
+
|
|
10
|
+
public static converters: Converter[] = []
|
|
11
|
+
|
|
12
|
+
public static registerErrorClass(resilientErrorClass: ResilientErrorConstructor, errorClass?: ErrorConstructor): Converter {
|
|
13
|
+
const converter = createErrorConverter(resilientErrorClass, errorClass)
|
|
14
|
+
this.converters.push(converter)
|
|
15
|
+
|
|
16
|
+
return converter
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public static ensure(err: Error | string, throwOnUnknown?: boolean): ResilientError {
|
|
20
|
+
err = typeof err === 'string' ? new Error(err) : err
|
|
21
|
+
if (err instanceof ResilientError) {
|
|
22
|
+
return err
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// We don't proceed SyntaxError - system should crash in this case
|
|
26
|
+
if (err instanceof SyntaxError) {
|
|
27
|
+
throw err
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Umarshal marhalled error that is wrapepd to ordinary error
|
|
31
|
+
const unmarhaller = this.converters.toReversed().find(converter => converter.isMarshaled(err))
|
|
32
|
+
if (unmarhaller != null) {
|
|
33
|
+
return unmarhaller.unmarshal(err)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Convert object of Error subtypes to ResilientError subtype
|
|
37
|
+
const converter = this.converters.find(converter => converter.match(err))
|
|
38
|
+
if (converter != null) {
|
|
39
|
+
return converter.convert(err)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (throwOnUnknown === true) {
|
|
43
|
+
throw err
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return new ResilientError(this.typeName, err.message, err.stack)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public static marshal(err: Error): Error {
|
|
50
|
+
if (err instanceof ResilientError) {
|
|
51
|
+
return new Error([err.type, err.message, err.oiriginalStack].join(this.separator))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return new Error([this.typeName, err.message, err.stack].join(this.separator))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public type: string = RESILENT_ERROR
|
|
58
|
+
|
|
59
|
+
public oiriginalStack?: string
|
|
60
|
+
|
|
61
|
+
constructor(type: string, message: string, stack?: string) {
|
|
62
|
+
super(message)
|
|
63
|
+
this.type = type
|
|
64
|
+
if (stack != null) {
|
|
65
|
+
this.oiriginalStack = stack
|
|
66
|
+
} else {
|
|
67
|
+
this.oiriginalStack = this.stack
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
marshal(): Error {
|
|
72
|
+
return ResilientError.marshal(this)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
finalizeUnmarshal(): void { }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
ResilientError.converters.push({
|
|
79
|
+
...createErrorConverter(ResilientError as ResilientErrorConstructor),
|
|
80
|
+
match: () => true
|
|
81
|
+
})
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ResilientError } from './resilient.js'
|
|
2
|
+
|
|
3
|
+
export type ValueOrError<T> = T | ResilientError
|
|
4
|
+
|
|
5
|
+
export interface Converter {
|
|
6
|
+
match: (err: Error) => boolean
|
|
7
|
+
convert: (err: Error) => ResilientError
|
|
8
|
+
isMarshaled: (err: Error) => boolean
|
|
9
|
+
unmarshal: (err: Error) => ResilientError
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ResilientErrorConstructor<T extends ResilientError = ResilientError> {
|
|
13
|
+
new (type: string, message: string, stack?: string): T
|
|
14
|
+
new (message: string, stack?: string): T
|
|
15
|
+
typeName: string
|
|
16
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ResilientError } from './resilient.js'
|
|
2
|
+
import type { Converter, ResilientErrorConstructor } from './types.js'
|
|
3
|
+
|
|
4
|
+
export const createErrorConverter = (
|
|
5
|
+
resilientErrorClass: ResilientErrorConstructor,
|
|
6
|
+
errorClass?: ErrorConstructor
|
|
7
|
+
): Converter => {
|
|
8
|
+
return {
|
|
9
|
+
match: err => errorClass != null && err instanceof errorClass,
|
|
10
|
+
convert: err => new resilientErrorClass(err.message, err.stack),
|
|
11
|
+
isMarshaled: err =>
|
|
12
|
+
err.message.startsWith(resilientErrorClass.typeName + ResilientError.separator),
|
|
13
|
+
unmarshal: unmarshal(resilientErrorClass)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const unmarshal = <T extends ResilientError = ResilientError>(errorClass: ResilientErrorConstructor) =>
|
|
18
|
+
(err: Error): T => {
|
|
19
|
+
if (err instanceof errorClass) {
|
|
20
|
+
return err as T
|
|
21
|
+
}
|
|
22
|
+
const args = err.message.split(ResilientError.separator, 3) as [string, string, string?]
|
|
23
|
+
if (args.length < 2) {
|
|
24
|
+
throw SyntaxError('Invalid marshaled error')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (args[0] === errorClass.typeName) {
|
|
28
|
+
args.shift()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const error = new errorClass(...args)
|
|
32
|
+
error.message = args[0]
|
|
33
|
+
error.finalizeUnmarshal()
|
|
34
|
+
return error as T
|
|
35
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": [
|
|
3
|
+
"../tsconfig.default.json",
|
|
4
|
+
],
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"rootDir": "./src/", /* Specify the root folder within your source files. */
|
|
7
|
+
"outDir": "./build/", /* Specify an output folder for all emitted files. */
|
|
8
|
+
},
|
|
9
|
+
"exclude": [
|
|
10
|
+
"./dist/**/*",
|
|
11
|
+
"./build/**/*",
|
|
12
|
+
"./*.ts"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["./src/consts.ts","./src/helper.ts","./src/i18n.ts","./src/index.ts","./src/resilient.ts","./src/types.ts","./src/utils.ts"],"version":"5.6.3"}
|