intellerror 1.0.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/LICENSE +21 -0
- package/README.md +164 -0
- package/dist/formatter/index.d.ts +1 -0
- package/dist/index.cjs +174 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +135 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/index.d.ts +2 -0
- package/dist/parser/index.d.ts +9 -0
- package/dist/register.cjs +148 -0
- package/dist/register.cjs.map +1 -0
- package/dist/register.d.ts +1 -0
- package/dist/register.js +124 -0
- package/dist/register.js.map +1 -0
- package/dist/suggestions/index.d.ts +2 -0
- package/dist/suggestions/rules.d.ts +7 -0
- package/examples/async-error.ts +16 -0
- package/examples/basic-usage.ts +18 -0
- package/examples/custom-error.ts +21 -0
- package/examples/express-middleware.ts +19 -0
- package/package.json +64 -0
- package/src/config.ts +31 -0
- package/src/formatter/browser.ts +166 -0
- package/src/formatter/index.ts +120 -0
- package/src/formatter/snapshot.ts +34 -0
- package/src/index.ts +6 -0
- package/src/integrations/webhook.ts +23 -0
- package/src/middleware/index.ts +23 -0
- package/src/parser/index.ts +38 -0
- package/src/register.ts +68 -0
- package/src/suggestions/index.ts +12 -0
- package/src/suggestions/links.ts +12 -0
- package/src/suggestions/rules.ts +236 -0
- package/test.ts +16 -0
- package/tests/config.test.ts +25 -0
- package/tests/rules.test.ts +33 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +9 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
|
|
25
|
+
// src/formatter/index.ts
|
|
26
|
+
var import_chalk = __toESM(require("chalk"), 1);
|
|
27
|
+
|
|
28
|
+
// src/parser/index.ts
|
|
29
|
+
var stackTraceParser = __toESM(require("stacktrace-parser"), 1);
|
|
30
|
+
function parseStack(error) {
|
|
31
|
+
if (!error.stack) return [];
|
|
32
|
+
const parsed = stackTraceParser.parse(error.stack);
|
|
33
|
+
return parsed.map((frame) => {
|
|
34
|
+
const file = frame.file || "";
|
|
35
|
+
const isNodeInternal = file.startsWith("node:") || file.startsWith("internal/") || !file.includes("/") && !file.includes("\\");
|
|
36
|
+
const isNodeModule = file.includes("node_modules");
|
|
37
|
+
return {
|
|
38
|
+
file: frame.file,
|
|
39
|
+
methodName: frame.methodName || "<unknown>",
|
|
40
|
+
lineNumber: frame.lineNumber,
|
|
41
|
+
column: frame.column,
|
|
42
|
+
isNodeInternal: Boolean(isNodeInternal),
|
|
43
|
+
isNodeModule: Boolean(isNodeModule)
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/suggestions/rules.ts
|
|
49
|
+
var rules = [
|
|
50
|
+
{
|
|
51
|
+
match: (err) => err.message.includes("Cannot read properties of undefined (reading '") || err.message.includes("is not defined"),
|
|
52
|
+
message: "Accessing property on undefined/not defined object.",
|
|
53
|
+
fix: "User optional chaining like 'obj?.prop' or ensure the object is initialized.",
|
|
54
|
+
description: "You are trying to read a property from a variable that currently holds an undefined value."
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
match: (err) => err instanceof SyntaxError && err.message.includes("Unexpected token"),
|
|
58
|
+
message: "JSON Parsing or Code Syntax error.",
|
|
59
|
+
fix: "Check your JSON string for trailing commas, proper quotes, or syntax issues in your code.",
|
|
60
|
+
description: "The engine encountered something it didn't expect while parsing."
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
match: (err) => err.message.includes("is not a function"),
|
|
64
|
+
message: "Attempted to call a non-function value.",
|
|
65
|
+
fix: "Ensure that you're calling a property that is actually a function.",
|
|
66
|
+
description: "You probably tried to execute something that is a string, number, or object as if it were a function."
|
|
67
|
+
}
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
// src/suggestions/index.ts
|
|
71
|
+
function getSuggestions(error) {
|
|
72
|
+
return rules.filter((rule) => rule.match(error));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/formatter/index.ts
|
|
76
|
+
function formatError(error) {
|
|
77
|
+
if (!(error instanceof Error)) {
|
|
78
|
+
return String(error);
|
|
79
|
+
}
|
|
80
|
+
const stackFrames = parseStack(error);
|
|
81
|
+
const userFrame = stackFrames.find((frame) => !frame.isNodeInternal && !frame.isNodeModule);
|
|
82
|
+
const errorType = error.constructor.name || "Error";
|
|
83
|
+
const typeLabel = import_chalk.default.bgRed.white(` ${errorType} `);
|
|
84
|
+
let output = `
|
|
85
|
+
${typeLabel} ${import_chalk.default.red(error.message)}
|
|
86
|
+
|
|
87
|
+
`;
|
|
88
|
+
if (userFrame && userFrame.file) {
|
|
89
|
+
output += `${import_chalk.default.bold("\u{1F4CD} Location:")}
|
|
90
|
+
`;
|
|
91
|
+
output += `${import_chalk.default.cyan(userFrame.file)}:${import_chalk.default.yellow(userFrame.lineNumber)}:${import_chalk.default.yellow(userFrame.column)} ${import_chalk.default.gray("\u2190 YOUR CODE")}
|
|
92
|
+
|
|
93
|
+
`;
|
|
94
|
+
}
|
|
95
|
+
const suggestions = getSuggestions(error);
|
|
96
|
+
if (suggestions.length > 0) {
|
|
97
|
+
output += `${import_chalk.default.bold("\u{1F4A1} Suggestions:")}
|
|
98
|
+
`;
|
|
99
|
+
for (const suggestion of suggestions) {
|
|
100
|
+
output += `\u2022 ${import_chalk.default.green(suggestion.message)}
|
|
101
|
+
`;
|
|
102
|
+
if (suggestion.fix) {
|
|
103
|
+
output += ` ${import_chalk.default.dim("Fix: " + suggestion.fix)}
|
|
104
|
+
`;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
output += `
|
|
108
|
+
`;
|
|
109
|
+
}
|
|
110
|
+
output += `${import_chalk.default.bold("\u{1F4E6} Stack:")}
|
|
111
|
+
`;
|
|
112
|
+
let hiddenInternalsCount = 0;
|
|
113
|
+
let hiddenModulesCount = 0;
|
|
114
|
+
for (const frame of stackFrames) {
|
|
115
|
+
if (frame.isNodeInternal) {
|
|
116
|
+
hiddenInternalsCount++;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (frame.isNodeModule) {
|
|
120
|
+
hiddenModulesCount++;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
const fileStr = frame.file ? `${import_chalk.default.cyan(frame.file)}:${import_chalk.default.yellow(frame.lineNumber)}:${import_chalk.default.yellow(frame.column)}` : import_chalk.default.gray("<unknown>");
|
|
124
|
+
output += `\u2192 ${fileStr}
|
|
125
|
+
`;
|
|
126
|
+
}
|
|
127
|
+
if (hiddenModulesCount > 0 || hiddenInternalsCount > 0) {
|
|
128
|
+
const hiddenMessages = [];
|
|
129
|
+
if (hiddenModulesCount > 0) hiddenMessages.push(`${hiddenModulesCount} node_modules`);
|
|
130
|
+
if (hiddenInternalsCount > 0) hiddenMessages.push(`${hiddenInternalsCount} node internals`);
|
|
131
|
+
output += import_chalk.default.dim(`(${hiddenMessages.join(" and ")} hidden)
|
|
132
|
+
`);
|
|
133
|
+
}
|
|
134
|
+
return output;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// src/register.ts
|
|
138
|
+
process.on("uncaughtException", (err) => {
|
|
139
|
+
console.error("\n \u{1F525} UNCAUGHT EXCEPTION DETECTED:");
|
|
140
|
+
console.error(formatError(err));
|
|
141
|
+
process.exit(1);
|
|
142
|
+
});
|
|
143
|
+
process.on("unhandledRejection", (reason) => {
|
|
144
|
+
console.error("\n \u{1F525} UNHANDLED REJECTION DETECTED:");
|
|
145
|
+
console.error(formatError(reason instanceof Error ? reason : new Error(String(reason))));
|
|
146
|
+
});
|
|
147
|
+
console.log("\u2705 IntellError register loaded.");
|
|
148
|
+
//# sourceMappingURL=register.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/formatter/index.ts","../src/parser/index.ts","../src/suggestions/rules.ts","../src/suggestions/index.ts","../src/register.ts"],"sourcesContent":["import chalk from 'chalk';\nimport { parseStack } from '../parser/index.js';\nimport { getSuggestions } from '../suggestions/index.js';\n\nexport function formatError(error: Error | unknown): string {\n if (!(error instanceof Error)) {\n return String(error);\n }\n\n const stackFrames = parseStack(error);\n \n // Find first user frame that is not node internal or node_modules\n const userFrame = stackFrames.find(frame => !frame.isNodeInternal && !frame.isNodeModule);\n\n const errorType = error.constructor.name || 'Error';\n const typeLabel = chalk.bgRed.white(` ${errorType} `);\n let output = `\\n${typeLabel} ${chalk.red(error.message)}\\n\\n`;\n\n if (userFrame && userFrame.file) {\n output += `${chalk.bold('š Location:')}\\n`;\n output += `${chalk.cyan(userFrame.file)}:${chalk.yellow(userFrame.lineNumber)}:${chalk.yellow(userFrame.column)} ${chalk.gray('ā YOUR CODE')}\\n\\n`;\n }\n\n const suggestions = getSuggestions(error);\n if (suggestions.length > 0) {\n output += `${chalk.bold('š” Suggestions:')}\\n`;\n for (const suggestion of suggestions) {\n output += `⢠${chalk.green(suggestion.message)}\\n`;\n if (suggestion.fix) {\n output += ` ${chalk.dim('Fix: ' + suggestion.fix)}\\n`;\n }\n }\n output += `\\n`;\n }\n\n output += `${chalk.bold('š¦ Stack:')}\\n`;\n \n let hiddenInternalsCount = 0;\n let hiddenModulesCount = 0;\n\n for (const frame of stackFrames) {\n if (frame.isNodeInternal) {\n hiddenInternalsCount++;\n continue;\n }\n \n if (frame.isNodeModule) {\n hiddenModulesCount++;\n continue; \n }\n \n const fileStr = frame.file \n ? `${chalk.cyan(frame.file)}:${chalk.yellow(frame.lineNumber)}:${chalk.yellow(frame.column)}` \n : chalk.gray('<unknown>');\n \n output += `ā ${fileStr}\\n`;\n }\n\n if (hiddenModulesCount > 0 || hiddenInternalsCount > 0) {\n const hiddenMessages = [];\n if (hiddenModulesCount > 0) hiddenMessages.push(`${hiddenModulesCount} node_modules`);\n if (hiddenInternalsCount > 0) hiddenMessages.push(`${hiddenInternalsCount} node internals`);\n output += chalk.dim(`(${hiddenMessages.join(' and ')} hidden)\\n`);\n }\n\n return output;\n}\n","import * as stackTraceParser from 'stacktrace-parser';\n\nexport interface ParsedStackFrame {\n file: string | null;\n methodName: string;\n lineNumber: number | null;\n column: number | null;\n isNodeInternal: boolean;\n isNodeModule: boolean;\n}\n\nexport function parseStack(error: Error): ParsedStackFrame[] {\n if (!error.stack) return [];\n \n const parsed = stackTraceParser.parse(error.stack);\n \n return parsed.map(frame => {\n const file = frame.file || '';\n \n // Check if it's a built-in node module or internal\n // e.g. node:internal/... or just internal/... or events.js\n const isNodeInternal = \n file.startsWith('node:') || \n file.startsWith('internal/') ||\n !file.includes('/') && !file.includes('\\\\'); // typically a core module if no path separators\n \n const isNodeModule = file.includes('node_modules');\n\n return {\n file: frame.file,\n methodName: frame.methodName || '<unknown>',\n lineNumber: frame.lineNumber,\n column: frame.column,\n isNodeInternal: Boolean(isNodeInternal),\n isNodeModule: Boolean(isNodeModule)\n };\n });\n}\n","export interface SuggesionRule {\n match: (error: Error) => boolean;\n message: string;\n fix?: string;\n description?: string;\n}\n\nexport const rules: SuggesionRule[] = [\n {\n match: (err) => err.message.includes(\"Cannot read properties of undefined (reading '\") || err.message.includes(\"is not defined\"),\n message: \"Accessing property on undefined/not defined object.\",\n fix: \"User optional chaining like 'obj?.prop' or ensure the object is initialized.\",\n description: \"You are trying to read a property from a variable that currently holds an undefined value.\"\n },\n {\n match: (err) => err instanceof SyntaxError && err.message.includes(\"Unexpected token\"),\n message: \"JSON Parsing or Code Syntax error.\",\n fix: \"Check your JSON string for trailing commas, proper quotes, or syntax issues in your code.\",\n description: \"The engine encountered something it didn't expect while parsing.\"\n },\n {\n match: (err) => err.message.includes(\"is not a function\"),\n message: \"Attempted to call a non-function value.\",\n fix: \"Ensure that you're calling a property that is actually a function.\",\n description: \"You probably tried to execute something that is a string, number, or object as if it were a function.\"\n }\n];\n","import { rules, SuggesionRule } from './rules.js';\n\nexport function getSuggestions(error: Error): SuggesionRule[] {\n return rules.filter(rule => rule.match(error));\n}\n","import { formatError } from './formatter/index.js';\n\n// Global hooks to catch all unhandled errors\nprocess.on('uncaughtException', (err) => {\n console.error('\\n š„ UNCAUGHT EXCEPTION DETECTED:');\n console.error(formatError(err));\n process.exit(1);\n});\n\nprocess.on('unhandledRejection', (reason) => {\n console.error('\\n š„ UNHANDLED REJECTION DETECTED:');\n console.error(formatError(reason instanceof Error ? reason : new Error(String(reason))));\n});\n\nconsole.log('ā
IntellError register loaded.');\n"],"mappings":"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","names":["chalk"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/register.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// src/formatter/index.ts
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
|
|
4
|
+
// src/parser/index.ts
|
|
5
|
+
import * as stackTraceParser from "stacktrace-parser";
|
|
6
|
+
function parseStack(error) {
|
|
7
|
+
if (!error.stack) return [];
|
|
8
|
+
const parsed = stackTraceParser.parse(error.stack);
|
|
9
|
+
return parsed.map((frame) => {
|
|
10
|
+
const file = frame.file || "";
|
|
11
|
+
const isNodeInternal = file.startsWith("node:") || file.startsWith("internal/") || !file.includes("/") && !file.includes("\\");
|
|
12
|
+
const isNodeModule = file.includes("node_modules");
|
|
13
|
+
return {
|
|
14
|
+
file: frame.file,
|
|
15
|
+
methodName: frame.methodName || "<unknown>",
|
|
16
|
+
lineNumber: frame.lineNumber,
|
|
17
|
+
column: frame.column,
|
|
18
|
+
isNodeInternal: Boolean(isNodeInternal),
|
|
19
|
+
isNodeModule: Boolean(isNodeModule)
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// src/suggestions/rules.ts
|
|
25
|
+
var rules = [
|
|
26
|
+
{
|
|
27
|
+
match: (err) => err.message.includes("Cannot read properties of undefined (reading '") || err.message.includes("is not defined"),
|
|
28
|
+
message: "Accessing property on undefined/not defined object.",
|
|
29
|
+
fix: "User optional chaining like 'obj?.prop' or ensure the object is initialized.",
|
|
30
|
+
description: "You are trying to read a property from a variable that currently holds an undefined value."
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
match: (err) => err instanceof SyntaxError && err.message.includes("Unexpected token"),
|
|
34
|
+
message: "JSON Parsing or Code Syntax error.",
|
|
35
|
+
fix: "Check your JSON string for trailing commas, proper quotes, or syntax issues in your code.",
|
|
36
|
+
description: "The engine encountered something it didn't expect while parsing."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
match: (err) => err.message.includes("is not a function"),
|
|
40
|
+
message: "Attempted to call a non-function value.",
|
|
41
|
+
fix: "Ensure that you're calling a property that is actually a function.",
|
|
42
|
+
description: "You probably tried to execute something that is a string, number, or object as if it were a function."
|
|
43
|
+
}
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
// src/suggestions/index.ts
|
|
47
|
+
function getSuggestions(error) {
|
|
48
|
+
return rules.filter((rule) => rule.match(error));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/formatter/index.ts
|
|
52
|
+
function formatError(error) {
|
|
53
|
+
if (!(error instanceof Error)) {
|
|
54
|
+
return String(error);
|
|
55
|
+
}
|
|
56
|
+
const stackFrames = parseStack(error);
|
|
57
|
+
const userFrame = stackFrames.find((frame) => !frame.isNodeInternal && !frame.isNodeModule);
|
|
58
|
+
const errorType = error.constructor.name || "Error";
|
|
59
|
+
const typeLabel = chalk.bgRed.white(` ${errorType} `);
|
|
60
|
+
let output = `
|
|
61
|
+
${typeLabel} ${chalk.red(error.message)}
|
|
62
|
+
|
|
63
|
+
`;
|
|
64
|
+
if (userFrame && userFrame.file) {
|
|
65
|
+
output += `${chalk.bold("\u{1F4CD} Location:")}
|
|
66
|
+
`;
|
|
67
|
+
output += `${chalk.cyan(userFrame.file)}:${chalk.yellow(userFrame.lineNumber)}:${chalk.yellow(userFrame.column)} ${chalk.gray("\u2190 YOUR CODE")}
|
|
68
|
+
|
|
69
|
+
`;
|
|
70
|
+
}
|
|
71
|
+
const suggestions = getSuggestions(error);
|
|
72
|
+
if (suggestions.length > 0) {
|
|
73
|
+
output += `${chalk.bold("\u{1F4A1} Suggestions:")}
|
|
74
|
+
`;
|
|
75
|
+
for (const suggestion of suggestions) {
|
|
76
|
+
output += `\u2022 ${chalk.green(suggestion.message)}
|
|
77
|
+
`;
|
|
78
|
+
if (suggestion.fix) {
|
|
79
|
+
output += ` ${chalk.dim("Fix: " + suggestion.fix)}
|
|
80
|
+
`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
output += `
|
|
84
|
+
`;
|
|
85
|
+
}
|
|
86
|
+
output += `${chalk.bold("\u{1F4E6} Stack:")}
|
|
87
|
+
`;
|
|
88
|
+
let hiddenInternalsCount = 0;
|
|
89
|
+
let hiddenModulesCount = 0;
|
|
90
|
+
for (const frame of stackFrames) {
|
|
91
|
+
if (frame.isNodeInternal) {
|
|
92
|
+
hiddenInternalsCount++;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (frame.isNodeModule) {
|
|
96
|
+
hiddenModulesCount++;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const fileStr = frame.file ? `${chalk.cyan(frame.file)}:${chalk.yellow(frame.lineNumber)}:${chalk.yellow(frame.column)}` : chalk.gray("<unknown>");
|
|
100
|
+
output += `\u2192 ${fileStr}
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
103
|
+
if (hiddenModulesCount > 0 || hiddenInternalsCount > 0) {
|
|
104
|
+
const hiddenMessages = [];
|
|
105
|
+
if (hiddenModulesCount > 0) hiddenMessages.push(`${hiddenModulesCount} node_modules`);
|
|
106
|
+
if (hiddenInternalsCount > 0) hiddenMessages.push(`${hiddenInternalsCount} node internals`);
|
|
107
|
+
output += chalk.dim(`(${hiddenMessages.join(" and ")} hidden)
|
|
108
|
+
`);
|
|
109
|
+
}
|
|
110
|
+
return output;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/register.ts
|
|
114
|
+
process.on("uncaughtException", (err) => {
|
|
115
|
+
console.error("\n \u{1F525} UNCAUGHT EXCEPTION DETECTED:");
|
|
116
|
+
console.error(formatError(err));
|
|
117
|
+
process.exit(1);
|
|
118
|
+
});
|
|
119
|
+
process.on("unhandledRejection", (reason) => {
|
|
120
|
+
console.error("\n \u{1F525} UNHANDLED REJECTION DETECTED:");
|
|
121
|
+
console.error(formatError(reason instanceof Error ? reason : new Error(String(reason))));
|
|
122
|
+
});
|
|
123
|
+
console.log("\u2705 IntellError register loaded.");
|
|
124
|
+
//# sourceMappingURL=register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/formatter/index.ts","../src/parser/index.ts","../src/suggestions/rules.ts","../src/suggestions/index.ts","../src/register.ts"],"sourcesContent":["import chalk from 'chalk';\nimport { parseStack } from '../parser/index.js';\nimport { getSuggestions } from '../suggestions/index.js';\n\nexport function formatError(error: Error | unknown): string {\n if (!(error instanceof Error)) {\n return String(error);\n }\n\n const stackFrames = parseStack(error);\n \n // Find first user frame that is not node internal or node_modules\n const userFrame = stackFrames.find(frame => !frame.isNodeInternal && !frame.isNodeModule);\n\n const errorType = error.constructor.name || 'Error';\n const typeLabel = chalk.bgRed.white(` ${errorType} `);\n let output = `\\n${typeLabel} ${chalk.red(error.message)}\\n\\n`;\n\n if (userFrame && userFrame.file) {\n output += `${chalk.bold('š Location:')}\\n`;\n output += `${chalk.cyan(userFrame.file)}:${chalk.yellow(userFrame.lineNumber)}:${chalk.yellow(userFrame.column)} ${chalk.gray('ā YOUR CODE')}\\n\\n`;\n }\n\n const suggestions = getSuggestions(error);\n if (suggestions.length > 0) {\n output += `${chalk.bold('š” Suggestions:')}\\n`;\n for (const suggestion of suggestions) {\n output += `⢠${chalk.green(suggestion.message)}\\n`;\n if (suggestion.fix) {\n output += ` ${chalk.dim('Fix: ' + suggestion.fix)}\\n`;\n }\n }\n output += `\\n`;\n }\n\n output += `${chalk.bold('š¦ Stack:')}\\n`;\n \n let hiddenInternalsCount = 0;\n let hiddenModulesCount = 0;\n\n for (const frame of stackFrames) {\n if (frame.isNodeInternal) {\n hiddenInternalsCount++;\n continue;\n }\n \n if (frame.isNodeModule) {\n hiddenModulesCount++;\n continue; \n }\n \n const fileStr = frame.file \n ? `${chalk.cyan(frame.file)}:${chalk.yellow(frame.lineNumber)}:${chalk.yellow(frame.column)}` \n : chalk.gray('<unknown>');\n \n output += `ā ${fileStr}\\n`;\n }\n\n if (hiddenModulesCount > 0 || hiddenInternalsCount > 0) {\n const hiddenMessages = [];\n if (hiddenModulesCount > 0) hiddenMessages.push(`${hiddenModulesCount} node_modules`);\n if (hiddenInternalsCount > 0) hiddenMessages.push(`${hiddenInternalsCount} node internals`);\n output += chalk.dim(`(${hiddenMessages.join(' and ')} hidden)\\n`);\n }\n\n return output;\n}\n","import * as stackTraceParser from 'stacktrace-parser';\n\nexport interface ParsedStackFrame {\n file: string | null;\n methodName: string;\n lineNumber: number | null;\n column: number | null;\n isNodeInternal: boolean;\n isNodeModule: boolean;\n}\n\nexport function parseStack(error: Error): ParsedStackFrame[] {\n if (!error.stack) return [];\n \n const parsed = stackTraceParser.parse(error.stack);\n \n return parsed.map(frame => {\n const file = frame.file || '';\n \n // Check if it's a built-in node module or internal\n // e.g. node:internal/... or just internal/... or events.js\n const isNodeInternal = \n file.startsWith('node:') || \n file.startsWith('internal/') ||\n !file.includes('/') && !file.includes('\\\\'); // typically a core module if no path separators\n \n const isNodeModule = file.includes('node_modules');\n\n return {\n file: frame.file,\n methodName: frame.methodName || '<unknown>',\n lineNumber: frame.lineNumber,\n column: frame.column,\n isNodeInternal: Boolean(isNodeInternal),\n isNodeModule: Boolean(isNodeModule)\n };\n });\n}\n","export interface SuggesionRule {\n match: (error: Error) => boolean;\n message: string;\n fix?: string;\n description?: string;\n}\n\nexport const rules: SuggesionRule[] = [\n {\n match: (err) => err.message.includes(\"Cannot read properties of undefined (reading '\") || err.message.includes(\"is not defined\"),\n message: \"Accessing property on undefined/not defined object.\",\n fix: \"User optional chaining like 'obj?.prop' or ensure the object is initialized.\",\n description: \"You are trying to read a property from a variable that currently holds an undefined value.\"\n },\n {\n match: (err) => err instanceof SyntaxError && err.message.includes(\"Unexpected token\"),\n message: \"JSON Parsing or Code Syntax error.\",\n fix: \"Check your JSON string for trailing commas, proper quotes, or syntax issues in your code.\",\n description: \"The engine encountered something it didn't expect while parsing.\"\n },\n {\n match: (err) => err.message.includes(\"is not a function\"),\n message: \"Attempted to call a non-function value.\",\n fix: \"Ensure that you're calling a property that is actually a function.\",\n description: \"You probably tried to execute something that is a string, number, or object as if it were a function.\"\n }\n];\n","import { rules, SuggesionRule } from './rules.js';\n\nexport function getSuggestions(error: Error): SuggesionRule[] {\n return rules.filter(rule => rule.match(error));\n}\n","import { formatError } from './formatter/index.js';\n\n// Global hooks to catch all unhandled errors\nprocess.on('uncaughtException', (err) => {\n console.error('\\n š„ UNCAUGHT EXCEPTION DETECTED:');\n console.error(formatError(err));\n process.exit(1);\n});\n\nprocess.on('unhandledRejection', (reason) => {\n console.error('\\n š„ UNHANDLED REJECTION DETECTED:');\n console.error(formatError(reason instanceof Error ? reason : new Error(String(reason))));\n});\n\nconsole.log('ā
IntellError register loaded.');\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { formatError } from '../src/index.js';
|
|
2
|
+
|
|
3
|
+
async function fetchFromBackend() {
|
|
4
|
+
throw new Error("Failed to connect to the backend server.");
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
async function handleRequest() {
|
|
8
|
+
try {
|
|
9
|
+
await fetchFromBackend();
|
|
10
|
+
} catch (error) {
|
|
11
|
+
console.log('--- ASYNC ERROR EXAMPLE ---');
|
|
12
|
+
console.error(formatError(error));
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
handleRequest();
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { formatError } from '../src/index.js';
|
|
2
|
+
|
|
3
|
+
function triggerError() {
|
|
4
|
+
const obj: any = {};
|
|
5
|
+
// TypeError: Cannot read properties of undefined (reading 'first')
|
|
6
|
+
return obj.name.first;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function processData() {
|
|
10
|
+
triggerError();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
processData();
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.log('--- BASIC ERROR EXAMPLE ---');
|
|
17
|
+
console.log(formatError(error));
|
|
18
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { formatError } from '../src/index.js';
|
|
2
|
+
|
|
3
|
+
class ValidationError extends Error {
|
|
4
|
+
constructor(message: string) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = 'ValidationError';
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function validateInput(input: string) {
|
|
11
|
+
if (input.length < 5) {
|
|
12
|
+
throw new ValidationError("Input is too short! Must be at least 5 characters.");
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
validateInput("abc");
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.log('--- CUSTOM ERROR EXAMPLE ---');
|
|
20
|
+
console.log(formatError(error));
|
|
21
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { errorFormatter } from '../src/index.js';
|
|
3
|
+
|
|
4
|
+
const app = express();
|
|
5
|
+
const port = 3000;
|
|
6
|
+
|
|
7
|
+
app.get('/error', (_req, _res) => {
|
|
8
|
+
// Triggering a custom error
|
|
9
|
+
throw new Error("Simulated API Error in route handler");
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
// Use the IntellError middleware
|
|
13
|
+
app.use(errorFormatter());
|
|
14
|
+
|
|
15
|
+
console.log(`Server started at http://localhost:${port}`);
|
|
16
|
+
console.log(`To see the formatted error, visit http://localhost:${port}/error`);
|
|
17
|
+
|
|
18
|
+
// Don't listen to avoid hanging during build or automated test runs
|
|
19
|
+
// app.listen(port);
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "intellerror",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Intelligent error formatting for Node.js and Browser. Transforms ugly stack traces into clean, readable, and actionable output with smart code suggestions.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"require": "./dist/index.cjs"
|
|
12
|
+
},
|
|
13
|
+
"./register": {
|
|
14
|
+
"import": "./dist/register.js",
|
|
15
|
+
"require": "./dist/register.cjs"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"postbuild": "tsc --emitDeclarationOnly",
|
|
21
|
+
"dev": "tsup --watch",
|
|
22
|
+
"test": "tsx test.ts",
|
|
23
|
+
"test:unit": "vitest run",
|
|
24
|
+
"example:basic": "tsx examples/basic-usage.ts",
|
|
25
|
+
"example:async": "tsx examples/async-error.ts",
|
|
26
|
+
"example:custom": "tsx examples/custom-error.ts",
|
|
27
|
+
"examples:all": "npm run example:basic && npm run example:async && npm run example:custom"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/darshan1005/IntellError.git"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"error",
|
|
35
|
+
"stack-trace",
|
|
36
|
+
"formatter",
|
|
37
|
+
"pretty-error",
|
|
38
|
+
"node",
|
|
39
|
+
"browser",
|
|
40
|
+
"react",
|
|
41
|
+
"vite",
|
|
42
|
+
"suggestions"
|
|
43
|
+
],
|
|
44
|
+
"author": "",
|
|
45
|
+
"license": "ISC",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/darshan1005/IntellError/issues"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/darshan1005/IntellError#readme",
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"chalk": "^5.6.2",
|
|
52
|
+
"diff": "^8.0.4",
|
|
53
|
+
"source-map-js": "^1.2.1",
|
|
54
|
+
"stacktrace-parser": "^0.1.11"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/express": "^5.0.6",
|
|
58
|
+
"@types/node": "^25.5.0",
|
|
59
|
+
"tsup": "^8.5.1",
|
|
60
|
+
"tsx": "^4.21.0",
|
|
61
|
+
"typescript": "^6.0.2",
|
|
62
|
+
"vitest": "^4.1.2"
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface IntellErrorConfig {
|
|
2
|
+
showNodeModules: boolean;
|
|
3
|
+
showNodeInternals: boolean;
|
|
4
|
+
suggestionsEnabled: boolean;
|
|
5
|
+
interceptWarnings: boolean;
|
|
6
|
+
showSearchLinks: boolean;
|
|
7
|
+
webhookUrl?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let config: IntellErrorConfig = {
|
|
11
|
+
showNodeModules: false,
|
|
12
|
+
showNodeInternals: false,
|
|
13
|
+
suggestionsEnabled: true,
|
|
14
|
+
interceptWarnings: false,
|
|
15
|
+
showSearchLinks: true,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Updates the global configuration for IntellError.
|
|
20
|
+
* @param newConfig Partial configuration object
|
|
21
|
+
*/
|
|
22
|
+
export function setupConfig(newConfig: Partial<IntellErrorConfig>) {
|
|
23
|
+
config = { ...config, ...newConfig };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns the current configuration.
|
|
28
|
+
*/
|
|
29
|
+
export function getConfig(): IntellErrorConfig {
|
|
30
|
+
return config;
|
|
31
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { parseStack } from '../parser/index.js';
|
|
2
|
+
import { getSuggestions } from '../suggestions/index.js';
|
|
3
|
+
import { getConfig } from '../config.js';
|
|
4
|
+
import { getSearchLinks } from '../suggestions/links.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Formats an error for the browser console using CSS styling (%c).
|
|
8
|
+
* Use it like: console.log(...formatErrorBrowser(err));
|
|
9
|
+
*/
|
|
10
|
+
export function formatErrorBrowser(error: Error | unknown): any[] {
|
|
11
|
+
if (!(error instanceof Error)) {
|
|
12
|
+
return [String(error)];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const config = getConfig();
|
|
16
|
+
const stackFrames = parseStack(error);
|
|
17
|
+
const userFrame = stackFrames.find(frame => !frame.isNodeInternal && !frame.isNodeModule);
|
|
18
|
+
|
|
19
|
+
const errorType = error.constructor.name || 'Error';
|
|
20
|
+
|
|
21
|
+
let text = `\n%c ${errorType} %c ${error.message}\n\n`;
|
|
22
|
+
const styles = [
|
|
23
|
+
'background: #ff4d4f; color: white; padding: 2px 4px; border-radius: 3px; font-weight: bold;',
|
|
24
|
+
'color: #ff4d4f; font-weight: bold;'
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
if (userFrame && userFrame.file) {
|
|
28
|
+
text += `%cš Location:%c\n`;
|
|
29
|
+
styles.push('font-weight: bold;', '');
|
|
30
|
+
|
|
31
|
+
text += `%c${userFrame.file}:${userFrame.lineNumber}:${userFrame.column}%c (YOUR CODE)\n\n`;
|
|
32
|
+
styles.push('color: #1890ff; text-decoration: underline;', 'color: grey; font-style: italic;');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const suggestions = config.suggestionsEnabled ? getSuggestions(error) : [];
|
|
36
|
+
if (suggestions.length > 0) {
|
|
37
|
+
text += `%cš” Suggestions:%c\n`;
|
|
38
|
+
styles.push('font-weight: bold;', '');
|
|
39
|
+
|
|
40
|
+
for (const suggestion of suggestions) {
|
|
41
|
+
text += `%c⢠${suggestion.message}%c\n`;
|
|
42
|
+
styles.push('color: #52c41a; font-weight: bold;', '');
|
|
43
|
+
|
|
44
|
+
if (suggestion.description) {
|
|
45
|
+
text += ` %c${suggestion.description}%c\n`;
|
|
46
|
+
styles.push('color: grey;', '');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (suggestion.fix) {
|
|
50
|
+
text += ` %cFix: ${suggestion.fix}%c\n`;
|
|
51
|
+
styles.push('color: #1890ff;', '');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
text += `\n`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (config.showSearchLinks) {
|
|
58
|
+
const searchLinks = getSearchLinks(error);
|
|
59
|
+
text += `%cš Troubleshoot:%c\n`;
|
|
60
|
+
styles.push('font-weight: bold;', '');
|
|
61
|
+
|
|
62
|
+
text += `Google: %c${searchLinks[0]}%c\n`;
|
|
63
|
+
styles.push('color: #1890ff; text-decoration: underline;', '');
|
|
64
|
+
|
|
65
|
+
text += `StackOverflow: %c${searchLinks[1]}%c\n`;
|
|
66
|
+
styles.push('color: #1890ff; text-decoration: underline;', '');
|
|
67
|
+
|
|
68
|
+
text += `GitHub: %c${searchLinks[2]}%c\n\n`;
|
|
69
|
+
styles.push('color: #1890ff; text-decoration: underline;', '');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
text += `%cš¦ Stack:%c\n`;
|
|
73
|
+
styles.push('font-weight: bold;', '');
|
|
74
|
+
|
|
75
|
+
let hiddenInternalsCount = 0;
|
|
76
|
+
let hiddenModulesCount = 0;
|
|
77
|
+
|
|
78
|
+
for (const frame of stackFrames) {
|
|
79
|
+
if (frame.isNodeInternal && !config.showNodeInternals) {
|
|
80
|
+
hiddenInternalsCount++;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (frame.isNodeModule && !config.showNodeModules) {
|
|
85
|
+
hiddenModulesCount++;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const fileStr = frame.file
|
|
90
|
+
? `${frame.file}:${frame.lineNumber}:${frame.column}`
|
|
91
|
+
: '<unknown>';
|
|
92
|
+
|
|
93
|
+
text += `ā %c${fileStr}%c\n`;
|
|
94
|
+
styles.push('color: #1890ff;', '');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (hiddenModulesCount > 0 || hiddenInternalsCount > 0) {
|
|
98
|
+
const messages = [];
|
|
99
|
+
if (hiddenModulesCount > 0) messages.push(`${hiddenModulesCount} node_modules`);
|
|
100
|
+
if (hiddenInternalsCount > 0) messages.push(`${hiddenInternalsCount} node internals`);
|
|
101
|
+
text += `%c(${messages.join(' and ')} hidden)%c\n`;
|
|
102
|
+
styles.push('color: grey; font-style: italic;', '');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return [text, ...styles];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function formatWarningBrowser(message: string, error?: Error): any[] {
|
|
109
|
+
const config = getConfig();
|
|
110
|
+
const actualError = error || new Error(message);
|
|
111
|
+
const stackFrames = parseStack(actualError);
|
|
112
|
+
const userFrame = stackFrames.find(frame => !frame.isNodeInternal && !frame.isNodeModule);
|
|
113
|
+
|
|
114
|
+
let text = `\n%c WARNING %c ${message}\n\n`;
|
|
115
|
+
const styles = [
|
|
116
|
+
'background: #ffec3d; color: black; padding: 2px 4px; border-radius: 3px; font-weight: bold;',
|
|
117
|
+
'color: #d4b106; font-weight: bold;'
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
if (userFrame && userFrame.file) {
|
|
121
|
+
text += `%cš Location:%c\n`;
|
|
122
|
+
styles.push('font-weight: bold;', '');
|
|
123
|
+
|
|
124
|
+
text += `%c${userFrame.file}:${userFrame.lineNumber}:${userFrame.column}%c (YOUR CODE)\n\n`;
|
|
125
|
+
styles.push('color: #1890ff; text-decoration: underline;', 'color: grey; font-style: italic;');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const suggestions = config.suggestionsEnabled ? getSuggestions(actualError) : [];
|
|
129
|
+
if (suggestions.length > 0) {
|
|
130
|
+
text += `%cš” Suggestions:%c\n`;
|
|
131
|
+
styles.push('font-weight: bold;', '');
|
|
132
|
+
|
|
133
|
+
for (const suggestion of suggestions) {
|
|
134
|
+
text += `%c⢠${suggestion.message}%c\n`;
|
|
135
|
+
styles.push('color: #52c41a; font-weight: bold;', '');
|
|
136
|
+
|
|
137
|
+
if (suggestion.description) {
|
|
138
|
+
text += ` %c${suggestion.description}%c\n`;
|
|
139
|
+
styles.push('color: grey;', '');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (suggestion.fix) {
|
|
143
|
+
text += ` %cFix: ${suggestion.fix}%c\n`;
|
|
144
|
+
styles.push('color: #1890ff;', '');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
text += `\n`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (config.showSearchLinks) {
|
|
151
|
+
const searchLinks = getSearchLinks(actualError);
|
|
152
|
+
text += `%cš Troubleshoot:%c\n`;
|
|
153
|
+
styles.push('font-weight: bold;', '');
|
|
154
|
+
|
|
155
|
+
text += `Google: %c${searchLinks[0]}%c\n`;
|
|
156
|
+
styles.push('color: #1890ff; text-decoration: underline;', '');
|
|
157
|
+
|
|
158
|
+
text += `StackOverflow: %c${searchLinks[1]}%c\n`;
|
|
159
|
+
styles.push('color: #1890ff; text-decoration: underline;', '');
|
|
160
|
+
|
|
161
|
+
text += `GitHub: %c${searchLinks[2]}%c\n\n`;
|
|
162
|
+
styles.push('color: #1890ff; text-decoration: underline;', '');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return [text, ...styles];
|
|
166
|
+
}
|