index.js 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. 'use strict';
  2. class NonError extends Error {
  3. constructor(message) {
  4. super(NonError._prepareSuperMessage(message));
  5. Object.defineProperty(this, 'name', {
  6. value: 'NonError',
  7. configurable: true,
  8. writable: true
  9. });
  10. if (Error.captureStackTrace) {
  11. Error.captureStackTrace(this, NonError);
  12. }
  13. }
  14. static _prepareSuperMessage(message) {
  15. try {
  16. return JSON.stringify(message);
  17. } catch (_) {
  18. return String(message);
  19. }
  20. }
  21. }
  22. const commonProperties = [
  23. {property: 'name', enumerable: false},
  24. {property: 'message', enumerable: false},
  25. {property: 'stack', enumerable: false},
  26. {property: 'code', enumerable: true}
  27. ];
  28. const destroyCircular = ({from, seen, to_, forceEnumerable}) => {
  29. const to = to_ || (Array.isArray(from) ? [] : {});
  30. seen.push(from);
  31. for (const [key, value] of Object.entries(from)) {
  32. if (typeof value === 'function') {
  33. continue;
  34. }
  35. if (!value || typeof value !== 'object') {
  36. to[key] = value;
  37. continue;
  38. }
  39. if (!seen.includes(from[key])) {
  40. to[key] = destroyCircular({from: from[key], seen: seen.slice(), forceEnumerable});
  41. continue;
  42. }
  43. to[key] = '[Circular]';
  44. }
  45. for (const {property, enumerable} of commonProperties) {
  46. if (typeof from[property] === 'string') {
  47. Object.defineProperty(to, property, {
  48. value: from[property],
  49. enumerable: forceEnumerable ? true : enumerable,
  50. configurable: true,
  51. writable: true
  52. });
  53. }
  54. }
  55. return to;
  56. };
  57. const serializeError = value => {
  58. if (typeof value === 'object' && value !== null) {
  59. return destroyCircular({from: value, seen: [], forceEnumerable: true});
  60. }
  61. // People sometimes throw things besides Error objects…
  62. if (typeof value === 'function') {
  63. // `JSON.stringify()` discards functions. We do too, unless a function is thrown directly.
  64. return `[Function: ${(value.name || 'anonymous')}]`;
  65. }
  66. return value;
  67. };
  68. const deserializeError = value => {
  69. if (value instanceof Error) {
  70. return value;
  71. }
  72. if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
  73. const newError = new Error();
  74. destroyCircular({from: value, seen: [], to_: newError});
  75. return newError;
  76. }
  77. return new NonError(value);
  78. };
  79. module.exports = {
  80. serializeError,
  81. deserializeError
  82. };