connect-devtools
Version:
A Connect interceptor to enable viewing requests in gRPC-Web Devtools.
90 lines (89 loc) • 2.87 kB
JavaScript
import { toJson } from "@bufbuild/protobuf";
import { ConnectError } from "@connectrpc/connect";
// Intercept messages and forward them to the gRPC-Web DevTools extension.
export const devtoolsInterceptor = (next) => async (req) => {
if (req.stream) {
return next(req); // skip streaming requests
}
// log unary calls
const path = req.method.name;
const reqObj = safeToJson(req.method.input, req.message);
try {
const result = await next(req);
if (result.stream) {
return {
...result,
message: readMessage(req, reqObj, result.message),
};
}
const resObj = safeToJson(req.method.output, result.message);
window.postMessage({
method: path,
methodType: "unary",
request: reqObj,
response: resObj,
type: "__GRPCWEB_DEVTOOLS__",
}, "*");
return result;
}
catch (error) {
if (error instanceof ConnectError) {
window.postMessage({
error: {
code: error?.code,
message: `${error?.message}`,
name: error?.name,
stack: error?.stack,
},
method: path,
methodType: "unary",
request: reqObj,
type: "__GRPCWEB_DEVTOOLS__",
}, "*");
}
else if (error instanceof Error) {
window.postMessage({
error: {
code: 2,
message: `${error?.message}`,
name: error?.name,
stack: error?.stack,
},
method: path,
methodType: "unary",
request: reqObj,
type: "__GRPCWEB_DEVTOOLS__",
}, "*");
}
throw error;
}
};
// Convert a message to JSON, or return an error object if it fails. This
// prevents the interceptor from crashing the application.
function safeToJson(input, message) {
try {
return toJson(input, message);
}
catch (error) {
return {
error: `Unable to serialize request to JSON: ${error instanceof Error ? error.message : "Unknown"}`,
};
}
}
// Read messages from a stream and forward them to the gRPC-Web DevTools
// extension.
async function* readMessage(req, reqObj, stream) {
for await (const m of stream) {
if (m) {
const resp = safeToJson(req.method.output, m);
window.postMessage({
type: "__GRPCWEB_DEVTOOLS__",
methodType: "server_streaming",
method: req.method.name,
request: reqObj,
response: resp,
}, "*");
}
yield m;
}
}