Skip to content

Commit ba39784

Browse files
authored
Runtime Server: Pass error through properly (sst#938)
1 parent d94a176 commit ba39784

File tree

3 files changed

+90
-3
lines changed

3 files changed

+90
-3
lines changed

.eslintrc.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@
4848
"rules": {
4949
"@typescript-eslint/explicit-module-boundary-types": "off",
5050
"@typescript-eslint/no-non-null-assertion": "off",
51-
"@typescript-eslint/no-explicit-any": "off"
51+
"@typescript-eslint/no-explicit-any": "off",
52+
"@typescript-eslint/ban-ts-comment": "off"
5253
}
5354
}
5455
]

packages/core/src/runtime/error.ts

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// @ts-nocheck
2+
3+
// Serialize error
4+
// https://github.com/sindresorhus/serialize-error/blob/master/index.js
5+
const commonProperties = [
6+
{ property: "name", enumerable: false },
7+
{ property: "message", enumerable: false },
8+
{ property: "stack", enumerable: false },
9+
{ property: "code", enumerable: true },
10+
];
11+
12+
const destroyCircular = ({ from, seen, to_, forceEnumerable }) => {
13+
const to = to_ || (Array.isArray(from) ? [] : {});
14+
15+
seen.push(from);
16+
17+
for (const [key, value] of Object.entries(from)) {
18+
if (typeof value === "function") {
19+
continue;
20+
}
21+
22+
if (!value || typeof value !== "object") {
23+
to[key] = value;
24+
continue;
25+
}
26+
27+
if (!seen.includes(from[key])) {
28+
to[key] = destroyCircular({
29+
from: from[key],
30+
seen: seen.slice(),
31+
forceEnumerable,
32+
});
33+
continue;
34+
}
35+
36+
to[key] = "[Circular]";
37+
}
38+
39+
for (const { property, enumerable } of commonProperties) {
40+
if (typeof from[property] === "string") {
41+
Object.defineProperty(to, property, {
42+
value: from[property],
43+
enumerable: forceEnumerable ? true : enumerable,
44+
configurable: true,
45+
writable: true,
46+
});
47+
}
48+
}
49+
50+
return to;
51+
};
52+
53+
export const serializeError = (value) => {
54+
if (typeof value === "object" && value !== null) {
55+
return destroyCircular({ from: value, seen: [], forceEnumerable: true });
56+
}
57+
58+
// People sometimes throw things besides Error objects…
59+
if (typeof value === "function") {
60+
// `JSON.stringify()` discards functions. We do too, unless a function is thrown directly.
61+
return `[Function: ${value.name || "anonymous"}]`;
62+
}
63+
64+
return value;
65+
};
66+
67+
export const deserializeError = (value) => {
68+
if (value instanceof Error) {
69+
return value;
70+
}
71+
72+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
73+
const newError = new Error();
74+
destroyCircular({ from: value, seen: [], to_: newError });
75+
return newError;
76+
}
77+
78+
return value;
79+
};

packages/core/src/runtime/server.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import path from "path";
44
import { ChildProcess } from "child_process";
55
import { getChildLogger } from "../logger";
66
import crypto from "crypto";
7+
import { serializeError } from "./error";
78

89
const logger = getChildLogger("client");
910

@@ -36,7 +37,8 @@ type ResponseTimeout = {
3637
};
3738
type ResponseFailure = {
3839
type: "failure";
39-
error: any;
40+
error: Error;
41+
rawError: any;
4042
};
4143

4244
type Response = ResponseSuccess | ResponseFailure | ResponseTimeout;
@@ -119,9 +121,14 @@ export class Server {
119121
req.params.awsRequestId,
120122
req.params.fun
121123
);
124+
const err = new Error();
125+
err.name = req.body.errorType;
126+
err.message = req.body.errorMessage;
127+
delete err.stack;
122128
this.response(req.params.fun, req.params.awsRequestId, {
123129
type: "failure",
124-
error: req.body,
130+
error: serializeError(err),
131+
rawError: req.body,
125132
});
126133
res.status(202).send();
127134
}

0 commit comments

Comments
 (0)