@naturalcycles/js-lib 14.171.0 → 14.173.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.
Files changed (42) hide show
  1. package/dist/error/assert.d.ts +0 -4
  2. package/dist/error/assert.js +7 -14
  3. package/dist/error/error.model.d.ts +8 -5
  4. package/dist/error/error.util.d.ts +96 -1
  5. package/dist/error/error.util.js +167 -3
  6. package/dist/error/try.d.ts +0 -9
  7. package/dist/error/try.js +6 -19
  8. package/dist/http/fetcher.d.ts +1 -1
  9. package/dist/http/fetcher.js +3 -4
  10. package/dist/index.d.ts +0 -3
  11. package/dist/index.js +0 -3
  12. package/dist/promise/pTimeout.d.ts +1 -4
  13. package/dist/promise/pTimeout.js +2 -9
  14. package/dist/string/json.util.js +2 -2
  15. package/dist-esm/error/assert.js +1 -7
  16. package/dist-esm/error/error.util.js +159 -2
  17. package/dist-esm/error/try.js +1 -13
  18. package/dist-esm/http/fetcher.js +2 -3
  19. package/dist-esm/index.js +0 -3
  20. package/dist-esm/promise/pTimeout.js +1 -7
  21. package/dist-esm/string/json.util.js +1 -1
  22. package/package.json +2 -2
  23. package/src/error/assert.ts +1 -8
  24. package/src/error/error.model.ts +9 -8
  25. package/src/error/error.util.ts +230 -2
  26. package/src/error/try.ts +1 -18
  27. package/src/http/fetcher.ts +8 -3
  28. package/src/index.ts +0 -3
  29. package/src/promise/pTimeout.ts +1 -8
  30. package/src/string/json.util.ts +1 -1
  31. package/dist/error/app.error.d.ts +0 -31
  32. package/dist/error/app.error.js +0 -57
  33. package/dist/error/httpRequestError.d.ts +0 -28
  34. package/dist/error/httpRequestError.js +0 -32
  35. package/dist/error/jsonParseError.d.ts +0 -11
  36. package/dist/error/jsonParseError.js +0 -14
  37. package/dist-esm/error/app.error.js +0 -53
  38. package/dist-esm/error/httpRequestError.js +0 -28
  39. package/dist-esm/error/jsonParseError.js +0 -10
  40. package/src/error/app.error.ts +0 -81
  41. package/src/error/httpRequestError.ts +0 -38
  42. package/src/error/jsonParseError.ts +0 -20
@@ -1,4 +1,4 @@
1
- import { AppError, _jsonParseIfPossible, _stringifyAny } from '..';
1
+ import { _jsonParseIfPossible, _stringifyAny, _truncate, _truncateMiddle } from '..';
2
2
  /**
3
3
  * Useful to ensure that error in `catch (err) { ... }`
4
4
  * is indeed an Error (and not e.g `string` or `undefined`).
@@ -31,7 +31,6 @@ export function _anyToError(o, errorClass = Error, errorData) {
31
31
  */
