@exodus/error-tracking 3.3.2 → 3.4.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 +6 -0
- package/atoms/index.js +7 -0
- package/index.js +17 -2
- package/module/index.js +1 -0
- package/module/unsafe-error-tracking.js +34 -0
- package/package.json +4 -4
- package/plugin/index.js +48 -21
- package/plugin/why-is-remote-tracking-disabled.js +26 -21
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.4.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/error-tracking@3.3.2...@exodus/error-tracking@3.4.0) (2026-04-03)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- feat: add `unsafe__errorTracking` module (#15400)
|
|
11
|
+
|
|
6
12
|
## [3.3.2](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/error-tracking@3.3.1...@exodus/error-tracking@3.3.2) (2026-02-12)
|
|
7
13
|
|
|
8
14
|
### Bug Fixes
|
package/atoms/index.js
CHANGED
|
@@ -18,3 +18,10 @@ export const remoteErrorTrackingEnabledAtomDefinition = {
|
|
|
18
18
|
// eslint-disable-next-line @exodus/hydra/in-memory-atom-default-value
|
|
19
19
|
factory: () => createInMemoryAtom(),
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
export const unsafeRemoteErrorTrackingEnabledAtomDefinition = {
|
|
23
|
+
id: 'unsafeRemoteErrorTrackingEnabledAtom',
|
|
24
|
+
type: 'atom',
|
|
25
|
+
// eslint-disable-next-line @exodus/hydra/in-memory-atom-default-value
|
|
26
|
+
factory: () => createInMemoryAtom(),
|
|
27
|
+
}
|
package/index.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import typeforce from '@exodus/typeforce'
|
|
2
2
|
|
|
3
3
|
import { errorTrackingApiDefinition } from './api/index.js'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
errorsAtomDefinition,
|
|
6
|
+
remoteErrorTrackingEnabledAtomDefinition,
|
|
7
|
+
unsafeRemoteErrorTrackingEnabledAtomDefinition,
|
|
8
|
+
} from './atoms/index.js'
|
|
5
9
|
import errorTrackingReportDefinition from './report/index.js'
|
|
6
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
errorTrackingDefinition,
|
|
12
|
+
remoteErrorTrackingDefinition,
|
|
13
|
+
unsafeErrorTrackingDefinition,
|
|
14
|
+
} from './module/index.js'
|
|
7
15
|
import errorTrackingPluginDefinition from './plugin/index.js'
|
|
8
16
|
|
|
9
17
|
const defaultConfig = {
|
|
@@ -46,6 +54,9 @@ const errorTracking = (config = Object.create(null)) => {
|
|
|
46
54
|
{
|
|
47
55
|
definition: remoteErrorTrackingEnabledAtomDefinition,
|
|
48
56
|
},
|
|
57
|
+
{
|
|
58
|
+
definition: unsafeRemoteErrorTrackingEnabledAtomDefinition,
|
|
59
|
+
},
|
|
49
60
|
{
|
|
50
61
|
definition: errorTrackingApiDefinition,
|
|
51
62
|
},
|
|
@@ -58,6 +69,10 @@ const errorTracking = (config = Object.create(null)) => {
|
|
|
58
69
|
definition: remoteErrorTrackingDefinition,
|
|
59
70
|
config: sentryConfig,
|
|
60
71
|
},
|
|
72
|
+
{
|
|
73
|
+
if: remoteErrorTrackingAvailable,
|
|
74
|
+
definition: unsafeErrorTrackingDefinition,
|
|
75
|
+
},
|
|
61
76
|
// deprecated, errors will go to sentry
|
|
62
77
|
{
|
|
63
78
|
definition: errorTrackingReportDefinition,
|
package/module/index.js
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { captureStackTrace } from '@exodus/errors'
|
|
2
|
+
|
|
3
|
+
const createUnsafeErrorTracking = ({
|
|
4
|
+
unsafeRemoteErrorTrackingEnabledAtom,
|
|
5
|
+
remoteErrorTracking,
|
|
6
|
+
logger,
|
|
7
|
+
}) => {
|
|
8
|
+
const track = async ({ error, context }) => {
|
|
9
|
+
if (!remoteErrorTracking) return
|
|
10
|
+
|
|
11
|
+
if (!(error instanceof Error)) {
|
|
12
|
+
logger.error(new TypeError('error must be an instance of Error'))
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
captureStackTrace(error)
|
|
17
|
+
logger.error(error)
|
|
18
|
+
const enabled = await unsafeRemoteErrorTrackingEnabledAtom.get()
|
|
19
|
+
if (!enabled) return
|
|
20
|
+
|
|
21
|
+
await remoteErrorTracking.track({ error, context })
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return { track }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const unsafeErrorTrackingDefinition = {
|
|
28
|
+
// eslint-disable-next-line @exodus/hydra/dependency-ids
|
|
29
|
+
id: 'unsafe__errorTracking',
|
|
30
|
+
type: 'module',
|
|
31
|
+
factory: createUnsafeErrorTracking,
|
|
32
|
+
dependencies: ['unsafeRemoteErrorTrackingEnabledAtom', 'remoteErrorTracking?', 'logger'],
|
|
33
|
+
public: true,
|
|
34
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/error-tracking",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A simple error tracking package to let any feature collect errors and create the report",
|
|
6
6
|
"author": "Exodus Movement, Inc.",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@exodus/atoms": "^9.0.0",
|
|
36
|
-
"@exodus/basic-utils": "^3.
|
|
36
|
+
"@exodus/basic-utils": "^3.7.3",
|
|
37
37
|
"@exodus/errors": "^3.7.0",
|
|
38
38
|
"@exodus/safe-string": "^1.4.0",
|
|
39
39
|
"@exodus/sentry-client": "^6.2.0",
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
"@exodus/zod": "^3.24.2"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@exodus/traceparent": "^
|
|
44
|
+
"@exodus/traceparent": "^3.0.1"
|
|
45
45
|
},
|
|
46
46
|
"publishConfig": {
|
|
47
47
|
"access": "public",
|
|
48
48
|
"provenance": false
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "d70ddc5076017b6f1b5845c99eff340fc5ef0cdf"
|
|
51
51
|
}
|
package/plugin/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { combine, compute } from '@exodus/atoms'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
whyIsRemoteTrackingDisabled,
|
|
5
|
+
whyIsUnsafeRemoteTrackingDisabled,
|
|
6
|
+
} from './why-is-remote-tracking-disabled.js'
|
|
4
7
|
|
|
5
8
|
function errorTrackingPlugin({
|
|
6
9
|
abTestingAtom,
|
|
@@ -8,6 +11,7 @@ function errorTrackingPlugin({
|
|
|
8
11
|
walletCreatedAtAtom,
|
|
9
12
|
getBuildMetadata,
|
|
10
13
|
remoteErrorTrackingEnabledAtom,
|
|
14
|
+
unsafeRemoteErrorTrackingEnabledAtom,
|
|
11
15
|
config: {
|
|
12
16
|
remoteErrorTrackingABExperimentId,
|
|
13
17
|
remoteErrorTrackingFundedWalletsABExperimentId,
|
|
@@ -18,6 +22,16 @@ function errorTrackingPlugin({
|
|
|
18
22
|
}) {
|
|
19
23
|
const subscriptions = []
|
|
20
24
|
|
|
25
|
+
const toEnabled = (reasonDisabled, label) => {
|
|
26
|
+
if (reasonDisabled) {
|
|
27
|
+
logger.debug(`${label} is disabled: ${reasonDisabled}`)
|
|
28
|
+
return false
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
logger.debug(`${label} is enabled`)
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
34
|
+
|
|
21
35
|
const onAssetsSynced = async () => {
|
|
22
36
|
if (!abTestingAtom) {
|
|
23
37
|
logger.debug('remote error tracking is disabled, ab-testing feature not found')
|
|
@@ -32,31 +46,43 @@ function errorTrackingPlugin({
|
|
|
32
46
|
earliestTxDate: earliestTxDateAtom,
|
|
33
47
|
walletCreatedAt: walletCreatedAtAtom,
|
|
34
48
|
}),
|
|
35
|
-
selector: async ({ abTesting, earliestTxDate, walletCreatedAt }) =>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
selector: async ({ abTesting, earliestTxDate, walletCreatedAt }) =>
|
|
50
|
+
toEnabled(
|
|
51
|
+
whyIsRemoteTrackingDisabled({
|
|
52
|
+
remoteErrorTrackingABExperimentId,
|
|
53
|
+
remoteErrorTrackingFundedWalletsABExperimentId,
|
|
54
|
+
abTesting,
|
|
55
|
+
trackWalletsCreatedAfter,
|
|
56
|
+
walletCreatedAt,
|
|
57
|
+
trackFundedWallets,
|
|
58
|
+
earliestTxDate,
|
|
59
|
+
buildMetadata: await getBuildMetadata(),
|
|
60
|
+
logger,
|
|
61
|
+
}),
|
|
62
|
+
'remote error tracking'
|
|
63
|
+
),
|
|
64
|
+
})
|
|
47
65
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
66
|
+
subscriptions.push(
|
|
67
|
+
internalRemoteErrorTrackingEnabledAtom.observe(remoteErrorTrackingEnabledAtom.set)
|
|
68
|
+
)
|
|
52
69
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
70
|
+
const internalUnsafeRemoteErrorTrackingEnabledAtom = compute({
|
|
71
|
+
atom: abTestingAtom,
|
|
72
|
+
selector: async (abTesting) =>
|
|
73
|
+
toEnabled(
|
|
74
|
+
whyIsUnsafeRemoteTrackingDisabled({
|
|
75
|
+
remoteErrorTrackingABExperimentId,
|
|
76
|
+
abTesting,
|
|
77
|
+
buildMetadata: await getBuildMetadata(),
|
|
78
|
+
logger,
|
|
79
|
+
}),
|
|
80
|
+
'unsafe remote error tracking'
|
|
81
|
+
),
|
|
56
82
|
})
|
|
57
83
|
|
|
58
84
|
subscriptions.push(
|
|
59
|
-
|
|
85
|
+
internalUnsafeRemoteErrorTrackingEnabledAtom.observe(unsafeRemoteErrorTrackingEnabledAtom.set)
|
|
60
86
|
)
|
|
61
87
|
}
|
|
62
88
|
|
|
@@ -80,6 +106,7 @@ const errorTrackingPluginDefinition = {
|
|
|
80
106
|
'earliestTxDateAtom',
|
|
81
107
|
'walletCreatedAtAtom',
|
|
82
108
|
'remoteErrorTrackingEnabledAtom',
|
|
109
|
+
'unsafeRemoteErrorTrackingEnabledAtom',
|
|
83
110
|
'getBuildMetadata',
|
|
84
111
|
'config',
|
|
85
112
|
'logger',
|
|
@@ -59,6 +59,14 @@ export class ErrorTrackingAbExperiment {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
function whyIsExperimentDisabled({ experimentId, abTesting, logger, prefix = '' }) {
|
|
63
|
+
const experiment = new ErrorTrackingAbExperiment({ experimentId, abTesting, logger })
|
|
64
|
+
|
|
65
|
+
if (!experiment.hasExperimentId) return `${prefix}missing-ab-experiment-id`
|
|
66
|
+
if (!experiment.enabled) return `${prefix}ab-experiment-disabled`
|
|
67
|
+
if (experiment.variant !== 'enabled') return `${prefix}ab-experiment-variant-disabled`
|
|
68
|
+
}
|
|
69
|
+
|
|
62
70
|
export function whyIsRemoteTrackingDisabled({
|
|
63
71
|
remoteErrorTrackingABExperimentId,
|
|
64
72
|
remoteErrorTrackingFundedWalletsABExperimentId,
|
|
@@ -72,20 +80,12 @@ export function whyIsRemoteTrackingDisabled({
|
|
|
72
80
|
}) {
|
|
73
81
|
if (buildMetadata.dev) return 'dev-mode'
|
|
74
82
|
|
|
75
|
-
const
|
|
83
|
+
const baseDisabledReason = whyIsExperimentDisabled({
|
|
76
84
|
experimentId: remoteErrorTrackingABExperimentId,
|
|
77
85
|
abTesting,
|
|
78
86
|
logger,
|
|
79
87
|
})
|
|
80
|
-
|
|
81
|
-
if (!unfundedExperiment.hasExperimentId) return 'missing-ab-experiment-id'
|
|
82
|
-
if (!unfundedExperiment.enabled) {
|
|
83
|
-
return 'ab-experiment-disabled'
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (unfundedExperiment.variant !== 'enabled') {
|
|
87
|
-
return 'ab-experiment-variant-disabled'
|
|
88
|
-
}
|
|
88
|
+
if (baseDisabledReason) return baseDisabledReason
|
|
89
89
|
|
|
90
90
|
if (
|
|
91
91
|
trackWalletsCreatedAfter &&
|
|
@@ -103,21 +103,26 @@ export function whyIsRemoteTrackingDisabled({
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
// FUNDED WALLETS: separate AB experiment
|
|
106
|
-
const
|
|
106
|
+
const fundedDisabledReason = whyIsExperimentDisabled({
|
|
107
107
|
experimentId: remoteErrorTrackingFundedWalletsABExperimentId,
|
|
108
108
|
abTesting,
|
|
109
109
|
logger,
|
|
110
|
+
prefix: 'funded-wallets-',
|
|
110
111
|
})
|
|
112
|
+
if (fundedDisabledReason) return fundedDisabledReason
|
|
113
|
+
}
|
|
111
114
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
export function whyIsUnsafeRemoteTrackingDisabled({
|
|
116
|
+
remoteErrorTrackingABExperimentId,
|
|
117
|
+
abTesting,
|
|
118
|
+
buildMetadata,
|
|
119
|
+
logger,
|
|
120
|
+
}) {
|
|
121
|
+
if (buildMetadata.dev) return 'dev-mode'
|
|
119
122
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
+
return whyIsExperimentDisabled({
|
|
124
|
+
experimentId: remoteErrorTrackingABExperimentId,
|
|
125
|
+
abTesting,
|
|
126
|
+
logger,
|
|
127
|
+
})
|
|
123
128
|
}
|