Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

new-error

Package Overview
Dependencies
Maintainers
2
Versions
46
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

new-error - npm Package Compare versions

Comparing version 1.0.13 to 1.1.1

build/Interrnal.d.ts

17

build/error-types/BaseError.d.ts
import ExtendableError from 'es6-error';
import { IBaseError, SerializedError, SerializedErrorSafe } from '../interfaces';
import { DeserializeOpts, IBaseError, SerializedError, SerializedErrorSafe } from '../interfaces';
/**

@@ -125,2 +125,17 @@ * Improved error class.

toJSONSafe(fieldsToOmit?: string[]): Partial<SerializedErrorSafe>;
/**
* Helper method for use with fromJson()
* @param errInstance An error instance that extends BaseError
* @param {string} data JSON.parse()'d error object from
* BaseError#toJSON() or BaseError#toJSONSafe()
* @param {DeserializeOpts} [opts] Deserialization options
*/
static copyDeserializationData<T extends IBaseError = IBaseError, U extends DeserializeOpts = DeserializeOpts>(errInstance: T, data: Partial<SerializedError>, opts: U): void;
/**
* Deserializes an error into an instance
* @param {string} data JSON.parse()'d error object from
* BaseError#toJSON() or BaseError#toJSONSafe()
* @param {DeserializeOpts} [opts] Deserialization options
*/
static fromJSON<T extends DeserializeOpts = DeserializeOpts>(data: Partial<SerializedError>, opts?: T): IBaseError;
}

@@ -184,2 +184,3 @@ "use strict";

statusCode: this._statusCode,
logLevel: this._logLevel,
meta: {

@@ -229,4 +230,71 @@ ...this._metadata,

}
/**
* Helper method for use with fromJson()
* @param errInstance An error instance that extends BaseError
* @param {string} data JSON.parse()'d error object from
* BaseError#toJSON() or BaseError#toJSONSafe()
* @param {DeserializeOpts} [opts] Deserialization options
*/
static copyDeserializationData(errInstance, data, opts) {
if (data.code) {
errInstance.withErrorCode(data.code);
}
if (data.subCode) {
errInstance.withErrorSubCode(data.subCode);
}
if (data.errId) {
errInstance.withErrorId(data.errId);
}
if (data.statusCode) {
errInstance.withStatusCode(data.statusCode);
}
if (data.stack) {
errInstance.stack = data.stack;
}
if (data.logLevel) {
errInstance.withLogLevel(data.logLevel);
}
// not possible to know what the underlying causedBy type is
// so we can't deserialize to its original representation
if (data.causedBy) {
errInstance.causedBy(data.causedBy);
}
// if defined, pluck the metadata fields to their respective safe and unsafe counterparts
if (data.meta && opts && opts.safeMetadataFields) {
Object.keys(data.meta).forEach(key => {
if (opts.safeMetadataFields[key]) {
errInstance.withSafeMetadata({
[key]: data.meta[key]
});
}
else {
errInstance.withMetadata({
[key]: data.meta[key]
});
}
});
}
else {
errInstance.withMetadata(data.meta);
}
}
/**
* Deserializes an error into an instance
* @param {string} data JSON.parse()'d error object from
* BaseError#toJSON() or BaseError#toJSONSafe()
* @param {DeserializeOpts} [opts] Deserialization options
*/
static fromJSON(data, opts) {
if (!opts) {
opts = {};
}
if (typeof data !== 'object') {
throw new Error(`fromJSON(): Data is not an object.`);
}
let err = new this(data.message);
this.copyDeserializationData(err, data, opts);
return err;
}
}
exports.BaseError = BaseError;
//# sourceMappingURL=BaseError.js.map

@@ -1,2 +0,2 @@

import { BaseError } from '../index';
import { BaseError, DeserializeOpts, IBaseError, SerializedError } from '../index';
import { HighLevelError, LowLevelErrorInternal } from '../interfaces';

@@ -8,2 +8,9 @@ /**

constructor(highLevelErrorDef: HighLevelError, lowLevelErrorDef: LowLevelErrorInternal);
/**
* Deserializes an error into an instance
* @param {string} data JSON.parse()'d error object from
* BaseError#toJSON() or BaseError#toJSONSafe()
* @param {DeserializeOpts} [opts] Deserialization options
*/
static fromJSON<T extends DeserializeOpts = DeserializeOpts>(data: Partial<SerializedError>, opts?: T): IBaseError;
}

