@nexusts/grpc 0.7.0 → 0.7.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/README.md CHANGED
@@ -15,7 +15,7 @@ This module is part of the NexusTS monorepo. Each module is published as its own
15
15
  Most apps start with just the core:
16
16
 
17
17
  ```bash
18
- bun add @nexusts/core reflect-metadata zod hono
18
+ bun add @nexusts/core
19
19
  ```
20
20
 
21
21
  Then add this module only if you need it:
@@ -26,7 +26,7 @@ bun add @nexusts/grpc
26
26
 
27
27
  ## Peer dependencies
28
28
 
29
- None. This module is fully self-contained.
29
+ **None.** No external runtime dependencies. `gRPC` is built into the Bun runtime. On Node.js, install `@grpc/grpc-js` and `@grpc/proto-loader` separately.
30
30
 
31
31
  ## Usage
32
32
 
@@ -0,0 +1,33 @@
1
+ /**
2
+ * gRPC decorators.
3
+ *
4
+ * @Injectable()
5
+ * @GrpcService("UserService")
6
+ * class UserServiceImpl {
7
+ * @GrpcMethod("FindById")
8
+ * async findById(request: { id: number }) {
9
+ * return { name: "Alice", email: "alice@example.com" };
10
+ * }
11
+ * }
12
+ *
13
+ * The framework reads the metadata at start time and wires
14
+ * each method to the corresponding gRPC handler. The method
15
+ * name in the decorator must match the .proto file's method
16
+ * name (e.g. `FindById`, not `findById`).
17
+ */
18
+ /**
19
+ * Mark a class as a gRPC service implementation. The `name`
20
+ * must match a `service` declaration in the .proto file.
21
+ */
22
+ export declare function GrpcService(name: string): ClassDecorator;
23
+ /**
24
+ * Mark a method as a gRPC handler. The `name` must match the
25
+ * method name in the .proto file. The method receives the
26
+ * request object and must return a `Promise<TResponse>` (for
27
+ * unary methods).
28
+ */
29
+ export declare function GrpcMethod(name: string): MethodDecorator;
30
+ /** Read the gRPC service name. Internal. */
31
+ export declare function getGrpcServiceName(target: object): string | undefined;
32
+ /** Read the bound method names for a gRPC service. Internal. */
33
+ export declare function getGrpcMethodNames(target: object): Record<string, string>;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * `nexusjs/grpc` — gRPC integration for the Bun-native stack.
3
+ *
4
+ * Public API:
5
+ * - `GrpcService` — the main service. Owns the @grpc/grpc-js server
6
+ * and the loaded proto definition.
7
+ * - `GrpcModule.forRoot(config)` — wires the service into the DI
8
+ * container, registers service implementations, and prepares the
9
+ * handlers. The user calls `start()` to bind the server to a
10
+ * port.
11
+ * - `@GrpcService(name)` — class decorator marking an impl class.
12
+ * - `@GrpcMethod(name)` — method decorator binding a method to a
13
+ * gRPC method declared in the `.proto` file.
14
+ * - `client<T>(name, { url })` — build a typed client for a gRPC
15
+ * service. Methods return Promises.
16
+ *
17
+ * Optional peer dependencies:
18
+ * - `@grpc/grpc-js` (^1.10.0) — the server runtime
19
+ * - `@grpc/proto-loader` (^0.7.0) — loads `.proto` files
20
+ *
21
+ * Install with:
22
+ * bun add @grpc/grpc-js @grpc/proto-loader
23
+ *
24
+ * Quick start:
25
+ *
26
+ * // user.proto
27
+ * syntax = "proto3";
28
+ * package user;
29
+ * service UserService {
30
+ * rpc FindById (UserRequest) returns (UserResponse);
31
+ * }
32
+ * message UserRequest { int32 id = 1; }
33
+ * message UserResponse { string name = 1; string email = 2; }
34
+ *
35
+ * // user.grpc.ts
36
+ * @Injectable()
37
+ * @GrpcService("UserService")
38
+ * class UserServiceImpl {
39
+ * @GrpcMethod("FindById")
40
+ * async findById(req: { id: number }) {
41
+ * return { name: "Alice", email: "alice@example.com" };
42
+ * }
43
+ * }
44
+ *
45
+ * // app.module.ts
46
+ * @Module({
47
+ * imports: [GrpcModule.forRoot({
48
+ * protoPath: "./proto/user.proto",
49
+ * services: [UserServiceImpl],
50
+ * })],
51
+ * })
52
+ * class AppModule {}
53
+ *
54
+ * // main.ts
55
+ * const app = new Application(AppModule);
56
+ * const grpc = app.container.resolve(GrpcService);
57
+ * await grpc.start(); // binds to 0.0.0.0:50051
58
+ *
59
+ * // client usage (e.g. in another service)
60
+ * const userClient = grpc.client<{ findById(req: { id: number }): Promise<{ name: string; email: string }> }>("UserService");
61
+ * const user = await userClient.findById({ id: 1 });
62
+ *
63
+ * Unary methods only for v1. Streaming (server / client / bidi)
64
+ * is a future addition.
65
+ */
66
+ export { GrpcService, GRPC_SERVICE_TOKEN } from "./service.js";
67
+ export { GrpcModule } from "./module.js";
68
+ export { GrpcService as GrpcServiceDecorator, GrpcMethod, getGrpcServiceName, getGrpcMethodNames, } from "./decorators.js";
69
+ export type { GrpcConfig, GrpcMethodMeta, GrpcClient, GrpcClientOptions, } from "./types.js";
package/dist/index.js CHANGED
@@ -236,7 +236,7 @@ function makeUnaryHandler(fn, instance) {
236
236
  };
237
237
  }
238
238
  // packages/grpc/src/module.ts
239
- import { Module } from "@nexusts/core/decorators/index.js";
239
+ import { Module } from "@nexusts/core";
240
240
  class GrpcModule {
241
241
  static forRoot(config) {
242
242
  class ConfiguredGrpcModule {
@@ -281,5 +281,5 @@ export {
281
281
  GRPC_SERVICE_TOKEN
282
282
  };
283
283
 
284
- //# debugId=ECB12AA4CE8F00C764756E2164756E21
284
+ //# debugId=68F9B756B6DC011B64756E2164756E21
285
285
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -4,9 +4,9 @@
4
4
  "sourcesContent": [
5
5
  "/**\n * `GrpcService` — the main gRPC service.\n *\n * Owns the @grpc/grpc-js server, the loaded proto definition, and\n * the registered service implementations. The service is\n * registered in the DI container; the user calls `start()` to\n * bind the server to a port.\n *\n * const grpc = container.resolve(GrpcService);\n * await grpc.start();\n * // ...\n * await grpc.stop();\n *\n * The framework also exposes a `client<T>('ServiceName')` helper\n * for creating typed clients against the same (or a different)\n * server. Clients returned by this method are async — each method\n * returns a Promise.\n */\n\nimport { existsSync } from \"node:fs\";\nimport { resolve as resolvePath } from \"node:path\";\nimport {\n\tloadPackageDefinition,\n\tServer as GrpcServer,\n\tServerCredentials,\n\tcredentials as grpcCredentials,\n} from \"@grpc/grpc-js\";\nimport * as protoLoader from \"@grpc/proto-loader\";\nimport {\n\tgetGrpcMethodNames,\n\tgetGrpcServiceName,\n} from \"./decorators.js\";\nimport type { GrpcConfig } from \"./types.js\";\n// logger is injected via the service in production; we use\n// console here to avoid a circular dependency with @core/logger.\n\nexport const GRPC_SERVICE_TOKEN = Symbol.for(\"nexus:GrpcService\");\n\nexport class GrpcService {\n\treadonly name = \"grpc\";\n\t#config: Required<Omit<GrpcConfig, \"tls\" | \"onBound\">> &\n\t\tPick<GrpcConfig, \"tls\" | \"onBound\">;\n\t#server: GrpcServer | null = null;\n\t#bound = false;\n\t#proto: any = null;\n\t#instanceByService: Map<string, unknown> = new Map();\n\t#clientCtors: Map<string, new (url: string, creds: any) => any> = new Map();\n\t#resolve: (<T>(token: unknown) => T) | null = null;\n\t#host: string | null = null;\n\t#port: number | null = null;\n\n\tconstructor(config: GrpcConfig) {\n\t\tthis.#config = {\n\t\t\tprotoPath: config.protoPath,\n\t\t\tpackage: config.package ?? \"\",\n\t\t\tservices: config.services ?? [],\n\t\t\tport: config.port ?? 50051,\n\t\t\thost: config.host ?? \"0.0.0.0\",\n\t\t\ttls: config.tls,\n\t\t\tonBound: config.onBound,\n\t\t};\n\t}\n\n\t/** True if the server is currently bound to a port. */\n\tget isRunning(): boolean {\n\t\treturn this.#bound;\n\t}\n\n\t/** The actual host the server bound to. `null` until start(). */\n\tget host(): string | null {\n\t\treturn this.#host;\n\t}\n\n\t/** Inject a resolver function. Called by GrpcModule.forRoot(). */\n\tsetResolver(resolve: <T>(token: unknown) => T): void {\n\t\tthis.#resolve = resolve;\n\t}\n\n\t/** The actual port the server bound to. `null` until start(). */\n\tget port(): number | null {\n\t\treturn this.#port;\n\t}\n\n\t/**\n\t * Load the .proto file(s) and prepare the server.\n\t * Idempotent — call once at boot, before `start()`.\n\t */\n\tasync prepare(resolve: <T>(token: unknown) => T): Promise<void> {\n\t\t// Load proto(s)\n\t\tconst files = Array.isArray(this.#config.protoPath)\n\t\t\t? this.#config.protoPath\n\t\t\t: [this.#config.protoPath];\n\t\tfor (const f of files) {\n\t\t\tif (!existsSync(f)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[grpc] proto file not found: ${f}. ` +\n\t\t\t\t\t\t`Resolve the path relative to your project root.`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// loadPackageDefinition takes a single PackageDefinition. We\n\t\t// load each .proto separately and merge them into one\n\t\t// package tree.\n\t\tconst merged: Record<string, unknown> = {};\n\t\tfor (const f of files) {\n\t\t\tconst def = protoLoader.loadSync(f, {\n\t\t\t\tkeepCase: false,\n\t\t\t\tlongs: String,\n\t\t\t\tenums: String,\n\t\t\t\tdefaults: true,\n\t\t\t\toneofs: true,\n\t\t\t});\n\t\t\tconst loaded = loadPackageDefinition(def);\n\t\t\tObject.assign(merged, loaded);\n\t\t}\n\t\tthis.#proto = merged;\n\n\t\t// Build the server and register services\n\t\tthis.#server = new GrpcServer();\n\t\tfor (const ServiceImpl of this.#config.services) {\n\t\t\tconst svcName = getGrpcServiceName(ServiceImpl);\n\t\t\tif (!svcName) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[grpc] service ${ServiceImpl.name} is missing @GrpcService(name)`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst methodNames = getGrpcMethodNames(ServiceImpl.prototype);\n\t\t\t// @grpc/proto-loader produces nested objects, so we walk\n\t\t\t// the package path: proto.<package>.<serviceName>.\n\t\t\tconst segments = this.#config.package\n\t\t\t\t? this.#config.package.split(\".\")\n\t\t\t\t: [];\n\t\t\tsegments.push(svcName);\n\t\t\tlet svcSpec: { service: any; prototype?: unknown } | Record<string, unknown> | undefined =\n\t\t\t\tthis.#proto as Record<string, unknown>;\n\t\t\tfor (const seg of segments) {\n\t\t\t\tif (svcSpec && typeof svcSpec === \"object\" && seg in svcSpec) {\n\t\t\t\t\tsvcSpec = (svcSpec as Record<string, unknown>)[seg] as typeof svcSpec;\n\t\t\t\t} else {\n\t\t\t\t\tsvcSpec = undefined;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!svcSpec) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[grpc] service \"${segments.join(\".\")}\" not found in proto definition. ` +\n\t\t\t\t\t\t`Check the .proto file and the @GrpcService name.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Resolve the implementation from the DI container\n\t\t\tconst instance = resolve(ServiceImpl);\n\t\t\tthis.#instanceByService.set(svcName, instance);\n\n\t\t\t// Build the handler map. For each @GrpcMethod, wrap\n\t\t\t// the user's method so it can be called as\n\t\t\t// (call, callback) => void and we await the Promise.\n\t\t\tconst handlers: Record<string, any> = {};\n\t\t\tfor (const [methodKey, protoMethodName] of Object.entries(\n\t\t\t\tmethodNames,\n\t\t\t)) {\n\t\t\t\tconst fn = (instance as Record<string, (...args: unknown[]) => unknown>)[\n\t\t\t\t\tmethodKey\n\t\t\t\t];\n\t\t\t\tif (typeof fn !== \"function\") continue;\n\t\t\t\t// Bind the method to the instance so `this` is preserved\n\t\t\t\t// (the user's handler may rely on `this` to access\n\t\t\t\t// injected dependencies).\n\t\t\t\thandlers[protoMethodName] = makeUnaryHandler(fn, instance);\n\t\t\t}\n\n\t\t\t// svcSpec is the Client constructor; .service is the ServiceDefinition.\n\t\t\tthis.#server!.addService((svcSpec as { service: any }).service, handlers);\n\t\t\tthis.#clientCtors.set(svcName, svcSpec as unknown as new (...a: any[]) => any);\n\t\t}\n\t}\n\n\t/**\n\t * Bind the server to the configured host/port. Resolves when\n\t * the port is bound. Use `port: 0` in tests to let the OS\n\t * pick a free port; the actual port is available via the\n\t * `port` getter or the `onBound` callback.\n\t */\n\tasync start(): Promise<void> {\n\t\tif (this.#resolve && !this.#server) {\n\t\t\tawait this.prepare(this.#resolve);\n\t\t}\n\t\tif (!this.#server) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[grpc] start() called before prepare(). Call prepare() first \" +\n\t\t\t\t\t\"(typically done automatically by the DI module).\",\n\t\t\t);\n\t\t}\n\t\tif (this.isRunning) return;\n\n\t\tconst creds = this.#config.tls\n\t\t\t? ServerCredentials.createSsl(\n\t\t\t\t\tthis.#config.tls.cert as Buffer,\n\t\t\t\t\tArray.isArray(this.#config.tls.key) ? this.#config.tls.key[0] : (this.#config.tls.key as Buffer),\n\t\t\t\t)\n\t\t\t: ServerCredentials.createInsecure();\n\n\t\tconst port: number = await new Promise((resolveP, rejectP) => {\n\t\t\t\t// bindAsync(port: \"host:port\", creds, callback) — the\n\t\t\t// first arg is a single string, not separate host/port.\n\t\t\t(this.#server as GrpcServer).bindAsync(\n\t\t\t\t`${this.#config.host}:${this.#config.port}`,\n\t\t\t\tcreds,\n\t\t\t\t(err: Error | null, p: number) => {\n\t\t\t\t\t\t\t\treturn err ? rejectP(err) : resolveP(p);\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\t\tthis.#host = this.#config.host;\n\t\tthis.#port = port;\n\t\tthis.#bound = true;\n\t\tconsole.log(\n\t\t\t`✓ gRPC server listening on ${this.#config.tls ? \"https\" : \"http\"}://${this.#host}:${this.#port}`,\n\t\t);\n\t\tthis.#config.onBound?.(this.#host, this.#port);\n\t}\n\n\t/** Stop the server and release the port. */\n\tasync stop(): Promise<void> {\n\t\tif (!this.#server) return;\n\t\t// tryShutdown waits for all pending RPCs to complete. If\n\t\t// a client is hung, this can hang forever. Race it against\n\t\t// a 1s timeout and force-shutdown if it doesn't complete.\n\t\tawait Promise.race([\n\t\t\tnew Promise<void>((resolveP, rejectP) => {\n\t\t\t\t(this.#server as GrpcServer).tryShutdown((err?: Error) =>\n\t\t\t\t\terr ? rejectP(err) : resolveP(),\n\t\t\t\t);\n\t\t\t}),\n\t\t\tnew Promise<void>((resolveP) => setTimeout(resolveP, 1000)),\n\t\t]).catch(() => {\n\t\t\t(this.#server as GrpcServer).forceShutdown();\n\t\t});\n\t\tthis.#server = null;\n\t\tthis.#host = null;\n\t\tthis.#port = null;\n\t\tthis.#bound = false;\n\t}\n\n\t/**\n\t * Build a typed client for a gRPC service. The returned object\n\t * has one Promise-returning method per service method defined\n\t * in the .proto file. Method names are converted to\n\t * camelCase on the client side to match JS convention\n\t * (e.g. `FindById` → `findById`).\n\t */\n\tclient<T = Record<string, (...args: unknown[]) => Promise<unknown>>>(\n\t\tserviceName: string,\n\t\toptions: { url: string; tls?: boolean } = {\n\t\t\turl: `127.0.0.1:${this.#port ?? 50051}`,\n\t\t},\n\t): T {\n\t\tif (!this.#proto) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[grpc] client() called before prepare(). \" +\n\t\t\t\t\t\"Did you forget to call grpc.start()?\",\n\t\t\t);\n\t\t}\n\t\t// Walk the nested proto package to find the service spec.\n\t\tconst segments = this.#config.package\n\t\t\t? this.#config.package.split(\".\")\n\t\t\t: [];\n\t\tsegments.push(serviceName);\n\t\tlet svcSpec: { service: any; prototype?: unknown } | Record<string, unknown> | undefined =\n\t\t\tthis.#proto as Record<string, unknown>;\n\t\tfor (const seg of segments) {\n\t\t\tif (svcSpec && typeof svcSpec === \"object\" && seg in svcSpec) {\n\t\t\t\tsvcSpec = (svcSpec as Record<string, unknown>)[seg] as typeof svcSpec;\n\t\t\t} else {\n\t\t\t\tsvcSpec = undefined;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!svcSpec) {\n\t\t\tthrow new Error(\n\t\t\t\t`[grpc] service \"${segments.join(\".\")}\" not found in proto definition`,\n\t\t\t);\n\t\t}\n\t\t// svcSpec is the Client constructor itself.\n\t\tconst ClientCtor = svcSpec as unknown as {\n\t\t\tnew (url: string, creds: unknown): Record<string, (...args: unknown[]) => unknown>;\n\t\t\tservice: Record<string, Record<string, unknown>>;\n\t\t};\n\t\tconst creds = options.tls\n\t\t\t? // @ts-ignore\n\t\t\t\trequire(\"@grpc/grpc-js\").credentials.createSsl()\n\t\t\t: // @ts-ignore\n\t\t\t\trequire(\"@grpc/grpc-js\").credentials.createInsecure();\n\t\tconst underlying = new ClientCtor(options.url, creds);\n\t\t// ServiceDefinition lists methods in PascalCase (proto form).\n\t\t// The client exposes them as camelCase on its prototype.\n\t\tconst protoMethodNames = Object.keys(ClientCtor.service);\n\t\tconst methodNames = protoMethodNames.map(\n\t\t\t(n) => n.charAt(0).toLowerCase() + n.slice(1),\n\t\t);\n\t\tconst wrapped: Record<string, (...args: unknown[]) => Promise<unknown>> = {};\n\t\tfor (const methodName of methodNames) {\n\t\t\t// @ts-ignore - method exists on the prototype at runtime.\n\t\t\tconst fn = underlying[methodName];\n\t\t\tif (typeof fn !== \"function\") continue;\n\t\t\twrapped[methodName] = (req: unknown) =>\n\t\t\t\tnew Promise((resolveP, rejectP) => {\n\t\t\t\t\tfn.call(underlying, req, (err: Error | null, res: unknown) => {\n\t\t\t\t\t\tif (err) {\n\t\t\t\t\t\t\trejectP(err);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresolveP(res);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t}\n\t\treturn wrapped as unknown as T;\n\t}\n}\n\n/**\n * Wrap a user's async method so it can be passed to gRPC's\n * callback-style handlers. Translates thrown errors into gRPC's\n * status code 13 (INTERNAL) by default.\n */\nfunction makeUnaryHandler(fn: (...args: unknown[]) => unknown, instance: unknown) {\n\treturn (call: unknown, callback: (err: unknown, res?: unknown) => void) => {\n\t\tconst req = (call as { request: unknown }).request;\n\t\t// Bind the method to the instance so `this` is preserved.\n\t\t// Use .then/.catch so we can correctly forward both\n\t\t// synchronous throws and async rejections to gRPC.\n\t\tlet result: unknown;\n\t\ttry {\n\t\t\tresult = fn.call(instance, req);\n\t\t} catch (syncErr) {\n\t\t\tconst e = syncErr as Error;\n\t\t\tconst code = (e as Error & { code?: number }).code ?? 13;\n\t\t\tcallback({ code, details: e.message });\n\t\t\treturn;\n\t\t}\n\t\tPromise.resolve(result).then(\n\t\t\t(value) => callback(null, value),\n\t\t\t(err: Error) => {\n\t\t\t\tconst code = (err as Error & { code?: number }).code ?? 13;\n\t\t\t\t// gRPC expects a plain { code, details } object, NOT\n\t\t\t\t// an Error instance.\n\t\t\t\tcallback({ code, details: err.message });\n\t\t\t},\n\t\t);\n\t};\n}\n\nexport default GrpcService;\n",
6
6
  "/**\n * gRPC decorators.\n *\n * @Injectable()\n * @GrpcService(\"UserService\")\n * class UserServiceImpl {\n * @GrpcMethod(\"FindById\")\n * async findById(request: { id: number }) {\n * return { name: \"Alice\", email: \"alice@example.com\" };\n * }\n * }\n *\n * The framework reads the metadata at start time and wires\n * each method to the corresponding gRPC handler. The method\n * name in the decorator must match the .proto file's method\n * name (e.g. `FindById`, not `findById`).\n */\n\nconst GRPC_SERVICE_KEY = Symbol.for(\"nexus:grpc:service\");\nconst GRPC_METHOD_KEY = Symbol.for(\"nexus:grpc:method\");\n\n/**\n * Mark a class as a gRPC service implementation. The `name`\n * must match a `service` declaration in the .proto file.\n */\nexport function GrpcService(name: string): ClassDecorator {\n\treturn function (target: Function) {\n\t\tconst proto = (target as { prototype: object }).prototype ?? target;\n\t\t(proto as Record<symbol, unknown>)[GRPC_SERVICE_KEY] = { name };\n\t};\n}\n\n/**\n * Mark a method as a gRPC handler. The `name` must match the\n * method name in the .proto file. The method receives the\n * request object and must return a `Promise<TResponse>` (for\n * unary methods).\n */\nexport function GrpcMethod(name: string): MethodDecorator {\n\treturn function (\n\t\t_target: object,\n\t\tpropertyKey: string | symbol,\n\t\t_descriptor: PropertyDescriptor,\n\t) {\n\t\tconst proto = _target as Record<symbol, unknown>;\n\t\tproto[GRPC_METHOD_KEY] = proto[GRPC_METHOD_KEY] ?? {};\n\t\t(proto[GRPC_METHOD_KEY] as Record<string | symbol, string>)[\n\t\t\tpropertyKey\n\t\t] = name;\n\t\treturn _descriptor;\n\t};\n}\n\n/** Read the gRPC service name. Internal. */\nexport function getGrpcServiceName(target: object): string | undefined {\n\tconst t = (target as { prototype?: object }).prototype ?? target;\n\treturn (t as Record<symbol, { name?: string } | undefined>)[\n\t\tGRPC_SERVICE_KEY\n\t]?.name;\n}\n\n/** Read the bound method names for a gRPC service. Internal. */\nexport function getGrpcMethodNames(target: object): Record<string, string> {\n\tconst t = (target as { prototype?: object }).prototype ?? target;\n\treturn (\n\t\t((t as Record<symbol, unknown>)[GRPC_METHOD_KEY] as\n\t\t\t| Record<string, string>\n\t\t\t| undefined) ?? {}\n\t);\n}\n",
7
- "/**\n * `GrpcModule` — wires `GrpcService` into the DI container.\n *\n * @Module({\n * imports: [\n * GrpcModule.forRoot({\n * protoPath: \"./proto/user.proto\",\n * services: [UserServiceImpl],\n * port: 50051,\n * }),\n * ],\n * })\n * class AppModule {}\n *\n * After boot, the user resolves `GrpcService` from the\n * container and calls `start()` / `stop()` to control the\n * gRPC server's lifecycle:\n *\n * const grpc = container.resolve(GrpcService);\n * await grpc.start();\n * // ...\n * await grpc.stop();\n *\n * The framework also auto-registers the service impl classes\n * as DI providers, so their `@Inject()`-decorated dependencies\n * are wired up automatically.\n */\n\nimport { Module } from \"@nexusts/core/decorators/index.js\";\nimport { GrpcService, GRPC_SERVICE_TOKEN } from \"./service.js\";\nimport type { GrpcConfig } from \"./types.js\";\n\n@Module({\n\tproviders: [GrpcService, { provide: GRPC_SERVICE_TOKEN, useExisting: GrpcService }],\n\texports: [GrpcService, GRPC_SERVICE_TOKEN],\n})\nexport class GrpcModule {\n\tstatic forRoot(config: GrpcConfig) {\n\t\t@Module({\n\t\t\tproviders: [\n\t\t\t\t{ provide: \"GRPC_CONFIG\", useValue: config },\n\t\t\t\t// Service impl classes must be registered as providers so\n\t\t\t\t// the DI container can instantiate them.\n\t\t\t\t...config.services,\n\t\t\t\t{\n\t\t\t\t\tprovide: GrpcService,\n\t\t\t\t\tuseFactory: (resolve: <T>(t: unknown) => T) => {\n\n\t\t\t\t\t\tconst svc = new GrpcService(config);\n\t\t\t\t\t\t// prepare() resolves the service impls from the\n\t\t\t\t\t\t// container and registers their handlers on the\n\t\t\t\t\t\t// gRPC server. The user calls start() later to\n\t\t\t\t\t\t// bind to a port.\n\t\t\t\t\t\tsvc.setResolver((t) => resolve(t));\n\t\t\t\t\t\treturn svc;\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ provide: GRPC_SERVICE_TOKEN, useExisting: GrpcService },\n\t\t\t],\n\t\t\texports: [GrpcService, GRPC_SERVICE_TOKEN, \"GRPC_CONFIG\", ...config.services],\n\t\t})\n\t\tclass ConfiguredGrpcModule {}\n\t\tObject.defineProperty(ConfiguredGrpcModule, \"name\", {\n\t\t\tvalue: \"ConfiguredGrpcModule\",\n\t\t});\n\t\treturn ConfiguredGrpcModule as unknown as typeof GrpcModule;\n\t}\n}\n"
7
+ "/**\n * `GrpcModule` — wires `GrpcService` into the DI container.\n *\n * @Module({\n * imports: [\n * GrpcModule.forRoot({\n * protoPath: \"./proto/user.proto\",\n * services: [UserServiceImpl],\n * port: 50051,\n * }),\n * ],\n * })\n * class AppModule {}\n *\n * After boot, the user resolves `GrpcService` from the\n * container and calls `start()` / `stop()` to control the\n * gRPC server's lifecycle:\n *\n * const grpc = container.resolve(GrpcService);\n * await grpc.start();\n * // ...\n * await grpc.stop();\n *\n * The framework also auto-registers the service impl classes\n * as DI providers, so their `@Inject()`-decorated dependencies\n * are wired up automatically.\n */\n\nimport { Module } from \"@nexusts/core\";\nimport { GrpcService, GRPC_SERVICE_TOKEN } from \"./service.js\";\nimport type { GrpcConfig } from \"./types.js\";\n\n@Module({\n\tproviders: [GrpcService, { provide: GRPC_SERVICE_TOKEN, useExisting: GrpcService }],\n\texports: [GrpcService, GRPC_SERVICE_TOKEN],\n})\nexport class GrpcModule {\n\tstatic forRoot(config: GrpcConfig) {\n\t\t@Module({\n\t\t\tproviders: [\n\t\t\t\t{ provide: \"GRPC_CONFIG\", useValue: config },\n\t\t\t\t// Service impl classes must be registered as providers so\n\t\t\t\t// the DI container can instantiate them.\n\t\t\t\t...config.services,\n\t\t\t\t{\n\t\t\t\t\tprovide: GrpcService,\n\t\t\t\t\tuseFactory: (resolve: <T>(t: unknown) => T) => {\n\n\t\t\t\t\t\tconst svc = new GrpcService(config);\n\t\t\t\t\t\t// prepare() resolves the service impls from the\n\t\t\t\t\t\t// container and registers their handlers on the\n\t\t\t\t\t\t// gRPC server. The user calls start() later to\n\t\t\t\t\t\t// bind to a port.\n\t\t\t\t\t\tsvc.setResolver((t) => resolve(t));\n\t\t\t\t\t\treturn svc;\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t{ provide: GRPC_SERVICE_TOKEN, useExisting: GrpcService },\n\t\t\t],\n\t\t\texports: [GrpcService, GRPC_SERVICE_TOKEN, \"GRPC_CONFIG\", ...config.services],\n\t\t})\n\t\tclass ConfiguredGrpcModule {}\n\t\tObject.defineProperty(ConfiguredGrpcModule, \"name\", {\n\t\t\tvalue: \"ConfiguredGrpcModule\",\n\t\t});\n\t\treturn ConfiguredGrpcModule as unknown as typeof GrpcModule;\n\t}\n}\n"
8
8
  ],
9
9
  "mappings": ";;;;;;;;;;;;;;AAmBA;AAEA;AAAA;AAAA,YAEC;AAAA;AAAA;AAID;;;ACTA,IAAM,mBAAmB,OAAO,IAAI,oBAAoB;AACxD,IAAM,kBAAkB,OAAO,IAAI,mBAAmB;AAM/C,SAAS,WAAW,CAAC,MAA8B;AAAA,EACzD,OAAO,QAAS,CAAC,QAAkB;AAAA,IAClC,MAAM,QAAS,OAAiC,aAAa;AAAA,IAC5D,MAAkC,oBAAoB,EAAE,KAAK;AAAA;AAAA;AAUzD,SAAS,UAAU,CAAC,MAA+B;AAAA,EACzD,OAAO,QAAS,CACf,SACA,aACA,aACC;AAAA,IACD,MAAM,QAAQ;AAAA,IACd,MAAM,mBAAmB,MAAM,oBAAoB,CAAC;AAAA,IACnD,MAAM,iBACN,eACG;AAAA,IACJ,OAAO;AAAA;AAAA;AAKF,SAAS,kBAAkB,CAAC,QAAoC;AAAA,EACtE,MAAM,IAAK,OAAkC,aAAa;AAAA,EAC1D,OAAQ,EACP,mBACE;AAAA;AAIG,SAAS,kBAAkB,CAAC,QAAwC;AAAA,EAC1E,MAAM,IAAK,OAAkC,aAAa;AAAA,EAC1D,OACG,EAA8B,oBAEf,CAAC;AAAA;;;AD/Bb,IAAM,qBAAqB,OAAO,IAAI,mBAAmB;AAAA;AAEzD,MAAM,aAAY;AAAA,EACf,OAAO;AAAA,EAChB;AAAA,EAEA,UAA6B;AAAA,EAC7B,SAAS;AAAA,EACT,SAAc;AAAA,EACd,qBAA2C,IAAI;AAAA,EAC/C,eAAkE,IAAI;AAAA,EACtE,WAA8C;AAAA,EAC9C,QAAuB;AAAA,EACvB,QAAuB;AAAA,EAEvB,WAAW,CAAC,QAAoB;AAAA,IAC/B,KAAK,UAAU;AAAA,MACd,WAAW,OAAO;AAAA,MAClB,SAAS,OAAO,WAAW;AAAA,MAC3B,UAAU,OAAO,YAAY,CAAC;AAAA,MAC9B,MAAM,OAAO,QAAQ;AAAA,MACrB,MAAM,OAAO,QAAQ;AAAA,MACrB,KAAK,OAAO;AAAA,MACZ,SAAS,OAAO;AAAA,IACjB;AAAA;AAAA,MAIG,SAAS,GAAY;AAAA,IACxB,OAAO,KAAK;AAAA;AAAA,MAIT,IAAI,GAAkB;AAAA,IACzB,OAAO,KAAK;AAAA;AAAA,EAIb,WAAW,CAAC,SAAyC;AAAA,IACpD,KAAK,WAAW;AAAA;AAAA,MAIb,IAAI,GAAkB;AAAA,IACzB,OAAO,KAAK;AAAA;AAAA,OAOP,QAAO,CAAC,SAAkD;AAAA,IAE/D,MAAM,QAAQ,MAAM,QAAQ,KAAK,QAAQ,SAAS,IAC/C,KAAK,QAAQ,YACb,CAAC,KAAK,QAAQ,SAAS;AAAA,IAC1B,WAAW,KAAK,OAAO;AAAA,MACtB,IAAI,CAAC,WAAW,CAAC,GAAG;AAAA,QACnB,MAAM,IAAI,MACT,gCAAgC,QAC/B,iDACF;AAAA,MACD;AAAA,IACD;AAAA,IAKA,MAAM,SAAkC,CAAC;AAAA,IACzC,WAAW,KAAK,OAAO;AAAA,MACtB,MAAM,MAAkB,qBAAS,GAAG;AAAA,QACnC,UAAU;AAAA,QACV,OAAO;AAAA,QACP,OAAO;AAAA,QACP,UAAU;AAAA,QACV,QAAQ;AAAA,MACT,CAAC;AAAA,MACD,MAAM,SAAS,sBAAsB,GAAG;AAAA,MACxC,OAAO,OAAO,QAAQ,MAAM;AAAA,IAC7B;AAAA,IACA,KAAK,SAAS;AAAA,IAGd,KAAK,UAAU,IAAI;AAAA,IACnB,WAAW,eAAe,KAAK,QAAQ,UAAU;AAAA,MAChD,MAAM,UAAU,mBAAmB,WAAW;AAAA,MAC9C,IAAI,CAAC,SAAS;AAAA,QACb,MAAM,IAAI,MACT,kBAAkB,YAAY,oCAC/B;AAAA,MACD;AAAA,MACA,MAAM,cAAc,mBAAmB,YAAY,SAAS;AAAA,MAG5D,MAAM,WAAW,KAAK,QAAQ,UAC3B,KAAK,QAAQ,QAAQ,MAAM,GAAG,IAC9B,CAAC;AAAA,MACJ,SAAS,KAAK,OAAO;AAAA,MACrB,IAAI,UACH,KAAK;AAAA,MACN,WAAW,OAAO,UAAU;AAAA,QAC3B,IAAI,WAAW,OAAO,YAAY,YAAY,OAAO,SAAS;AAAA,UAC7D,UAAW,QAAoC;AAAA,QAChD,EAAO;AAAA,UACN,UAAU;AAAA,UACV;AAAA;AAAA,MAEF;AAAA,MACA,IAAI,CAAC,SAAS;AAAA,QACb,MAAM,IAAI,MACT,mBAAmB,SAAS,KAAK,GAAG,uCACnC,kDACF;AAAA,MACD;AAAA,MAGA,MAAM,WAAW,QAAQ,WAAW;AAAA,MACpC,KAAK,mBAAmB,IAAI,SAAS,QAAQ;AAAA,MAK7C,MAAM,WAAgC,CAAC;AAAA,MACvC,YAAY,WAAW,oBAAoB,OAAO,QACjD,WACD,GAAG;AAAA,QACF,MAAM,KAAM,SACX;AAAA,QAED,IAAI,OAAO,OAAO;AAAA,UAAY;AAAA,QAI9B,SAAS,mBAAmB,iBAAiB,IAAI,QAAQ;AAAA,MAC1D;AAAA,MAGA,KAAK,QAAS,WAAY,QAA6B,SAAS,QAAQ;AAAA,MACxE,KAAK,aAAa,IAAI,SAAS,OAA8C;AAAA,IAC9E;AAAA;AAAA,OASK,MAAK,GAAkB;AAAA,IAC5B,IAAI,KAAK,YAAY,CAAC,KAAK,SAAS;AAAA,MACnC,MAAM,KAAK,QAAQ,KAAK,QAAQ;AAAA,IACjC;AAAA,IACA,IAAI,CAAC,KAAK,SAAS;AAAA,MAClB,MAAM,IAAI,MACT,kEACC,kDACF;AAAA,IACD;AAAA,IACA,IAAI,KAAK;AAAA,MAAW;AAAA,IAEpB,MAAM,QAAQ,KAAK,QAAQ,MACxB,kBAAkB,UAClB,KAAK,QAAQ,IAAI,MACjB,MAAM,QAAQ,KAAK,QAAQ,IAAI,GAAG,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAM,KAAK,QAAQ,IAAI,GACnF,IACC,kBAAkB,eAAe;AAAA,IAEpC,MAAM,OAAe,MAAM,IAAI,QAAQ,CAAC,UAAU,YAAY;AAAA,MAG5D,KAAK,QAAuB,UAC5B,GAAG,KAAK,QAAQ,QAAQ,KAAK,QAAQ,QACrC,OACA,CAAC,KAAmB,MAAc;AAAA,QAC9B,OAAO,MAAM,QAAQ,GAAG,IAAI,SAAS,CAAC;AAAA,OAE3C;AAAA,KACA;AAAA,IACD,KAAK,QAAQ,KAAK,QAAQ;AAAA,IAC1B,KAAK,QAAQ;AAAA,IACb,KAAK,SAAS;AAAA,IACd,QAAQ,IACP,mCAA6B,KAAK,QAAQ,MAAM,UAAU,YAAY,KAAK,SAAS,KAAK,OAC1F;AAAA,IACA,KAAK,QAAQ,UAAU,KAAK,OAAO,KAAK,KAAK;AAAA;AAAA,OAIxC,KAAI,GAAkB;AAAA,IAC3B,IAAI,CAAC,KAAK;AAAA,MAAS;AAAA,IAInB,MAAM,QAAQ,KAAK;AAAA,MAClB,IAAI,QAAc,CAAC,UAAU,YAAY;AAAA,QACvC,KAAK,QAAuB,YAAY,CAAC,QACzC,MAAM,QAAQ,GAAG,IAAI,SAAS,CAC/B;AAAA,OACA;AAAA,MACD,IAAI,QAAc,CAAC,aAAa,WAAW,UAAU,IAAI,CAAC;AAAA,IAC3D,CAAC,EAAE,MAAM,MAAM;AAAA,MACb,KAAK,QAAuB,cAAc;AAAA,KAC3C;AAAA,IACD,KAAK,UAAU;AAAA,IACf,KAAK,QAAQ;AAAA,IACb,KAAK,QAAQ;AAAA,IACb,KAAK,SAAS;AAAA;AAAA,EAUf,MAAoE,CACnE,aACA,UAA0C;AAAA,IACzC,KAAK,aAAa,KAAK,SAAS;AAAA,EACjC,GACI;AAAA,IACJ,IAAI,CAAC,KAAK,QAAQ;AAAA,MACjB,MAAM,IAAI,MACT,8CACC,sCACF;AAAA,IACD;AAAA,IAEA,MAAM,WAAW,KAAK,QAAQ,UAC3B,KAAK,QAAQ,QAAQ,MAAM,GAAG,IAC9B,CAAC;AAAA,IACJ,SAAS,KAAK,WAAW;AAAA,IACzB,IAAI,UACH,KAAK;AAAA,IACN,WAAW,OAAO,UAAU;AAAA,MAC3B,IAAI,WAAW,OAAO,YAAY,YAAY,OAAO,SAAS;AAAA,QAC7D,UAAW,QAAoC;AAAA,MAChD,EAAO;AAAA,QACN,UAAU;AAAA,QACV;AAAA;AAAA,IAEF;AAAA,IACA,IAAI,CAAC,SAAS;AAAA,MACb,MAAM,IAAI,MACT,mBAAmB,SAAS,KAAK,GAAG,kCACrC;AAAA,IACD;AAAA,IAEA,MAAM,aAAa;AAAA,IAInB,MAAM,QAAQ,QAAQ,iCAEK,YAAY,UAAU,+BAEtB,YAAY,eAAe;AAAA,IACtD,MAAM,aAAa,IAAI,WAAW,QAAQ,KAAK,KAAK;AAAA,IAGpD,MAAM,mBAAmB,OAAO,KAAK,WAAW,OAAO;AAAA,IACvD,MAAM,cAAc,iBAAiB,IACpC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,IAAI,EAAE,MAAM,CAAC,CAC7C;AAAA,IACA,MAAM,UAAoE,CAAC;AAAA,IAC3E,WAAW,cAAc,aAAa;AAAA,MAErC,MAAM,KAAK,WAAW;AAAA,MACtB,IAAI,OAAO,OAAO;AAAA,QAAY;AAAA,MAC9B,QAAQ,cAAc,CAAC,QACtB,IAAI,QAAQ,CAAC,UAAU,YAAY;AAAA,QAClC,GAAG,KAAK,YAAY,KAAK,CAAC,KAAmB,QAAiB;AAAA,UAC7D,IAAI,KAAK;AAAA,YACR,QAAQ,GAAG;AAAA,UACZ,EAAO;AAAA,YACN,SAAS,GAAG;AAAA;AAAA,SAEb;AAAA,OACD;AAAA,IACH;AAAA,IACA,OAAO;AAAA;AAET;AAOA,SAAS,gBAAgB,CAAC,IAAqC,UAAmB;AAAA,EACjF,OAAO,CAAC,MAAe,aAAoD;AAAA,IAC1E,MAAM,MAAO,KAA8B;AAAA,IAI3C,IAAI;AAAA,IACJ,IAAI;AAAA,MACH,SAAS,GAAG,KAAK,UAAU,GAAG;AAAA,MAC7B,OAAO,SAAS;AAAA,MACjB,MAAM,IAAI;AAAA,MACV,MAAM,OAAQ,EAAgC,QAAQ;AAAA,MACtD,SAAS,EAAE,MAAM,SAAS,EAAE,QAAQ,CAAC;AAAA,MACrC;AAAA;AAAA,IAED,QAAQ,QAAQ,MAAM,EAAE,KACvB,CAAC,UAAU,SAAS,MAAM,KAAK,GAC/B,CAAC,QAAe;AAAA,MACf,MAAM,OAAQ,IAAkC,QAAQ;AAAA,MAGxD,SAAS,EAAE,MAAM,SAAS,IAAI,QAAQ,CAAC;AAAA,KAEzC;AAAA;AAAA;;AEjUF;AAQO,MAAM,WAAW;AAAA,SAChB,OAAO,CAAC,QAAoB;AAAA,IAwBlC,MAAM,qBAAqB;AAAA,IAAC;AAAA,IAAtB,uBAAN;AAAA,MAvBC,OAAO;AAAA,QACP,WAAW;AAAA,UACV,EAAE,SAAS,eAAe,UAAU,OAAO;AAAA,UAG3C,GAAG,OAAO;AAAA,UACV;AAAA,YACC,SAAS;AAAA,YACT,YAAY,CAAC,YAAkC;AAAA,cAE9C,MAAM,MAAM,IAAI,aAAY,MAAM;AAAA,cAKlC,IAAI,YAAY,CAAC,MAAM,QAAQ,CAAC,CAAC;AAAA,cACjC,OAAO;AAAA;AAAA,UAET;AAAA,UACA,EAAE,SAAS,oBAAoB,aAAa,aAAY;AAAA,QACzD;AAAA,QACA,SAAS,CAAC,cAAa,oBAAoB,eAAe,GAAG,OAAO,QAAQ;AAAA,MAC7E,CAAC;AAAA,OACK;AAAA,IACN,OAAO,eAAe,sBAAsB,QAAQ;AAAA,MACnD,OAAO;AAAA,IACR,CAAC;AAAA,IACD,OAAO;AAAA;AAET;AA/Ba,aAAN;AAAA,EAJN,OAAO;AAAA,IACP,WAAW,CAAC,cAAa,EAAE,SAAS,oBAAoB,aAAa,aAAY,CAAC;AAAA,IAClF,SAAS,CAAC,cAAa,kBAAkB;AAAA,EAC1C,CAAC;AAAA,GACY;",
10
- "debugId": "ECB12AA4CE8F00C764756E2164756E21",
10
+ "debugId": "68F9B756B6DC011B64756E2164756E21",
11
11
  "names": []
12
12
  }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `GrpcModule` — wires `GrpcService` into the DI container.
3
+ *
4
+ * @Module({
5
+ * imports: [
6
+ * GrpcModule.forRoot({
7
+ * protoPath: "./proto/user.proto",
8
+ * services: [UserServiceImpl],
9
+ * port: 50051,
10
+ * }),
11
+ * ],
12
+ * })
13
+ * class AppModule {}
14
+ *
15
+ * After boot, the user resolves `GrpcService` from the
16
+ * container and calls `start()` / `stop()` to control the
17
+ * gRPC server's lifecycle:
18
+ *
19
+ * const grpc = container.resolve(GrpcService);
20
+ * await grpc.start();
21
+ * // ...
22
+ * await grpc.stop();
23
+ *
24
+ * The framework also auto-registers the service impl classes
25
+ * as DI providers, so their `@Inject()`-decorated dependencies
26
+ * are wired up automatically.
27
+ */
28
+ import type { GrpcConfig } from "./types.js";
29
+ export declare class GrpcModule {
30
+ static forRoot(config: GrpcConfig): typeof GrpcModule;
31
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * `GrpcService` — the main gRPC service.
3
+ *
4
+ * Owns the @grpc/grpc-js server, the loaded proto definition, and
5
+ * the registered service implementations. The service is
6
+ * registered in the DI container; the user calls `start()` to
7
+ * bind the server to a port.
8
+ *
9
+ * const grpc = container.resolve(GrpcService);
10
+ * await grpc.start();
11
+ * // ...
12
+ * await grpc.stop();
13
+ *
14
+ * The framework also exposes a `client<T>('ServiceName')` helper
15
+ * for creating typed clients against the same (or a different)
16
+ * server. Clients returned by this method are async — each method
17
+ * returns a Promise.
18
+ */
19
+ import type { GrpcConfig } from "./types.js";
20
+ export declare const GRPC_SERVICE_TOKEN: unique symbol;
21
+ export declare class GrpcService {
22
+ #private;
23
+ readonly name = "grpc";
24
+ constructor(config: GrpcConfig);
25
+ /** True if the server is currently bound to a port. */
26
+ get isRunning(): boolean;
27
+ /** The actual host the server bound to. `null` until start(). */
28
+ get host(): string | null;
29
+ /** Inject a resolver function. Called by GrpcModule.forRoot(). */
30
+ setResolver(resolve: <T>(token: unknown) => T): void;
31
+ /** The actual port the server bound to. `null` until start(). */
32
+ get port(): number | null;
33
+ /**
34
+ * Load the .proto file(s) and prepare the server.
35
+ * Idempotent — call once at boot, before `start()`.
36
+ */
37
+ prepare(resolve: <T>(token: unknown) => T): Promise<void>;
38
+ /**
39
+ * Bind the server to the configured host/port. Resolves when
40
+ * the port is bound. Use `port: 0` in tests to let the OS
41
+ * pick a free port; the actual port is available via the
42
+ * `port` getter or the `onBound` callback.
43
+ */
44
+ start(): Promise<void>;
45
+ /** Stop the server and release the port. */
46
+ stop(): Promise<void>;
47
+ /**
48
+ * Build a typed client for a gRPC service. The returned object
49
+ * has one Promise-returning method per service method defined
50
+ * in the .proto file. Method names are converted to
51
+ * camelCase on the client side to match JS convention
52
+ * (e.g. `FindById` → `findById`).
53
+ */
54
+ client<T = Record<string, (...args: unknown[]) => Promise<unknown>>>(serviceName: string, options?: {
55
+ url: string;
56
+ tls?: boolean;
57
+ }): T;
58
+ }
59
+ export default GrpcService;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Public types for `nexusjs/grpc`.
3
+ *
4
+ * `nexusjs/grpc` is a reflection-based gRPC integration. The user
5
+ * provides a `.proto` file and a service implementation class
6
+ * with `@GrpcService` / `@GrpcMethod` decorators. The framework
7
+ * loads the proto at runtime and wires the methods to the
8
+ * implementation.
9
+ *
10
+ * The gRPC server runs on a separate port (it needs HTTP/2) and
11
+ * is started manually via `GrpcService.start()`. This keeps the
12
+ * Hono HTTP/1 server independent and lets the user choose the
13
+ * port.
14
+ *
15
+ * Cross-runtime: works on Bun and Node via `@grpc/grpc-js`
16
+ * (which is built on Node's `http2` module — Bun supports
17
+ * this via Node-API compatibility).
18
+ *
19
+ * Unary methods only for v1; streaming (server / client / bidi)
20
+ * is a future addition.
21
+ */
22
+ import type { ServiceDefinition, UntypedServiceImplementation } from "@grpc/grpc-js";
23
+ export interface GrpcConfig {
24
+ /**
25
+ * Path to one or more `.proto` files. Paths are resolved
26
+ * against the process's cwd.
27
+ */
28
+ protoPath: string | string[];
29
+ /**
30
+ * Optional override for the proto package name. If omitted,
31
+ * the package is taken from the `.proto` file. Useful when
32
+ * the .proto file has multiple `package` declarations.
33
+ */
34
+ package?: string;
35
+ /**
36
+ * Service implementations to register. Each class must be
37
+ * decorated with `@GrpcService('Name')` and have its methods
38
+ * decorated with `@GrpcMethod('ProtoMethodName')`.
39
+ */
40
+ services: Array<new (...args: never[]) => unknown>;
41
+ /** Port to bind the gRPC server to. Default: 50051. */
42
+ port?: number;
43
+ /** Host to bind the gRPC server to. Default: "0.0.0.0". */
44
+ host?: string;
45
+ /**
46
+ * TLS credentials. If omitted, the server runs in plaintext
47
+ * (h2c). For production, provide a `tls: { cert, key }` pair.
48
+ */
49
+ tls?: {
50
+ cert: Buffer | string;
51
+ key: Buffer | string;
52
+ };
53
+ /**
54
+ * Optional callback that runs after the server is bound. Useful
55
+ * for tests that need to know the actual port (when `port: 0`
56
+ * is used).
57
+ */
58
+ onBound?: (host: string, port: number) => void;
59
+ }
60
+ export interface GrpcMethodMeta {
61
+ /** Service name (matches `@GrpcService(name)`). */
62
+ serviceName: string;
63
+ /** Method name as it appears in the .proto file. */
64
+ methodName: string;
65
+ }
66
+ /**
67
+ * Build a typed client for a gRPC service. The returned object
68
+ * has one method per service method defined in the .proto file,
69
+ * with the right TypeScript signature.
70
+ */
71
+ export type GrpcClient<T> = T;
72
+ export interface GrpcClientOptions {
73
+ /** Service URL (e.g. "localhost:50051"). */
74
+ url: string;
75
+ /** Use TLS. Default: false (insecure). */
76
+ tls?: boolean;
77
+ }
78
+ export type { ServiceDefinition, UntypedServiceImplementation };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexusts/grpc",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "gRPC server + typed client (reflection-based)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -12,20 +12,15 @@
12
12
  "import": "./dist/index.js"
13
13
  }
14
14
  },
15
- "files": [
16
- "dist",
17
- "README.md"
18
- ],
15
+ "files": ["dist", "README.md"],
19
16
  "scripts": {
20
17
  "build": "bun run ../../build.ts"
21
18
  },
22
- "keywords": [
23
- "nexusts",
24
- "framework",
25
- "bun"
26
- ],
19
+ "keywords": ["nexusts", "framework", "bun"],
27
20
  "license": "MIT",
21
+
22
+
28
23
  "dependencies": {
29
- "@nexusts/core": "^0.7.0"
24
+ "@nexusts/core": "file:../core"
30
25
  }
31
26
  }