32
32
  export function _anyToErrorObject(o, errorData) {
33
33
  let eo;
34
- // if (o instanceof Error) {
35
34
  if (_isErrorLike(o)) {
36
35
  eo = _errorLikeToErrorObject(o);
37
36
  }
@@ -63,6 +62,14 @@ export function _anyToErrorObject(o, errorData) {
63
62
  return eo;
64
63
  }
65
64
  export function _errorLikeToErrorObject(e) {
65
+ // If it's already an ErrorObject - just return it
66
+ // AppError satisfies ErrorObject interface
67
+ // Error does not satisfy (lacks `data`)
68
+ // UPD: no, we expect a "plain object" here as an output,
69
+ // because Error classes sometimes have non-enumerable properties (e.g data)
70
+ if (!(e instanceof Error) && _isErrorObject(e)) {
71
+ return e;
72
+ }
66
73
  const obj = {
67
74
  name: e.name,
68
75
  message: e.message,
@@ -119,6 +126,47 @@ export function _errorObjectToError(o, errorClass = Error) {
119
126
  }
120
127
  return err;
121
128
  }
129
+ // These "common" error classes will not be printed as part of the Error snippet
130
+ const commonErrorClasses = new Set([
131
+ 'Error',
132
+ 'AppError',
133
+ 'AssertionError',
134
+ 'HttpRequestError',
135
+ 'JoiValidationError',
136
+ ]);
137
+ /**
138
+ * Provides a short semi-user-friendly error message snippet,
139
+ * that would allow to give a hint to the user what went wrong,
140
+ * also to developers and CS to distinguish between different errors.
141
+ *
142
+ * It's not supposed to have full information about the error, just a small extract from it.
143
+ */
144
+ export function _errorSnippet(err, opt = {}) {
145
+ const { maxLineLength = 60, maxLines = 3 } = opt;
146
+ const e = _anyToErrorObject(err);
147
+ const lines = [errorObjectToSnippet(e)];
148
+ let { cause } = e;
149
+ while (cause && lines.length < maxLines) {
150
+ lines.push('Caused by ' + errorObjectToSnippet(cause));
151
+ cause = cause.cause; // insert DiCaprio Inception meme
152
+ }
153
+ return lines.map(line => _truncate(line, maxLineLength)).join('\n');
154
+ function errorObjectToSnippet(e) {
155
+ // Return snippet if it was already prepared
156
+ if (e.data.snippet)
157
+ return e.data.snippet;
158
+ // Code already serves the purpose of the snippet, so we can just return it
159
+ if (e.data.code)
160
+ return e.data.code;
161
+ return [
162
+ !commonErrorClasses.has(e.name) && e.name,
163
+ // replace "1+ white space characters" with a single space
164
+ e.message.replaceAll(/\s+/gm, ' ').trim(),
165
+ ]
166
+ .filter(Boolean)
167
+ .join(': ');
168
+ }
169
+ }
122
170
  export function _isBackendErrorResponseObject(o) {
123
171
  return _isErrorObject(o === null || o === void 0 ? void 0 : o.error);
124
172
  }
@@ -159,3 +207,112 @@ export function _errorDataAppend(err, data) {
159
207
  err.data = Object.assign(Object.assign({}, err.data), data);
160
208
  return err;
161
209
  }
210
+ /**
211
+ * Base class for all our (not system) errors.
212
+ *
213
+ * message - "technical" message. Frontend decides to show it or not.
214
+ * data - optional "any" payload.
215
+ * data.userFriendly - if present, will be displayed to the User as is.
216
+ *
217
+ * Based on: https://medium.com/@xpl/javascript-deriving-from-error-properly-8d2f8f315801
218
+ */
219
+ export class AppError extends Error {
220
+ constructor(message, data = {}, opt = {}) {
221
+ super(message);
222
+ const { name = this.constructor.name, cause } = opt;
223
+ Object.defineProperties(this, {
224
+ name: {
225
+ value: name,
226
+ configurable: true,
227
+ writable: true,
228
+ },
229
+ data: {
230
+ value: data,
231
+ writable: true,
232
+ configurable: true,
233
+ enumerable: false,
234
+ },
235
+ });
236
+ if (cause) {
237
+ Object.defineProperty(this, 'cause', {
238
+ value: _anyToErrorObject(cause),
239
+ writable: true,
240
+ configurable: true,
241
+ enumerable: true, // unlike standard - setting it to true for "visibility"
242
+ });
243
+ }
244
+ // this is to allow changing this.constuctor.name to a non-minified version
245
+ Object.defineProperty(this.constructor, 'name', {
246
+ value: name,
247
+ configurable: true,
248
+ writable: true,
249
+ });
250
+ // todo: check if it's needed at all!
251
+ // if (Error.captureStackTrace) {
252
+ // Error.captureStackTrace(this, this.constructor)
253
+ // } else {
254
+ // Object.defineProperty(this, 'stack', {
255
+ // value: new Error().stack, // eslint-disable-line unicorn/error-message
256
+ // writable: true,
257
+ // configurable: true,
258
+ // })
259
+ // }
260
+ }
261
+ }
262
+ /**
263
+ * Error that is thrown when Http Request was made and returned an error.
264
+ * Thrown by, for example, Fetcher.
265
+ *
266
+ * On the Frontend this Error class represents the error when calling the API,
267
+ * contains all the necessary request and response information.
268
+ *
269
+ * On the Backend, similarly, it represents the error when calling some 3rd-party API
270
+ * (backend-to-backend call).
271
+ * On the Backend it often propagates all the way to the Backend error handler,
272
+ * where it would be wrapped in BackendErrorResponseObject.
273
+ *
274
+ * Please note that `ErrorData.backendResponseStatusCode` is NOT exactly the same as
275
+ * `HttpRequestErrorData.responseStatusCode`.
276
+ * E.g 3rd-party call may return 401, but our Backend will still wrap it into an 500 error
277
+ * (by default).
278
+ */
279
+ export class HttpRequestError extends AppError {
280
+ constructor(message, data, opt) {
281
+ if (data.response) {
282
+ Object.defineProperty(data, 'response', {
283
+ enumerable: false,
284
+ });
285
+ }
286
+ super(message, data, Object.assign(Object.assign({}, opt), { name: 'HttpRequestError' }));
287
+ }
288
+ }
289
+ export class AssertionError extends AppError {
290
+ constructor(message, data) {
291
+ super(message, data, { name: 'AssertionError' });
292
+ }
293
+ }
294
+ export class JsonParseError extends AppError {
295
+ constructor(data) {
296
+ const message = ['Failed to parse', data.text && _truncateMiddle(data.text, 200)]
297
+ .filter(Boolean)
298
+ .join(': ');
299
+ super(message, data, { name: 'JsonParseError' });
300
+ }
301
+ }
302
+ export class TimeoutError extends AppError {
303
+ constructor(message, data, opt) {
304
+ super(message, data, Object.assign(Object.assign({}, opt), { name: 'TimeoutError' }));
305
+ }
306
+ }
307
+ /**
308
+ * It is thrown when Error was expected, but didn't happen
309
+ * ("pass" happened instead).
310
+ * "Pass" means "no error".
311
+ */
312
+ export class UnexpectedPassError extends AppError {
313
+ constructor() {
314
+ super('expected error was not thrown', {}, {
315
+ name: 'UnexpectedPassError',
316
+ });
317
+ }
318
+ }
@@ -1,6 +1,6 @@
1
1
  import { _stringifyAny } from '../string/stringifyAny';
2
- import { AppError } from './app.error';
3
2
  import { _assertErrorClassOrRethrow } from './assert';
3
+ import { UnexpectedPassError } from './error.util';
4
4
  /**
5
5
  * Calls a function, returns a Tuple of [error, value].
6
6
  * Allows to write shorter code that avoids `try/catch`.
@@ -45,18 +45,6 @@ export async function pTry(promise, errorClass) {
45
45
  return [err, null];
46
46
  }
47
47
  }
48
- /**
49
- * It is thrown when Error was expected, but didn't happen
50
- * ("pass" happened instead).
51
- * "Pass" means "no error".
52
- */
53
- export class UnexpectedPassError extends AppError {
54
- constructor() {
55
- super('expected error was not thrown', {}, {
56
- name: 'UnexpectedPassError',
57
- });
58
- }
59
- }
60
48
  /**
61
49
  * Calls `fn`, expects is to throw, catches the expected error and returns.
62
50
  * If error was NOT thrown - throws UnexpectedPassError instead.
@@ -3,12 +3,11 @@
3
3
  var _a;
4
4
  import { isServerSide } from '../env';
5
5
  import { _assertErrorClassOrRethrow } from '../error/assert';
6
- import { _anyToError, _anyToErrorObject, _errorLikeToErrorObject } from '../error/error.util';
7
- import { HttpRequestError } from '../error/httpRequestError';
6
+ import { _anyToError, _anyToErrorObject, _errorLikeToErrorObject, HttpRequestError, TimeoutError, } from '../error/error.util';
8
7
  import { _clamp } from '../number/number.util';
9
8
  import { _filterNullishValues, _filterUndefinedValues, _mapKeys, _merge, _omit, _pick, } from '../object/object.util';
10
9
  import { pDelay } from '../promise/pDelay';
11
- import { pTimeout, TimeoutError } from '../promise/pTimeout';
10
+ import { pTimeout } from '../promise/pTimeout';
12
11
  import { _jsonParse, _jsonParseIfPossible } from '../string/json.util';
13
12
  import { _stringifyAny } from '../string/stringifyAny';
14
13
  import { _ms, _since } from '../time/time.util';
package/dist-esm/index.js CHANGED
@@ -14,16 +14,13 @@ export * from './decorators/memoFn';
14
14
  export * from './decorators/memoFnAsync';
15
15
  export * from './decorators/retry.decorator';
16
16
  export * from './decorators/timeout.decorator';
17
- export * from './error/app.error';
18
17
  export * from './error/assert';
19
18
  export * from './enum.util';
20
19
  export * from './error/error.model';
21
20
  export * from './error/error.util';
22
21
  export * from './error/errorMode';
23
- export * from './error/httpRequestError';
24
22
  export * from './error/try';
25
23
  export * from './error/tryCatch';
26
- export * from './error/jsonParseError';
27
24
  export * from './json-schema/from-data/generateJsonSchemaFromData';
28
25
  export * from './json-schema/jsonSchema.cnst';
29
26
  export * from './json-schema/jsonSchema.model';
@@ -1,10 +1,4 @@
1
- import { AppError } from '../error/app.error';
2
- import { _errorDataAppend } from '../error/error.util';
3
- export class TimeoutError extends AppError {
4
- constructor(message, data, opt) {
5
- super(message, data, Object.assign(Object.assign({}, opt), { name: 'TimeoutError' }));
6
- }
7
- }
1
+ import { _errorDataAppend, TimeoutError } from '../error/error.util';
8
2
  /**
9
3
  * Decorates a Function with a timeout.
10
4
  * Returns a decorated Function.
@@ -1,4 +1,4 @@
1
- import { JsonParseError } from '../error/jsonParseError';
1
+ import { JsonParseError } from '../error/error.util';
2
2
  // const possibleJsonStartTokens = ['{', '[', '"']
3
3
  const DETECT_JSON = /^\s*[{["\-\d]/;
4
4
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/js-lib",
3
- "version": "14.171.0",
3
+ "version": "14.173.0",
4
4
  "scripts": {
5
5
  "prepare": "husky install",
6
6
  "build-prod": "build-prod-esm-cjs",
@@ -14,7 +14,7 @@
14
14
  "devDependencies": {
15
15
  "@naturalcycles/bench-lib": "^1.5.0",
16
16
  "@naturalcycles/dev-lib": "^13.0.1",
17
- "@naturalcycles/nodejs-lib": "^12.33.4",
17
+ "@naturalcycles/nodejs-lib": "^13.0.1",
18
18
  "@naturalcycles/time-lib": "^3.5.1",
19
19
  "@types/crypto-js": "^4.1.1",
20
20
  "@types/node": "^20.1.0",
@@ -1,6 +1,5 @@
1
1
  import type { ErrorData, ErrorObject } from '..'
2
- import { _deepEquals, _isErrorObject, _stringifyAny, Class } from '..'
3
- import { AppError } from './app.error'
2
+ import { _deepEquals, _isErrorObject, _stringifyAny, AssertionError, Class } from '..'
4
3
 
5
4
  /**
6
5
  * Evaluates the `condition` (casts it to Boolean).
@@ -150,9 +149,3 @@ export function _assertTypeOf<T>(v: any, expectedType: string, message?: string)
150
149
  })
151
150
  }
152
151
  }
153
-
154
- export class AssertionError extends AppError {
155
- constructor(message: string, data?: ErrorData) {
156
- super(message, data, { name: 'AssertionError' })
157
- }
158
- }
@@ -21,6 +21,15 @@ export interface ErrorData {
21
21
  */
22
22
  errorId?: string
23
23
 
24
+ /**
25
+ * If set - provides a short semi-user-friendly error message snippet,
26
+ * that would allow to give a hint to the user what went wrong,
27
+ * also to developers and CS to distinguish between different errors.
28
+ *
29
+ * It's not supposed to have full information about the error, just a small extract from it.
30
+ */
31
+ snippet?: string
32
+
24
33
  /**
25
34
  * Set to true to force reporting this error (e.g to Sentry).
26
35
  * Useful to be able to force-report e.g a 4xx error, which by default wouldn't be reported.
@@ -42,14 +51,6 @@ export interface ErrorData {
42
51
  */
43
52
  reportRate?: number
44
53
 
45
- /**
46
- * Sometimes error.message gets "decorated" with extra information
47
- * (e.g frontend-lib adds a method, url, etc for all the errors)
48
- * `originalMessage` is used to preserve the original `error.message` as it came from the backend.
49
- */
50
- // originalMessage?: string
51
- // use .cause.message instead
52
-
53
54
  /**
54
55
  * Can be used by error-reporting tools (e.g Sentry).
55
56
  * If fingerprint is defined - it'll be used INSTEAD of default fingerprint of a tool.
@@ -6,7 +6,7 @@ import type {
6
6
  HttpRequestErrorData,
7
7
  ErrorLike,
8
8
  } from '..'
9
- import { AppError, _jsonParseIfPossible, _stringifyAny } from '..'
9
+ import { _jsonParseIfPossible, _stringifyAny, _truncate, _truncateMiddle } from '..'
10
10
 
11
11
  /**
12
12
  * Useful to ensure that error in `catch (err) { ... }`
@@ -54,7 +54,6 @@ export function _anyToErrorObject<DATA_TYPE extends ErrorData = ErrorData>(
54
54
  ): ErrorObject<DATA_TYPE> {
55
55
  let eo: ErrorObject<DATA_TYPE>
56
56
 
57
- // if (o instanceof Error) {
58
57
  if (_isErrorLike(o)) {
59
58
  eo = _errorLikeToErrorObject(o)
60
59
  } else {
@@ -88,6 +87,15 @@ export function _anyToErrorObject<DATA_TYPE extends ErrorData = ErrorData>(
88
87
  export function _errorLikeToErrorObject<DATA_TYPE extends ErrorData = ErrorData>(
89
88
  e: AppError<DATA_TYPE> | Error | ErrorLike,
90
89
  ): ErrorObject<DATA_TYPE> {
90
+ // If it's already an ErrorObject - just return it
91
+ // AppError satisfies ErrorObject interface
92
+ // Error does not satisfy (lacks `data`)
93
+ // UPD: no, we expect a "plain object" here as an output,
94
+ // because Error classes sometimes have non-enumerable properties (e.g data)
95
+ if (!(e instanceof Error) && _isErrorObject(e)) {
96
+ return e as ErrorObject<DATA_TYPE>
97
+ }
98
+
91
99
  const obj: ErrorObject<DATA_TYPE> = {
92
100
  name: e.name,
93
101
  message: e.message,
@@ -156,6 +164,64 @@ export function _errorObjectToError<DATA_TYPE extends ErrorData, ERROR_TYPE exte
156
164
  return err
157
165
  }
158
166
 
167
+ export interface ErrorSnippetOptions {
168
+ /**
169
+ * Max length of the error line.
170
+ * Snippet may have multiple lines, one original and one per `cause`.
171
+ */
172
+ maxLineLength?: number
173
+
174
+ maxLines?: number
175
+ }
176
+
177
+ // These "common" error classes will not be printed as part of the Error snippet
178
+ const commonErrorClasses = new Set([
179
+ 'Error',
180
+ 'AppError',
181
+ 'AssertionError',
182
+ 'HttpRequestError',
183
+ 'JoiValidationError',
184
+ ])
185
+
186
+ /**
187
+ * Provides a short semi-user-friendly error message snippet,
188
+ * that would allow to give a hint to the user what went wrong,
189
+ * also to developers and CS to distinguish between different errors.
190
+ *
191
+ * It's not supposed to have full information about the error, just a small extract from it.
192
+ */
193
+ export function _errorSnippet(err: any, opt: ErrorSnippetOptions = {}): string {
194
+ const { maxLineLength = 60, maxLines = 3 } = opt
195
+ const e = _anyToErrorObject(err)
196
+
197
+ const lines = [errorObjectToSnippet(e)]
198
+
199
+ let { cause } = e
200
+
201
+ while (cause && lines.length < maxLines) {
202
+ lines.push('Caused by ' + errorObjectToSnippet(cause))
203
+ cause = cause.cause // insert DiCaprio Inception meme
204
+ }
205
+
206
+ return lines.map(line => _truncate(line, maxLineLength)).join('\n')
207
+
208
+ function errorObjectToSnippet(e: ErrorObject): string {
209
+ // Return snippet if it was already prepared
210
+ if (e.data.snippet) return e.data.snippet
211
+
212
+ // Code already serves the purpose of the snippet, so we can just return it
213
+ if (e.data.code) return e.data.code
214
+
215
+ return [
216
+ !commonErrorClasses.has(e.name) && e.name,
217
+ // replace "1+ white space characters" with a single space
218
+ e.message.replaceAll(/\s+/gm, ' ').trim(),
219
+ ]
220
+ .filter(Boolean)
221
+ .join(': ')
222
+ }
223
+ }
224
+
159
225
  export function _isBackendErrorResponseObject(o: any): o is BackendErrorResponseObject {
160
226
  return _isErrorObject(o?.error)
161
227
  }
@@ -206,3 +272,165 @@ export function _errorDataAppend<ERR>(err: ERR, data?: ErrorData): ERR {
206
272
 
207
273
  return err
208
274
  }
275
+
276
+ /**
277
+ * Base class for all our (not system) errors.
278
+ *
279
+ * message - "technical" message. Frontend decides to show it or not.
280
+ * data - optional "any" payload.
281
+ * data.userFriendly - if present, will be displayed to the User as is.
282
+ *
283
+ * Based on: https://medium.com/@xpl/javascript-deriving-from-error-properly-8d2f8f315801
284
+ */
285
+ export class AppError<DATA_TYPE extends ErrorData = ErrorData> extends Error {
286
+ data!: DATA_TYPE
287
+
288
+ /**
289
+ * `cause` here is normalized to be an ErrorObject
290
+ */
291
+ override cause?: ErrorObject
292
+
293
+ constructor(message: string, data = {} as DATA_TYPE, opt: AppErrorOptions = {}) {
294
+ super(message)
295
+ const { name = this.constructor.name, cause } = opt
296
+
297
+ Object.defineProperties(this, {
298
+ name: {
299
+ value: name,
300
+ configurable: true,
301
+ writable: true,
302
+ },
303
+ data: {
304
+ value: data,
305
+ writable: true,
306
+ configurable: true,
307
+ enumerable: false,
308
+ },
309
+ })
310
+
311
+ if (cause) {
312
+ Object.defineProperty(this, 'cause', {
313
+ value: _anyToErrorObject(cause),
314
+ writable: true,
315
+ configurable: true,
316
+ enumerable: true, // unlike standard - setting it to true for "visibility"
317
+ })
318
+ }
319
+
320
+ // this is to allow changing this.constuctor.name to a non-minified version
321
+ Object.defineProperty(this.constructor, 'name', {
322
+ value: name,
323
+ configurable: true,
324
+ writable: true,
325
+ })
326
+
327
+ // todo: check if it's needed at all!
328
+ // if (Error.captureStackTrace) {
329
+ // Error.captureStackTrace(this, this.constructor)
330
+ // } else {
331
+ // Object.defineProperty(this, 'stack', {
332
+ // value: new Error().stack, // eslint-disable-line unicorn/error-message
333
+ // writable: true,
334
+ // configurable: true,
335
+ // })
336
+ // }
337
+ }
338
+ }
339
+
340
+ /**
341
+ * Extra options for AppError constructor.
342
+ */
343
+ export interface AppErrorOptions {
344
+ /**
345
+ * Overrides Error.name and Error.constructor.name
346
+ */
347
+ name?: string
348
+
349
+ /**
350
+ * Sets Error.cause.
351
+ * It is transformed with _anyToErrorObject()
352
+ */
353
+ cause?: any
354
+ }
355
+
356
+ /**
357
+ * Error that is thrown when Http Request was made and returned an error.
358
+ * Thrown by, for example, Fetcher.
359
+ *
360
+ * On the Frontend this Error class represents the error when calling the API,
361
+ * contains all the necessary request and response information.
362
+ *
363
+ * On the Backend, similarly, it represents the error when calling some 3rd-party API
364
+ * (backend-to-backend call).
365
+ * On the Backend it often propagates all the way to the Backend error handler,
366
+ * where it would be wrapped in BackendErrorResponseObject.
367
+ *
368
+ * Please note that `ErrorData.backendResponseStatusCode` is NOT exactly the same as
369
+ * `HttpRequestErrorData.responseStatusCode`.
370
+ * E.g 3rd-party call may return 401, but our Backend will still wrap it into an 500 error
371
+ * (by default).
372
+ */
373
+ export class HttpRequestError extends AppError<HttpRequestErrorData> {
374
+ constructor(message: string, data: HttpRequestErrorData, opt?: AppErrorOptions) {
375
+ if (data.response) {
376
+ Object.defineProperty(data, 'response', {
377
+ enumerable: false,
378
+ })
379
+ }
380
+
381
+ super(message, data, { ...opt, name: 'HttpRequestError' })
382
+ }
383
+
384
+ /**
385
+ * Cause is strictly-defined for HttpRequestError,
386
+ * so it always has a cause.
387
+ * (for dev convenience)
388
+ */
389
+ override cause!: ErrorObject
390
+ }
391
+
392
+ export class AssertionError extends AppError {
393
+ constructor(message: string, data?: ErrorData) {
394
+ super(message, data, { name: 'AssertionError' })
395
+ }
396
+ }
397
+
398
+ export interface JsonParseErrorData extends ErrorData {
399
+ /**
400
+ * Original text that failed to get parsed.
401
+ */
402
+ text?: string
403
+ }
404
+
405
+ export class JsonParseError extends AppError<JsonParseErrorData> {
406
+ constructor(data: JsonParseErrorData) {
407
+ const message = ['Failed to parse', data.text && _truncateMiddle(data.text, 200)]
408
+ .filter(Boolean)
409
+ .join(': ')
410
+
411
+ super(message, data, { name: 'JsonParseError' })
412
+ }
413
+ }
414
+
415
+ export class TimeoutError extends AppError {
416
+ constructor(message: string, data?: ErrorData, opt?: AppErrorOptions) {
417
+ super(message, data, { ...opt, name: 'TimeoutError' })
418
+ }
419
+ }
420
+
421
+ /**
422
+ * It is thrown when Error was expected, but didn't happen
423
+ * ("pass" happened instead).
424
+ * "Pass" means "no error".
425
+ */
426
+ export class UnexpectedPassError extends AppError {
427
+ constructor() {
428
+ super(
429
+ 'expected error was not thrown',
430
+ {},
431
+ {
432
+ name: 'UnexpectedPassError',
433
+ },
434
+ )
435
+ }
436
+ }
package/src/error/try.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { _stringifyAny } from '../string/stringifyAny'
2
2
  import type { Class } from '../typeFest'
3
3
  import type { AnyFunction, ErrorDataTuple } from '../types'
4
- import { AppError } from './app.error'
5
4
  import { _assertErrorClassOrRethrow } from './assert'
5
+ import { UnexpectedPassError } from './error.util'
6
6
 
7
7
  /**
8
8
  * Calls a function, returns a Tuple of [error, value].
@@ -55,23 +55,6 @@ export async function pTry<T, ERR extends Error = Error>(
55
55
  }
56
56
  }
57
57
 
58
- /**
59
- * It is thrown when Error was expected, but didn't happen
60
- * ("pass" happened instead).
61
- * "Pass" means "no error".
62
- */
63
- export class UnexpectedPassError extends AppError {
64
- constructor() {
65
- super(
66
- 'expected error was not thrown',
67
- {},
68
- {
69
- name: 'UnexpectedPassError',
70
- },
71
- )
72
- }
73
- }
74
-
75
58
  /**
76
59
  * Calls `fn`, expects is to throw, catches the expected error and returns.
77
60
  * If error was NOT thrown - throws UnexpectedPassError instead.
@@ -4,8 +4,13 @@
4
4
  import { isServerSide } from '../env'
5
5
  import { _assertErrorClassOrRethrow } from '../error/assert'
6
6
  import { ErrorLike, ErrorObject } from '../error/error.model'
7
- import { _anyToError, _anyToErrorObject, _errorLikeToErrorObject } from '../error/error.util'
8
- import { HttpRequestError } from '../error/httpRequestError'
7
+ import {
8
+ _anyToError,
9
+ _anyToErrorObject,
10
+ _errorLikeToErrorObject,
11
+ HttpRequestError,
12
+ TimeoutError,
13
+ } from '../error/error.util'
9
14
  import { _clamp } from '../number/number.util'
10
15
  import {
11
16
  _filterNullishValues,
@@ -16,7 +21,7 @@ import {
16
21
  _pick,
17
22
  } from '../object/object.util'
18
23
  import { pDelay } from '../promise/pDelay'
19
- import { pTimeout, TimeoutError } from '../promise/pTimeout'
24
+ import { pTimeout } from '../promise/pTimeout'
20
25
  import { _jsonParse, _jsonParseIfPossible } from '../string/json.util'
21
26
  import { _stringifyAny } from '../string/stringifyAny'
22
27
  import { _ms, _since } from '../time/time.util'
package/src/index.ts CHANGED
@@ -14,16 +14,13 @@ export * from './decorators/memoFn'
14
14
  export * from './decorators/memoFnAsync'
15
15
  export * from './decorators/retry.decorator'
16
16
  export * from './decorators/timeout.decorator'
17
- export * from './error/app.error'
18
17
  export * from './error/assert'
19
18
  export * from './enum.util'
20
19
  export * from './error/error.model'
21
20
  export * from './error/error.util'
22
21
  export * from './error/errorMode'
23
- export * from './error/httpRequestError'
24
22
  export * from './error/try'
25
23
  export * from './error/tryCatch'
26
- export * from './error/jsonParseError'
27
24
  export * from './json-schema/from-data/generateJsonSchemaFromData'
28
25
  export * from './json-schema/jsonSchema.cnst'
29
26
  export * from './json-schema/jsonSchema.model'