{"id":15117129,"url":"https://github.com/ehmicky/modern-errors-serialize","last_synced_at":"2025-08-18T19:05:37.635Z","repository":{"id":62746643,"uuid":"561925589","full_name":"ehmicky/modern-errors-serialize","owner":"ehmicky","description":"`modern-errors` plugin to serialize/parse errors.","archived":false,"fork":false,"pushed_at":"2025-06-03T17:15:22.000Z","size":7739,"stargazers_count":11,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-09T11:19:23.978Z","etag":null,"topics":["browser","error","error-handler","error-handling","error-monitoring","javascript","json","library","modern-errors","modern-errors-plugin","nodejs","parse","parsing","plugins","serialization","serialization-library","serializer","stringify","typescript","yaml"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ehmicky.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-11-04T20:14:04.000Z","updated_at":"2025-06-03T17:15:23.000Z","dependencies_parsed_at":"2024-04-08T01:26:24.568Z","dependency_job_id":"adf60a44-3ee3-4429-958c-e854ec152611","html_url":"https://github.com/ehmicky/modern-errors-serialize","commit_stats":{"total_commits":243,"total_committers":3,"mean_commits":81.0,"dds":"0.020576131687242816","last_synced_commit":"2aafd280cff085dc802c1dd891515550d7cc81f0"},"previous_names":[],"tags_count":15,"template":false,"template_full_name":"ehmicky/template-javascript","purl":"pkg:github/ehmicky/modern-errors-serialize","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehmicky%2Fmodern-errors-serialize","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehmicky%2Fmodern-errors-serialize/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehmicky%2Fmodern-errors-serialize/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehmicky%2Fmodern-errors-serialize/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ehmicky","download_url":"https://codeload.github.com/ehmicky/modern-errors-serialize/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehmicky%2Fmodern-errors-serialize/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271043530,"owners_count":24689773,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-18T02:00:08.743Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["browser","error","error-handler","error-handling","error-monitoring","javascript","json","library","modern-errors","modern-errors-plugin","nodejs","parse","parsing","plugins","serialization","serialization-library","serializer","stringify","typescript","yaml"],"created_at":"2024-09-26T01:45:50.789Z","updated_at":"2025-08-18T19:05:37.619Z","avatar_url":"https://github.com/ehmicky.png","language":"JavaScript","readme":"\u003cpicture\u003e\n  \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://raw.githubusercontent.com/ehmicky/design/main/modern-errors/modern-errors_dark.svg\"/\u003e\n  \u003cimg alt=\"modern-errors logo\" src=\"https://raw.githubusercontent.com/ehmicky/design/main/modern-errors/modern-errors.svg\" width=\"600\"/\u003e\n\u003c/picture\u003e\n\n[![Node](https://img.shields.io/badge/-Node.js-808080?logo=node.js\u0026colorA=404040\u0026logoColor=66cc33)](https://www.npmjs.com/package/modern-errors-serialize)\n[![Browsers](https://img.shields.io/badge/-Browsers-808080?logo=firefox\u0026colorA=404040)](https://unpkg.com/modern-errors-serialize?module)\n[![TypeScript](https://img.shields.io/badge/-Typed-808080?logo=typescript\u0026colorA=404040\u0026logoColor=0096ff)](/src/main.d.ts)\n[![Codecov](https://img.shields.io/badge/-Tested%20100%25-808080?logo=codecov\u0026colorA=404040)](https://codecov.io/gh/ehmicky/modern-errors-serialize)\n[![Minified size](https://img.shields.io/bundlephobia/minzip/modern-errors-serialize?label\u0026colorA=404040\u0026colorB=808080\u0026logo=webpack)](https://bundlephobia.com/package/modern-errors-serialize)\n[![Mastodon](https://img.shields.io/badge/-Mastodon-808080.svg?logo=mastodon\u0026colorA=404040\u0026logoColor=9590F9)](https://fosstodon.org/@ehmicky)\n[![Medium](https://img.shields.io/badge/-Medium-808080.svg?logo=medium\u0026colorA=404040)](https://medium.com/@ehmicky)\n\n[`modern-errors`](https://github.com/ehmicky/modern-errors)\n[plugin](https://github.com/ehmicky/modern-errors#-plugins) to serialize/parse\nerrors.\n\nThis adds [`BaseError.serialize()`](#baseerrorserializeerror) and\n[`BaseError.parse()`](#baseerrorparseerrorobject) to serialize/parse errors\nto/from plain objects.\n\n# Features\n\n- Ensures errors are [safe to serialize with JSON](#json-safety)\n- [Deep serialization/parsing](#deep-serializationparsing)\n- [Custom serialization/parsing](#custom-serializationparsing) (e.g. YAML or\n  `process.send()`)\n- Keeps [error classes](#baseerrorparseerrorobject)\n- Preserves errors' [additional properties](#additional-error-properties)\n- Works [recursively](#aggregate-errors) with\n  [`AggregateError`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError)\n- Safe: this never throws\n\n# Example\n\n[Adding the plugin](https://github.com/ehmicky/modern-errors#adding-plugins) to\n[`modern-errors`](https://github.com/ehmicky/modern-errors).\n\n```js\nimport ModernError from 'modern-errors'\n\nimport modernErrorsSerialize from 'modern-errors-serialize'\n\nexport const BaseError = ModernError.subclass('BaseError', {\n  plugins: [modernErrorsSerialize],\n})\n// ...\n```\n\n[Serializing](#baseerrorserializeerror) errors to plain objects.\n\n```js\nconst error = new ExampleError('message', { props: { filePath } })\n\nconst errorObject = BaseError.serialize(error)\n// { name: 'ExampleError', message: 'message', stack: '...', filePath: '...' }\nconst errorString = JSON.stringify(errorObject)\n// '{\"name\":\"ExampleError\",...}'\n```\n\n[Parsing](#baseerrorparseerrorobject) errors from plain objects.\n\n```js\nconst newErrorObject = JSON.parse(errorString)\nconst newError = BaseError.parse(newErrorObject)\n// ExampleError: message\n//     at ...\n//   filePath: '...'\n```\n\n# Install\n\n```bash\nnpm install modern-errors-serialize\n```\n\nThis package works in both Node.js \u003e=18.18.0 and\n[browsers](https://raw.githubusercontent.com/ehmicky/dev-tasks/main/src/browserslist).\n\nThis is an ES module. It must be loaded using\n[an `import` or `import()` statement](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c),\nnot `require()`. If TypeScript is used, it must be configured to\n[output ES modules](https://www.typescriptlang.org/docs/handbook/esm-node.html),\nnot CommonJS.\n\n# API\n\n## modernErrorsSerialize\n\n_Type_: `Plugin`\n\nPlugin object to pass to the\n[`plugins` option](https://github.com/ehmicky/modern-errors#adding-plugins) of\n`ErrorClass.subclass()`.\n\n## BaseError.serialize(error)\n\n`error`: `ErrorInstance`\\\n_Return value_: `ErrorObject`\n\nConverts `error` to an error plain object. All\n[error properties](https://github.com/ehmicky/error-serializer#additional-error-properties)\nare kept.\n[Plugin options](https://github.com/ehmicky/modern-errors#plugin-options) are\nalso preserved.\n\n## BaseError.parse(errorObject)\n\n`errorObject`: `ErrorObject`\\\n_Return value_: `ErrorInstance`\n\nConverts `errorObject` to an error instance. The original error classes are\npreserved providing they are\n[subclasses](https://github.com/ehmicky/modern-errors#create-error-classes) of\n`BaseError`.\n\n## Options\n\n_Type_: `object`\n\n### shallow\n\n_Type_: `boolean`\\\n_Default_: `false`\n\nUnless this option is `true`, nested errors are also serialized/parsed. They can\nbe inside other errors, plain objects or arrays.\n\n\u003c!-- eslint-disable no-unused-expressions --\u003e\n\n```js\nconst inner = new ExampleError('inner')\nconst error = new ExampleError('example', { props: { inner } })\n\nBaseError.serialize(error).inner // { name: 'BaseError', message: 'inner', ... }\nBaseError.serialize(error, { shallow: true }).inner // BaseError\n\nconst errorObject = BaseError.serialize(error)\nBaseError.parse(errorObject).inner // BaseError\nBaseError.parse(errorObject, { shallow: true }).inner // { name: '...', ... }\n```\n\n### loose\n\n_Type_: `boolean`\\\n_Default_: `false`\n\nBy default, when the argument is not an `Error` instance or an error plain\nobject, it is converted to one. If this option is `true`, it is kept as is\ninstead.\n\n```js\nBaseError.serialize('example') // { name: 'BaseError', message: 'example', ... }\nBaseError.serialize('example', { loose: true }) // 'example'\n\nBaseError.parse('example') // BaseError\nBaseError.parse('example', { loose: true }) // 'example'\n```\n\n#### include\n\n_Type_: `string[]`\n\nDuring [serialization](#baseerrorserializeerror), only pick\n[specific properties](#omit-additional-error-properties).\n\n```js\nBaseError.serialize(error, { include: ['message'] }) // { message: 'example' }\n```\n\n#### exclude\n\n_Type_: `string[]`\n\nDuring [serialization](#baseerrorserializeerror), omit\n[specific properties](#omit-stack-traces).\n\n```js\nBaseError.serialize(error, { exclude: ['stack'] }) // { name: 'Error', message: 'example' }\n```\n\n### transformObject(errorObject, errorInstance)\n\n_Type_: `(errorObject, errorInstance) =\u003e void`\n\nDuring [serialization](#baseerrorserializeerror), [transform](#transforming)\neach error plain object.\n\n`errorObject` is the error after serialization. It must be directly mutated.\n\n`errorInstance` is the error before serialization.\n\n### transformArgs(constructorArgs, errorObject, ErrorClass)\n\n_Type_: `(constructorArgs, errorObject, ErrorClass) =\u003e void`\n\nDuring [parsing](#baseerrorparseerrorobject), [transform](#transforming) the\narguments passed to each `new Error()`.\n\n`constructorArgs` is the array of arguments. Usually, `constructorArgs[0]` is\nthe\n[error message](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/message)\nand `constructorArgs[1]` is the\n[constructor options object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Error#parameters).\n`constructorArgs` must be directly mutated.\n\n`errorObject` is the error before parsing. `ErrorClass` is its class.\n\n### transformInstance(errorInstance, errorObject)\n\n_Type_: `(errorInstance, errorObject) =\u003e void`\n\nDuring [parsing](#baseerrorparseerrorobject), [transform](#transforming) each\n`Error` instance.\n\n`errorInstance` is the error after parsing. It must be directly mutated.\n\n`errorObject` is the error before parsing.\n\n## Configuration\n\n[Options](#options) can apply to (in priority order):\n\n- Any error: second argument to\n  [`ModernError.subclass()`](https://github.com/ehmicky/modern-errors#options-1)\n\n```js\nexport const BaseError = ModernError.subclass('BaseError', {\n  plugins: [modernErrorsSerialize],\n  serialize: options,\n})\n```\n\n- Any error of a specific class (and its subclasses): second argument to\n  [`ErrorClass.subclass()`](https://github.com/ehmicky/modern-errors#options-1)\n\n```js\nexport const ExampleError = BaseError.subclass('ExampleError', {\n  serialize: options,\n})\n```\n\n- A specific error: second argument to\n  [`new ErrorClass()`](https://github.com/ehmicky/modern-errors#options-3)\n\n```js\nthrow new ExampleError('...', { serialize: options })\n```\n\n- A specific [`BaseError.serialize(error)`](#baseerrorserializeerror) or\n  [`BaseError.parse(errorObject)`](#baseerrorparseerrorobject) call\n\n```js\nBaseError.serialize(error, options)\n```\n\n```js\nBaseError.parse(errorObject, options)\n```\n\n# Usage\n\n## JSON safety\n\nError plain objects are always\n[safe to serialize with JSON](https://github.com/ehmicky/safe-json-value).\n\n```js\nconst error = new ExampleError('message')\nerror.cycle = error\n\n// Cycles make `JSON.stringify()` throw, so they are removed\nconsole.log(BaseError.serialize(error).cycle) // undefined\n```\n\n## Deep serialization/parsing\n\nThe [`loose` option](#loose) can be used to deeply serialize/parse objects and\narrays.\n\n```js\nconst error = new ExampleError('message')\nconst deepArray = BaseError.serialize([{}, { error }], { loose: true })\n\nconst jsonString = JSON.stringify(deepArray)\nconst newDeepArray = JSON.parse(jsonString)\n\nconst newError = BaseError.parse(newDeepArray, { loose: true })[1].error\n// ExampleError: message\n//     at ...\n```\n\n## Automatic serialization\n\n[`error.toJSON()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#tojson_behavior)\nis defined. It is automatically called by\n[`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify).\n\n```js\nconst error = new ExampleError('message')\nconst deepArray = [{}, { error }]\n\nconst jsonString = JSON.stringify(deepArray)\nconst newDeepArray = JSON.parse(jsonString)\n\nconst newError = BaseError.parse(newDeepArray, { loose: true })[1].error\n// ExampleError: message\n//     at ...\n```\n\n## Omit additional error properties\n\n```js\nconst ExampleError = BaseError.subclass('ExampleError', {\n  serialize: { include: ['name', 'message', 'stack'] },\n})\nconst error = new ExampleError('example')\nerror.prop = true\n\nconst errorObject = ExampleError.serialize(error)\nconsole.log(errorObject.prop) // undefined\nconsole.log(errorObject) // { name: 'Error', message: 'example', stack: '...' }\n```\n\n## Omit stack traces\n\n```js\nconst ExampleError = BaseError.subclass('ExampleError', {\n  serialize: { exclude: ['stack'] },\n})\nconst error = new ExampleError('example')\n\nconst errorObject = ExampleError.serialize(error)\nconsole.log(errorObject.stack) // undefined\nconsole.log(errorObject) // { name: 'Error', message: 'example' }\n```\n\n## Transforming\n\n\u003c!-- eslint-disable fp/no-mutation, no-param-reassign --\u003e\n\n```js\nconst errors = [new ExampleError('message secret')]\nerrors[0].date = new Date()\n\nconst errorObjects = BaseError.serialize(errors, {\n  loose: true,\n  // Serialize `Date` instances as strings\n  transformObject: (errorObject) =\u003e {\n    errorObject.date = errorObject.date.toString()\n  },\n})\nconsole.log(errorObjects[0].date) // Date string\n\nconst newErrors = BaseError.parse(errorObjects, {\n  loose: true,\n  // Transform error message\n  transformArgs: (constructorArgs) =\u003e {\n    constructorArgs[0] = constructorArgs[0].replace('secret', '***')\n  },\n  // Parse date strings as `Date` instances\n  transformInstance: (error) =\u003e {\n    error.date = new Date(error.date)\n  },\n})\nconsole.log(newErrors[0].message) // 'message ***'\nconsole.log(newErrors[0].date) // `Date` instance\n```\n\n## Custom serialization/parsing\n\nErrors are converted to/from plain objects, not strings. This allows any\nserialization/parsing logic to be performed.\n\n```js\nimport { dump, load } from 'js-yaml'\n\nconst error = new ExampleError('message')\nconst errorObject = BaseError.serialize(error)\nconst errorYamlString = dump(errorObject)\n// name: ExampleError\n// message: message\n// stack: ExampleError: message ...\nconst newErrorObject = load(errorYamlString)\nconst newError = BaseError.parse(newErrorObject) // ExampleError: message\n```\n\n## Additional error properties\n\n```js\nconst error = new ExampleError('message', { props: { prop: true } })\nconst errorObject = BaseError.serialize(error)\nconsole.log(errorObject.prop) // true\nconst newError = BaseError.parse(errorObject)\nconsole.log(newError.prop) // true\n```\n\n## Aggregate `errors`\n\n```js\nconst error = new ExampleError('message', {\n  errors: [new ExampleError('one'), new ExampleError('two')],\n})\n\nconst errorObject = BaseError.serialize(error)\n// {\n//   name: 'ExampleError',\n//   message: 'message',\n//   stack: '...',\n//   errors: [{ name: 'ExampleError', message: 'one', stack: '...' }, ...],\n// }\nconst newError = BaseError.parse(errorObject)\n// ExampleError: message\n//   [errors]: [ExampleError: one, ExampleError: two]\n```\n\n## Constructors\n\nIf an error with a\n[`custom` class](https://github.com/ehmicky/modern-errors#-custom-logic) is\nparsed, its custom constructor is not called. However, any property previously\nset by that constructor is still preserved, providing it is serializable and\nenumerable.\n\n\u003c!-- eslint-disable fp/no-this, fp/no-mutation --\u003e\n\n```js\nconst ExampleError = BaseError.subclass('ExampleError', {\n  custom: class extends BaseError {\n    constructor(message, options, prop) {\n      super(message, options, prop)\n      this.prop = prop\n    }\n  },\n})\n\nconst error = new ExampleError('message', {}, true)\nconst errorObject = BaseError.serialize(error)\n// `constructor(message, options, prop)` is not called\nconst newError = BaseError.parse(errorObject)\n// But properties set by that `constructor(...)` are kept\nconsole.log(newError.prop) // true\n```\n\n# Related projects\n\n- [`error-serializer`](https://github.com/ehmicky/error-serializer): Convert\n  errors to/from plain objects\n- [`modern-errors`](https://github.com/ehmicky/modern-errors): Handle errors in\n  a simple, stable, consistent way\n- [`modern-errors-cli`](https://github.com/ehmicky/modern-errors-cli): Handle\n  errors in CLI modules\n- [`modern-errors-beautiful`](https://github.com/ehmicky/modern-errors-beautiful):\n  Prettify errors messages and stacks\n- [`modern-errors-process`](https://github.com/ehmicky/modern-errors-process):\n  Handle process errors\n- [`modern-errors-bugs`](https://github.com/ehmicky/modern-errors-bugs): Print\n  where to report bugs\n- [`modern-errors-clean`](https://github.com/ehmicky/modern-errors-clean): Clean\n  stack traces\n- [`modern-errors-http`](https://github.com/ehmicky/modern-errors-http): Create\n  HTTP error responses\n- [`modern-errors-winston`](https://github.com/ehmicky/modern-errors-winston):\n  Log errors with Winston\n- [`modern-errors-switch`](https://github.com/ehmicky/modern-errors-switch):\n  Execute class-specific logic\n\n# Support\n\nFor any question, _don't hesitate_ to [submit an issue on GitHub](../../issues).\n\nEveryone is welcome regardless of personal background. We enforce a\n[Code of conduct](CODE_OF_CONDUCT.md) in order to promote a positive and\ninclusive environment.\n\n# Contributing\n\nThis project was made with ❤️. The simplest way to give back is by starring and\nsharing it online.\n\nIf the documentation is unclear or has a typo, please click on the page's `Edit`\nbutton (pencil icon) and suggest a correction.\n\nIf you would like to help us fix a bug or add a new feature, please check our\n[guidelines](CONTRIBUTING.md). Pull requests are welcome!\n\n\u003c!-- Thanks go to our wonderful contributors: --\u003e\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://fosstodon.org/@ehmicky\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/8136211?v=4?s=100\" width=\"100px;\" alt=\"ehmicky\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eehmicky\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ehmicky/modern-errors-serialize/commits?author=ehmicky\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"#design-ehmicky\" title=\"Design\"\u003e🎨\u003c/a\u003e \u003ca href=\"#ideas-ehmicky\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e \u003ca href=\"https://github.com/ehmicky/modern-errors-serialize/commits?author=ehmicky\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://vegardit.com\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/7782055?v=4?s=100\" width=\"100px;\" alt=\"Benjamin Kroeger\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eBenjamin Kroeger\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#ideas-benkroeger\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-restore --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fehmicky%2Fmodern-errors-serialize","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fehmicky%2Fmodern-errors-serialize","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fehmicky%2Fmodern-errors-serialize/lists"}