@spikard/node 0.11.0 → 0.13.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/README.md +60 -46
- package/dist/index.d.mts +198 -123
- package/dist/index.d.ts +198 -123
- package/dist/index.js +366 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +364 -3
- package/dist/index.mjs.map +1 -1
- package/index.d.ts +11 -0
- package/index.js +52 -52
- package/package.json +6 -6
- package/spikard-node.linux-x64-gnu.node +0 -0
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
GrpcError: () => GrpcError,
|
|
34
|
+
GrpcService: () => GrpcService,
|
|
34
35
|
GrpcStatusCode: () => GrpcStatusCode,
|
|
35
36
|
Spikard: () => Spikard,
|
|
36
37
|
StreamingResponse: () => StreamingResponse,
|
|
@@ -51,7 +52,7 @@ __export(index_exports, {
|
|
|
51
52
|
});
|
|
52
53
|
module.exports = __toCommonJS(index_exports);
|
|
53
54
|
|
|
54
|
-
// ../../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.
|
|
55
|
+
// ../../node_modules/.pnpm/tsup@8.5.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js
|
|
55
56
|
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
56
57
|
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
57
58
|
|
|
@@ -599,6 +600,8 @@ var Spikard = class {
|
|
|
599
600
|
handlers = {};
|
|
600
601
|
websocketRoutes = [];
|
|
601
602
|
websocketHandlers = {};
|
|
603
|
+
grpcMethods = [];
|
|
604
|
+
grpcHandlers = {};
|
|
602
605
|
lifecycleHooks = {
|
|
603
606
|
onRequest: [],
|
|
604
607
|
preValidation: [],
|
|
@@ -642,13 +645,111 @@ var Spikard = class {
|
|
|
642
645
|
this.websocketRoutes.push(route2);
|
|
643
646
|
this.websocketHandlers[handlerName] = handlerWrapper;
|
|
644
647
|
}
|
|
648
|
+
/**
|
|
649
|
+
* Register a unary gRPC method on the application.
|
|
650
|
+
*
|
|
651
|
+
* @param serviceName - Fully-qualified service name
|
|
652
|
+
* @param methodName - gRPC method name
|
|
653
|
+
* @param handler - gRPC handler implementation
|
|
654
|
+
* @returns The application for chaining
|
|
655
|
+
*/
|
|
656
|
+
addGrpcUnary(serviceName, methodName, handler) {
|
|
657
|
+
if (typeof handler?.handleRequest !== "function") {
|
|
658
|
+
throw new TypeError("Unary handler must implement handleRequest(request)");
|
|
659
|
+
}
|
|
660
|
+
return this.registerGrpcMethod(serviceName, methodName, "unary", {
|
|
661
|
+
handleRequest: (request) => handler.handleRequest(request)
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
addGrpcServerStreaming(serviceName, methodName, handler) {
|
|
665
|
+
if (typeof handler?.handleServerStream !== "function") {
|
|
666
|
+
throw new TypeError("Server-streaming handler must implement handleServerStream(request)");
|
|
667
|
+
}
|
|
668
|
+
return this.registerGrpcMethod(serviceName, methodName, "serverStreaming", {
|
|
669
|
+
handleServerStream: (request) => handler.handleServerStream(request)
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
addGrpcClientStreaming(serviceName, methodName, handler) {
|
|
673
|
+
if (typeof handler?.handleClientStream !== "function") {
|
|
674
|
+
throw new TypeError("Client-streaming handler must implement handleClientStream(request)");
|
|
675
|
+
}
|
|
676
|
+
return this.registerGrpcMethod(serviceName, methodName, "clientStreaming", {
|
|
677
|
+
handleClientStream: (request) => handler.handleClientStream(request)
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
addGrpcBidirectionalStreaming(serviceName, methodName, handler) {
|
|
681
|
+
if (typeof handler?.handleBidiStream !== "function") {
|
|
682
|
+
throw new TypeError("Bidirectional-streaming handler must implement handleBidiStream(request)");
|
|
683
|
+
}
|
|
684
|
+
return this.registerGrpcMethod(serviceName, methodName, "bidirectionalStreaming", {
|
|
685
|
+
handleBidiStream: (request) => handler.handleBidiStream(request)
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
registerGrpcMethod(serviceName, methodName, rpcMode, handlerWrapper) {
|
|
689
|
+
if (!serviceName) {
|
|
690
|
+
throw new Error("Service name cannot be empty");
|
|
691
|
+
}
|
|
692
|
+
if (!methodName) {
|
|
693
|
+
throw new Error("Method name cannot be empty");
|
|
694
|
+
}
|
|
695
|
+
const previous = this.grpcMethods.find(
|
|
696
|
+
(entry) => entry.serviceName === serviceName && entry.methodName === methodName
|
|
697
|
+
);
|
|
698
|
+
if (previous) {
|
|
699
|
+
delete this.grpcHandlers[previous.handlerName];
|
|
700
|
+
}
|
|
701
|
+
const handlerName = `grpc_${this.grpcMethods.length}_${serviceName}_${methodName}`.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
702
|
+
this.grpcHandlers[handlerName] = handlerWrapper;
|
|
703
|
+
this.grpcMethods = this.grpcMethods.filter(
|
|
704
|
+
(entry) => !(entry.serviceName === serviceName && entry.methodName === methodName)
|
|
705
|
+
);
|
|
706
|
+
this.grpcMethods.push({ serviceName, methodName, rpcMode, handlerName });
|
|
707
|
+
return this;
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Mount all handlers from a gRPC service registry on the application.
|
|
711
|
+
*
|
|
712
|
+
* @param service - Registry containing one or more service methods
|
|
713
|
+
* @returns The application for chaining
|
|
714
|
+
*/
|
|
715
|
+
useGrpc(service) {
|
|
716
|
+
for (const method of service.entries()) {
|
|
717
|
+
switch (method.rpcMode) {
|
|
718
|
+
case "unary":
|
|
719
|
+
this.addGrpcUnary(method.serviceName, method.methodName, method.handler);
|
|
720
|
+
break;
|
|
721
|
+
case "serverStreaming":
|
|
722
|
+
this.addGrpcServerStreaming(
|
|
723
|
+
method.serviceName,
|
|
724
|
+
method.methodName,
|
|
725
|
+
method.handler
|
|
726
|
+
);
|
|
727
|
+
break;
|
|
728
|
+
case "clientStreaming":
|
|
729
|
+
this.addGrpcClientStreaming(
|
|
730
|
+
method.serviceName,
|
|
731
|
+
method.methodName,
|
|
732
|
+
method.handler
|
|
733
|
+
);
|
|
734
|
+
break;
|
|
735
|
+
case "bidirectionalStreaming":
|
|
736
|
+
this.addGrpcBidirectionalStreaming(
|
|
737
|
+
method.serviceName,
|
|
738
|
+
method.methodName,
|
|
739
|
+
method.handler
|
|
740
|
+
);
|
|
741
|
+
break;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return this;
|
|
745
|
+
}
|
|
645
746
|
/**
|
|
646
747
|
* Run the server
|
|
647
748
|
*
|
|
648
|
-
* @param
|
|
749
|
+
* @param config - Server configuration
|
|
649
750
|
*/
|
|
650
|
-
run(
|
|
651
|
-
runServer(this,
|
|
751
|
+
run(config = {}) {
|
|
752
|
+
runServer(this, config);
|
|
652
753
|
}
|
|
653
754
|
/**
|
|
654
755
|
* Register an onRequest lifecycle hook
|
|
@@ -874,6 +975,135 @@ var GrpcError = class extends Error {
|
|
|
874
975
|
this.name = "GrpcError";
|
|
875
976
|
}
|
|
876
977
|
};
|
|
978
|
+
var GrpcService = class {
|
|
979
|
+
methods = /* @__PURE__ */ new Map();
|
|
980
|
+
methodKey(serviceName, methodName) {
|
|
981
|
+
return `${serviceName}/${methodName}`;
|
|
982
|
+
}
|
|
983
|
+
registerMethod(config) {
|
|
984
|
+
if (!config.serviceName) {
|
|
985
|
+
throw new Error("Service name cannot be empty");
|
|
986
|
+
}
|
|
987
|
+
if (!config.methodName) {
|
|
988
|
+
throw new Error("Method name cannot be empty");
|
|
989
|
+
}
|
|
990
|
+
switch (config.rpcMode) {
|
|
991
|
+
case "unary":
|
|
992
|
+
if (typeof config.handler?.handleRequest !== "function") {
|
|
993
|
+
throw new TypeError("Unary handler must implement handleRequest(request)");
|
|
994
|
+
}
|
|
995
|
+
break;
|
|
996
|
+
case "serverStreaming":
|
|
997
|
+
if (typeof config.handler?.handleServerStream !== "function") {
|
|
998
|
+
throw new TypeError("Server-streaming handler must implement handleServerStream(request)");
|
|
999
|
+
}
|
|
1000
|
+
break;
|
|
1001
|
+
case "clientStreaming":
|
|
1002
|
+
if (typeof config.handler?.handleClientStream !== "function") {
|
|
1003
|
+
throw new TypeError("Client-streaming handler must implement handleClientStream(request)");
|
|
1004
|
+
}
|
|
1005
|
+
break;
|
|
1006
|
+
case "bidirectionalStreaming":
|
|
1007
|
+
if (typeof config.handler?.handleBidiStream !== "function") {
|
|
1008
|
+
throw new TypeError("Bidirectional-streaming handler must implement handleBidiStream(request)");
|
|
1009
|
+
}
|
|
1010
|
+
break;
|
|
1011
|
+
}
|
|
1012
|
+
this.methods.set(this.methodKey(config.serviceName, config.methodName), config);
|
|
1013
|
+
return this;
|
|
1014
|
+
}
|
|
1015
|
+
/**
|
|
1016
|
+
* Register a unary handler for a fully-qualified service method.
|
|
1017
|
+
*
|
|
1018
|
+
* @param serviceName - Service name such as `mypackage.UserService`
|
|
1019
|
+
* @param methodName - Method name such as `GetUser`
|
|
1020
|
+
* @param handler - Handler implementation for that method
|
|
1021
|
+
* @returns The registry for chaining
|
|
1022
|
+
*/
|
|
1023
|
+
registerUnary(serviceName, methodName, handler) {
|
|
1024
|
+
return this.registerMethod({ serviceName, methodName, rpcMode: "unary", handler });
|
|
1025
|
+
}
|
|
1026
|
+
registerServerStreaming(serviceName, methodName, handler) {
|
|
1027
|
+
return this.registerMethod({ serviceName, methodName, rpcMode: "serverStreaming", handler });
|
|
1028
|
+
}
|
|
1029
|
+
registerClientStreaming(serviceName, methodName, handler) {
|
|
1030
|
+
return this.registerMethod({ serviceName, methodName, rpcMode: "clientStreaming", handler });
|
|
1031
|
+
}
|
|
1032
|
+
registerBidirectionalStreaming(serviceName, methodName, handler) {
|
|
1033
|
+
return this.registerMethod({ serviceName, methodName, rpcMode: "bidirectionalStreaming", handler });
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* Remove a handler from the registry.
|
|
1037
|
+
*
|
|
1038
|
+
* @param serviceName - Fully-qualified service name
|
|
1039
|
+
* @param methodName - Method name
|
|
1040
|
+
*/
|
|
1041
|
+
unregister(serviceName, methodName) {
|
|
1042
|
+
if (!this.methods.delete(this.methodKey(serviceName, methodName))) {
|
|
1043
|
+
throw new Error(`No handler registered for method: ${serviceName}/${methodName}`);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Get the registration for a service method.
|
|
1048
|
+
*
|
|
1049
|
+
* @param serviceName - Fully-qualified service name
|
|
1050
|
+
* @param methodName - Method name
|
|
1051
|
+
* @returns The registered method configuration, if present
|
|
1052
|
+
*/
|
|
1053
|
+
getMethod(serviceName, methodName) {
|
|
1054
|
+
return this.methods.get(this.methodKey(serviceName, methodName));
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* List all registered service names.
|
|
1058
|
+
*
|
|
1059
|
+
* @returns Fully-qualified service names
|
|
1060
|
+
*/
|
|
1061
|
+
serviceNames() {
|
|
1062
|
+
return Array.from(new Set(Array.from(this.methods.values(), (entry) => entry.serviceName)));
|
|
1063
|
+
}
|
|
1064
|
+
methodNames(serviceName) {
|
|
1065
|
+
return Array.from(this.methods.values()).filter((entry) => entry.serviceName === serviceName).map((entry) => entry.methodName);
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Check whether a specific service method is registered.
|
|
1069
|
+
*
|
|
1070
|
+
* @param serviceName - Fully-qualified service name
|
|
1071
|
+
* @param methodName - Method name
|
|
1072
|
+
* @returns True when a handler is registered for the method
|
|
1073
|
+
*/
|
|
1074
|
+
hasMethod(serviceName, methodName) {
|
|
1075
|
+
return this.methods.has(this.methodKey(serviceName, methodName));
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Return registered method entries.
|
|
1079
|
+
*/
|
|
1080
|
+
entries() {
|
|
1081
|
+
return Array.from(this.methods.values());
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* Route a unary request to the registered method handler.
|
|
1085
|
+
*
|
|
1086
|
+
* @param request - Incoming gRPC request
|
|
1087
|
+
* @returns Promise resolving to the handler response
|
|
1088
|
+
* @throws GrpcError when no service is registered
|
|
1089
|
+
*/
|
|
1090
|
+
async handleRequest(request) {
|
|
1091
|
+
const method = this.getMethod(request.serviceName, request.methodName);
|
|
1092
|
+
if (!method) {
|
|
1093
|
+
throw new GrpcError(
|
|
1094
|
+
12 /* UNIMPLEMENTED */,
|
|
1095
|
+
`No handler registered for method: ${request.serviceName}/${request.methodName}`
|
|
1096
|
+
);
|
|
1097
|
+
}
|
|
1098
|
+
if (method.rpcMode !== "unary") {
|
|
1099
|
+
throw new GrpcError(
|
|
1100
|
+
12 /* UNIMPLEMENTED */,
|
|
1101
|
+
`Method ${request.serviceName}/${request.methodName} is registered as ${method.rpcMode}`
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
return method.handler.handleRequest(request);
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
877
1107
|
function createUnaryHandler(methodName, handler, requestType, responseType) {
|
|
878
1108
|
return {
|
|
879
1109
|
async handleRequest(request) {
|
|
@@ -954,6 +1184,36 @@ var import_promises = __toESM(require("fs/promises"));
|
|
|
954
1184
|
var import_node_module3 = require("module");
|
|
955
1185
|
var import_node_path = __toESM(require("path"));
|
|
956
1186
|
var import_node_zlib = require("zlib");
|
|
1187
|
+
var GRAPHQL_WS_TIMEOUT_MS = 2e3;
|
|
1188
|
+
var GRAPHQL_WS_MAX_CONTROL_MESSAGES = 32;
|
|
1189
|
+
var withTimeout = async (promise, timeoutMs, context) => {
|
|
1190
|
+
let timer;
|
|
1191
|
+
try {
|
|
1192
|
+
return await Promise.race([
|
|
1193
|
+
promise,
|
|
1194
|
+
new Promise((_resolve, reject) => {
|
|
1195
|
+
timer = setTimeout(() => reject(new Error(`Timed out waiting for ${context}`)), timeoutMs);
|
|
1196
|
+
})
|
|
1197
|
+
]);
|
|
1198
|
+
} finally {
|
|
1199
|
+
if (timer) {
|
|
1200
|
+
clearTimeout(timer);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
};
|
|
1204
|
+
var decodeGraphqlWsMessage = (value) => {
|
|
1205
|
+
if (typeof value === "string") {
|
|
1206
|
+
const parsed = JSON.parse(value);
|
|
1207
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1208
|
+
return parsed;
|
|
1209
|
+
}
|
|
1210
|
+
throw new Error("Expected GraphQL WebSocket JSON object message");
|
|
1211
|
+
}
|
|
1212
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1213
|
+
return value;
|
|
1214
|
+
}
|
|
1215
|
+
throw new Error("Expected GraphQL WebSocket message object");
|
|
1216
|
+
};
|
|
957
1217
|
var MockWebSocketConnection = class {
|
|
958
1218
|
handler;
|
|
959
1219
|
queue = [];
|
|
@@ -1667,6 +1927,107 @@ var TestClient = class {
|
|
|
1667
1927
|
bodyText: response.text()
|
|
1668
1928
|
};
|
|
1669
1929
|
}
|
|
1930
|
+
/**
|
|
1931
|
+
* Send a GraphQL subscription over WebSocket and return the first event payload.
|
|
1932
|
+
*/
|
|
1933
|
+
async graphqlSubscription(query, variables, operationName, path2 = "/graphql") {
|
|
1934
|
+
const operationId = "spikard-subscription-1";
|
|
1935
|
+
const subscriptionPayload = { query };
|
|
1936
|
+
if (variables !== null && variables !== void 0) {
|
|
1937
|
+
subscriptionPayload.variables = variables;
|
|
1938
|
+
}
|
|
1939
|
+
if (operationName !== null && operationName !== void 0) {
|
|
1940
|
+
subscriptionPayload.operationName = operationName;
|
|
1941
|
+
}
|
|
1942
|
+
const ws = await this.websocketConnect(path2);
|
|
1943
|
+
try {
|
|
1944
|
+
await ws.sendJson({ type: "connection_init" });
|
|
1945
|
+
let acknowledged = false;
|
|
1946
|
+
for (let i = 0; i < GRAPHQL_WS_MAX_CONTROL_MESSAGES; i++) {
|
|
1947
|
+
const message = decodeGraphqlWsMessage(
|
|
1948
|
+
await withTimeout(ws.receiveJson(), GRAPHQL_WS_TIMEOUT_MS, "GraphQL connection_ack")
|
|
1949
|
+
);
|
|
1950
|
+
const messageType = typeof message.type === "string" ? message.type : "";
|
|
1951
|
+
if (messageType === "connection_ack") {
|
|
1952
|
+
acknowledged = true;
|
|
1953
|
+
break;
|
|
1954
|
+
}
|
|
1955
|
+
if (messageType === "ping") {
|
|
1956
|
+
const pong = { type: "pong" };
|
|
1957
|
+
if ("payload" in message) {
|
|
1958
|
+
pong.payload = message.payload;
|
|
1959
|
+
}
|
|
1960
|
+
await ws.sendJson(pong);
|
|
1961
|
+
continue;
|
|
1962
|
+
}
|
|
1963
|
+
if (messageType === "connection_error" || messageType === "error") {
|
|
1964
|
+
throw new Error(`GraphQL subscription rejected during init: ${JSON.stringify(message)}`);
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
if (!acknowledged) {
|
|
1968
|
+
throw new Error("No GraphQL connection_ack received");
|
|
1969
|
+
}
|
|
1970
|
+
await ws.sendJson({
|
|
1971
|
+
id: operationId,
|
|
1972
|
+
type: "subscribe",
|
|
1973
|
+
payload: subscriptionPayload
|
|
1974
|
+
});
|
|
1975
|
+
let event = null;
|
|
1976
|
+
const errors = [];
|
|
1977
|
+
let completeReceived = false;
|
|
1978
|
+
for (let i = 0; i < GRAPHQL_WS_MAX_CONTROL_MESSAGES; i++) {
|
|
1979
|
+
const message = decodeGraphqlWsMessage(
|
|
1980
|
+
await withTimeout(ws.receiveJson(), GRAPHQL_WS_TIMEOUT_MS, "GraphQL subscription message")
|
|
1981
|
+
);
|
|
1982
|
+
const messageType = typeof message.type === "string" ? message.type : "";
|
|
1983
|
+
const messageId = typeof message.id === "string" ? message.id : void 0;
|
|
1984
|
+
const idMatches = messageId === void 0 || messageId === operationId;
|
|
1985
|
+
if (messageType === "next" && idMatches) {
|
|
1986
|
+
event = "payload" in message ? message.payload : null;
|
|
1987
|
+
await ws.sendJson({ id: operationId, type: "complete" });
|
|
1988
|
+
try {
|
|
1989
|
+
const maybeComplete = decodeGraphqlWsMessage(
|
|
1990
|
+
await withTimeout(ws.receiveJson(), GRAPHQL_WS_TIMEOUT_MS, "GraphQL complete message")
|
|
1991
|
+
);
|
|
1992
|
+
const completeType = typeof maybeComplete.type === "string" ? maybeComplete.type : "";
|
|
1993
|
+
const completeId = typeof maybeComplete.id === "string" ? maybeComplete.id : void 0;
|
|
1994
|
+
if (completeType === "complete" && (completeId === void 0 || completeId === operationId)) {
|
|
1995
|
+
completeReceived = true;
|
|
1996
|
+
}
|
|
1997
|
+
} catch {
|
|
1998
|
+
}
|
|
1999
|
+
break;
|
|
2000
|
+
}
|
|
2001
|
+
if (messageType === "error") {
|
|
2002
|
+
errors.push("payload" in message ? message.payload : message);
|
|
2003
|
+
break;
|
|
2004
|
+
}
|
|
2005
|
+
if (messageType === "complete" && idMatches) {
|
|
2006
|
+
completeReceived = true;
|
|
2007
|
+
break;
|
|
2008
|
+
}
|
|
2009
|
+
if (messageType === "ping") {
|
|
2010
|
+
const pong = { type: "pong" };
|
|
2011
|
+
if ("payload" in message) {
|
|
2012
|
+
pong.payload = message.payload;
|
|
2013
|
+
}
|
|
2014
|
+
await ws.sendJson(pong);
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
if (event === null && errors.length === 0 && !completeReceived) {
|
|
2018
|
+
throw new Error("No GraphQL subscription event received before timeout");
|
|
2019
|
+
}
|
|
2020
|
+
return {
|
|
2021
|
+
operationId,
|
|
2022
|
+
acknowledged,
|
|
2023
|
+
event,
|
|
2024
|
+
errors,
|
|
2025
|
+
completeReceived
|
|
2026
|
+
};
|
|
2027
|
+
} finally {
|
|
2028
|
+
await ws.close();
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
1670
2031
|
/**
|
|
1671
2032
|
* Cleanup resources when test client is done
|
|
1672
2033
|
*/
|
|
@@ -1676,6 +2037,7 @@ var TestClient = class {
|
|
|
1676
2037
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1677
2038
|
0 && (module.exports = {
|
|
1678
2039
|
GrpcError,
|
|
2040
|
+
GrpcService,
|
|
1679
2041
|
GrpcStatusCode,
|
|
1680
2042
|
Spikard,
|
|
1681
2043
|
StreamingResponse,
|