@funduck/connectrpc-fastify 1.0.0 → 1.0.1
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/package.json +4 -5
- package/.editorconfig +0 -12
- package/.prettierrc +0 -7
- package/pnpm-workspace.yaml +0 -2
- package/publish.sh +0 -3
- package/src/connectrpc.ts +0 -87
- package/src/execution-context.ts +0 -45
- package/src/fastify-plugin.ts +0 -99
- package/src/guards.ts +0 -130
- package/src/helpers.ts +0 -92
- package/src/index.ts +0 -21
- package/src/interfaces.ts +0 -175
- package/src/middlewares.ts +0 -71
- package/src/stores.ts +0 -180
- package/src/types.ts +0 -24
- package/test/buf.gen.yaml +0 -7
- package/test/buf.lock +0 -6
- package/test/buf.yaml +0 -12
- package/test/controller.ts +0 -73
- package/test/e2e-demo.ts +0 -252
- package/test/gen/buf/validate/validate_pb.ts +0 -4938
- package/test/gen/connectrpc/eliza/v1/eliza_pb.ts +0 -97
- package/test/guards.ts +0 -13
- package/test/middlewares.ts +0 -50
- package/test/proto/connectrpc/eliza/v1/eliza.proto +0 -26
- package/test/server.ts +0 -47
- package/tsconfig.build.json +0 -4
- package/tsconfig.json +0 -26
package/package.json
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@funduck/connectrpc-fastify",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"author": "Oleg Milekhin <qlfunduck@gmail.com>",
|
|
5
5
|
"description": "Wrapper for official @connectrpc/connect and fastify. Simplifies configuration, type safe binding to controller, simplifies use of middlewares and guards.",
|
|
6
|
-
"main": "dist/index.js",
|
|
6
|
+
"main": "dist/src/index.js",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"build": "tsc -p tsconfig.build.json",
|
|
8
|
+
"build": "npx rimraf dist; tsc -p tsconfig.build.json",
|
|
9
9
|
"compile-proto": "cd test && npx buf dep update; npx buf lint; npx buf generate",
|
|
10
|
-
"
|
|
11
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
10
|
+
"test": "cd test && DEBUG=true npx tsx e2e-demo.ts"
|
|
12
11
|
},
|
|
13
12
|
"license": "MIT",
|
|
14
13
|
"dependencies": {
|
package/.editorconfig
DELETED
package/.prettierrc
DELETED
package/pnpm-workspace.yaml
DELETED
package/publish.sh
DELETED
package/src/connectrpc.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { GenService, GenServiceMethods } from '@bufbuild/protobuf/codegenv2';
|
|
2
|
-
import { FastifyInstance } from 'fastify';
|
|
3
|
-
import { registerFastifyPlugin } from './fastify-plugin';
|
|
4
|
-
import { initGuards } from './guards';
|
|
5
|
-
import { setLogger } from './helpers';
|
|
6
|
-
import {
|
|
7
|
-
Guard,
|
|
8
|
-
Logger,
|
|
9
|
-
Middleware,
|
|
10
|
-
MiddlewareConfigUnion,
|
|
11
|
-
Service,
|
|
12
|
-
} from './interfaces';
|
|
13
|
-
import { initMiddlewares } from './middlewares';
|
|
14
|
-
import { ControllersStore, GuardsStore, MiddlewareStore } from './stores';
|
|
15
|
-
|
|
16
|
-
class ConnectRPCClass {
|
|
17
|
-
setLogger(customLogger: Logger) {
|
|
18
|
-
setLogger(customLogger);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
registerMiddleware(
|
|
22
|
-
self: Middleware,
|
|
23
|
-
options?: {
|
|
24
|
-
allowMultipleInstances?: boolean;
|
|
25
|
-
},
|
|
26
|
-
) {
|
|
27
|
-
MiddlewareStore.registerInstance(self, options);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* @param self - instance of controller
|
|
32
|
-
* @param service - generated service that is implemented by controller
|
|
33
|
-
*/
|
|
34
|
-
registerController<T extends GenServiceMethods>(
|
|
35
|
-
self: Service<GenService<T>>,
|
|
36
|
-
service: GenService<T>,
|
|
37
|
-
options?: {
|
|
38
|
-
allowMultipleInstances?: boolean;
|
|
39
|
-
},
|
|
40
|
-
) {
|
|
41
|
-
ControllersStore.registerInstance(self, service, options);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
registerGuard(
|
|
45
|
-
self: Guard,
|
|
46
|
-
options?: {
|
|
47
|
-
allowMultipleInstances?: boolean;
|
|
48
|
-
},
|
|
49
|
-
) {
|
|
50
|
-
GuardsStore.registerInstance(self, options);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
registerFastifyPlugin(server: FastifyInstance) {
|
|
54
|
-
return registerFastifyPlugin(server);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
private _middlewaresInitialized = false;
|
|
58
|
-
|
|
59
|
-
initMiddlewares(
|
|
60
|
-
server: FastifyInstance,
|
|
61
|
-
middlewareConfigs: MiddlewareConfigUnion[],
|
|
62
|
-
) {
|
|
63
|
-
if (this._middlewaresInitialized) {
|
|
64
|
-
throw new Error('Middlewares have already been initialized!');
|
|
65
|
-
}
|
|
66
|
-
if (this._guardsInitialized) {
|
|
67
|
-
throw new Error('Middlewares must be initialized before guards!');
|
|
68
|
-
}
|
|
69
|
-
this._middlewaresInitialized = true;
|
|
70
|
-
return initMiddlewares(server, middlewareConfigs);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
private _guardsInitialized = false;
|
|
74
|
-
|
|
75
|
-
initGuards(server: FastifyInstance) {
|
|
76
|
-
if (this._guardsInitialized) {
|
|
77
|
-
throw new Error('Guards have already been initialized!');
|
|
78
|
-
}
|
|
79
|
-
this._guardsInitialized = true;
|
|
80
|
-
return initGuards(server);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Main ConnectRPC class to manage registration of controllers and middlewares
|
|
86
|
-
*/
|
|
87
|
-
export const ConnectRPC = new ConnectRPCClass();
|
package/src/execution-context.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { FastifyReply, FastifyRequest } from 'fastify';
|
|
2
|
-
import { ExecutionContext, Type } from './interfaces';
|
|
3
|
-
|
|
4
|
-
export class ManualExecutionContext implements ExecutionContext {
|
|
5
|
-
constructor(
|
|
6
|
-
readonly request: FastifyRequest['raw'],
|
|
7
|
-
readonly response: FastifyReply['raw'],
|
|
8
|
-
readonly next: <T = any>() => T,
|
|
9
|
-
readonly args: any[],
|
|
10
|
-
readonly constructorRef: Type<any> | null = null,
|
|
11
|
-
readonly handler: Function | null = null,
|
|
12
|
-
) {}
|
|
13
|
-
|
|
14
|
-
getClass<T = any>(): Type<T> {
|
|
15
|
-
return this.constructorRef!;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
getHandler(): Function {
|
|
19
|
-
return this.handler!;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
getArgs<T extends Array<any> = any[]>(): T {
|
|
23
|
-
return this.args as T;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
getArgByIndex<T = any>(index: number): T {
|
|
27
|
-
return this.args[index] as T;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
switchToHttp() {
|
|
31
|
-
return Object.assign(this, {
|
|
32
|
-
getRequest: () => this.request,
|
|
33
|
-
getResponse: () => this.response,
|
|
34
|
-
getNext: () => this.next,
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
switchToRpc() {
|
|
39
|
-
throw new Error('Context switching to RPC is not supported.');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
switchToWs() {
|
|
43
|
-
throw new Error('Context switching to WebSockets is not supported.');
|
|
44
|
-
}
|
|
45
|
-
}
|
package/src/fastify-plugin.ts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { GenService } from '@bufbuild/protobuf/codegenv2';
|
|
2
|
-
import { ConnectRouter } from '@connectrpc/connect';
|
|
3
|
-
import { fastifyConnectPlugin } from '@connectrpc/connect-fastify';
|
|
4
|
-
import { Compression } from '@connectrpc/connect/protocol';
|
|
5
|
-
import { FastifyInstance } from 'fastify';
|
|
6
|
-
import { getGuards } from './guards';
|
|
7
|
-
import { discoverMethodMappings, logger } from './helpers';
|
|
8
|
-
import { ControllersStore, RouteMetadataStore } from './stores';
|
|
9
|
-
|
|
10
|
-
export async function registerFastifyPlugin(
|
|
11
|
-
server: FastifyInstance,
|
|
12
|
-
options: {
|
|
13
|
-
acceptCompression?: Compression[];
|
|
14
|
-
} = {},
|
|
15
|
-
) {
|
|
16
|
-
// Create implementations from controller instances
|
|
17
|
-
const implementations = new Map<GenService<any>, any>();
|
|
18
|
-
|
|
19
|
-
for (const { instance, service } of ControllersStore.values()) {
|
|
20
|
-
const guards = getGuards(instance);
|
|
21
|
-
if (guards.length > 0) {
|
|
22
|
-
logger.log(
|
|
23
|
-
`Found ${guards.length} guards on controller ${instance.constructor.name}`,
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const methodMappings = discoverMethodMappings(instance.__proto__, service);
|
|
28
|
-
|
|
29
|
-
// Create the implementation object
|
|
30
|
-
const implementation: any = {};
|
|
31
|
-
|
|
32
|
-
// Bind each method from the service
|
|
33
|
-
for (const methodDesc of service.methods) {
|
|
34
|
-
const { name } = methodDesc;
|
|
35
|
-
const methodName = name[0].toLowerCase() + name.slice(1);
|
|
36
|
-
|
|
37
|
-
// Check if there's a mapped controller method
|
|
38
|
-
const controllerMethodName = methodMappings[name];
|
|
39
|
-
|
|
40
|
-
if (controllerMethodName) {
|
|
41
|
-
const controllerMethod = instance[controllerMethodName];
|
|
42
|
-
|
|
43
|
-
if (controllerMethod) {
|
|
44
|
-
// Bind the method with proper 'this' context
|
|
45
|
-
const bindedMethod = controllerMethod.bind(instance);
|
|
46
|
-
implementation[methodName] = (...args: any[]) => {
|
|
47
|
-
return bindedMethod(...args);
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
// Store route metadata for guards and interceptors
|
|
51
|
-
RouteMetadataStore.registerRoute(
|
|
52
|
-
service.typeName,
|
|
53
|
-
name, // PascalCase method name (e.g., "Say")
|
|
54
|
-
instance.constructor,
|
|
55
|
-
controllerMethod,
|
|
56
|
-
controllerMethodName,
|
|
57
|
-
instance,
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
logger.log(
|
|
61
|
-
`Binding ${instance.constructor.name}.${controllerMethodName} to ${service.typeName}.${name}`,
|
|
62
|
-
);
|
|
63
|
-
} else {
|
|
64
|
-
logger.warn(
|
|
65
|
-
`Method ${controllerMethodName} not found in ${instance.constructor.name}`,
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
implementations.set(service, implementation);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const routes = (router: ConnectRouter) => {
|
|
75
|
-
for (const [service, implementation] of implementations.entries()) {
|
|
76
|
-
router.service(service, implementation);
|
|
77
|
-
logger.log(`Registered {/${service.typeName}} route`);
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
if (routes.length === 0) {
|
|
82
|
-
logger.warn('No controllers found to register');
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
await server.register(fastifyConnectPlugin, {
|
|
87
|
-
// For now we enable only Connect protocol by default and disable others.
|
|
88
|
-
// grpc: this.options.grpc ?? false,
|
|
89
|
-
// grpcWeb: this.options.grpcWeb ?? false,
|
|
90
|
-
// connect: this.options.connect ?? true,
|
|
91
|
-
grpc: false,
|
|
92
|
-
grpcWeb: false,
|
|
93
|
-
connect: true,
|
|
94
|
-
acceptCompression: options.acceptCompression ?? [],
|
|
95
|
-
routes: routes,
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
logger.log('Ready');
|
|
99
|
-
}
|
package/src/guards.ts
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
|
|
2
|
-
import { ManualExecutionContext } from './execution-context';
|
|
3
|
-
import { logger } from './helpers';
|
|
4
|
-
import { ExecutionContext, Guard } from './interfaces';
|
|
5
|
-
import { GuardsStore, RouteMetadataStore } from './stores';
|
|
6
|
-
|
|
7
|
-
export class ManualGuardExecutor {
|
|
8
|
-
async executeGuard(
|
|
9
|
-
guard: Guard,
|
|
10
|
-
context: ExecutionContext,
|
|
11
|
-
): Promise<boolean> {
|
|
12
|
-
const result = guard.canActivate(context);
|
|
13
|
-
|
|
14
|
-
// Handle synchronous boolean result
|
|
15
|
-
if (typeof result === 'boolean') {
|
|
16
|
-
return result;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Handle Promise result
|
|
20
|
-
return await result;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async executeGuards(
|
|
24
|
-
guards: Guard[],
|
|
25
|
-
context: ExecutionContext,
|
|
26
|
-
): Promise<boolean> {
|
|
27
|
-
for (const guard of guards) {
|
|
28
|
-
const canActivate = await this.executeGuard(guard, context);
|
|
29
|
-
if (!canActivate) {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function getGuards(controller: any): Guard[] {
|
|
38
|
-
// Return all registered guards
|
|
39
|
-
// In a more sophisticated implementation, you could filter guards
|
|
40
|
-
// based on decorators on the controller or method
|
|
41
|
-
return GuardsStore.getAllGuards();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Initialize guards middleware - this should be called after all other middlewares are registered
|
|
46
|
-
*/
|
|
47
|
-
export async function initGuards(server: FastifyInstance) {
|
|
48
|
-
const guardExecutor = new ManualGuardExecutor();
|
|
49
|
-
|
|
50
|
-
// Add a hook that runs after all other middlewares
|
|
51
|
-
server.addHook(
|
|
52
|
-
'preHandler',
|
|
53
|
-
async (request: FastifyRequest, reply: FastifyReply) => {
|
|
54
|
-
const url = request.url;
|
|
55
|
-
|
|
56
|
-
// Parse the URL to get the route
|
|
57
|
-
// Format: /package.ServiceName/MethodName
|
|
58
|
-
const match = url.match(/^\/([^/]+)\/([^/]+)$/);
|
|
59
|
-
|
|
60
|
-
if (!match) {
|
|
61
|
-
// Not a ConnectRPC route, skip guard execution
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Get route metadata
|
|
66
|
-
const routeMetadata = RouteMetadataStore.getRouteMetadata(url);
|
|
67
|
-
|
|
68
|
-
if (!routeMetadata) {
|
|
69
|
-
// No metadata found for this route
|
|
70
|
-
logger.warn(`No route metadata found for ${url}`);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const {
|
|
75
|
-
controllerClass,
|
|
76
|
-
controllerMethod,
|
|
77
|
-
controllerMethodName,
|
|
78
|
-
instance,
|
|
79
|
-
} = routeMetadata;
|
|
80
|
-
|
|
81
|
-
// Get guards for the controller
|
|
82
|
-
const guards = getGuards(instance);
|
|
83
|
-
|
|
84
|
-
if (guards.length === 0) {
|
|
85
|
-
// No guards to execute
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Create execution context
|
|
90
|
-
// Note: For ConnectRPC, we don't have the actual request arguments yet at this point
|
|
91
|
-
// They will be deserialized later by the ConnectRPC handler
|
|
92
|
-
const executionContext = new ManualExecutionContext(
|
|
93
|
-
request.raw,
|
|
94
|
-
reply.raw,
|
|
95
|
-
(() => undefined) as <T = any>() => T,
|
|
96
|
-
[], // args will be populated later if needed
|
|
97
|
-
controllerClass,
|
|
98
|
-
controllerMethod,
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
// Execute guards
|
|
102
|
-
try {
|
|
103
|
-
const canActivate = await guardExecutor.executeGuards(
|
|
104
|
-
guards,
|
|
105
|
-
executionContext,
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
if (!canActivate) {
|
|
109
|
-
// Guard rejected the request
|
|
110
|
-
reply.code(403).send({
|
|
111
|
-
code: 'permission_denied',
|
|
112
|
-
message: 'Forbidden',
|
|
113
|
-
});
|
|
114
|
-
throw new Error('Guard rejected the request');
|
|
115
|
-
}
|
|
116
|
-
} catch (error) {
|
|
117
|
-
// If guard throws an error, reject the request
|
|
118
|
-
if (!reply.sent) {
|
|
119
|
-
reply.code(403).send({
|
|
120
|
-
code: 'permission_denied',
|
|
121
|
-
message: error instanceof Error ? error.message : 'Forbidden',
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
throw error;
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
logger.log('Guards middleware initialized');
|
|
130
|
-
}
|
package/src/helpers.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { GenService } from '@bufbuild/protobuf/codegenv2';
|
|
2
|
-
import { FastifyReply, FastifyRequest } from 'fastify';
|
|
3
|
-
import { Logger } from './interfaces';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Automatically discover method mappings by matching service methods to controller methods
|
|
7
|
-
*/
|
|
8
|
-
export function discoverMethodMappings(
|
|
9
|
-
prototype,
|
|
10
|
-
service: GenService<any>,
|
|
11
|
-
): Record<string, string> {
|
|
12
|
-
const methodMappings: Record<string, string> = {};
|
|
13
|
-
|
|
14
|
-
// service.methods is an array of method descriptors
|
|
15
|
-
const serviceMethods = Array.isArray(service.methods) ? service.methods : [];
|
|
16
|
-
|
|
17
|
-
// Get all controller methods
|
|
18
|
-
const controllerMethods = Object.getOwnPropertyNames(prototype).filter(
|
|
19
|
-
(name) => name !== 'constructor' && typeof prototype[name] === 'function',
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
// Check each service method
|
|
23
|
-
for (const methodDesc of serviceMethods) {
|
|
24
|
-
const serviceMethodName = methodDesc.name; // e.g., "Say" - this is what connectrpc module uses as key
|
|
25
|
-
const localName = methodDesc.localName; // e.g., "say" (camelCase version)
|
|
26
|
-
|
|
27
|
-
// Try to find a matching controller method
|
|
28
|
-
// First try exact match with localName (camelCase), then try case-insensitive
|
|
29
|
-
let controllerMethodName = controllerMethods.find(
|
|
30
|
-
(name) => name === localName,
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
if (!controllerMethodName) {
|
|
34
|
-
controllerMethodName = controllerMethods.find(
|
|
35
|
-
(name) => name.toLowerCase() === serviceMethodName.toLowerCase(),
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (controllerMethodName) {
|
|
40
|
-
// Map using the service method name (e.g., "Say") because that's what the module looks for
|
|
41
|
-
methodMappings[serviceMethodName] = controllerMethodName;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return methodMappings;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Helper to convert NestJS middleware to Fastify hook
|
|
50
|
-
*/
|
|
51
|
-
export function convertMiddlewareToHook(
|
|
52
|
-
middlewareInstance: any,
|
|
53
|
-
): (request: FastifyRequest, reply: FastifyReply) => Promise<void> {
|
|
54
|
-
return async (request: FastifyRequest, reply: FastifyReply) => {
|
|
55
|
-
return new Promise<void>((resolve, reject) => {
|
|
56
|
-
try {
|
|
57
|
-
// NestJS middleware expects raw req/res
|
|
58
|
-
middlewareInstance.use(request.raw, reply.raw, (err?: any) => {
|
|
59
|
-
if (err) {
|
|
60
|
-
reject(err);
|
|
61
|
-
} else {
|
|
62
|
-
resolve();
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
} catch (error) {
|
|
66
|
-
reject(error);
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export let logger: Logger = {
|
|
73
|
-
log: (...args: any[]) => {
|
|
74
|
-
console.info(...args);
|
|
75
|
-
},
|
|
76
|
-
error: (...args: any[]) => {
|
|
77
|
-
console.error(...args);
|
|
78
|
-
},
|
|
79
|
-
warn: (...args: any[]) => {
|
|
80
|
-
console.warn(...args);
|
|
81
|
-
},
|
|
82
|
-
debug: (...args: any[]) => {
|
|
83
|
-
console.debug(...args);
|
|
84
|
-
},
|
|
85
|
-
verbose: (...args: any[]) => {
|
|
86
|
-
console.log(...args);
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
export function setLogger(customLogger: Logger) {
|
|
91
|
-
logger = customLogger;
|
|
92
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export function printMsg() {
|
|
2
|
-
console.log('Thanks for using connectrpc-fastify!');
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export { ConnectRPC } from './connectrpc';
|
|
6
|
-
|
|
7
|
-
export { middlewareConfig } from './interfaces';
|
|
8
|
-
|
|
9
|
-
export type {
|
|
10
|
-
ExecutionContext,
|
|
11
|
-
Guard,
|
|
12
|
-
Logger,
|
|
13
|
-
Middleware,
|
|
14
|
-
MiddlewareConfig,
|
|
15
|
-
MiddlewareConfigUnion,
|
|
16
|
-
Service,
|
|
17
|
-
} from './interfaces';
|
|
18
|
-
|
|
19
|
-
export { initGuards } from './guards';
|
|
20
|
-
|
|
21
|
-
export type { OmitConnectrpcFields } from './types';
|
package/src/interfaces.ts
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import type { GenMessage, GenService } from '@bufbuild/protobuf/codegenv2';
|
|
2
|
-
import { FastifyReply, FastifyRequest } from 'fastify';
|
|
3
|
-
import { OmitConnectrpcFields } from './types';
|
|
4
|
-
|
|
5
|
-
export interface Logger {
|
|
6
|
-
log: (...args: any[]) => void;
|
|
7
|
-
error: (...args: any[]) => void;
|
|
8
|
-
warn: (...args: any[]) => void;
|
|
9
|
-
debug: (...args: any[]) => void;
|
|
10
|
-
verbose: (...args: any[]) => void;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface Middleware {
|
|
14
|
-
use(
|
|
15
|
-
req: FastifyRequest['raw'],
|
|
16
|
-
res: FastifyReply['raw'],
|
|
17
|
-
next: (err?: any) => void,
|
|
18
|
-
): void;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface Type<T = any> extends Function {
|
|
22
|
-
new (...args: any[]): T;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Extract the input type from a method schema
|
|
27
|
-
*/
|
|
28
|
-
type ExtractInput<T> = T extends { input: GenMessage<infer M> } ? M : never;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Extract the output type from a method schema
|
|
32
|
-
*/
|
|
33
|
-
type ExtractOutput<T> = T extends { output: GenMessage<infer M> } ? M : never;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Convert a service method to a controller method signature
|
|
37
|
-
*/
|
|
38
|
-
type ServiceMethod<T> = T extends { methodKind: 'unary' }
|
|
39
|
-
? (
|
|
40
|
-
request: ExtractInput<T>,
|
|
41
|
-
) => Promise<OmitConnectrpcFields<ExtractOutput<T>>>
|
|
42
|
-
: T extends { methodKind: 'server_streaming' }
|
|
43
|
-
? (
|
|
44
|
-
request: ExtractInput<T>,
|
|
45
|
-
) => AsyncIterable<OmitConnectrpcFields<ExtractOutput<T>>>
|
|
46
|
-
: T extends { methodKind: 'client_streaming' }
|
|
47
|
-
? (
|
|
48
|
-
request: AsyncIterable<ExtractInput<T>>,
|
|
49
|
-
) => Promise<OmitConnectrpcFields<ExtractOutput<T>>>
|
|
50
|
-
: T extends { methodKind: 'bidi_streaming' }
|
|
51
|
-
? (
|
|
52
|
-
request: AsyncIterable<ExtractInput<T>>,
|
|
53
|
-
) => AsyncIterable<OmitConnectrpcFields<ExtractOutput<T>>>
|
|
54
|
-
: never;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Generic interface that maps a ConnectRPC service to controller methods
|
|
58
|
-
*
|
|
59
|
-
* Controllers can implement any subset of the service methods.
|
|
60
|
-
* TypeScript will enforce correct signatures for implemented methods.
|
|
61
|
-
*
|
|
62
|
-
* Usage:
|
|
63
|
-
* ```typescript
|
|
64
|
-
* export class ElizaController implements Service<typeof ElizaService> {
|
|
65
|
-
* constructor() {
|
|
66
|
-
* ConnectRPC.registerController(this, ElizaService);
|
|
67
|
-
* }
|
|
68
|
-
*
|
|
69
|
-
* async say(request: SayRequest): Promise<SayResponse> {
|
|
70
|
-
* // implementation
|
|
71
|
-
* }
|
|
72
|
-
* // Other methods are optional
|
|
73
|
-
* }
|
|
74
|
-
* ```
|
|
75
|
-
*/
|
|
76
|
-
export type Service<T> =
|
|
77
|
-
T extends GenService<infer Methods>
|
|
78
|
-
? {
|
|
79
|
-
[K in keyof Methods]?: ServiceMethod<Methods[K]>;
|
|
80
|
-
}
|
|
81
|
-
: never;
|
|
82
|
-
|
|
83
|
-
export type ServiceMethodNames<T> =
|
|
84
|
-
T extends GenService<infer Methods>
|
|
85
|
-
? {
|
|
86
|
-
[K in keyof Methods]: K;
|
|
87
|
-
}[keyof Methods]
|
|
88
|
-
: never;
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Middleware configuration for ConnectRPC routes - without service specified
|
|
92
|
-
*/
|
|
93
|
-
export type MiddlewareConfigGlobal = {
|
|
94
|
-
/**
|
|
95
|
-
* The middleware class to apply (must be decorated with @Middleware())
|
|
96
|
-
*/
|
|
97
|
-
use: Type<Middleware>;
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Middleware applies to all services and all methods
|
|
101
|
-
*/
|
|
102
|
-
on?: never;
|
|
103
|
-
methods?: never;
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Middleware configuration for ConnectRPC routes - with service specified
|
|
108
|
-
*/
|
|
109
|
-
export type MiddlewareConfig<T extends GenService<any>> = {
|
|
110
|
-
/**
|
|
111
|
-
* The middleware class to apply (must be decorated with @Middleware())
|
|
112
|
-
*/
|
|
113
|
-
use: Type<Middleware>;
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* The service to apply middleware to
|
|
117
|
-
*/
|
|
118
|
-
on: T;
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Optional: Specific method names to apply middleware to.
|
|
122
|
-
* If omitted, middleware applies to all methods of the service.
|
|
123
|
-
* Method names should match the protobuf method names (e.g., 'say', 'sayMany')
|
|
124
|
-
*/
|
|
125
|
-
methods?: Array<ServiceMethodNames<T>>;
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Middleware configuration for ConnectRPC routes
|
|
130
|
-
*/
|
|
131
|
-
export type MiddlewareConfigUnion =
|
|
132
|
-
| MiddlewareConfigGlobal
|
|
133
|
-
| MiddlewareConfig<any>;
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Helper function to create a type-safe middleware configuration
|
|
137
|
-
* This ensures proper type inference for method names based on the service
|
|
138
|
-
*/
|
|
139
|
-
export function middlewareConfig<T extends GenService<any>>(
|
|
140
|
-
use: Type<Middleware>,
|
|
141
|
-
on?: T,
|
|
142
|
-
methods?: Array<ServiceMethodNames<T>>,
|
|
143
|
-
): MiddlewareConfigUnion {
|
|
144
|
-
return {
|
|
145
|
-
use,
|
|
146
|
-
on,
|
|
147
|
-
methods,
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
export interface ExecutionContext {
|
|
152
|
-
getClass<T = any>(): Type<T>;
|
|
153
|
-
|
|
154
|
-
getHandler(): Function;
|
|
155
|
-
|
|
156
|
-
getArgs<T extends Array<any> = any[]>(): T;
|
|
157
|
-
|
|
158
|
-
getArgByIndex<T = any>(index: number): T;
|
|
159
|
-
|
|
160
|
-
switchToHttp(): {
|
|
161
|
-
getRequest(): FastifyRequest['raw'];
|
|
162
|
-
getResponse(): FastifyReply['raw'];
|
|
163
|
-
getNext<T = any>(): () => T;
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
// Adding these two only for compatibility with NestJS ExecutionContext
|
|
167
|
-
// Implementations will throw error
|
|
168
|
-
switchToRpc(): any;
|
|
169
|
-
|
|
170
|
-
switchToWs(): any;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export interface Guard {
|
|
174
|
-
canActivate(context: ExecutionContext): boolean | Promise<boolean>;
|
|
175
|
-
}
|