Skip to content

Commit 0b6949b

Browse files
committed
feat: provide error utils
1 parent 0a9a386 commit 0b6949b

File tree

10 files changed

+410
-11
lines changed

10 files changed

+410
-11
lines changed

package.json

+6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
"import": "./target/esm/index.mjs",
1212
"default": "./target/esm/index.mjs"
1313
},
14+
"./error": {
15+
"types": "./target/dts/error.d.ts",
16+
"require": "./target/cjs/error.cjs",
17+
"import": "./target/esm/error.mjs",
18+
"default": "./target/esm/error.mjs"
19+
},
1420
"./spawn": {
1521
"types": "./target/dts/spawn.d.ts",
1622
"require": "./target/cjs/spawn.cjs",

src/main/ts/error.ts

+209
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
export const EXIT_CODES = {
2+
2: 'Misuse of shell builtins',
3+
126: 'Invoked command cannot execute',
4+
127: 'Command not found',
5+
128: 'Invalid exit argument',
6+
129: 'Hangup',
7+
130: 'Interrupt',
8+
131: 'Quit and dump core',
9+
132: 'Illegal instruction',
10+
133: 'Trace/breakpoint trap',
11+
134: 'Process aborted',
12+
135: 'Bus error: "access to undefined portion of memory object"',
13+
136: 'Floating point exception: "erroneous arithmetic operation"',
14+
137: 'Kill (terminate immediately)',
15+
138: 'User-defined 1',
16+
139: 'Segmentation violation',
17+
140: 'User-defined 2',
18+
141: 'Write to pipe with no one reading',
19+
142: 'Signal raised by alarm',
20+
143: 'Termination (request to terminate)',
21+
145: 'Child process terminated, stopped (or continued*)',
22+
146: 'Continue if stopped',
23+
147: 'Stop executing temporarily',
24+
148: 'Terminal stop signal',
25+
149: 'Background process attempting to read from tty ("in")',
26+
150: 'Background process attempting to write to tty ("out")',
27+
151: 'Urgent data available on socket',
28+
152: 'CPU time limit exceeded',
29+
153: 'File size limit exceeded',
30+
154: 'Signal raised by timer counting virtual time: "virtual timer expired"',
31+
155: 'Profiling timer expired',
32+
157: 'Pollable event',
33+
159: 'Bad syscall',
34+
}
35+
36+
export const ERRNO_CODES = {
37+
0: 'Success',
38+
1: 'Not super-user',
39+
2: 'No such file or directory',
40+
3: 'No such process',
41+
4: 'Interrupted system call',
42+
5: 'I/O error',
43+
6: 'No such device or address',
44+
7: 'Arg list too long',
45+
8: 'Exec format error',
46+
9: 'Bad file number',
47+
10: 'No children',
48+
11: 'No more processes',
49+
12: 'Not enough core',
50+
13: 'Permission denied',
51+
14: 'Bad address',
52+
15: 'Block device required',
53+
16: 'Mount device busy',
54+
17: 'File exists',
55+
18: 'Cross-device link',
56+
19: 'No such device',
57+
20: 'Not a directory',
58+
21: 'Is a directory',
59+
22: 'Invalid argument',
60+
23: 'Too many open files in system',
61+
24: 'Too many open files',
62+
25: 'Not a typewriter',
63+
26: 'Text file busy',
64+
27: 'File too large',
65+
28: 'No space left on device',
66+
29: 'Illegal seek',
67+
30: 'Read only file system',
68+
31: 'Too many links',
69+
32: 'Broken pipe',
70+
33: 'Math arg out of domain of func',
71+
34: 'Math result not representable',
72+
35: 'File locking deadlock error',
73+
36: 'File or path name too long',
74+
37: 'No record locks available',
75+
38: 'Function not implemented',
76+
39: 'Directory not empty',
77+
40: 'Too many symbolic links',
78+
42: 'No message of desired type',
79+
43: 'Identifier removed',
80+
44: 'Channel number out of range',
81+
45: 'Level 2 not synchronized',
82+
46: 'Level 3 halted',
83+
47: 'Level 3 reset',
84+
48: 'Link number out of range',
85+
49: 'Protocol driver not attached',
86+
50: 'No CSI structure available',
87+
51: 'Level 2 halted',
88+
52: 'Invalid exchange',
89+
53: 'Invalid request descriptor',
90+
54: 'Exchange full',
91+
55: 'No anode',
92+
56: 'Invalid request code',
93+
57: 'Invalid slot',
94+
59: 'Bad font file fmt',
95+
60: 'Device not a stream',
96+
61: 'No data (for no delay io)',
97+
62: 'Timer expired',
98+
63: 'Out of streams resources',
99+
64: 'Machine is not on the network',
100+
65: 'Package not installed',
101+
66: 'The object is remote',
102+
67: 'The link has been severed',
103+
68: 'Advertise error',
104+
69: 'Srmount error',
105+
70: 'Communication error on send',
106+
71: 'Protocol error',
107+
72: 'Multihop attempted',
108+
73: 'Cross mount point (not really error)',
109+
74: 'Trying to read unreadable message',
110+
75: 'Value too large for defined data type',
111+
76: 'Given log. name not unique',
112+
77: 'f.d. invalid for this operation',
113+
78: 'Remote address changed',
114+
79: 'Can access a needed shared lib',
115+
80: 'Accessing a corrupted shared lib',
116+
81: '.lib section in a.out corrupted',
117+
82: 'Attempting to link in too many libs',
118+
83: 'Attempting to exec a shared library',
119+
84: 'Illegal byte sequence',
120+
86: 'Streams pipe error',
121+
87: 'Too many users',
122+
88: 'Socket operation on non-socket',
123+
89: 'Destination address required',
124+
90: 'Message too long',
125+
91: 'Protocol wrong type for socket',
126+
92: 'Protocol not available',
127+
93: 'Unknown protocol',
128+
94: 'Socket type not supported',
129+
95: 'Not supported',
130+
96: 'Protocol family not supported',
131+
97: 'Address family not supported by protocol family',
132+
98: 'Address already in use',
133+
99: 'Address not available',
134+
100: 'Network interface is not configured',
135+
101: 'Network is unreachable',
136+
102: 'Connection reset by network',
137+
103: 'Connection aborted',
138+
104: 'Connection reset by peer',
139+
105: 'No buffer space available',
140+
106: 'Socket is already connected',
141+
107: 'Socket is not connected',
142+
108: "Can't send after socket shutdown",
143+
109: 'Too many references',
144+
110: 'Connection timed out',
145+
111: 'Connection refused',
146+
112: 'Host is down',
147+
113: 'Host is unreachable',
148+
114: 'Socket already connected',
149+
115: 'Connection already in progress',
150+
116: 'Stale file handle',
151+
122: 'Quota exceeded',
152+
123: 'No medium (in tape drive)',
153+
125: 'Operation canceled',
154+
130: 'Previous owner died',
155+
131: 'State not recoverable',
156+
}
157+
158+
export function getErrnoMessage(errno?: number): string {
159+
return (
160+
ERRNO_CODES[-(errno as number) as keyof typeof ERRNO_CODES] ||
161+
'Unknown error'
162+
)
163+
}
164+
165+
export function getExitCodeInfo(exitCode: number | null): string | undefined {
166+
return EXIT_CODES[exitCode as keyof typeof EXIT_CODES]
167+
}
168+
169+
export const getExitMessage = (
170+
code: number | null,
171+
signal: NodeJS.Signals | null,
172+
stderr: string,
173+
from: string
174+
) => {
175+
let message = `exit code: ${code}`
176+
if (code != 0 || signal != null) {
177+
message = `${stderr || '\n'} at ${from}`
178+
message += `\n exit code: ${code}${
179+
getExitCodeInfo(code) ? ' (' + getExitCodeInfo(code) + ')' : ''
180+
}`
181+
if (signal != null) {
182+
message += `\n signal: ${signal}`
183+
}
184+
}
185+
186+
return message
187+
}
188+
189+
export const getErrorMessage = (err: NodeJS.ErrnoException, from: string) => {
190+
return (
191+
`${err.message}\n` +
192+
` errno: ${err.errno} (${getErrnoMessage(err.errno)})\n` +
193+
` code: ${err.code}\n` +
194+
` at ${from}`
195+
)
196+
}
197+
198+
export function getCallerLocation(err = new Error()) {
199+
return getCallerLocationFromString(err.stack)
200+
}
201+
202+
export function getCallerLocationFromString(stackString = 'unknown') {
203+
return (
204+
stackString
205+
.split(/^\s*(at\s)?/m)
206+
.filter((s) => s?.includes(':'))[2]
207+
?.trim() || stackString
208+
)
209+
}

src/main/ts/spawn.ts

+15-9
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export type TSpawnResult = {
3333
ctx: TSpawnCtxNormalized
3434
error?: TSpawnError,
3535
child?: TChild
36+
stack: string
3637
}
3738

3839
export type TSpawnListeners = {
@@ -77,6 +78,7 @@ export interface TSpawnCtxNormalized {
7778
fulfilled?: TSpawnResult
7879
error?: any
7980
run: (cb: () => void, ctx: TSpawnCtxNormalized) => void
81+
stack: string
8082
}
8183

8284
export const defaults: TSpawnCtxNormalized = {
@@ -102,7 +104,8 @@ export const defaults: TSpawnCtxNormalized = {
102104
get stdout(){ return new VoidStream() },
103105
get stderr(){ return new VoidStream() },
104106
stdio: ['pipe', 'pipe', 'pipe'],
105-
run: immediate
107+
run: immediate,
108+
stack: ''
106109
}
107110

108111
export const normalizeCtx = (...ctxs: TSpawnCtx[]): TSpawnCtxNormalized => assign({
@@ -184,7 +187,8 @@ export const invoke = (c: TSpawnCtxNormalized): TSpawnCtxNormalized => {
184187
get stdall() { return c.store.stdall.join('') },
185188
stdio,
186189
duration: Date.now() - now,
187-
ctx: c
190+
stack: c.stack,
191+
ctx: c
188192
})
189193
c.ee.emit('end', c.fulfilled, c)
190194

@@ -238,7 +242,8 @@ export const invoke = (c: TSpawnCtxNormalized): TSpawnCtxNormalized => {
238242
get stdall() { return c.store.stdall.join('') },
239243
stdio,
240244
duration: Date.now() - now,
241-
ctx: c
245+
stack: c.stack,
246+
ctx: c
242247
}
243248
opts.signal?.removeEventListener('abort', onAbort)
244249
c.callback(error, c.fulfilled)
@@ -251,14 +256,15 @@ export const invoke = (c: TSpawnCtxNormalized): TSpawnCtxNormalized => {
251256
error,
252257
c.fulfilled = {
253258
error,
254-
status: null,
255-
signal: null,
256-
stdout: '',
257-
stderr: '',
258-
stdall: '',
259+
status: null,
260+
signal: null,
261+
stdout: '',
262+
stderr: '',
263+
stdall: '',
259264
stdio,
260265
duration: Date.now() - now,
261-
ctx: c
266+
stack: c.stack,
267+
ctx: c
262268
}
263269
)
264270
c.ee.emit('err', error, c)

src/main/ts/zurk.ts

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ class Zurk implements TZurk {
136136
this.ctx.stdout,
137137
this.ctx.stderr
138138
]}
139+
get stack() { return this.ctx.stack }
139140
get duration() { return this.ctx.fulfilled?.duration ?? 0 }
140141
toString(){ return this.stdall.trim() }
141142
valueOf(){ return this.stdall.trim() }

target/cjs/spawn.cjs

+5-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ var defaults = {
7171
return new VoidStream();
7272
},
7373
stdio: ["pipe", "pipe", "pipe"],
74-
run: import_util.immediate
74+
run: import_util.immediate,
75+
stack: ""
7576
};
7677
var normalizeCtx = (...ctxs) => (0, import_util.assign)(
7778
__spreadProps(__spreadValues({}, defaults), {
@@ -154,6 +155,7 @@ var invoke = (c) => {
154155
},
155156
stdio,
156157
duration: Date.now() - now,
158+
stack: c.stack,
157159
ctx: c
158160
}));
159161
c.ee.emit("end", c.fulfilled, c);
@@ -208,6 +210,7 @@ var invoke = (c) => {
208210
},
209211
stdio,
210212
duration: Date.now() - now,
213+
stack: c.stack,
211214
ctx: c
212215
};
213216
(_a3 = opts.signal) == null ? void 0 : _a3.removeEventListener("abort", onAbort);
@@ -228,6 +231,7 @@ var invoke = (c) => {
228231
stdall: "",
229232
stdio,
230233
duration: Date.now() - now,
234+
stack: c.stack,
231235
ctx: c
232236
}
233237
);

target/cjs/zurk.cjs

+3
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ var Zurk = class {
132132
this.ctx.stderr
133133
];
134134
}
135+
get stack() {
136+
return this.ctx.stack;
137+
}
135138
get duration() {
136139
var _a2, _b;
137140
return (_b = (_a2 = this.ctx.fulfilled) == null ? void 0 : _a2.duration) != null ? _b : 0;

0 commit comments

Comments
 (0)