hardhat 3.4.3 → 3.4.4
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 +8 -0
- package/dist/src/internal/builtin-plugins/coverage/coverage-manager.d.ts +1 -1
- package/dist/src/internal/builtin-plugins/coverage/coverage-manager.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/coverage/coverage-manager.js +9 -9
- package/dist/src/internal/builtin-plugins/coverage/coverage-manager.js.map +1 -1
- package/dist/src/internal/cli/telemetry/error-classification/classifier.d.ts +58 -0
- package/dist/src/internal/cli/telemetry/error-classification/classifier.d.ts.map +1 -0
- package/dist/src/internal/cli/telemetry/error-classification/classifier.js +402 -0
- package/dist/src/internal/cli/telemetry/error-classification/classifier.js.map +1 -0
- package/dist/src/internal/cli/telemetry/error-classification/codebase-dependent-helpers.d.ts +67 -0
- package/dist/src/internal/cli/telemetry/error-classification/codebase-dependent-helpers.d.ts.map +1 -0
- package/dist/src/internal/cli/telemetry/error-classification/codebase-dependent-helpers.js +140 -0
- package/dist/src/internal/cli/telemetry/error-classification/codebase-dependent-helpers.js.map +1 -0
- package/dist/src/internal/cli/telemetry/error-classification/filter.d.ts +15 -0
- package/dist/src/internal/cli/telemetry/error-classification/filter.d.ts.map +1 -0
- package/dist/src/internal/cli/telemetry/error-classification/filter.js +114 -0
- package/dist/src/internal/cli/telemetry/error-classification/filter.js.map +1 -0
- package/dist/src/internal/cli/telemetry/error-classification/helpers.d.ts +52 -0
- package/dist/src/internal/cli/telemetry/error-classification/helpers.d.ts.map +1 -0
- package/dist/src/internal/cli/telemetry/error-classification/helpers.js +163 -0
- package/dist/src/internal/cli/telemetry/error-classification/helpers.js.map +1 -0
- package/dist/src/internal/cli/telemetry/error-reporter/reporter.d.ts.map +1 -1
- package/dist/src/internal/cli/telemetry/error-reporter/reporter.js +14 -0
- package/dist/src/internal/cli/telemetry/error-reporter/reporter.js.map +1 -1
- package/dist/src/internal/cli/telemetry/sentry/anonymizer.d.ts +0 -2
- package/dist/src/internal/cli/telemetry/sentry/anonymizer.d.ts.map +1 -1
- package/dist/src/internal/cli/telemetry/sentry/anonymizer.js +0 -117
- package/dist/src/internal/cli/telemetry/sentry/anonymizer.js.map +1 -1
- package/dist/src/internal/cli/telemetry/sentry/init.d.ts.map +1 -1
- package/dist/src/internal/cli/telemetry/sentry/init.js +14 -9
- package/dist/src/internal/cli/telemetry/sentry/init.js.map +1 -1
- package/dist/src/internal/cli/telemetry/sentry/reporter.d.ts.map +1 -1
- package/dist/src/internal/cli/telemetry/sentry/reporter.js +1 -27
- package/dist/src/internal/cli/telemetry/sentry/reporter.js.map +1 -1
- package/dist/src/internal/cli/telemetry/sentry/subprocess.js +11 -17
- package/dist/src/internal/cli/telemetry/sentry/subprocess.js.map +1 -1
- package/package.json +1 -1
- package/src/internal/builtin-plugins/coverage/coverage-manager.ts +14 -10
- package/src/internal/cli/telemetry/error-classification/classifier.ts +636 -0
- package/src/internal/cli/telemetry/error-classification/codebase-dependent-helpers.ts +200 -0
- package/src/internal/cli/telemetry/error-classification/filter.ts +140 -0
- package/src/internal/cli/telemetry/error-classification/helpers.ts +235 -0
- package/src/internal/cli/telemetry/error-reporter/reporter.ts +21 -0
- package/src/internal/cli/telemetry/sentry/anonymizer.ts +0 -168
- package/src/internal/cli/telemetry/sentry/init.ts +42 -33
- package/src/internal/cli/telemetry/sentry/reporter.ts +1 -44
- package/src/internal/cli/telemetry/sentry/subprocess.ts +11 -19
- package/templates/hardhat-3/01-node-test-runner-viem/package.json +2 -2
- package/templates/hardhat-3/02-mocha-ethers/package.json +1 -1
- package/templates/hardhat-3/03-minimal/package.json +1 -1
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HardhatError,
|
|
3
|
+
assertHardhatInvariant,
|
|
4
|
+
} from "@nomicfoundation/hardhat-errors";
|
|
5
|
+
import {
|
|
6
|
+
DirectoryNotEmptyError,
|
|
7
|
+
FileAlreadyExistsError,
|
|
8
|
+
FileNotFoundError,
|
|
9
|
+
FileSystemAccessError,
|
|
10
|
+
InvalidFileFormatError,
|
|
11
|
+
IsDirectoryError,
|
|
12
|
+
NotADirectoryError,
|
|
13
|
+
} from "@nomicfoundation/hardhat-utils/fs";
|
|
14
|
+
import {
|
|
15
|
+
PackageJsonNotFoundError,
|
|
16
|
+
PackageJsonReadError,
|
|
17
|
+
} from "@nomicfoundation/hardhat-utils/package";
|
|
18
|
+
import {
|
|
19
|
+
DispatcherError,
|
|
20
|
+
RequestError,
|
|
21
|
+
ResponseStatusCodeError,
|
|
22
|
+
} from "@nomicfoundation/hardhat-utils/request";
|
|
23
|
+
import {
|
|
24
|
+
SubprocessFileNotFoundError,
|
|
25
|
+
SubprocessPathIsDirectoryError,
|
|
26
|
+
} from "@nomicfoundation/hardhat-utils/subprocess";
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
EdrProviderStackTraceGenerationError,
|
|
30
|
+
SolidityTestStackTraceGenerationError,
|
|
31
|
+
} from "../../../builtin-plugins/network-manager/edr/stack-traces/stack-trace-generation-errors.js";
|
|
32
|
+
import {
|
|
33
|
+
ProviderError,
|
|
34
|
+
UnknownError,
|
|
35
|
+
} from "../../../builtin-plugins/network-manager/provider-errors.js";
|
|
36
|
+
import { UsingHardhat2PluginError } from "../../../using-hardhat2-plugin-errors.js";
|
|
37
|
+
|
|
38
|
+
import {
|
|
39
|
+
getHookExecutionFrame,
|
|
40
|
+
getTaskExecutionFrame,
|
|
41
|
+
isConfigLoadingBoundaryFrame,
|
|
42
|
+
isConsoleEvaluationBoundaryFrame,
|
|
43
|
+
isEdrFrame,
|
|
44
|
+
isFirstPartyPluginFrame,
|
|
45
|
+
isHookHandlerBoundaryFrame,
|
|
46
|
+
isMochaTestExecutionBoundaryFrame,
|
|
47
|
+
isNodeTestExecutionBoundaryFrame,
|
|
48
|
+
isRunningInsideHardhatMonorepo,
|
|
49
|
+
isScriptExecutionBoundaryFrame,
|
|
50
|
+
isTaskActionBoundaryFrame,
|
|
51
|
+
isThirdPartyFrame,
|
|
52
|
+
} from "./codebase-dependent-helpers.js";
|
|
53
|
+
import {
|
|
54
|
+
type ErrorContext,
|
|
55
|
+
type StackFrame,
|
|
56
|
+
FrameOrigin,
|
|
57
|
+
createErrorContext,
|
|
58
|
+
getNodeErrorCode,
|
|
59
|
+
hasErrorClassName,
|
|
60
|
+
includesAny,
|
|
61
|
+
} from "./helpers.js";
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Classifies the error based on a set of heuristics.
|
|
65
|
+
*
|
|
66
|
+
* This classification is later used to select different criteria to decide if
|
|
67
|
+
* the error should be reported or not, and in some cases, how to display it in
|
|
68
|
+
* the CLI.
|
|
69
|
+
*
|
|
70
|
+
* @param error The error to classify.
|
|
71
|
+
* @param ignoreDevelopmentTimeFilter If true, the classifier will ignore the
|
|
72
|
+
* development-time filter, which is used to exclude errors that happen during
|
|
73
|
+
* development of Hardhat itself. This is only meant to be used for testing.
|
|
74
|
+
* @returns The error category.
|
|
75
|
+
*/
|
|
76
|
+
export function classifyError(
|
|
77
|
+
error: Error,
|
|
78
|
+
ignoreDevelopmentTimeFilter = false,
|
|
79
|
+
): ErrorCategory {
|
|
80
|
+
const context = createErrorContext(error);
|
|
81
|
+
|
|
82
|
+
for (const matcher of ERROR_CATEGORY_MATCHERS) {
|
|
83
|
+
if (ignoreDevelopmentTimeFilter && matcher === isDevelopmentTimeError) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const category = matcher(context);
|
|
88
|
+
if (category !== undefined) {
|
|
89
|
+
return category;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return ErrorCategory.UNEXPECTED_ERROR;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export enum ErrorCategory {
|
|
97
|
+
CJS_TO_ESM_MIGRATION_ERROR = "CJS_TO_ESM_MIGRATION_ERROR",
|
|
98
|
+
HH2_TO_HH3_MIGRATION_ERROR = "HH2_TO_HH3_MIGRATION_ERROR",
|
|
99
|
+
TYPESCRIPT_SUPPORT_ERROR = "TYPESCRIPT_SUPPORT_ERROR",
|
|
100
|
+
DEVELOPMENT_TIME_ERROR = "DEVELOPMENT_TIME_ERROR",
|
|
101
|
+
HARDHAT_ERROR = "HARDHAT_ERROR",
|
|
102
|
+
CONFIG_LOADING_ERROR = "CONFIG_LOADING_ERROR",
|
|
103
|
+
CONSOLE_EVALUATION_ERROR = "CONSOLE_EVALUATION_ERROR",
|
|
104
|
+
SCRIPT_EXECUTION_ERROR = "SCRIPT_EXECUTION_ERROR",
|
|
105
|
+
NODE_TEST_EXECUTION_ERROR = "NODE_TEST_EXECUTION_ERROR",
|
|
106
|
+
MOCHA_TEST_EXECUTION_ERROR = "MOCHA_TEST_EXECUTION_ERROR",
|
|
107
|
+
TASK_ACTION_ERROR = "TASK_ACTION_ERROR",
|
|
108
|
+
PLUGIN_TASK_ACTION_ERROR = "PLUGIN_TASK_ACTION_ERROR",
|
|
109
|
+
USER_TASK_ACTION_ERROR = "USER_TASK_ACTION_ERROR",
|
|
110
|
+
PLUGIN_HOOK_HANDLER_ERROR = "PLUGIN_HOOK_HANDLER_ERROR",
|
|
111
|
+
PROVIDER_INTERACTION_ERROR = "PROVIDER_INTERACTION_ERROR",
|
|
112
|
+
EDR_ERROR = "EDR_ERROR",
|
|
113
|
+
NETWORK_INTERACTION_ERROR = "NETWORK_INTERACTION_ERROR",
|
|
114
|
+
RUNTIME_ENVIRONMENT_ERROR = "RUNTIME_ENVIRONMENT_ERROR",
|
|
115
|
+
FILESYSTEM_INTERACTION_ERROR = "FILESYSTEM_INTERACTION_ERROR",
|
|
116
|
+
UNEXPECTED_ERROR = "UNEXPECTED_ERROR",
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
type ErrorCategoryMatcher = (
|
|
120
|
+
context: ErrorContext,
|
|
121
|
+
) => ErrorCategory | undefined;
|
|
122
|
+
|
|
123
|
+
export type UserCodeBoundaryCategory =
|
|
124
|
+
| ErrorCategory.CONFIG_LOADING_ERROR
|
|
125
|
+
| ErrorCategory.CONSOLE_EVALUATION_ERROR
|
|
126
|
+
| ErrorCategory.SCRIPT_EXECUTION_ERROR
|
|
127
|
+
| ErrorCategory.NODE_TEST_EXECUTION_ERROR
|
|
128
|
+
| ErrorCategory.MOCHA_TEST_EXECUTION_ERROR
|
|
129
|
+
| ErrorCategory.PLUGIN_TASK_ACTION_ERROR
|
|
130
|
+
| ErrorCategory.USER_TASK_ACTION_ERROR
|
|
131
|
+
| ErrorCategory.PLUGIN_HOOK_HANDLER_ERROR;
|
|
132
|
+
|
|
133
|
+
export const USER_CODE_BOUNDARY_FRAME_MATCHERS: Record<
|
|
134
|
+
UserCodeBoundaryCategory,
|
|
135
|
+
(frame: StackFrame) => boolean
|
|
136
|
+
> = {
|
|
137
|
+
[ErrorCategory.CONFIG_LOADING_ERROR]: isConfigLoadingBoundaryFrame,
|
|
138
|
+
[ErrorCategory.CONSOLE_EVALUATION_ERROR]: isConsoleEvaluationBoundaryFrame,
|
|
139
|
+
[ErrorCategory.SCRIPT_EXECUTION_ERROR]: isScriptExecutionBoundaryFrame,
|
|
140
|
+
[ErrorCategory.NODE_TEST_EXECUTION_ERROR]: isNodeTestExecutionBoundaryFrame,
|
|
141
|
+
[ErrorCategory.MOCHA_TEST_EXECUTION_ERROR]: isMochaTestExecutionBoundaryFrame,
|
|
142
|
+
[ErrorCategory.PLUGIN_TASK_ACTION_ERROR]: isTaskActionBoundaryFrame,
|
|
143
|
+
[ErrorCategory.USER_TASK_ACTION_ERROR]: isTaskActionBoundaryFrame,
|
|
144
|
+
[ErrorCategory.PLUGIN_HOOK_HANDLER_ERROR]: isHookHandlerBoundaryFrame,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// These are categories that only need the boundary check for classification
|
|
148
|
+
const BOUNDARY_ONLY_ERROR_CATEGORIES = [
|
|
149
|
+
ErrorCategory.CONFIG_LOADING_ERROR,
|
|
150
|
+
ErrorCategory.SCRIPT_EXECUTION_ERROR,
|
|
151
|
+
ErrorCategory.NODE_TEST_EXECUTION_ERROR,
|
|
152
|
+
ErrorCategory.MOCHA_TEST_EXECUTION_ERROR,
|
|
153
|
+
ErrorCategory.CONSOLE_EVALUATION_ERROR,
|
|
154
|
+
] as const;
|
|
155
|
+
|
|
156
|
+
// IMPORTANT: The order here matters, as the first matcher that returns a
|
|
157
|
+
// category wins
|
|
158
|
+
const ERROR_CATEGORY_MATCHERS: ErrorCategoryMatcher[] = [
|
|
159
|
+
isDevelopmentTimeError,
|
|
160
|
+
isESMMigrationError,
|
|
161
|
+
isHH3MigrationError,
|
|
162
|
+
isTypescriptSupportError,
|
|
163
|
+
isHardhatError,
|
|
164
|
+
isProviderInteractionError,
|
|
165
|
+
isEdrError,
|
|
166
|
+
isNetworkInteractionError,
|
|
167
|
+
isRuntimeEnvironmentError,
|
|
168
|
+
isFilesystemInteractionError,
|
|
169
|
+
isTaskActionError,
|
|
170
|
+
isBoundaryOnlyError,
|
|
171
|
+
isPluginTaskActionError,
|
|
172
|
+
isUserTaskActionError,
|
|
173
|
+
isPluginHookHandlerError,
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
const ESM_MIGRATION_MARKERS = [
|
|
177
|
+
"is not defined in es module scope",
|
|
178
|
+
"cannot use import statement outside a module",
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Classifies common CommonJS/ESM migration failures by matching standard Node
|
|
183
|
+
* runtime markers.
|
|
184
|
+
*/
|
|
185
|
+
function isESMMigrationError(
|
|
186
|
+
context: ErrorContext,
|
|
187
|
+
): ErrorCategory.CJS_TO_ESM_MIGRATION_ERROR | undefined {
|
|
188
|
+
for (const lowercaseMessage of context.lowercaseMessageByError.values()) {
|
|
189
|
+
if (
|
|
190
|
+
includesAny(lowercaseMessage, ...ESM_MIGRATION_MARKERS) ||
|
|
191
|
+
/require\(\) of es module/.test(lowercaseMessage)
|
|
192
|
+
) {
|
|
193
|
+
return ErrorCategory.CJS_TO_ESM_MIGRATION_ERROR;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const HH3_MIGRATION_MARKERS = [
|
|
199
|
+
"class extends value undefined is not a constructor or null",
|
|
200
|
+
"the requested module 'hardhat' does not provide an export named",
|
|
201
|
+
'the requested module "hardhat" does not provide an export named',
|
|
202
|
+
"the requested module 'hardhat/config' does not provide an export named",
|
|
203
|
+
'the requested module "hardhat/config" does not provide an export named',
|
|
204
|
+
"the requested module 'hardhat/plugins' does not provide an export named",
|
|
205
|
+
'the requested module "hardhat/plugins" does not provide an export named',
|
|
206
|
+
"the requested module 'hardhat/builtin-tasks/task-names' does not provide an export named",
|
|
207
|
+
'the requested module "hardhat/builtin-tasks/task-names" does not provide an export named',
|
|
208
|
+
"the requested module 'hardhat/types/runtime' does not provide an export named",
|
|
209
|
+
'the requested module "hardhat/types/runtime" does not provide an export named',
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
const HH2_PLUGIN_ERROR_MARKER =
|
|
213
|
+
"you are trying to use a hardhat 2 plugin in a hardhat 3 project";
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Classifies Hardhat 2 to Hardhat 3 migration failures by checking for known
|
|
217
|
+
* migration error types and message patterns anywhere in the cause chain.
|
|
218
|
+
*
|
|
219
|
+
* This is temporary, and will be removed once the HH2 to HH3 migration by the
|
|
220
|
+
* community is in a solid state. The matcher is broad, but right now mostly
|
|
221
|
+
* correct.
|
|
222
|
+
*/
|
|
223
|
+
function isHH3MigrationError(
|
|
224
|
+
context: ErrorContext,
|
|
225
|
+
): ErrorCategory.HH2_TO_HH3_MIGRATION_ERROR | undefined {
|
|
226
|
+
if (
|
|
227
|
+
context.errorChain.some(
|
|
228
|
+
(candidate) =>
|
|
229
|
+
hasErrorClassName(candidate, UsingHardhat2PluginError) ||
|
|
230
|
+
(context.lowercaseMessageByError.get(candidate) ?? "").includes(
|
|
231
|
+
HH2_PLUGIN_ERROR_MARKER,
|
|
232
|
+
),
|
|
233
|
+
)
|
|
234
|
+
) {
|
|
235
|
+
return ErrorCategory.HH2_TO_HH3_MIGRATION_ERROR;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
for (const lowercaseMessage of context.lowercaseMessageByError.values()) {
|
|
239
|
+
if (includesAny(lowercaseMessage, ...HH3_MIGRATION_MARKERS)) {
|
|
240
|
+
return ErrorCategory.HH2_TO_HH3_MIGRATION_ERROR;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (
|
|
245
|
+
context.errorChain.some(
|
|
246
|
+
(candidate) => candidate.stack?.includes("@nomiclabs") === true,
|
|
247
|
+
)
|
|
248
|
+
) {
|
|
249
|
+
return ErrorCategory.HH2_TO_HH3_MIGRATION_ERROR;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* If Hardhat is being run from the monorepo, we don't report the error.
|
|
255
|
+
*/
|
|
256
|
+
function isDevelopmentTimeError(
|
|
257
|
+
_context: ErrorContext,
|
|
258
|
+
): ErrorCategory.DEVELOPMENT_TIME_ERROR | undefined {
|
|
259
|
+
if (isRunningInsideHardhatMonorepo()) {
|
|
260
|
+
return ErrorCategory.DEVELOPMENT_TIME_ERROR;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return undefined;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const TYPESCRIPT_SUPPORT_ERROR_CODES = new Set([
|
|
267
|
+
"ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX",
|
|
268
|
+
"ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING",
|
|
269
|
+
"ERR_NO_TYPESCRIPT",
|
|
270
|
+
"ERR_UNKNOWN_FILE_EXTENSION",
|
|
271
|
+
]);
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Classifies Node.js TypeScript support failures by matching Node's stable
|
|
275
|
+
* error codes. ERR_UNKNOWN_FILE_EXTENSION also happens for non-TypeScript
|
|
276
|
+
* extensions, so require a TypeScript extension in that error's message.
|
|
277
|
+
*/
|
|
278
|
+
function isTypescriptSupportError(
|
|
279
|
+
context: ErrorContext,
|
|
280
|
+
): ErrorCategory.TYPESCRIPT_SUPPORT_ERROR | undefined {
|
|
281
|
+
if (
|
|
282
|
+
context.errorChain.some((candidate) => {
|
|
283
|
+
const code = getTypescriptSupportErrorCode(candidate);
|
|
284
|
+
|
|
285
|
+
if (code === undefined) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return (
|
|
290
|
+
code !== "ERR_UNKNOWN_FILE_EXTENSION" ||
|
|
291
|
+
includesAny(
|
|
292
|
+
context.lowercaseMessageByError.get(candidate),
|
|
293
|
+
".ts",
|
|
294
|
+
".mts",
|
|
295
|
+
".cts",
|
|
296
|
+
)
|
|
297
|
+
);
|
|
298
|
+
})
|
|
299
|
+
) {
|
|
300
|
+
return ErrorCategory.TYPESCRIPT_SUPPORT_ERROR;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Classifies top-level HardhatError instances that were not captured by a more
|
|
306
|
+
* specific matcher earlier in the chain.
|
|
307
|
+
*/
|
|
308
|
+
function isHardhatError(
|
|
309
|
+
context: ErrorContext,
|
|
310
|
+
): ErrorCategory.HARDHAT_ERROR | undefined {
|
|
311
|
+
if (HardhatError.isHardhatError(context.error)) {
|
|
312
|
+
return ErrorCategory.HARDHAT_ERROR;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function isBoundaryOnlyError(
|
|
317
|
+
context: ErrorContext,
|
|
318
|
+
): (typeof BOUNDARY_ONLY_ERROR_CATEGORIES)[number] | undefined {
|
|
319
|
+
for (const category of BOUNDARY_ONLY_ERROR_CATEGORIES) {
|
|
320
|
+
const boundaryMatcher = USER_CODE_BOUNDARY_FRAME_MATCHERS[category];
|
|
321
|
+
|
|
322
|
+
if (context.allStackFrames.some(boundaryMatcher)) {
|
|
323
|
+
return category;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Classifies task-action failures routed through ResolvedTask when the nearest
|
|
330
|
+
* task-action frame belongs to first-party code.
|
|
331
|
+
*/
|
|
332
|
+
function isTaskActionError(
|
|
333
|
+
context: ErrorContext,
|
|
334
|
+
): ErrorCategory.TASK_ACTION_ERROR | undefined {
|
|
335
|
+
const taskActionFrame = getTaskExecutionFrame(context.allStackFrames);
|
|
336
|
+
|
|
337
|
+
if (
|
|
338
|
+
taskActionFrame !== undefined &&
|
|
339
|
+
isFirstPartyPluginFrame(taskActionFrame.location)
|
|
340
|
+
) {
|
|
341
|
+
return ErrorCategory.TASK_ACTION_ERROR;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Classifies task-action failures routed through ResolvedTask when the nearest
|
|
347
|
+
* task-action frame belongs to a third-party plugin.
|
|
348
|
+
*/
|
|
349
|
+
function isPluginTaskActionError(
|
|
350
|
+
context: ErrorContext,
|
|
351
|
+
): ErrorCategory.PLUGIN_TASK_ACTION_ERROR | undefined {
|
|
352
|
+
const taskActionFrame = getTaskExecutionFrame(context.allStackFrames);
|
|
353
|
+
|
|
354
|
+
if (
|
|
355
|
+
taskActionFrame !== undefined &&
|
|
356
|
+
isThirdPartyFrame(taskActionFrame.location)
|
|
357
|
+
) {
|
|
358
|
+
return ErrorCategory.PLUGIN_TASK_ACTION_ERROR;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Classifies task-action failures routed through ResolvedTask when the nearest
|
|
364
|
+
* task-action frame belongs to user project code.
|
|
365
|
+
*/
|
|
366
|
+
function isUserTaskActionError(
|
|
367
|
+
context: ErrorContext,
|
|
368
|
+
): ErrorCategory.USER_TASK_ACTION_ERROR | undefined {
|
|
369
|
+
const taskActionFrame = getTaskExecutionFrame(context.allStackFrames);
|
|
370
|
+
|
|
371
|
+
if (
|
|
372
|
+
taskActionFrame !== undefined &&
|
|
373
|
+
taskActionFrame.origin === FrameOrigin.USER_PROJECT
|
|
374
|
+
) {
|
|
375
|
+
return ErrorCategory.USER_TASK_ACTION_ERROR;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Classifies hook execution failures routed through HookManager when the
|
|
381
|
+
* nearest hook-handler frame belongs to a third-party plugin.
|
|
382
|
+
*/
|
|
383
|
+
function isPluginHookHandlerError(
|
|
384
|
+
context: ErrorContext,
|
|
385
|
+
): ErrorCategory.PLUGIN_HOOK_HANDLER_ERROR | undefined {
|
|
386
|
+
const hookExecutionFrame = getHookExecutionFrame(context.allStackFrames);
|
|
387
|
+
|
|
388
|
+
if (
|
|
389
|
+
hookExecutionFrame !== undefined &&
|
|
390
|
+
isThirdPartyFrame(hookExecutionFrame.location)
|
|
391
|
+
) {
|
|
392
|
+
return ErrorCategory.PLUGIN_HOOK_HANDLER_ERROR;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const PROVIDER_INTERACTION_ERROR_WRAPPED_IN_UNKNOWN_EDR_ERROR_MARKERS = [
|
|
397
|
+
"unauthorized",
|
|
398
|
+
"rate limit",
|
|
399
|
+
"too many requests",
|
|
400
|
+
"historical state unavailable",
|
|
401
|
+
];
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Classifies provider-facing failures, including Solidity errors, expected
|
|
405
|
+
* provider errors, and selected UnknownError cases with provider-like causes.
|
|
406
|
+
*/
|
|
407
|
+
function isProviderInteractionError(
|
|
408
|
+
context: ErrorContext,
|
|
409
|
+
): ErrorCategory.PROVIDER_INTERACTION_ERROR | undefined {
|
|
410
|
+
if (
|
|
411
|
+
context.errorChain.some(
|
|
412
|
+
(candidate) =>
|
|
413
|
+
candidate.name === "SolidityError" ||
|
|
414
|
+
(ProviderError.isProviderError(candidate) &&
|
|
415
|
+
isUnknownEdrError(candidate, context) === false),
|
|
416
|
+
)
|
|
417
|
+
) {
|
|
418
|
+
return ErrorCategory.PROVIDER_INTERACTION_ERROR;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (
|
|
422
|
+
isUnknownEdrError(context.errorChain[0], context) &&
|
|
423
|
+
context.errorChain[1] !== undefined &&
|
|
424
|
+
includesAny(
|
|
425
|
+
context.lowercaseMessageByError.get(context.errorChain[1]),
|
|
426
|
+
...PROVIDER_INTERACTION_ERROR_WRAPPED_IN_UNKNOWN_EDR_ERROR_MARKERS,
|
|
427
|
+
)
|
|
428
|
+
) {
|
|
429
|
+
return ErrorCategory.PROVIDER_INTERACTION_ERROR;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Classifies EDR-specific failures, including stack-trace generation errors
|
|
435
|
+
* and remaining UnknownError cases from the provider layer.
|
|
436
|
+
*/
|
|
437
|
+
function isEdrError(
|
|
438
|
+
context: ErrorContext,
|
|
439
|
+
): ErrorCategory.EDR_ERROR | undefined {
|
|
440
|
+
if (
|
|
441
|
+
context.errorChain.some(
|
|
442
|
+
(candidate) =>
|
|
443
|
+
hasErrorClassName(candidate, EdrProviderStackTraceGenerationError) ||
|
|
444
|
+
hasErrorClassName(candidate, SolidityTestStackTraceGenerationError) ||
|
|
445
|
+
isUnknownEdrError(candidate, context),
|
|
446
|
+
)
|
|
447
|
+
) {
|
|
448
|
+
return ErrorCategory.EDR_ERROR;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Classifies network-related failures by collapsing request setup, request
|
|
454
|
+
* transport, response status, and telemetry transport errors into one bucket.
|
|
455
|
+
*/
|
|
456
|
+
function isNetworkInteractionError(
|
|
457
|
+
context: ErrorContext,
|
|
458
|
+
): ErrorCategory.NETWORK_INTERACTION_ERROR | undefined {
|
|
459
|
+
if (
|
|
460
|
+
context.errorChain.some((candidate) =>
|
|
461
|
+
hasErrorClassName(candidate, ResponseStatusCodeError),
|
|
462
|
+
) ||
|
|
463
|
+
context.errorChain.some((candidate) =>
|
|
464
|
+
hasErrorClassName(candidate, DispatcherError),
|
|
465
|
+
) ||
|
|
466
|
+
context.errorChain.some((candidate) =>
|
|
467
|
+
hasErrorClassName(candidate, RequestError),
|
|
468
|
+
) ||
|
|
469
|
+
(context.lowercaseMessageByError.get(context.error) ?? "").includes(
|
|
470
|
+
"fetch failed",
|
|
471
|
+
)
|
|
472
|
+
) {
|
|
473
|
+
return ErrorCategory.NETWORK_INTERACTION_ERROR;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Classifies runtime-environment incompatibilities by matching a small set of
|
|
479
|
+
* capability-related error messages.
|
|
480
|
+
*/
|
|
481
|
+
function isRuntimeEnvironmentError(
|
|
482
|
+
context: ErrorContext,
|
|
483
|
+
): ErrorCategory.RUNTIME_ENVIRONMENT_ERROR | undefined {
|
|
484
|
+
if (
|
|
485
|
+
Array.from(context.lowercaseMessageByError.values()).some((message) =>
|
|
486
|
+
includesAny(
|
|
487
|
+
message,
|
|
488
|
+
"toreversed is not a function",
|
|
489
|
+
"flatmap is not a function",
|
|
490
|
+
"crypto is not defined",
|
|
491
|
+
),
|
|
492
|
+
)
|
|
493
|
+
) {
|
|
494
|
+
return ErrorCategory.RUNTIME_ENVIRONMENT_ERROR;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Classifies project-data and filesystem-related failures by matching a known
|
|
500
|
+
* filesystem/project-data error type or a raw Node.js filesystem error code
|
|
501
|
+
* anywhere in the cause chain.
|
|
502
|
+
*/
|
|
503
|
+
function isFilesystemInteractionError(
|
|
504
|
+
context: ErrorContext,
|
|
505
|
+
): ErrorCategory.FILESYSTEM_INTERACTION_ERROR | undefined {
|
|
506
|
+
if (
|
|
507
|
+
context.errorChain.some(
|
|
508
|
+
(candidate) =>
|
|
509
|
+
isKnownFilesystemOrProjectDataError(candidate) ||
|
|
510
|
+
isNodeFilesystemError(candidate),
|
|
511
|
+
)
|
|
512
|
+
) {
|
|
513
|
+
return ErrorCategory.FILESYSTEM_INTERACTION_ERROR;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function isUnknownEdrError(error: Error, context: ErrorContext): boolean {
|
|
518
|
+
const errorIndex = context.errorChain.indexOf(error);
|
|
519
|
+
assertHardhatInvariant(
|
|
520
|
+
errorIndex !== -1,
|
|
521
|
+
"isUnknownEdrError must be called with an error from the error chain",
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
return (
|
|
525
|
+
ProviderError.isProviderError(error) &&
|
|
526
|
+
(error.code === UnknownError.CODE || error.name === "UnknownError") &&
|
|
527
|
+
context.errorChain
|
|
528
|
+
.slice(errorIndex)
|
|
529
|
+
.some((candidate) =>
|
|
530
|
+
(context.stackFramesByError.get(candidate) ?? []).some(isEdrFrame),
|
|
531
|
+
)
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// This list should be kept up to date with hardhat-utils/fs errors
|
|
536
|
+
const HARDHAT_UTILS_FILESYSTEM_ERROR_CLASSES = [
|
|
537
|
+
PackageJsonReadError,
|
|
538
|
+
PackageJsonNotFoundError,
|
|
539
|
+
InvalidFileFormatError,
|
|
540
|
+
FileNotFoundError,
|
|
541
|
+
IsDirectoryError,
|
|
542
|
+
NotADirectoryError,
|
|
543
|
+
FileAlreadyExistsError,
|
|
544
|
+
DirectoryNotEmptyError,
|
|
545
|
+
] as const;
|
|
546
|
+
|
|
547
|
+
// This list should be kept up to date with hardhat-utils/subprocess errors
|
|
548
|
+
const HARDHAT_UTILS_SUBPROCESS_ERROR_CLASSES = [
|
|
549
|
+
SubprocessFileNotFoundError,
|
|
550
|
+
SubprocessPathIsDirectoryError,
|
|
551
|
+
] as const;
|
|
552
|
+
|
|
553
|
+
const NODE_FILESYSTEM_ERROR_CODES = new Set([
|
|
554
|
+
"EACCES",
|
|
555
|
+
"EAGAIN",
|
|
556
|
+
"EBADF",
|
|
557
|
+
"EBUSY",
|
|
558
|
+
"EEXIST",
|
|
559
|
+
"EFBIG",
|
|
560
|
+
"EINTR",
|
|
561
|
+
"EINVAL",
|
|
562
|
+
"EIO",
|
|
563
|
+
"EISDIR",
|
|
564
|
+
"ELOOP",
|
|
565
|
+
"EMFILE",
|
|
566
|
+
"ENAMETOOLONG",
|
|
567
|
+
"ENFILE",
|
|
568
|
+
"ENODEV",
|
|
569
|
+
"ENOENT",
|
|
570
|
+
"ENOSPC",
|
|
571
|
+
"ENOTDIR",
|
|
572
|
+
"ENOTEMPTY",
|
|
573
|
+
"ENOTSUP",
|
|
574
|
+
"ENXIO",
|
|
575
|
+
"EOPNOTSUPP",
|
|
576
|
+
"EOVERFLOW",
|
|
577
|
+
"EPERM",
|
|
578
|
+
"EROFS",
|
|
579
|
+
"ESPIPE",
|
|
580
|
+
"ETXTBSY",
|
|
581
|
+
"EXDEV",
|
|
582
|
+
]);
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Returns `true` for any of the filesystem/project-data error classes the
|
|
586
|
+
* classifier knows about. This is the gate for the FILESYSTEM_INTERACTION_ERROR
|
|
587
|
+
* category.
|
|
588
|
+
*/
|
|
589
|
+
export function isKnownFilesystemOrProjectDataError(error: Error): boolean {
|
|
590
|
+
return (
|
|
591
|
+
isHardhatUtilsFilesystemError(error) ||
|
|
592
|
+
isSubprocessFilesystemError(error) ||
|
|
593
|
+
hasErrorClassName(error, FileSystemAccessError)
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Returns `true` for filesystem errors that callers commonly expect to surface
|
|
599
|
+
* during normal operation (e.g. missing files, format errors). Used by both
|
|
600
|
+
* the classifier (as part of the filesystem-interaction gate) and the filter
|
|
601
|
+
* (to drop these errors from reporting).
|
|
602
|
+
*/
|
|
603
|
+
export function isHardhatUtilsFilesystemError(error: Error): boolean {
|
|
604
|
+
return HARDHAT_UTILS_FILESYSTEM_ERROR_CLASSES.some((cls) =>
|
|
605
|
+
hasErrorClassName(error, cls),
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Returns `true` for filesystem errors raised by the subprocess-spawning
|
|
611
|
+
* helpers. These are unexpected enough to be worth reporting on their own.
|
|
612
|
+
*/
|
|
613
|
+
export function isSubprocessFilesystemError(error: Error): boolean {
|
|
614
|
+
return HARDHAT_UTILS_SUBPROCESS_ERROR_CLASSES.some((cls) =>
|
|
615
|
+
hasErrorClassName(error, cls),
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Returns `true` for the node errors with fs related codes.
|
|
621
|
+
*/
|
|
622
|
+
function isNodeFilesystemError(error: Error): boolean {
|
|
623
|
+
const code = getNodeErrorCode(error);
|
|
624
|
+
|
|
625
|
+
return code !== undefined && NODE_FILESYSTEM_ERROR_CODES.has(code);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function getTypescriptSupportErrorCode(error: Error): string | undefined {
|
|
629
|
+
if (
|
|
630
|
+
"code" in error &&
|
|
631
|
+
typeof error.code === "string" &&
|
|
632
|
+
TYPESCRIPT_SUPPORT_ERROR_CODES.has(error.code)
|
|
633
|
+
) {
|
|
634
|
+
return error.code;
|
|
635
|
+
}
|
|
636
|
+
}
|