@@ -31,4 +31,25 @@ "use strict";

}
/**
* Deserializes an error into an instance
* @param {string} data JSON.parse()'d error object from
* BaseError#toJSON() or BaseError#toJSONSafe()
* @param {DeserializeOpts} [opts] Deserialization options
*/
static fromJSON(data, opts) {
if (!opts) {
opts = {};
}
if (typeof data !== 'object') {
throw new Error(`fromJSON(): Data is not an object.`);
}
let err = new this({
code: data.code
}, {
message: data.message
});
this.copyDeserializationData(err, data, opts);
return err;
}
}
exports.BaseRegistryError = BaseRegistryError;
//# sourceMappingURL=BaseRegistryError.js.map

@@ -1,2 +0,2 @@

import { HighLevelErrorInternal, LowLevelErrorInternal } from './interfaces';
import { DeserializeOpts, HighLevelErrorInternal, IBaseError, LowLevelErrorInternal, SerializedError } from './interfaces';
import { BaseRegistryError } from './error-types/BaseRegistryError';

@@ -13,2 +13,7 @@ /**

/**
* A map of high level names to class name
* @protected
*/
protected classNameHighLevelNameMap: Record<keyof HLError, string>;
/**
* Cached high level error classes

@@ -55,2 +60,9 @@ */

newError(highLvErrName: keyof HLError, lowLvErrName: keyof LLError): BaseRegistryError;
/**
* Deserializes data into an error
* @param {string} data JSON.parse()'d error object from
* BaseError#toJSON() or BaseError#toJSONSafe()
* @param {DeserializeOpts} [opts] Deserialization options
*/
fromJSON<T extends IBaseError = IBaseError, U extends DeserializeOpts = DeserializeOpts>(data: Partial<SerializedError>, opts?: U): T;
}

@@ -5,2 +5,3 @@ "use strict";

const BaseRegistryError_1 = require("./error-types/BaseRegistryError");
const BaseError_1 = require("./error-types/BaseError");
/**

@@ -14,3 +15,7 @@ * Contains the definitions for High and Low Level Errors and

this.lowLevelErrors = {};
this.classNameHighLevelNameMap = {};
this.highLevelErrorClasses = {};
Object.keys(highLvErrors).forEach(name => {
this.classNameHighLevelNameMap[highLvErrors[name].className] = name;
});
// populate the lowLevelErrors dictionary

@@ -87,4 +92,30 @@ Object.keys(lowLvErrors).forEach(type => {

}
/**
* Deserializes data into an error
* @param {string} data JSON.parse()'d error object from
* BaseError#toJSON() or BaseError#toJSONSafe()
* @param {DeserializeOpts} [opts] Deserialization options
*/
fromJSON(data, opts) {
if (typeof data !== 'object') {
throw new Error(`fromJSON(): Data is not an object.`);
}
// data.name is the class name - we need to resolve it to the name of the high level class definition
const errorName = this.classNameHighLevelNameMap[data.name];
// use the lookup results to see if we can get the class definition of the high level error
const highLevelDef = this.getHighLevelError(errorName);
let err = null;
// Can deserialize into an custom error instance class
if (highLevelDef) {
// get the class for the error type
const C = this.getClass(errorName);
err = C.fromJSON(data, opts);
}
else {
err = BaseError_1.BaseError.fromJSON(data, opts);
}
return err;
}
}
exports.ErrorRegistry = ErrorRegistry;
//# sourceMappingURL=ErrorRegistry.js.map

4

build/index.d.ts
import { BaseError } from './error-types/BaseError';
import { BaseRegistryError } from './error-types/BaseRegistryError';
import { ErrorRegistry } from './ErrorRegistry';
import { IBaseError, SerializedError, SerializedErrorSafe, HighLevelError, LowLevelError } from './interfaces';
export { BaseError, BaseRegistryError, ErrorRegistry, IBaseError, HighLevelError, LowLevelError, SerializedError, SerializedErrorSafe };
import { IBaseError, SerializedError, SerializedErrorSafe, HighLevelError, LowLevelError, DeserializeOpts } from './interfaces';
export { BaseError, BaseRegistryError, ErrorRegistry, IBaseError, HighLevelError, LowLevelError, SerializedError, SerializedErrorSafe, DeserializeOpts };

@@ -1,4 +0,1 @@

/**
* A High Level Error definition defined by the user
*/
export interface HighLevelError {

@@ -156,2 +153,7 @@ /**

/**
* Set a protocol-specific status code
* @param statusCode
*/
withStatusCode(statusCode: any): this;
/**
* Replaces printf flags in an error message, if present.

@@ -175,3 +177,3 @@ * @see https://www.npmjs.com/package/sprintf-js

*/
toJSON(fieldsToOmit: string[]): Partial<SerializedError>;
toJSON(fieldsToOmit?: string[]): Partial<SerializedError>;
/**

@@ -182,3 +184,7 @@ * Returns a safe json representation of the error (error stack / causedBy is removed).

*/
toJSONSafe(fieldsToOmit: string[]): Partial<SerializedErrorSafe>;
toJSONSafe(fieldsToOmit?: string[]): Partial<SerializedErrorSafe>;
/**
* Stack trace
*/
stack?: any;
}

