@spikard/node 0.12.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 +183 -123
- package/dist/index.d.ts +183 -123
- package/dist/index.js +235 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +233 -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.mjs
CHANGED
|
@@ -548,6 +548,8 @@ var Spikard = class {
|
|
|
548
548
|
handlers = {};
|
|
549
549
|
websocketRoutes = [];
|
|
550
550
|
websocketHandlers = {};
|
|
551
|
+
grpcMethods = [];
|
|
552
|
+
grpcHandlers = {};
|
|
551
553
|
lifecycleHooks = {
|
|
552
554
|
onRequest: [],
|
|
553
555
|
preValidation: [],
|
|
@@ -591,13 +593,111 @@ var Spikard = class {
|
|
|
591
593
|
this.websocketRoutes.push(route2);
|
|
592
594
|
this.websocketHandlers[handlerName] = handlerWrapper;
|
|
593
595
|
}
|
|
596
|
+
/**
|
|
597
|
+
* Register a unary gRPC method on the application.
|
|
598
|
+
*
|
|
599
|
+
* @param serviceName - Fully-qualified service name
|
|
600
|
+
* @param methodName - gRPC method name
|
|
601
|
+
* @param handler - gRPC handler implementation
|
|
602
|
+
* @returns The application for chaining
|
|
603
|
+
*/
|
|
604
|
+
addGrpcUnary(serviceName, methodName, handler) {
|
|
605
|
+
if (typeof handler?.handleRequest !== "function") {
|
|
606
|
+
throw new TypeError("Unary handler must implement handleRequest(request)");
|
|
607
|
+
}
|
|
608
|
+
return this.registerGrpcMethod(serviceName, methodName, "unary", {
|
|
609
|
+
handleRequest: (request) => handler.handleRequest(request)
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
addGrpcServerStreaming(serviceName, methodName, handler) {
|
|
613
|
+
if (typeof handler?.handleServerStream !== "function") {
|
|
614
|
+
throw new TypeError("Server-streaming handler must implement handleServerStream(request)");
|
|
615
|
+
}
|
|
616
|
+
return this.registerGrpcMethod(serviceName, methodName, "serverStreaming", {
|
|
617
|
+
handleServerStream: (request) => handler.handleServerStream(request)
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
addGrpcClientStreaming(serviceName, methodName, handler) {
|
|
621
|
+
if (typeof handler?.handleClientStream !== "function") {
|
|
622
|
+
throw new TypeError("Client-streaming handler must implement handleClientStream(request)");
|
|
623
|
+
}
|
|
624
|
+
return this.registerGrpcMethod(serviceName, methodName, "clientStreaming", {
|
|
625
|
+
handleClientStream: (request) => handler.handleClientStream(request)
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
addGrpcBidirectionalStreaming(serviceName, methodName, handler) {
|
|
629
|
+
if (typeof handler?.handleBidiStream !== "function") {
|
|
630
|
+
throw new TypeError("Bidirectional-streaming handler must implement handleBidiStream(request)");
|
|
631
|
+
}
|
|
632
|
+
return this.registerGrpcMethod(serviceName, methodName, "bidirectionalStreaming", {
|
|
633
|
+
handleBidiStream: (request) => handler.handleBidiStream(request)
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
registerGrpcMethod(serviceName, methodName, rpcMode, handlerWrapper) {
|
|
637
|
+
if (!serviceName) {
|
|
638
|
+
throw new Error("Service name cannot be empty");
|
|
639
|
+
}
|
|
640
|
+
if (!methodName) {
|
|
641
|
+
throw new Error("Method name cannot be empty");
|
|
642
|
+
}
|
|
643
|
+
const previous = this.grpcMethods.find(
|
|
644
|
+
(entry) => entry.serviceName === serviceName && entry.methodName === methodName
|
|
645
|
+
);
|
|
646
|
+
if (previous) {
|
|
647
|
+
delete this.grpcHandlers[previous.handlerName];
|
|
648
|
+
}
|
|
649
|
+
const handlerName = `grpc_${this.grpcMethods.length}_${serviceName}_${methodName}`.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
650
|
+
this.grpcHandlers[handlerName] = handlerWrapper;
|
|
651
|
+
this.grpcMethods = this.grpcMethods.filter(
|
|
652
|
+
(entry) => !(entry.serviceName === serviceName && entry.methodName === methodName)
|
|
653
|
+
);
|
|
654
|
+
this.grpcMethods.push({ serviceName, methodName, rpcMode, handlerName });
|
|
655
|
+
return this;
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Mount all handlers from a gRPC service registry on the application.
|
|
659
|
+
*
|
|
660
|
+
* @param service - Registry containing one or more service methods
|
|
661
|
+
* @returns The application for chaining
|
|
662
|
+
*/
|
|
663
|
+
useGrpc(service) {
|
|
664
|
+
for (const method of service.entries()) {
|
|
665
|
+
switch (method.rpcMode) {
|
|
666
|
+
case "unary":
|
|
667
|
+
this.addGrpcUnary(method.serviceName, method.methodName, method.handler);
|
|
668
|
+
break;
|
|
669
|
+
case "serverStreaming":
|
|
670
|
+
this.addGrpcServerStreaming(
|
|
671
|
+
method.serviceName,
|
|
672
|
+
method.methodName,
|
|
673
|
+
method.handler
|
|
674
|
+
);
|
|
675
|
+
break;
|
|
676
|
+
case "clientStreaming":
|
|
677
|
+
this.addGrpcClientStreaming(
|
|
678
|
+
method.serviceName,
|
|
679
|
+
method.methodName,
|
|
680
|
+
method.handler
|
|
681
|
+
);
|
|
682
|
+
break;
|
|
683
|
+
case "bidirectionalStreaming":
|
|
684
|
+
this.addGrpcBidirectionalStreaming(
|
|
685
|
+
method.serviceName,
|
|
686
|
+
method.methodName,
|
|
687
|
+
method.handler
|
|
688
|
+
);
|
|
689
|
+
break;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
return this;
|
|
693
|
+
}
|
|
594
694
|
/**
|
|
595
695
|
* Run the server
|
|
596
696
|
*
|
|
597
|
-
* @param
|
|
697
|
+
* @param config - Server configuration
|
|
598
698
|
*/
|
|
599
|
-
run(
|
|
600
|
-
runServer(this,
|
|
699
|
+
run(config = {}) {
|
|
700
|
+
runServer(this, config);
|
|
601
701
|
}
|
|
602
702
|
/**
|
|
603
703
|
* Register an onRequest lifecycle hook
|
|
@@ -823,6 +923,135 @@ var GrpcError = class extends Error {
|
|
|
823
923
|
this.name = "GrpcError";
|
|
824
924
|
}
|
|
825
925
|
};
|
|
926
|
+
var GrpcService = class {
|
|
927
|
+
methods = /* @__PURE__ */ new Map();
|
|
928
|
+
methodKey(serviceName, methodName) {
|
|
929
|
+
return `${serviceName}/${methodName}`;
|
|
930
|
+
}
|
|
931
|
+
registerMethod(config) {
|
|
932
|
+
if (!config.serviceName) {
|
|
933
|
+
throw new Error("Service name cannot be empty");
|
|
934
|
+
}
|
|
935
|
+
if (!config.methodName) {
|
|
936
|
+
throw new Error("Method name cannot be empty");
|
|
937
|
+
}
|
|
938
|
+
switch (config.rpcMode) {
|
|
939
|
+
case "unary":
|
|
940
|
+
if (typeof config.handler?.handleRequest !== "function") {
|
|
941
|
+
throw new TypeError("Unary handler must implement handleRequest(request)");
|
|
942
|
+
}
|
|
943
|
+
break;
|
|
944
|
+
case "serverStreaming":
|
|
945
|
+
if (typeof config.handler?.handleServerStream !== "function") {
|
|
946
|
+
throw new TypeError("Server-streaming handler must implement handleServerStream(request)");
|
|
947
|
+
}
|
|
948
|
+
break;
|
|
949
|
+
case "clientStreaming":
|
|
950
|
+
if (typeof config.handler?.handleClientStream !== "function") {
|
|
951
|
+
throw new TypeError("Client-streaming handler must implement handleClientStream(request)");
|
|
952
|
+
}
|
|
953
|
+
break;
|
|
954
|
+
case "bidirectionalStreaming":
|
|
955
|
+
if (typeof config.handler?.handleBidiStream !== "function") {
|
|
956
|
+
throw new TypeError("Bidirectional-streaming handler must implement handleBidiStream(request)");
|
|
957
|
+
}
|
|
958
|
+
break;
|
|
959
|
+
}
|
|
960
|
+
this.methods.set(this.methodKey(config.serviceName, config.methodName), config);
|
|
961
|
+
return this;
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Register a unary handler for a fully-qualified service method.
|
|
965
|
+
*
|
|
966
|
+
* @param serviceName - Service name such as `mypackage.UserService`
|
|
967
|
+
* @param methodName - Method name such as `GetUser`
|
|
968
|
+
* @param handler - Handler implementation for that method
|
|
969
|
+
* @returns The registry for chaining
|
|
970
|
+
*/
|
|
971
|
+
registerUnary(serviceName, methodName, handler) {
|
|
972
|
+
return this.registerMethod({ serviceName, methodName, rpcMode: "unary", handler });
|
|
973
|
+
}
|
|
974
|
+
registerServerStreaming(serviceName, methodName, handler) {
|
|
975
|
+
return this.registerMethod({ serviceName, methodName, rpcMode: "serverStreaming", handler });
|
|
976
|
+
}
|
|
977
|
+
registerClientStreaming(serviceName, methodName, handler) {
|
|
978
|
+
return this.registerMethod({ serviceName, methodName, rpcMode: "clientStreaming", handler });
|
|
979
|
+
}
|
|
980
|
+
registerBidirectionalStreaming(serviceName, methodName, handler) {
|
|
981
|
+
return this.registerMethod({ serviceName, methodName, rpcMode: "bidirectionalStreaming", handler });
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Remove a handler from the registry.
|
|
985
|
+
*
|
|
986
|
+
* @param serviceName - Fully-qualified service name
|
|
987
|
+
* @param methodName - Method name
|
|
988
|
+
*/
|
|
989
|
+
unregister(serviceName, methodName) {
|
|
990
|
+
if (!this.methods.delete(this.methodKey(serviceName, methodName))) {
|
|
991
|
+
throw new Error(`No handler registered for method: ${serviceName}/${methodName}`);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Get the registration for a service method.
|
|
996
|
+
*
|
|
997
|
+
* @param serviceName - Fully-qualified service name
|
|
998
|
+
* @param methodName - Method name
|
|
999
|
+
* @returns The registered method configuration, if present
|
|
1000
|
+
*/
|
|
1001
|
+
getMethod(serviceName, methodName) {
|
|
1002
|
+
return this.methods.get(this.methodKey(serviceName, methodName));
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* List all registered service names.
|
|
1006
|
+
*
|
|
1007
|
+
* @returns Fully-qualified service names
|
|
1008
|
+
*/
|
|
1009
|
+
serviceNames() {
|
|
1010
|
+
return Array.from(new Set(Array.from(this.methods.values(), (entry) => entry.serviceName)));
|
|
1011
|
+
}
|
|
1012
|
+
methodNames(serviceName) {
|
|
1013
|
+
return Array.from(this.methods.values()).filter((entry) => entry.serviceName === serviceName).map((entry) => entry.methodName);
|
|
1014
|
+
}
|
|
1015
|
+
/**
|
|
1016
|
+
* Check whether a specific service method is registered.
|
|
1017
|
+
*
|
|
1018
|
+
* @param serviceName - Fully-qualified service name
|
|
1019
|
+
* @param methodName - Method name
|
|
1020
|
+
* @returns True when a handler is registered for the method
|
|
1021
|
+
*/
|
|
1022
|
+
hasMethod(serviceName, methodName) {
|
|
1023
|
+
return this.methods.has(this.methodKey(serviceName, methodName));
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Return registered method entries.
|
|
1027
|
+
*/
|
|
1028
|
+
entries() {
|
|
1029
|
+
return Array.from(this.methods.values());
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Route a unary request to the registered method handler.
|
|
1033
|
+
*
|
|
1034
|
+
* @param request - Incoming gRPC request
|
|
1035
|
+
* @returns Promise resolving to the handler response
|
|
1036
|
+
* @throws GrpcError when no service is registered
|
|
1037
|
+
*/
|
|
1038
|
+
async handleRequest(request) {
|
|
1039
|
+
const method = this.getMethod(request.serviceName, request.methodName);
|
|
1040
|
+
if (!method) {
|
|
1041
|
+
throw new GrpcError(
|
|
1042
|
+
12 /* UNIMPLEMENTED */,
|
|
1043
|
+
`No handler registered for method: ${request.serviceName}/${request.methodName}`
|
|
1044
|
+
);
|
|
1045
|
+
}
|
|
1046
|
+
if (method.rpcMode !== "unary") {
|
|
1047
|
+
throw new GrpcError(
|
|
1048
|
+
12 /* UNIMPLEMENTED */,
|
|
1049
|
+
`Method ${request.serviceName}/${request.methodName} is registered as ${method.rpcMode}`
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
1052
|
+
return method.handler.handleRequest(request);
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
826
1055
|
function createUnaryHandler(methodName, handler, requestType, responseType) {
|
|
827
1056
|
return {
|
|
828
1057
|
async handleRequest(request) {
|
|
@@ -1755,6 +1984,7 @@ var TestClient = class {
|
|
|
1755
1984
|
};
|
|
1756
1985
|
export {
|
|
1757
1986
|
GrpcError,
|
|
1987
|
+
GrpcService,
|
|
1758
1988
|
GrpcStatusCode,
|
|
1759
1989
|
Spikard,
|
|
1760
1990
|
StreamingResponse,
|