@exodus/errors 3.0.1 → 3.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/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [3.1.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/errors@3.0.1...@exodus/errors@3.1.0) (2025-08-04)
7
+
8
+ ### Features
9
+
10
+ - feat: support interpolating non-dynamic safeString into safeString on parse (#13003)
11
+
6
12
  ## [3.0.1](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/errors@3.0.0...@exodus/errors@3.0.1) (2025-07-08)
7
13
 
8
14
  **Note:** Version bump only for package @exodus/errors
package/README.md CHANGED
@@ -45,6 +45,43 @@ Parsing/sanitization of error messages is unreliable and the cost of failure is
45
45
 
46
46
  Unfortunately, the built-in `.stack` property is mutable and outside of our control. Instead, we use the `Error.prepareStackTrace` API, which enables us to make sure we access the actual call stack and not a cached `err.stack` value that may have already been consumed and modified. We then parse it into a structured format that we can safely sanitize and control. This approach provides consistent, reliable stack traces across different environments (currently supporting both V8 and Hermes).
47
47
 
48
+ ## Recipes
49
+
50
+ > [!TIP]
51
+ > Before diving into the recipes, you might want to get familiar with what a 'Safe String' is: https://github.com/ExodusMovement/exodus-hydra/tree/master/libraries/safe-string
52
+
53
+ ### I want to preserve server errors in Safe Errors
54
+
55
+ Do you control the server? If so, better send short error codes from your server instead. `err.code` will [be passed through](https://github.com/ExodusOSS/hydra/blob/master/libraries/errors/src/safe-error.ts#L32) SafeError coercion.
56
+
57
+ If you do NOT control the server, and you know the specific error messages returned by the server, you can predefine them wrapped in `safeString`:
58
+
59
+ ```js
60
+ import { safeString } from '@exodus/safe-string'
61
+
62
+ // From: https://github.com/ethereum/go-ethereum/blob/master/core/txpool/errors.go.
63
+ export const KnownErrors = new Set([
64
+ safeString`already known`,
65
+ safeString`invalid sender`,
66
+ safeString`transaction underpriced`,
67
+ ])
68
+ ```
69
+
70
+ You can now handle failed requests like this:
71
+
72
+ ```js
73
+ import { safeString } from '@exodus/safe-string`
74
+ import { KnownErrors } from './errors.js'
75
+
76
+ const response = await this.request(request)
77
+ const error = response?.error
78
+
79
+ if (error) {
80
+ const message = KnownErrors.get(msg) ?? safeString`unknown error`
81
+ throw new Error(safeString`Bad rpc response: ${message}`)
82
+ }
83
+ ```
84
+
48
85
  ## Troubleshooting
49
86
 
50
87
  ### A SafeError instance returns `undefined` stack trace?
package/lib/safe-error.js CHANGED
@@ -1,4 +1,4 @@
1
- import { getAllowlist, isSafe } from '@exodus/safe-string';
1
+ import { getAllowlist, parseString } from '@exodus/safe-string';
2
2
  import assert from 'minimalistic-assert';
3
3
  import parseStackTraceNatively, { stackFramesToString } from './stack.js';
4
4
  import { omitUndefined } from './utils.js';
@@ -26,10 +26,6 @@ function isSafeCode(value) {
26
26
  return SAFE_CODES_SET.has(value) || isExodusErrorCode(value);
27
27
  }
28
28
  const staticAllowlist = getAllowlist();
29
- function getSafeString(str) {
30
- if (isSafe(str))
31
- return str;
32
- }
33
29
  const FACTORY_SYMBOL = Symbol('SafeError');
34
30
  export class SafeError {
35
31
  static from(err) {
@@ -44,7 +40,7 @@ export class SafeError {
44
40
  const hintCandidates = [hint, message].filter((str) => typeof str === 'string');
45
41
  safeHint =
46
42
  // chicken sacrifice to TypeScript, otherwise would be hintCandidates.find((str) => isSafe(str))
47
- hintCandidates.map((str) => getSafeString(str)).find(Boolean) ||
43
+ hintCandidates.map((str) => parseString(str)).find(Boolean) ||
48
44
  staticAllowlist.find((safePrefix) => hintCandidates.some((str) => str.startsWith(safePrefix))) ||
49
45
  toCommonErrorMessage(message);
50
46
  const safeStack = parseStackTraceNatively(err);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@exodus/errors",
3
3
  "type": "module",
4
- "version": "3.0.1",
4
+ "version": "3.1.0",
5
5
  "description": "Utilities for error handling in client code, such as sanitization",
6
6
  "author": "Exodus Movement, Inc.",
7
7
  "repository": {
@@ -34,7 +34,7 @@
34
34
  "test:hermes": "EXODUS_TEST_COVERAGE=0 run -T exodus-test --engine hermes:bundle --esbuild --jest src/__tests__/hermes/*.test.ts src/__tests__/engine-agnostic/*.test.ts"
35
35
  },
36
36
  "dependencies": {
37
- "@exodus/safe-string": "^1.0.0",
37
+ "@exodus/safe-string": "^1.2.0",
38
38
  "minimalistic-assert": "^1.0.1"
39
39
  },
40
40
  "devDependencies": {
@@ -44,5 +44,5 @@
44
44
  "publishConfig": {
45
45
  "access": "public"
46
46
  },
47
- "gitHead": "7fd991f460540c1396fc68391393ee690269fa7f"
47
+ "gitHead": "917eaccefaf98df19b72a145a52510759cea1237"
48
48
  }