@@ -191,2 +197,6 @@ /**

/**
* The error id
*/
errId?: string;
/**
* The high level code to show to a client.

@@ -204,2 +214,6 @@ */

/**
* Assigned log level
*/
logLevel?: string | number;
/**
* User-defined metadata

@@ -234,1 +248,7 @@ */

}
export interface DeserializeOpts {
/**
* Fields from meta to pluck as a safe metadata field
*/
safeMetadataFields?: Record<string, true>;
}

@@ -0,1 +1,9 @@

## 1.1.1 - Mon Sep 21 2020 03:57:31
**Contributor:** Theo Gravity
- Add deserialization support (#7)
Please read the README section on the limitations and security issues relating to deserialization.
## 1.0.13 - Sat Jun 20 2020 03:22:14

@@ -5,5 +13,9 @@

- Update README.md [skip ci]
- Add support for defining log levels
This adds the `logLevel` property to the error definitions along with
corresponding `getLogLevel()` and `withLogLevel()` methods.
Add another example for log level.
There are cases where certain errors do not warrant being logged
under an `error` log level when combined with a logging system.

@@ -10,0 +22,0 @@ ## 1.0.12 - Wed Jun 03 2020 03:54:55

{
"name": "new-error",
"version": "1.0.13",
"version": "1.1.1",
"description": "A production-grade error creation and serialization library designed for Typescript",

@@ -68,21 +68,21 @@ "main": "build/index.js",

"@theo.gravity/changelog-version": "2.1.10",
"@theo.gravity/version-bump": "2.0.9",
"@types/jest": "25.2.2",
"@types/node": "^14.0.1",
"@typescript-eslint/eslint-plugin": "^2.33.0",
"@typescript-eslint/parser": "^2.33.0",
"eslint": "7.0.0",
"@theo.gravity/version-bump": "2.0.10",
"@types/jest": "26.0.14",
"@types/node": "^14.11.1",
"@typescript-eslint/eslint-plugin": "^4.1.1",
"@typescript-eslint/parser": "^4.1.1",
"eslint": "7.9.0",
"git-commit-stamper": "^1.0.9",
"jest": "^25.5.4",
"jest-cli": "26.0.1",
"jest": "^26.4.2",
"jest-cli": "26.4.2",
"jest-junit-reporter": "1.1.0",
"lint-staged": "10.2.2",
"lint-staged": "10.4.0",
"pre-commit": "1.2.2",
"prettier-standard": "16.3.0",
"prettier-standard": "16.4.1",
"standardx": "^5.0.0",
"toc-md-alt": "^0.3.2",
"ts-jest": "25.5.1",
"ts-node": "8.10.1",
"ts-node-dev": "1.0.0-pre.44",
"typescript": "3.9.2"
"toc-md-alt": "^0.4.1",
"ts-jest": "26.4.0",
"ts-node": "9.0.0",
"ts-node-dev": "1.0.0-pre.62",
"typescript": "4.0.3"
},

@@ -89,0 +89,0 @@ "eslintConfig": {

@@ -25,2 +25,3 @@ # new-error

<!-- TOC -->
- [Motivation / Error handling use-cases](#motivation--error-handling-use-cases)

@@ -44,2 +45,3 @@ - [Installation](#installation)

- [Basic setters](#basic-setters)
- [Static methods](#static-methods)
- [Set an error id](#set-an-error-id)

@@ -54,2 +56,9 @@ - [Attaching errors](#attaching-errors)

- [Internal serialization](#internal-serialization)
- [Deserialization](#deserialization)
- [Issues with deserialization](#issues-with-deserialization)
- [Deserialization is not perfect](#deserialization-is-not-perfect)
- [potential security issues with deserialization:](#potential-security-issues-with-deserialization)
- [`ErrorRegistry#fromJSON()` method](#errorregistryfromjson-method)
- [`static BaseError#fromJSON()` method](#static-baseerrorfromjson-method)
- [Stand-alone instance-based deserialization](#stand-alone-instance-based-deserialization)

@@ -468,2 +477,6 @@ <!-- TOC END -->

## Static methods
- `static BaseError#fromJSON(data: object, options?: object): BaseError`
## Set an error id

@@ -579,2 +592,3 @@

- Omits `type`
- Omits `logLevel`
- Omits the stack trace

@@ -585,3 +599,2 @@ - Omits any data defined via `BaseError#withMetadata()`

err.withSafeMetadata({
errorId: 'err-12345',
requestId: 'req-12345'

@@ -601,3 +614,3 @@ })

statusCode: 500,
meta: { errorId: 'err-12345', requestId: 'req-12345' }
meta: { requestId: 'req-12345' }
}

@@ -621,3 +634,3 @@ ```

err.withSafeMetadata({
errorId: 'err-12345',
reqId: 'req-12345',
}).withMetadata({

@@ -655,1 +668,151 @@ email: 'test@test.com'

```
# Deserialization
## Issues with deserialization
### Deserialization is not perfect
- The serialized output may or may not include the `name` property (if using `toJSONSafe()`) that would be able to
hydrate it back into a specific error instance.
- The metadata is squashed in the serialized output that information is required to separate them.
- It is difficult to determine the original type / structure of the `causedBy` data. As a result, it will be copied as-is.
### potential security issues with deserialization:
- You need to be able to trust the data you're deserializing as the serialized data can be modified in various ways by
an untrusted party.
- The deserialization implementation does not perform `JSON.parse()` as `JSON.parse()` in its raw form is susceptible to
[prototype pollution](https://medium.com/intrinsic/javascript-prototype-poisoning-vulnerabilities-in-the-wild-7bc15347c96)
if the parse function does not have a proper sanitization function. It is up to the developer to properly
trust / sanitize / parse the data.
## `ErrorRegistry#fromJSON()` method
This method will attempt to deserialize into a registered error type. If it is unable to, a `BaseError` instance is
returned instead.
`ErrorRegistry#fromJSON(data: object, [options]: DeserializeOpts): IBaseError`
- `data`: Data that is the output of `BaseError#toJSON()`. The data must be an object, not a string.
- `options`: Optional deserialization options.
```typescript
interface DeserializeOpts {
/**
* Fields from meta to pluck as a safe metadata field
*/
safeMetadataFields?: {
// the value must be set to true.
[key: string]: true
}
}
```
Returns a `BaseError` instance or an instance of a registered error type.
```typescript
import { ErrorRegistry } from 'new-error'
const errors = {
INTERNAL_SERVER_ERROR: {
className: 'InternalServerError',
code: 'ERR_INT_500',
statusCode: 500,
logLevel: 'error'
}
}
const errorCodes = {
DATABASE_FAILURE: {
message: 'There was a database failure, SQL err code %s',
subCode: 'DB_0001',
statusCode: 500,
logLevel: 'error'
}
}
const errRegistry = new ErrorRegistry(errors, errorCodes)
const data = {
'errId': 'err-123',
'code': 'ERR_INT_500',
'subCode': 'DB_0001',
'message': 'test message',
'meta': { 'safeData': 'test454', 'test': 'test123' },
'name': 'InternalServerError',
'statusCode': 500,
'causedBy': 'test',
'stack': 'abcd'
}
// err should be an instance of InternalServerError
const err = errRegistry.toJSON(data, {
safeMetadataFields: {
safeData: true
}
})
```
## `static BaseError#fromJSON()` method
If you are not using the registry, you can deserialize using this method. This also applies to any class that extends
`BaseError`.
`static BaseError#fromJSON(data: object, [options]: DeserializeOpts): IBaseError`
- `data`: Data that is the output of `BaseError#toJSON()`. The data must be an object, not a string.
- `options`: Optional deserialization options.
Returns a `BaseError` instance or an instance of the class that extends it.
```typescript
import { BaseError } from 'new-error'
// assume we have serialized error data
const data = {
code: 'ERR_INT_500',
subCode: 'DB_0001',
statusCode: 500,
errId: 'err-1234',
meta: { requestId: 'req-12345', safeData: '123' }
}
// deserialize
// specify meta field assignment - fields that are not assigned will be assumed as withMetadata() type data
const err = BaseError.fromJSON(data, {
// (optional) Fields to pluck from 'meta' to be sent to BaseError#safeMetadataFields()
// value must be set to 'true'
safeMetadataFields: {
safeData: true
}
})
```
## Stand-alone instance-based deserialization
If the `name` property is present in the serialized data if it was serialized with `toJson()`, you can use a switch
to map to an instance:
```typescript
const data = {
// be sure that you trust the source of the deserialized data!
// anyone can modify the 'name' property to whatever
name: 'InternalServerError',
code: 'ERR_INT_500',
subCode: 'DB_0001',
statusCode: 500,
errId: 'err-1234',
meta: { requestId: 'req-12345', safeData: '123' }
}
let err = null
switch (data.name) {
case 'InternalServerError':
// assume InternalServerError extends BaseError
return InternalServerError.fromJSON(data)
default:
return BaseError.fromJSON(data)
}
```

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc