@nexusts/grpc 0.8.1 → 0.8.2

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.
@@ -1,33 +1,80 @@
1
1
  /**
2
2
  * gRPC decorators.
3
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
- * }
4
+ * Unary (request → response):
5
+ * @GrpcMethod("FindById")
6
+ * async findById(req: { id: number }): Promise<TRes> { ... }
7
+ *
8
+ * Server streaming (request stream<response>):
9
+ * @GrpcServerStream("ListItems")
10
+ * async *listItems(req: { page: number }): AsyncIterable<TItem> { yield ...; }
11
+ *
12
+ * Client streaming (stream<request> → response):
13
+ * @GrpcClientStream("UploadChunks")
14
+ * async uploadChunks(req: AsyncIterable<TChunk>): Promise<TResult> { ... }
12
15
  *
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`).
16
+ * Bidirectional streaming (stream<request> stream<response>):
17
+ * @GrpcBidiStream("Chat")
18
+ * async *chat(req: AsyncIterable<TMsg>): AsyncIterable<TMsg> { yield ...; }
17
19
  */
20
+ import type { GrpcMethodEntry } from "./types.js";
18
21
  /**
19
22
  * Mark a class as a gRPC service implementation. The `name`
20
23
  * must match a `service` declaration in the .proto file.
21
24
  */
22
25
  export declare function GrpcService(name: string): ClassDecorator;
23
26
  /**
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).
27
+ * Bind a method to a unary gRPC handler.
28
+ * The method receives `(request: TReq)` and returns `Promise<TRes>`.
28
29
  */
29
30
  export declare function GrpcMethod(name: string): MethodDecorator;
31
+ /**
32
+ * Bind a method to a server-streaming gRPC handler.
33
+ * The method receives `(request: TReq)` and returns `AsyncIterable<TRes>`.
34
+ *
35
+ * @example
36
+ * @GrpcServerStream("ListNumbers")
37
+ * async *listNumbers(req: { count: number }): AsyncIterable<{ n: number }> {
38
+ * for (let i = 0; i < req.count; i++) yield { n: i };
39
+ * }
40
+ */
41
+ export declare function GrpcServerStream(name: string): MethodDecorator;
42
+ /**
43
+ * Bind a method to a client-streaming gRPC handler.
44
+ * The method receives `(requests: AsyncIterable<TReq>)` and returns `Promise<TRes>`.
45
+ *
46
+ * @example
47
+ * @GrpcClientStream("Sum")
48
+ * async sum(reqs: AsyncIterable<{ n: number }>): Promise<{ total: number }> {
49
+ * let total = 0;
50
+ * for await (const { n } of reqs) total += n;
51
+ * return { total };
52
+ * }
53
+ */
54
+ export declare function GrpcClientStream(name: string): MethodDecorator;
55
+ /**
56
+ * Bind a method to a bidirectional-streaming gRPC handler.
57
+ * The method receives `(requests: AsyncIterable<TReq>)` and returns `AsyncIterable<TRes>`.
58
+ *
59
+ * @example
60
+ * @GrpcBidiStream("Echo")
61
+ * async *echo(reqs: AsyncIterable<{ msg: string }>): AsyncIterable<{ msg: string }> {
62
+ * for await (const { msg } of reqs) yield { msg: `echo: ${msg}` };
63
+ * }
64
+ */
65
+ export declare function GrpcBidiStream(name: string): MethodDecorator;
30
66
  /** Read the gRPC service name. Internal. */
31
67
  export declare function getGrpcServiceName(target: object): string | undefined;
32
- /** Read the bound method names for a gRPC service. Internal. */
68
+ /**
69
+ * Read all decorated method entries for a gRPC service.
70
+ * Returns `{ [propertyKey]: GrpcMethodEntry }`.
71
+ * Internal — used by `GrpcService.prepare()`.
72
+ */
73
+ export declare function getGrpcMethodEntries(target: object): Record<string, GrpcMethodEntry>;
74
+ /**
75
+ * Read the bound method names for a gRPC service.
76
+ * Returns `{ [propertyKey]: protoMethodName }`.
77
+ * Kept for backwards compatibility.
78
+ * @deprecated Use `getGrpcMethodEntries` for streaming type info.
79
+ */
33
80
  export declare function getGrpcMethodNames(target: object): Record<string, string>;
package/dist/index.d.ts CHANGED
@@ -60,10 +60,10 @@
60
60
  * const userClient = grpc.client<{ findById(req: { id: number }): Promise<{ name: string; email: string }> }>("UserService");
61
61
  * const user = await userClient.findById({ id: 1 });
62
62
  *
63
- * Unary methods only for v1. Streaming (server / client / bidi)
64
- * is a future addition.
63
+ * v2: All four gRPC call types are supported unary, server streaming,
64
+ * client streaming, and bidirectional streaming.
65
65
  */
66
66
  export { GrpcService, GRPC_SERVICE_TOKEN } from "./service.js";
67
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";
68
+ export { GrpcService as GrpcServiceDecorator, GrpcMethod, GrpcServerStream, GrpcClientStream, GrpcBidiStream, getGrpcServiceName, getGrpcMethodNames, getGrpcMethodEntries, } from "./decorators.js";
69
+ export type { GrpcConfig, GrpcMethodMeta, GrpcMethodEntry, GrpcStreamType, GrpcClient, GrpcClientOptions, } from "./types.js";
package/dist/index.js CHANGED
@@ -29,22 +29,42 @@ function GrpcService(name) {
29
29
  proto[GRPC_SERVICE_KEY] = { name };
30
30
  };
31
31
  }
32
- function GrpcMethod(name) {
32
+ function makeMethodDecorator(protoName, streamType) {
33
33
  return function(_target, propertyKey, _descriptor) {
34
34
  const proto = _target;
35
35
  proto[GRPC_METHOD_KEY] = proto[GRPC_METHOD_KEY] ?? {};
36
- proto[GRPC_METHOD_KEY][propertyKey] = name;
36
+ proto[GRPC_METHOD_KEY][propertyKey] = { protoName, streamType };
37
37
  return _descriptor;
38
38
  };
39
39
  }
40
+ function GrpcMethod(name) {
41
+ return makeMethodDecorator(name, "unary");
42
+ }
43
+ function GrpcServerStream(name) {
44
+ return makeMethodDecorator(name, "server");
45
+ }
46
+ function GrpcClientStream(name) {
47
+ return makeMethodDecorator(name, "client");
48
+ }
49
+ function GrpcBidiStream(name) {
50
+ return makeMethodDecorator(name, "bidi");
51
+ }
40
52
  function getGrpcServiceName(target) {
41
53
  const t = target.prototype ?? target;
42
54
  return t[GRPC_SERVICE_KEY]?.name;
43
55
  }
44
- function getGrpcMethodNames(target) {
56
+ function getGrpcMethodEntries(target) {
45
57
  const t = target.prototype ?? target;
46
58
  return t[GRPC_METHOD_KEY] ?? {};
47
59
  }
60
+ function getGrpcMethodNames(target) {
61
+ const entries = getGrpcMethodEntries(target);
62
+ const result = {};
63
+ for (const [key, entry] of Object.entries(entries)) {
64
+ result[key] = entry.protoName;
65
+ }
66
+ return result;
67
+ }
48
68
 
49
69
  // packages/grpc/src/service.ts
50
70
  var GRPC_SERVICE_TOKEN = Symbol.for("nexus:GrpcService");
@@ -109,7 +129,7 @@ class GrpcService2 {
109
129
  if (!svcName) {
110
130
  throw new Error(`[grpc] service ${ServiceImpl.name} is missing @GrpcService(name)`);
111
131
  }
112
- const methodNames = getGrpcMethodNames(ServiceImpl.prototype);
132
+ const methodEntries = getGrpcMethodEntries(ServiceImpl.prototype);
113
133
  const segments = this.#config.package ? this.#config.package.split(".") : [];
114
134
  segments.push(svcName);
115
135
  let svcSpec = this.#proto;
@@ -127,11 +147,11 @@ class GrpcService2 {
127
147
  const instance = resolve(ServiceImpl);
128
148
  this.#instanceByService.set(svcName, instance);
129
149
  const handlers = {};
130
- for (const [methodKey, protoMethodName] of Object.entries(methodNames)) {
150
+ for (const [methodKey, entry] of Object.entries(methodEntries)) {
131
151
  const fn = instance[methodKey];
132
152
  if (typeof fn !== "function")
133
153
  continue;
134
- handlers[protoMethodName] = makeUnaryHandler(fn, instance);
154
+ handlers[entry.protoName] = makeHandler(fn, instance, entry.streamType);
135
155
  }
136
156
  this.#server.addService(svcSpec.service, handlers);
137
157
  this.#clientCtors.set(svcName, svcSpec);
@@ -197,26 +217,31 @@ class GrpcService2 {
197
217
  const ClientCtor = svcSpec;
198
218
  const creds = options.tls ? __require("@grpc/grpc-js").credentials.createSsl() : __require("@grpc/grpc-js").credentials.createInsecure();
199
219
  const underlying = new ClientCtor(options.url, creds);
200
- const protoMethodNames = Object.keys(ClientCtor.service);
201
- const methodNames = protoMethodNames.map((n) => n.charAt(0).toLowerCase() + n.slice(1));
202
220
  const wrapped = {};
203
- for (const methodName of methodNames) {
221
+ for (const [protoName, methodDef] of Object.entries(ClientCtor.service)) {
222
+ const { requestStream, responseStream } = methodDef;
223
+ const methodName = protoName.charAt(0).toLowerCase() + protoName.slice(1);
204
224
  const fn = underlying[methodName];
205
225
  if (typeof fn !== "function")
206
226
  continue;
207
- wrapped[methodName] = (req) => new Promise((resolveP, rejectP) => {
208
- fn.call(underlying, req, (err, res) => {
209
- if (err) {
210
- rejectP(err);
211
- } else {
212
- resolveP(res);
213
- }
214
- });
215
- });
227
+ const streamType = classifyStreamType(requestStream, responseStream);
228
+ wrapped[methodName] = makeClientMethod(fn, underlying, streamType);
216
229
  }
217
230
  return wrapped;
218
231
  }
219
232
  }
233
+ function makeHandler(fn, instance, streamType) {
234
+ switch (streamType) {
235
+ case "unary":
236
+ return makeUnaryHandler(fn, instance);
237
+ case "server":
238
+ return makeServerStreamHandler(fn, instance);
239
+ case "client":
240
+ return makeClientStreamHandler(fn, instance);
241
+ case "bidi":
242
+ return makeBidiStreamHandler(fn, instance);
243
+ }
244
+ }
220
245
  function makeUnaryHandler(fn, instance) {
221
246
  return (call, callback) => {
222
247
  const req = call.request;
@@ -225,16 +250,227 @@ function makeUnaryHandler(fn, instance) {
225
250
  result = fn.call(instance, req);
226
251
  } catch (syncErr) {
227
252
  const e = syncErr;
228
- const code = e.code ?? 13;
229
- callback({ code, details: e.message });
253
+ callback({ code: e.code ?? 13, details: e.message });
230
254
  return;
231
255
  }
232
256
  Promise.resolve(result).then((value) => callback(null, value), (err) => {
233
- const code = err.code ?? 13;
234
- callback({ code, details: err.message });
257
+ callback({ code: err.code ?? 13, details: err.message });
235
258
  });
236
259
  };
237
260
  }
261
+ function makeServerStreamHandler(fn, instance) {
262
+ return (call) => {
263
+ const req = call.request;
264
+ let iter;
265
+ try {
266
+ iter = fn.call(instance, req);
267
+ } catch (syncErr) {
268
+ const e = syncErr;
269
+ call.emit("error", { code: e.code ?? 13, details: e.message });
270
+ return;
271
+ }
272
+ (async () => {
273
+ for await (const item of iter) {
274
+ call.write(item);
275
+ }
276
+ call.end();
277
+ })().catch((err) => {
278
+ call.emit("error", { code: err?.code ?? 13, details: err?.message ?? "Internal error" });
279
+ });
280
+ };
281
+ }
282
+ function makeClientStreamHandler(fn, instance) {
283
+ return (call, callback) => {
284
+ async function* iterRequest() {
285
+ for await (const msg of call) {
286
+ yield msg;
287
+ }
288
+ }
289
+ Promise.resolve(fn.call(instance, iterRequest())).then((result) => callback(null, result), (err) => {
290
+ callback({ code: err?.code ?? 13, details: err?.message ?? "Internal error" });
291
+ });
292
+ };
293
+ }
294
+ function makeBidiStreamHandler(fn, instance) {
295
+ return (call) => {
296
+ const queue = [];
297
+ let readEnded = false;
298
+ let readError = null;
299
+ let readWakeup = null;
300
+ const notifyRead = () => {
301
+ readWakeup?.();
302
+ readWakeup = null;
303
+ };
304
+ call.on("data", (msg) => {
305
+ queue.push(msg);
306
+ notifyRead();
307
+ });
308
+ call.on("end", () => {
309
+ readEnded = true;
310
+ notifyRead();
311
+ });
312
+ call.on("error", (err) => {
313
+ readError = err;
314
+ notifyRead();
315
+ });
316
+ async function* iterRequest() {
317
+ while (true) {
318
+ while (queue.length > 0)
319
+ yield queue.shift();
320
+ if (readEnded)
321
+ break;
322
+ if (readError)
323
+ throw readError;
324
+ await new Promise((r) => {
325
+ readWakeup = r;
326
+ });
327
+ }
328
+ }
329
+ let iter;
330
+ try {
331
+ iter = fn.call(instance, iterRequest());
332
+ } catch (syncErr) {
333
+ const e = syncErr;
334
+ call.emit("error", { code: e.code ?? 13, details: e.message });
335
+ return;
336
+ }
337
+ (async () => {
338
+ for await (const item of iter) {
339
+ call.write(item);
340
+ }
341
+ call.end();
342
+ })().catch((err) => {
343
+ call.emit("error", { code: err?.code ?? 13, details: err?.message ?? "Internal error" });
344
+ });
345
+ };
346
+ }
347
+ function classifyStreamType(requestStream, responseStream) {
348
+ if (!requestStream && !responseStream)
349
+ return "unary";
350
+ if (!requestStream && responseStream)
351
+ return "server";
352
+ if (requestStream && !responseStream)
353
+ return "client";
354
+ return "bidi";
355
+ }
356
+ function makeClientMethod(fn, underlying, streamType) {
357
+ switch (streamType) {
358
+ case "unary":
359
+ return (req) => new Promise((resolveP, rejectP) => {
360
+ fn.call(underlying, req, (err, res) => {
361
+ if (err)
362
+ rejectP(err);
363
+ else
364
+ resolveP(res);
365
+ });
366
+ });
367
+ case "server":
368
+ return (req) => {
369
+ const call = fn.call(underlying, req);
370
+ return readableToAsyncIter(call);
371
+ };
372
+ case "client":
373
+ return (source) => new Promise((resolveP, rejectP) => {
374
+ const call = fn.call(underlying, (err, res) => {
375
+ if (err)
376
+ rejectP(err);
377
+ else
378
+ resolveP(res);
379
+ });
380
+ (async () => {
381
+ for await (const msg of source) {
382
+ call.write(msg);
383
+ }
384
+ call.end();
385
+ })().catch(rejectP);
386
+ });
387
+ case "bidi":
388
+ return (source) => bidiClientImpl(fn, underlying, source);
389
+ }
390
+ }
391
+ async function* bidiClientImpl(fn, underlying, source) {
392
+ const call = fn.call(underlying);
393
+ const queue = [];
394
+ let ended = false;
395
+ let streamError = null;
396
+ let wakeup = null;
397
+ const notify = () => {
398
+ wakeup?.();
399
+ wakeup = null;
400
+ };
401
+ call.on("data", (msg) => {
402
+ queue.push(msg);
403
+ notify();
404
+ });
405
+ call.on("end", () => {
406
+ ended = true;
407
+ notify();
408
+ });
409
+ call.on("error", (err) => {
410
+ streamError = err;
411
+ notify();
412
+ });
413
+ const writeTask = (async () => {
414
+ try {
415
+ for await (const msg of source) {
416
+ call.write(msg);
417
+ }
418
+ } finally {
419
+ call.end();
420
+ }
421
+ })();
422
+ while (true) {
423
+ while (queue.length > 0)
424
+ yield queue.shift();
425
+ if (ended)
426
+ break;
427
+ if (streamError)
428
+ throw streamError;
429
+ await new Promise((r) => {
430
+ wakeup = r;
431
+ });
432
+ }
433
+ await writeTask;
434
+ }
435
+ async function* readableToAsyncIter(stream) {
436
+ if (typeof stream[Symbol.asyncIterator] === "function") {
437
+ for await (const msg of stream) {
438
+ yield msg;
439
+ }
440
+ return;
441
+ }
442
+ let done = false;
443
+ let error = null;
444
+ const buffer = [];
445
+ let resolve = null;
446
+ stream.on("data", (msg) => {
447
+ buffer.push(msg);
448
+ resolve?.();
449
+ resolve = null;
450
+ });
451
+ stream.on("end", () => {
452
+ done = true;
453
+ resolve?.();
454
+ resolve = null;
455
+ });
456
+ stream.on("error", (err) => {
457
+ error = err;
458
+ resolve?.();
459
+ resolve = null;
460
+ });
461
+ while (true) {
462
+ while (buffer.length > 0) {
463
+ yield buffer.shift();
464
+ }
465
+ if (done)
466
+ break;
467
+ if (error)
468
+ throw error;
469
+ await new Promise((r) => {
470
+ resolve = r;
471
+ });
472
+ }
473
+ }
238
474
  // packages/grpc/src/module.ts
239
475
  import { Module } from "@nexusts/core";
240
476
  class GrpcModule {
@@ -274,12 +510,16 @@ GrpcModule = __legacyDecorateClassTS([
274
510
  export {
275
511
  getGrpcServiceName,
276
512
  getGrpcMethodNames,
513
+ getGrpcMethodEntries,
277
514
  GrpcService as GrpcServiceDecorator,
278
515
  GrpcService2 as GrpcService,
516
+ GrpcServerStream,
279
517
  GrpcModule,
280
518
  GrpcMethod,
519
+ GrpcClientStream,
520
+ GrpcBidiStream,
281
521
  GRPC_SERVICE_TOKEN
282
522
  };
283
523
 
284
- //# debugId=FD4EC7DBDC88C74C64756E2164756E21
524
+ //# debugId=DD2F9280C753352864756E2164756E21
285
525
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -2,11 +2,11 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/service.ts", "../src/decorators.ts", "../src/module.ts"],
4
4
  "sourcesContent": [
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
- "/**\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",
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 (unary) or AsyncIterable (streaming).\n */\n\nimport { existsSync } from \"node:fs\";\nimport {\n\tloadPackageDefinition,\n\tServer as GrpcServer,\n\tServerCredentials,\n} from \"@grpc/grpc-js\";\nimport * as protoLoader from \"@grpc/proto-loader\";\nimport {\n\tgetGrpcMethodEntries,\n\tgetGrpcServiceName,\n} from \"./decorators.js\";\nimport type { GrpcConfig, GrpcStreamType } from \"./types.js\";\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\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 methodEntries = getGrpcMethodEntries(ServiceImpl.prototype);\n\n\t\t\t// Walk 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:\n\t\t\t\t| { service: any; prototype?: unknown }\n\t\t\t\t| Record<string, unknown>\n\t\t\t\t| undefined = this.#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 every decorated method\n\t\t\tconst handlers: Record<string, any> = {};\n\t\t\tfor (const [methodKey, entry] of Object.entries(methodEntries)) {\n\t\t\t\tconst fn = (\n\t\t\t\t\tinstance as Record<string, (...args: unknown[]) => unknown>\n\t\t\t\t)[methodKey];\n\t\t\t\tif (typeof fn !== \"function\") continue;\n\n\t\t\t\thandlers[entry.protoName] = makeHandler(fn, instance, entry.streamType);\n\t\t\t}\n\n\t\t\tthis.#server!.addService((svcSpec as { service: any }).service, handlers);\n\t\t\tthis.#clientCtors.set(\n\t\t\t\tsvcName,\n\t\t\t\tsvcSpec as unknown as new (...a: any[]) => any,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Bind the server to the configured host/port.\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)\n\t\t\t\t\t\t? this.#config.tls.key[0]\n\t\t\t\t\t\t: (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(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\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\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.\n\t *\n\t * Method wrappers by call type:\n\t * - **Unary** — `(req: TReq) => Promise<TRes>`\n\t * - **Server stream** — `(req: TReq) => AsyncIterable<TRes>`\n\t * - **Client stream** — `(src: AsyncIterable<TReq>) => Promise<TRes>`\n\t * - **Bidi stream** — `(src: AsyncIterable<TReq>) => AsyncIterable<TRes>`\n\t */\n\tclient<T = Record<string, (...args: unknown[]) => 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\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:\n\t\t\t| { service: any; prototype?: unknown }\n\t\t\t| Record<string, unknown>\n\t\t\t| undefined = this.#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\n\t\tconst ClientCtor = svcSpec as unknown as {\n\t\t\tnew (url: string, creds: unknown): Record<\n\t\t\t\tstring,\n\t\t\t\t(...args: unknown[]) => unknown\n\t\t\t>;\n\t\t\tservice: Record<\n\t\t\t\tstring,\n\t\t\t\t{ requestStream: boolean; responseStream: boolean }\n\t\t\t>;\n\t\t};\n\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\n\t\tconst underlying = new ClientCtor(options.url, creds);\n\t\tconst wrapped: Record<string, (...args: unknown[]) => unknown> = {};\n\n\t\tfor (const [protoName, methodDef] of Object.entries(ClientCtor.service)) {\n\t\t\tconst { requestStream, responseStream } = methodDef;\n\t\t\t// gRPC proto names are PascalCase; JS clients expose camelCase\n\t\t\tconst methodName =\n\t\t\t\tprotoName.charAt(0).toLowerCase() + protoName.slice(1);\n\n\t\t\t// @ts-ignore — method exists on prototype at runtime\n\t\t\tconst fn = underlying[methodName];\n\t\t\tif (typeof fn !== \"function\") continue;\n\n\t\t\tconst streamType = classifyStreamType(requestStream, responseStream);\n\t\t\twrapped[methodName] = makeClientMethod(fn, underlying, streamType);\n\t\t}\n\n\t\treturn wrapped as unknown as T;\n\t}\n}\n\n// ── Server-side handler factories ─────────────────────────────────────────────\n\nfunction makeHandler(\n\tfn: (...args: unknown[]) => unknown,\n\tinstance: unknown,\n\tstreamType: GrpcStreamType,\n): (...args: unknown[]) => void {\n\tswitch (streamType) {\n\t\tcase \"unary\":\n\t\t\treturn makeUnaryHandler(fn, instance);\n\t\tcase \"server\":\n\t\t\treturn makeServerStreamHandler(fn, instance);\n\t\tcase \"client\":\n\t\t\treturn makeClientStreamHandler(fn, instance);\n\t\tcase \"bidi\":\n\t\t\treturn makeBidiStreamHandler(fn, instance);\n\t}\n}\n\n/** Unary: (call, callback) => void */\nfunction makeUnaryHandler(\n\tfn: (...args: unknown[]) => unknown,\n\tinstance: unknown,\n) {\n\treturn (call: unknown, callback: (err: unknown, res?: unknown) => void) => {\n\t\tconst req = (call as { request: unknown }).request;\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 & { code?: number };\n\t\t\tcallback({ code: e.code ?? 13, 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 & { code?: number }) => {\n\t\t\t\tcallback({ code: err.code ?? 13, details: err.message });\n\t\t\t},\n\t\t);\n\t};\n}\n\n/** Server streaming: (call) => void — method returns AsyncIterable<TRes> */\nfunction makeServerStreamHandler(\n\tfn: (...args: unknown[]) => unknown,\n\tinstance: unknown,\n) {\n\treturn (call: any) => {\n\t\tconst req = call.request;\n\t\tlet iter: AsyncIterable<unknown>;\n\t\ttry {\n\t\t\titer = fn.call(instance, req) as AsyncIterable<unknown>;\n\t\t} catch (syncErr) {\n\t\t\tconst e = syncErr as Error & { code?: number };\n\t\t\tcall.emit(\"error\", { code: e.code ?? 13, details: e.message });\n\t\t\treturn;\n\t\t}\n\t\t(async () => {\n\t\t\tfor await (const item of iter) {\n\t\t\t\tcall.write(item);\n\t\t\t}\n\t\t\tcall.end();\n\t\t})().catch((err: Error & { code?: number }) => {\n\t\t\tcall.emit(\"error\", { code: err?.code ?? 13, details: err?.message ?? \"Internal error\" });\n\t\t});\n\t};\n}\n\n/** Client streaming: (call, callback) => void — method takes AsyncIterable<TReq> */\nfunction makeClientStreamHandler(\n\tfn: (...args: unknown[]) => unknown,\n\tinstance: unknown,\n) {\n\treturn (call: any, callback: (err: unknown, res?: unknown) => void) => {\n\t\tasync function* iterRequest() {\n\t\t\tfor await (const msg of call) {\n\t\t\t\tyield msg;\n\t\t\t}\n\t\t}\n\t\tPromise.resolve(fn.call(instance, iterRequest())).then(\n\t\t\t(result) => callback(null, result),\n\t\t\t(err: Error & { code?: number }) => {\n\t\t\t\tcallback({ code: err?.code ?? 13, details: err?.message ?? \"Internal error\" });\n\t\t\t},\n\t\t);\n\t};\n}\n\n/** Bidirectional streaming: (call) => void — method takes AsyncIterable<TReq>, returns AsyncIterable<TRes> */\nfunction makeBidiStreamHandler(\n\tfn: (...args: unknown[]) => unknown,\n\tinstance: unknown,\n) {\n\treturn (call: any) => {\n\t\t// Use event-based reading — Symbol.asyncIterator on ServerDuplexStream\n\t\t// conflicts with concurrent call.write() on the same object.\n\t\tconst queue: unknown[] = [];\n\t\tlet readEnded = false;\n\t\tlet readError: Error | null = null;\n\t\tlet readWakeup: (() => void) | null = null;\n\t\tconst notifyRead = () => { readWakeup?.(); readWakeup = null; };\n\n\t\tcall.on(\"data\", (msg: unknown) => { queue.push(msg); notifyRead(); });\n\t\tcall.on(\"end\", () => { readEnded = true; notifyRead(); });\n\t\tcall.on(\"error\", (err: Error) => { readError = err; notifyRead(); });\n\n\t\tasync function* iterRequest() {\n\t\t\twhile (true) {\n\t\t\t\twhile (queue.length > 0) yield queue.shift()!;\n\t\t\t\tif (readEnded) break;\n\t\t\t\tif (readError) throw readError;\n\t\t\t\tawait new Promise<void>((r) => { readWakeup = r; });\n\t\t\t}\n\t\t}\n\n\t\tlet iter: AsyncIterable<unknown>;\n\t\ttry {\n\t\t\titer = fn.call(instance, iterRequest()) as AsyncIterable<unknown>;\n\t\t} catch (syncErr) {\n\t\t\tconst e = syncErr as Error & { code?: number };\n\t\t\tcall.emit(\"error\", { code: e.code ?? 13, details: e.message });\n\t\t\treturn;\n\t\t}\n\t\t(async () => {\n\t\t\tfor await (const item of iter) {\n\t\t\t\tcall.write(item);\n\t\t\t}\n\t\t\tcall.end();\n\t\t})().catch((err: Error & { code?: number }) => {\n\t\t\tcall.emit(\"error\", { code: err?.code ?? 13, details: err?.message ?? \"Internal error\" });\n\t\t});\n\t};\n}\n\n// ── Client-side method wrappers ───────────────────────────────────────────────\n\nfunction classifyStreamType(\n\trequestStream: boolean,\n\tresponseStream: boolean,\n): GrpcStreamType {\n\tif (!requestStream && !responseStream) return \"unary\";\n\tif (!requestStream && responseStream) return \"server\";\n\tif (requestStream && !responseStream) return \"client\";\n\treturn \"bidi\";\n}\n\nfunction makeClientMethod(\n\tfn: (...args: unknown[]) => unknown,\n\tunderlying: Record<string, (...args: unknown[]) => unknown>,\n\tstreamType: GrpcStreamType,\n): (...args: unknown[]) => unknown {\n\tswitch (streamType) {\n\t\tcase \"unary\":\n\t\t\treturn (req: unknown) =>\n\t\t\t\tnew Promise((resolveP, rejectP) => {\n\t\t\t\t\t(fn as any).call(underlying, req, (err: Error | null, res: unknown) => {\n\t\t\t\t\t\tif (err) rejectP(err);\n\t\t\t\t\t\telse resolveP(res);\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\tcase \"server\":\n\t\t\treturn (req: unknown): AsyncIterable<unknown> => {\n\t\t\t\tconst call = (fn as any).call(underlying, req);\n\t\t\t\treturn readableToAsyncIter(call);\n\t\t\t};\n\n\t\tcase \"client\":\n\t\t\treturn (source: unknown): Promise<unknown> =>\n\t\t\t\tnew Promise((resolveP, rejectP) => {\n\t\t\t\t\tconst call = (fn as any).call(\n\t\t\t\t\t\tunderlying,\n\t\t\t\t\t\t(err: Error | null, res: unknown) => {\n\t\t\t\t\t\t\tif (err) rejectP(err);\n\t\t\t\t\t\t\telse resolveP(res);\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\t(async () => {\n\t\t\t\t\t\tfor await (const msg of source as AsyncIterable<unknown>) {\n\t\t\t\t\t\t\tcall.write(msg);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcall.end();\n\t\t\t\t\t})().catch(rejectP);\n\t\t\t\t});\n\n\t\tcase \"bidi\":\n\t\t\treturn (source: unknown): AsyncIterable<unknown> =>\n\t\t\t\tbidiClientImpl(fn as any, underlying, source as AsyncIterable<unknown>);\n\t}\n}\n\nasync function* bidiClientImpl(\n\tfn: (...args: any[]) => any,\n\tunderlying: Record<string, (...args: unknown[]) => unknown>,\n\tsource: AsyncIterable<unknown>,\n): AsyncIterable<unknown> {\n\tconst call = fn.call(underlying);\n\n\t// Collect responses via event listeners into a queue so that\n\t// the write-side can run concurrently without stream backpressure\n\t// issues that arise from using Symbol.asyncIterator on a Duplex.\n\tconst queue: unknown[] = [];\n\tlet ended = false;\n\tlet streamError: Error | null = null;\n\tlet wakeup: (() => void) | null = null;\n\n\tconst notify = () => { wakeup?.(); wakeup = null; };\n\tcall.on(\"data\", (msg: unknown) => { queue.push(msg); notify(); });\n\tcall.on(\"end\", () => { ended = true; notify(); });\n\tcall.on(\"error\", (err: Error) => { streamError = err; notify(); });\n\n\t// Write from source in background — runs concurrently with the read loop\n\tconst writeTask = (async () => {\n\t\ttry {\n\t\t\tfor await (const msg of source) {\n\t\t\t\tcall.write(msg);\n\t\t\t}\n\t\t} finally {\n\t\t\tcall.end();\n\t\t}\n\t})();\n\n\t// Yield messages as they arrive\n\twhile (true) {\n\t\twhile (queue.length > 0) yield queue.shift()!;\n\t\tif (ended) break;\n\t\tif (streamError) throw streamError;\n\t\tawait new Promise<void>((r) => { wakeup = r; });\n\t}\n\tawait writeTask;\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────────────\n\n/**\n * Convert a gRPC ClientReadableStream (or DuplexStream) to an AsyncIterable.\n * grpc-js streams extend Node.js Readable, which supports `for await...of`\n * via Symbol.asyncIterator.\n */\nasync function* readableToAsyncIter(stream: any): AsyncIterable<unknown> {\n\t// Use Symbol.asyncIterator if available (Node.js 10+ / Bun)\n\tif (typeof stream[Symbol.asyncIterator] === \"function\") {\n\t\tfor await (const msg of stream) {\n\t\t\tyield msg;\n\t\t}\n\t\treturn;\n\t}\n\t// Fallback: event-based conversion\n\tlet done = false;\n\tlet error: Error | null = null;\n\tconst buffer: unknown[] = [];\n\tlet resolve: (() => void) | null = null;\n\n\tstream.on(\"data\", (msg: unknown) => {\n\t\tbuffer.push(msg);\n\t\tresolve?.();\n\t\tresolve = null;\n\t});\n\tstream.on(\"end\", () => {\n\t\tdone = true;\n\t\tresolve?.();\n\t\tresolve = null;\n\t});\n\tstream.on(\"error\", (err: Error) => {\n\t\terror = err;\n\t\tresolve?.();\n\t\tresolve = null;\n\t});\n\n\twhile (true) {\n\t\twhile (buffer.length > 0) {\n\t\t\tyield buffer.shift()!;\n\t\t}\n\t\tif (done) break;\n\t\tif (error) throw error;\n\t\tawait new Promise<void>((r) => {\n\t\t\tresolve = r;\n\t\t});\n\t}\n}\n\nexport default GrpcService;\n",
6
+ "/**\n * gRPC decorators.\n *\n * Unary (request → response):\n * @GrpcMethod(\"FindById\")\n * async findById(req: { id: number }): Promise<TRes> { ... }\n *\n * Server streaming (request → stream<response>):\n * @GrpcServerStream(\"ListItems\")\n * async *listItems(req: { page: number }): AsyncIterable<TItem> { yield ...; }\n *\n * Client streaming (stream<request> response):\n * @GrpcClientStream(\"UploadChunks\")\n * async uploadChunks(req: AsyncIterable<TChunk>): Promise<TResult> { ... }\n *\n * Bidirectional streaming (stream<request> stream<response>):\n * @GrpcBidiStream(\"Chat\")\n * async *chat(req: AsyncIterable<TMsg>): AsyncIterable<TMsg> { yield ...; }\n */\n\nimport type { GrpcMethodEntry, GrpcStreamType } from \"./types.js\";\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// ── Shared factory ──────────────────────────────────────────────────────────\n\nfunction makeMethodDecorator(\n\tprotoName: string,\n\tstreamType: GrpcStreamType,\n): 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, GrpcMethodEntry>)[\n\t\t\tpropertyKey\n\t\t] = { protoName, streamType };\n\t\treturn _descriptor;\n\t};\n}\n\n// ── Public decorators ───────────────────────────────────────────────────────\n\n/**\n * Bind a method to a unary gRPC handler.\n * The method receives `(request: TReq)` and returns `Promise<TRes>`.\n */\nexport function GrpcMethod(name: string): MethodDecorator {\n\treturn makeMethodDecorator(name, \"unary\");\n}\n\n/**\n * Bind a method to a server-streaming gRPC handler.\n * The method receives `(request: TReq)` and returns `AsyncIterable<TRes>`.\n *\n * @example\n * @GrpcServerStream(\"ListNumbers\")\n * async *listNumbers(req: { count: number }): AsyncIterable<{ n: number }> {\n * for (let i = 0; i < req.count; i++) yield { n: i };\n * }\n */\nexport function GrpcServerStream(name: string): MethodDecorator {\n\treturn makeMethodDecorator(name, \"server\");\n}\n\n/**\n * Bind a method to a client-streaming gRPC handler.\n * The method receives `(requests: AsyncIterable<TReq>)` and returns `Promise<TRes>`.\n *\n * @example\n * @GrpcClientStream(\"Sum\")\n * async sum(reqs: AsyncIterable<{ n: number }>): Promise<{ total: number }> {\n * let total = 0;\n * for await (const { n } of reqs) total += n;\n * return { total };\n * }\n */\nexport function GrpcClientStream(name: string): MethodDecorator {\n\treturn makeMethodDecorator(name, \"client\");\n}\n\n/**\n * Bind a method to a bidirectional-streaming gRPC handler.\n * The method receives `(requests: AsyncIterable<TReq>)` and returns `AsyncIterable<TRes>`.\n *\n * @example\n * @GrpcBidiStream(\"Echo\")\n * async *echo(reqs: AsyncIterable<{ msg: string }>): AsyncIterable<{ msg: string }> {\n * for await (const { msg } of reqs) yield { msg: `echo: ${msg}` };\n * }\n */\nexport function GrpcBidiStream(name: string): MethodDecorator {\n\treturn makeMethodDecorator(name, \"bidi\");\n}\n\n// ── Metadata readers (internal) ─────────────────────────────────────────────\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/**\n * Read all decorated method entries for a gRPC service.\n * Returns `{ [propertyKey]: GrpcMethodEntry }`.\n * Internal — used by `GrpcService.prepare()`.\n */\nexport function getGrpcMethodEntries(\n\ttarget: object,\n): Record<string, GrpcMethodEntry> {\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, GrpcMethodEntry>\n\t\t\t| undefined) ?? {}\n\t);\n}\n\n/**\n * Read the bound method names for a gRPC service.\n * Returns `{ [propertyKey]: protoMethodName }`.\n * Kept for backwards compatibility.\n * @deprecated Use `getGrpcMethodEntries` for streaming type info.\n */\nexport function getGrpcMethodNames(target: object): Record<string, string> {\n\tconst entries = getGrpcMethodEntries(target);\n\tconst result: Record<string, string> = {};\n\tfor (const [key, entry] of Object.entries(entries)) {\n\t\tresult[key] = entry.protoName;\n\t}\n\treturn result;\n}\n",
7
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
- "mappings": "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",
10
- "debugId": "FD4EC7DBDC88C74C64756E2164756E21",
9
+ "mappings": ";;;;;;;;;;;;;;AAmBA;AACA;AAAA;AAAA,YAEC;AAAA;AAAA;AAGD;;;ACHA,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;AAMhE,SAAS,mBAAmB,CAC3B,WACA,YACkB;AAAA,EAClB,OAAO,QAAS,CACf,SACA,aACA,aACC;AAAA,IACD,MAAM,QAAQ;AAAA,IACd,MAAM,mBAAmB,MAAM,oBAAoB,CAAC;AAAA,IACnD,MAAM,iBACN,eACG,EAAE,WAAW,WAAW;AAAA,IAC5B,OAAO;AAAA;AAAA;AAUF,SAAS,UAAU,CAAC,MAA+B;AAAA,EACzD,OAAO,oBAAoB,MAAM,OAAO;AAAA;AAalC,SAAS,gBAAgB,CAAC,MAA+B;AAAA,EAC/D,OAAO,oBAAoB,MAAM,QAAQ;AAAA;AAenC,SAAS,gBAAgB,CAAC,MAA+B;AAAA,EAC/D,OAAO,oBAAoB,MAAM,QAAQ;AAAA;AAanC,SAAS,cAAc,CAAC,MAA+B;AAAA,EAC7D,OAAO,oBAAoB,MAAM,MAAM;AAAA;AAMjC,SAAS,kBAAkB,CAAC,QAAoC;AAAA,EACtE,MAAM,IAAK,OAAkC,aAAa;AAAA,EAC1D,OAAQ,EACP,mBACE;AAAA;AAQG,SAAS,oBAAoB,CACnC,QACkC;AAAA,EAClC,MAAM,IAAK,OAAkC,aAAa;AAAA,EAC1D,OACG,EAA8B,oBAEf,CAAC;AAAA;AAUb,SAAS,kBAAkB,CAAC,QAAwC;AAAA,EAC1E,MAAM,UAAU,qBAAqB,MAAM;AAAA,EAC3C,MAAM,SAAiC,CAAC;AAAA,EACxC,YAAY,KAAK,UAAU,OAAO,QAAQ,OAAO,GAAG;AAAA,IACnD,OAAO,OAAO,MAAM;AAAA,EACrB;AAAA,EACA,OAAO;AAAA;;;ADpHD,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,IAEA,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,gBAAgB,qBAAqB,YAAY,SAAS;AAAA,MAGhE,MAAM,WAAW,KAAK,QAAQ,UAC3B,KAAK,QAAQ,QAAQ,MAAM,GAAG,IAC9B,CAAC;AAAA,MACJ,SAAS,KAAK,OAAO;AAAA,MACrB,IAAI,UAGW,KAAK;AAAA,MACpB,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,MAG7C,MAAM,WAAgC,CAAC;AAAA,MACvC,YAAY,WAAW,UAAU,OAAO,QAAQ,aAAa,GAAG;AAAA,QAC/D,MAAM,KACL,SACC;AAAA,QACF,IAAI,OAAO,OAAO;AAAA,UAAY;AAAA,QAE9B,SAAS,MAAM,aAAa,YAAY,IAAI,UAAU,MAAM,UAAU;AAAA,MACvE;AAAA,MAEA,KAAK,QAAS,WAAY,QAA6B,SAAS,QAAQ;AAAA,MACxE,KAAK,aAAa,IACjB,SACA,OACD;AAAA,IACD;AAAA;AAAA,OAMK,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,IAC/B,KAAK,QAAQ,IAAI,IAAI,KACpB,KAAK,QAAQ,IAAI,GACtB,IACC,kBAAkB,eAAe;AAAA,IAEpC,MAAM,OAAe,MAAM,IAAI,QAAQ,CAAC,UAAU,YAAY;AAAA,MAC5D,KAAK,QAAuB,UAC5B,GAAG,KAAK,QAAQ,QAAQ,KAAK,QAAQ,QACrC,OACA,CAAC,KAAmB,MAAc;AAAA,QACjC,OAAO,MAAM,QAAQ,GAAG,IAAI,SAAS,CAAC;AAAA,OAExC;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,IACnB,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,EAYf,MAA2D,CAC1D,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,IACA,MAAM,WAAW,KAAK,QAAQ,UAC3B,KAAK,QAAQ,QAAQ,MAAM,GAAG,IAC9B,CAAC;AAAA,IACJ,SAAS,KAAK,WAAW;AAAA,IACzB,IAAI,UAGW,KAAK;AAAA,IACpB,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,IAWnB,MAAM,QAAQ,QAAQ,iCAEK,YAAY,UAAU,+BAEtB,YAAY,eAAe;AAAA,IAEtD,MAAM,aAAa,IAAI,WAAW,QAAQ,KAAK,KAAK;AAAA,IACpD,MAAM,UAA2D,CAAC;AAAA,IAElE,YAAY,WAAW,cAAc,OAAO,QAAQ,WAAW,OAAO,GAAG;AAAA,MACxE,QAAQ,eAAe,mBAAmB;AAAA,MAE1C,MAAM,aACL,UAAU,OAAO,CAAC,EAAE,YAAY,IAAI,UAAU,MAAM,CAAC;AAAA,MAGtD,MAAM,KAAK,WAAW;AAAA,MACtB,IAAI,OAAO,OAAO;AAAA,QAAY;AAAA,MAE9B,MAAM,aAAa,mBAAmB,eAAe,cAAc;AAAA,MACnE,QAAQ,cAAc,iBAAiB,IAAI,YAAY,UAAU;AAAA,IAClE;AAAA,IAEA,OAAO;AAAA;AAET;AAIA,SAAS,WAAW,CACnB,IACA,UACA,YAC+B;AAAA,EAC/B,QAAQ;AAAA,SACF;AAAA,MACJ,OAAO,iBAAiB,IAAI,QAAQ;AAAA,SAChC;AAAA,MACJ,OAAO,wBAAwB,IAAI,QAAQ;AAAA,SACvC;AAAA,MACJ,OAAO,wBAAwB,IAAI,QAAQ;AAAA,SACvC;AAAA,MACJ,OAAO,sBAAsB,IAAI,QAAQ;AAAA;AAAA;AAK5C,SAAS,gBAAgB,CACxB,IACA,UACC;AAAA,EACD,OAAO,CAAC,MAAe,aAAoD;AAAA,IAC1E,MAAM,MAAO,KAA8B;AAAA,IAC3C,IAAI;AAAA,IACJ,IAAI;AAAA,MACH,SAAS,GAAG,KAAK,UAAU,GAAG;AAAA,MAC7B,OAAO,SAAS;AAAA,MACjB,MAAM,IAAI;AAAA,MACV,SAAS,EAAE,MAAM,EAAE,QAAQ,IAAI,SAAS,EAAE,QAAQ,CAAC;AAAA,MACnD;AAAA;AAAA,IAED,QAAQ,QAAQ,MAAM,EAAE,KACvB,CAAC,UAAU,SAAS,MAAM,KAAK,GAC/B,CAAC,QAAmC;AAAA,MACnC,SAAS,EAAE,MAAM,IAAI,QAAQ,IAAI,SAAS,IAAI,QAAQ,CAAC;AAAA,KAEzD;AAAA;AAAA;AAKF,SAAS,uBAAuB,CAC/B,IACA,UACC;AAAA,EACD,OAAO,CAAC,SAAc;AAAA,IACrB,MAAM,MAAM,KAAK;AAAA,IACjB,IAAI;AAAA,IACJ,IAAI;AAAA,MACH,OAAO,GAAG,KAAK,UAAU,GAAG;AAAA,MAC3B,OAAO,SAAS;AAAA,MACjB,MAAM,IAAI;AAAA,MACV,KAAK,KAAK,SAAS,EAAE,MAAM,EAAE,QAAQ,IAAI,SAAS,EAAE,QAAQ,CAAC;AAAA,MAC7D;AAAA;AAAA,KAEA,YAAY;AAAA,MACZ,iBAAiB,QAAQ,MAAM;AAAA,QAC9B,KAAK,MAAM,IAAI;AAAA,MAChB;AAAA,MACA,KAAK,IAAI;AAAA,OACP,EAAE,MAAM,CAAC,QAAmC;AAAA,MAC9C,KAAK,KAAK,SAAS,EAAE,MAAM,KAAK,QAAQ,IAAI,SAAS,KAAK,WAAW,iBAAiB,CAAC;AAAA,KACvF;AAAA;AAAA;AAKH,SAAS,uBAAuB,CAC/B,IACA,UACC;AAAA,EACD,OAAO,CAAC,MAAW,aAAoD;AAAA,IACtE,gBAAgB,WAAW,GAAG;AAAA,MAC7B,iBAAiB,OAAO,MAAM;AAAA,QAC7B,MAAM;AAAA,MACP;AAAA;AAAA,IAED,QAAQ,QAAQ,GAAG,KAAK,UAAU,YAAY,CAAC,CAAC,EAAE,KACjD,CAAC,WAAW,SAAS,MAAM,MAAM,GACjC,CAAC,QAAmC;AAAA,MACnC,SAAS,EAAE,MAAM,KAAK,QAAQ,IAAI,SAAS,KAAK,WAAW,iBAAiB,CAAC;AAAA,KAE/E;AAAA;AAAA;AAKF,SAAS,qBAAqB,CAC7B,IACA,UACC;AAAA,EACD,OAAO,CAAC,SAAc;AAAA,IAGrB,MAAM,QAAmB,CAAC;AAAA,IAC1B,IAAI,YAAY;AAAA,IAChB,IAAI,YAA0B;AAAA,IAC9B,IAAI,aAAkC;AAAA,IACtC,MAAM,aAAa,MAAM;AAAA,MAAE,aAAa;AAAA,MAAG,aAAa;AAAA;AAAA,IAExD,KAAK,GAAG,QAAS,CAAC,QAAiB;AAAA,MAAE,MAAM,KAAK,GAAG;AAAA,MAAG,WAAW;AAAA,KAAI;AAAA,IACrE,KAAK,GAAG,OAAS,MAAM;AAAA,MAAE,YAAY;AAAA,MAAM,WAAW;AAAA,KAAI;AAAA,IAC1D,KAAK,GAAG,SAAS,CAAC,QAAe;AAAA,MAAE,YAAY;AAAA,MAAK,WAAW;AAAA,KAAI;AAAA,IAEnE,gBAAgB,WAAW,GAAG;AAAA,MAC7B,OAAO,MAAM;AAAA,QACZ,OAAO,MAAM,SAAS;AAAA,UAAG,MAAM,MAAM,MAAM;AAAA,QAC3C,IAAI;AAAA,UAAW;AAAA,QACf,IAAI;AAAA,UAAW,MAAM;AAAA,QACrB,MAAM,IAAI,QAAc,CAAC,MAAM;AAAA,UAAE,aAAa;AAAA,SAAI;AAAA,MACnD;AAAA;AAAA,IAGD,IAAI;AAAA,IACJ,IAAI;AAAA,MACH,OAAO,GAAG,KAAK,UAAU,YAAY,CAAC;AAAA,MACrC,OAAO,SAAS;AAAA,MACjB,MAAM,IAAI;AAAA,MACV,KAAK,KAAK,SAAS,EAAE,MAAM,EAAE,QAAQ,IAAI,SAAS,EAAE,QAAQ,CAAC;AAAA,MAC7D;AAAA;AAAA,KAEA,YAAY;AAAA,MACZ,iBAAiB,QAAQ,MAAM;AAAA,QAC9B,KAAK,MAAM,IAAI;AAAA,MAChB;AAAA,MACA,KAAK,IAAI;AAAA,OACP,EAAE,MAAM,CAAC,QAAmC;AAAA,MAC9C,KAAK,KAAK,SAAS,EAAE,MAAM,KAAK,QAAQ,IAAI,SAAS,KAAK,WAAW,iBAAiB,CAAC;AAAA,KACvF;AAAA;AAAA;AAMH,SAAS,kBAAkB,CAC1B,eACA,gBACiB;AAAA,EACjB,IAAI,CAAC,iBAAiB,CAAC;AAAA,IAAgB,OAAO;AAAA,EAC9C,IAAI,CAAC,iBAAiB;AAAA,IAAgB,OAAO;AAAA,EAC7C,IAAI,iBAAiB,CAAC;AAAA,IAAgB,OAAO;AAAA,EAC7C,OAAO;AAAA;AAGR,SAAS,gBAAgB,CACxB,IACA,YACA,YACkC;AAAA,EAClC,QAAQ;AAAA,SACF;AAAA,MACJ,OAAO,CAAC,QACP,IAAI,QAAQ,CAAC,UAAU,YAAY;AAAA,QACjC,GAAW,KAAK,YAAY,KAAK,CAAC,KAAmB,QAAiB;AAAA,UACtE,IAAI;AAAA,YAAK,QAAQ,GAAG;AAAA,UACf;AAAA,qBAAS,GAAG;AAAA,SACjB;AAAA,OACD;AAAA,SAEE;AAAA,MACJ,OAAO,CAAC,QAAyC;AAAA,QAChD,MAAM,OAAQ,GAAW,KAAK,YAAY,GAAG;AAAA,QAC7C,OAAO,oBAAoB,IAAI;AAAA;AAAA,SAG5B;AAAA,MACJ,OAAO,CAAC,WACP,IAAI,QAAQ,CAAC,UAAU,YAAY;AAAA,QAClC,MAAM,OAAQ,GAAW,KACxB,YACA,CAAC,KAAmB,QAAiB;AAAA,UACpC,IAAI;AAAA,YAAK,QAAQ,GAAG;AAAA,UACf;AAAA,qBAAS,GAAG;AAAA,SAEnB;AAAA,SACC,YAAY;AAAA,UACZ,iBAAiB,OAAO,QAAkC;AAAA,YACzD,KAAK,MAAM,GAAG;AAAA,UACf;AAAA,UACA,KAAK,IAAI;AAAA,WACP,EAAE,MAAM,OAAO;AAAA,OAClB;AAAA,SAEE;AAAA,MACJ,OAAO,CAAC,WACP,eAAe,IAAW,YAAY,MAAgC;AAAA;AAAA;AAI1E,gBAAgB,cAAc,CAC7B,IACA,YACA,QACyB;AAAA,EACzB,MAAM,OAAO,GAAG,KAAK,UAAU;AAAA,EAK/B,MAAM,QAAmB,CAAC;AAAA,EAC1B,IAAI,QAAQ;AAAA,EACZ,IAAI,cAA4B;AAAA,EAChC,IAAI,SAA8B;AAAA,EAElC,MAAM,SAAS,MAAM;AAAA,IAAE,SAAS;AAAA,IAAG,SAAS;AAAA;AAAA,EAC5C,KAAK,GAAG,QAAQ,CAAC,QAAiB;AAAA,IAAE,MAAM,KAAK,GAAG;AAAA,IAAG,OAAO;AAAA,GAAI;AAAA,EAChE,KAAK,GAAG,OAAQ,MAAM;AAAA,IAAE,QAAQ;AAAA,IAAM,OAAO;AAAA,GAAI;AAAA,EACjD,KAAK,GAAG,SAAS,CAAC,QAAe;AAAA,IAAE,cAAc;AAAA,IAAK,OAAO;AAAA,GAAI;AAAA,EAGjE,MAAM,aAAa,YAAY;AAAA,IAC9B,IAAI;AAAA,MACH,iBAAiB,OAAO,QAAQ;AAAA,QAC/B,KAAK,MAAM,GAAG;AAAA,MACf;AAAA,cACC;AAAA,MACD,KAAK,IAAI;AAAA;AAAA,KAER;AAAA,EAGH,OAAO,MAAM;AAAA,IACZ,OAAO,MAAM,SAAS;AAAA,MAAG,MAAM,MAAM,MAAM;AAAA,IAC3C,IAAI;AAAA,MAAO;AAAA,IACX,IAAI;AAAA,MAAa,MAAM;AAAA,IACvB,MAAM,IAAI,QAAc,CAAC,MAAM;AAAA,MAAE,SAAS;AAAA,KAAI;AAAA,EAC/C;AAAA,EACA,MAAM;AAAA;AAUP,gBAAgB,mBAAmB,CAAC,QAAqC;AAAA,EAExE,IAAI,OAAO,OAAO,OAAO,mBAAmB,YAAY;AAAA,IACvD,iBAAiB,OAAO,QAAQ;AAAA,MAC/B,MAAM;AAAA,IACP;AAAA,IACA;AAAA,EACD;AAAA,EAEA,IAAI,OAAO;AAAA,EACX,IAAI,QAAsB;AAAA,EAC1B,MAAM,SAAoB,CAAC;AAAA,EAC3B,IAAI,UAA+B;AAAA,EAEnC,OAAO,GAAG,QAAQ,CAAC,QAAiB;AAAA,IACnC,OAAO,KAAK,GAAG;AAAA,IACf,UAAU;AAAA,IACV,UAAU;AAAA,GACV;AAAA,EACD,OAAO,GAAG,OAAO,MAAM;AAAA,IACtB,OAAO;AAAA,IACP,UAAU;AAAA,IACV,UAAU;AAAA,GACV;AAAA,EACD,OAAO,GAAG,SAAS,CAAC,QAAe;AAAA,IAClC,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,UAAU;AAAA,GACV;AAAA,EAED,OAAO,MAAM;AAAA,IACZ,OAAO,OAAO,SAAS,GAAG;AAAA,MACzB,MAAM,OAAO,MAAM;AAAA,IACpB;AAAA,IACA,IAAI;AAAA,MAAM;AAAA,IACV,IAAI;AAAA,MAAO,MAAM;AAAA,IACjB,MAAM,IAAI,QAAc,CAAC,MAAM;AAAA,MAC9B,UAAU;AAAA,KACV;AAAA,EACF;AAAA;;AEljBD;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": "DD2F9280C753352864756E2164756E21",
11
11
  "names": []
12
12
  }
package/dist/service.d.ts CHANGED
@@ -14,7 +14,7 @@
14
14
  * The framework also exposes a `client<T>('ServiceName')` helper
15
15
  * for creating typed clients against the same (or a different)
16
16
  * server. Clients returned by this method are async — each method
17
- * returns a Promise.
17
+ * returns a Promise (unary) or AsyncIterable (streaming).
18
18
  */
19
19
  import type { GrpcConfig } from "./types.js";
20
20
  export declare const GRPC_SERVICE_TOKEN: unique symbol;
@@ -36,22 +36,21 @@ export declare class GrpcService {
36
36
  */
37
37
  prepare(resolve: <T>(token: unknown) => T): Promise<void>;
38
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.
39
+ * Bind the server to the configured host/port.
43
40
  */
44
41
  start(): Promise<void>;
45
42
  /** Stop the server and release the port. */
46
43
  stop(): Promise<void>;
47
44
  /**
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`).
45
+ * Build a typed client for a gRPC service.
46
+ *
47
+ * Method wrappers by call type:
48
+ * - **Unary** — `(req: TReq) => Promise<TRes>`
49
+ * - **Server stream** — `(req: TReq) => AsyncIterable<TRes>`
50
+ * - **Client stream** — `(src: AsyncIterable<TReq>) => Promise<TRes>`
51
+ * - **Bidi stream** — `(src: AsyncIterable<TReq>) => AsyncIterable<TRes>`
53
52
  */
54
- client<T = Record<string, (...args: unknown[]) => Promise<unknown>>>(serviceName: string, options?: {
53
+ client<T = Record<string, (...args: unknown[]) => unknown>>(serviceName: string, options?: {
55
54
  url: string;
56
55
  tls?: boolean;
57
56
  }): T;
package/dist/types.d.ts CHANGED
@@ -15,11 +15,23 @@
15
15
  * Cross-runtime: works on Bun and Node via `@grpc/grpc-js`
16
16
  * (which is built on Node's `http2` module — Bun supports
17
17
  * this via Node-API compatibility).
18
- *
19
- * Unary methods only for v1; streaming (server / client / bidi)
20
- * is a future addition.
21
18
  */
22
19
  import type { ServiceDefinition, UntypedServiceImplementation } from "@grpc/grpc-js";
20
+ /**
21
+ * The RPC streaming classification, matching gRPC's four call types:
22
+ * - `unary` — request → response (@GrpcMethod)
23
+ * - `server` — request → stream<response> (@GrpcServerStream)
24
+ * - `client` — stream<request> → response (@GrpcClientStream)
25
+ * - `bidi` — stream<request> → stream<res> (@GrpcBidiStream)
26
+ */
27
+ export type GrpcStreamType = "unary" | "server" | "client" | "bidi";
28
+ /** Internal method entry stored by the decorator. */
29
+ export interface GrpcMethodEntry {
30
+ /** Method name as declared in the .proto file (PascalCase). */
31
+ protoName: string;
32
+ /** Streaming classification. */
33
+ streamType: GrpcStreamType;
34
+ }
23
35
  export interface GrpcConfig {
24
36
  /**
25
37
  * Path to one or more `.proto` files. Paths are resolved
@@ -62,6 +74,8 @@ export interface GrpcMethodMeta {
62
74
  serviceName: string;
63
75
  /** Method name as it appears in the .proto file. */
64
76
  methodName: string;
77
+ /** Streaming classification. */
78
+ streamType: GrpcStreamType;
65
79
  }
66
80
  /**
67
81
  * Build a typed client for a gRPC service. The returned object
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexusts/grpc",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "description": "gRPC server + typed client (reflection-based)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -26,7 +26,7 @@
26
26
  ],
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
- "@nexusts/core": "^0.8.1"
29
+ "@nexusts/core": "^0.8.2"
30
30
  },
31
31
  "repository": {
32
32
  "type": "git",