@jdevel/tnest 0.0.1 → 0.0.3
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 +349 -0
- package/dist/__tests__/constants.spec.d.ts +1 -0
- package/dist/__tests__/constants.spec.js +21 -0
- package/dist/__tests__/constants.spec.js.map +1 -0
- package/dist/__tests__/tnest-module.spec.d.ts +1 -0
- package/dist/__tests__/tnest-module.spec.js +88 -0
- package/dist/__tests__/tnest-module.spec.js.map +1 -0
- package/dist/client/__tests__/typed-client-factory.spec.d.ts +1 -0
- package/dist/client/__tests__/typed-client-factory.spec.js +35 -0
- package/dist/client/__tests__/typed-client-factory.spec.js.map +1 -0
- package/dist/client/__tests__/typed-client-types.spec.d.ts +1 -0
- package/dist/client/__tests__/typed-client-types.spec.js +18 -0
- package/dist/client/__tests__/typed-client-types.spec.js.map +1 -0
- package/dist/client/__tests__/typed-client.spec.d.ts +1 -0
- package/dist/client/__tests__/typed-client.spec.js +59 -0
- package/dist/client/__tests__/typed-client.spec.js.map +1 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +8 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/typed-client-factory.d.ts +6 -0
- package/dist/client/typed-client-factory.js +25 -0
- package/dist/client/typed-client-factory.js.map +1 -0
- package/dist/client/typed-client.d.ts +11 -0
- package/dist/client/typed-client.js +22 -0
- package/dist/client/typed-client.js.map +1 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +10 -0
- package/dist/constants.js.map +1 -0
- package/dist/contracts/__tests__/contract-types.spec.d.ts +1 -0
- package/dist/contracts/__tests__/contract-types.spec.js +91 -0
- package/dist/contracts/__tests__/contract-types.spec.js.map +1 -0
- package/dist/contracts/__tests__/define-helpers.spec.d.ts +1 -0
- package/dist/contracts/__tests__/define-helpers.spec.js +52 -0
- package/dist/contracts/__tests__/define-helpers.spec.js.map +1 -0
- package/dist/contracts/command.d.ts +6 -0
- package/dist/contracts/command.js +3 -0
- package/dist/contracts/command.js.map +1 -0
- package/dist/contracts/define-helpers.d.ts +12 -0
- package/dist/contracts/define-helpers.js +19 -0
- package/dist/contracts/define-helpers.js.map +1 -0
- package/dist/contracts/event.d.ts +5 -0
- package/dist/contracts/event.js +3 -0
- package/dist/contracts/event.js.map +1 -0
- package/dist/contracts/index.d.ts +6 -0
- package/dist/contracts/index.js +9 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/query.d.ts +6 -0
- package/dist/contracts/query.js +3 -0
- package/dist/contracts/query.js.map +1 -0
- package/dist/contracts/registry.d.ts +8 -0
- package/dist/contracts/registry.js +3 -0
- package/dist/contracts/registry.js.map +1 -0
- package/dist/contracts/utility-types.d.ts +21 -0
- package/dist/contracts/utility-types.js +3 -0
- package/dist/contracts/utility-types.js.map +1 -0
- package/dist/handlers/__tests__/handler-types.spec.d.ts +1 -0
- package/dist/handlers/__tests__/handler-types.spec.js +17 -0
- package/dist/handlers/__tests__/handler-types.spec.js.map +1 -0
- package/dist/handlers/__tests__/typed-event-pattern.spec.d.ts +1 -0
- package/dist/handlers/__tests__/typed-event-pattern.spec.js +33 -0
- package/dist/handlers/__tests__/typed-event-pattern.spec.js.map +1 -0
- package/dist/handlers/__tests__/typed-message-pattern.spec.d.ts +1 -0
- package/dist/handlers/__tests__/typed-message-pattern.spec.js +49 -0
- package/dist/handlers/__tests__/typed-message-pattern.spec.js.map +1 -0
- package/dist/handlers/handler-types.d.ts +4 -0
- package/dist/handlers/handler-types.js +3 -0
- package/dist/handlers/handler-types.js.map +1 -0
- package/dist/handlers/index.d.ts +3 -0
- package/dist/handlers/index.js +8 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/handlers/typed-event-pattern.decorator.d.ts +2 -0
- package/dist/handlers/typed-event-pattern.decorator.js +8 -0
- package/dist/handlers/typed-event-pattern.decorator.js.map +1 -0
- package/dist/handlers/typed-message-pattern.decorator.d.ts +2 -0
- package/dist/handlers/typed-message-pattern.decorator.js +8 -0
- package/dist/handlers/typed-message-pattern.decorator.js.map +1 -0
- package/dist/index.d.ts +16 -1
- package/dist/index.js +29 -3
- package/dist/index.js.map +1 -1
- package/dist/interfaces/index.d.ts +1 -0
- package/dist/interfaces/index.js +3 -0
- package/dist/interfaces/index.js.map +1 -0
- package/dist/interfaces/module-options.d.ts +19 -0
- package/dist/interfaces/module-options.js +3 -0
- package/dist/interfaces/module-options.js.map +1 -0
- package/dist/serialization/__tests__/default-serializer.spec.d.ts +1 -0
- package/dist/serialization/__tests__/default-serializer.spec.js +32 -0
- package/dist/serialization/__tests__/default-serializer.spec.js.map +1 -0
- package/dist/serialization/default-serializer.d.ts +5 -0
- package/dist/serialization/default-serializer.js +23 -0
- package/dist/serialization/default-serializer.js.map +1 -0
- package/dist/serialization/index.d.ts +3 -0
- package/dist/serialization/index.js +9 -0
- package/dist/serialization/index.js.map +1 -0
- package/dist/serialization/serializer.interface.d.ts +8 -0
- package/dist/serialization/serializer.interface.js +6 -0
- package/dist/serialization/serializer.interface.js.map +1 -0
- package/dist/testing/__tests__/mock-typed-client.spec.d.ts +1 -0
- package/dist/testing/__tests__/mock-typed-client.spec.js +70 -0
- package/dist/testing/__tests__/mock-typed-client.spec.js.map +1 -0
- package/dist/testing/__tests__/test-contract-module.spec.d.ts +1 -0
- package/dist/testing/__tests__/test-contract-module.spec.js +30 -0
- package/dist/testing/__tests__/test-contract-module.spec.js.map +1 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +8 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/mock-typed-client.d.ts +20 -0
- package/dist/testing/mock-typed-client.js +36 -0
- package/dist/testing/mock-typed-client.js.map +1 -0
- package/dist/testing/test-contract-module.d.ts +10 -0
- package/dist/testing/test-contract-module.js +31 -0
- package/dist/testing/test-contract-module.js.map +1 -0
- package/dist/tnest.module.d.ts +7 -0
- package/dist/tnest.module.js +78 -7
- package/dist/tnest.module.js.map +1 -1
- package/dist/validation/__tests__/validate-contract.spec.d.ts +1 -0
- package/dist/validation/__tests__/validate-contract.spec.js +102 -0
- package/dist/validation/__tests__/validate-contract.spec.js.map +1 -0
- package/dist/validation/index.d.ts +3 -0
- package/dist/validation/index.js +8 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/validate-contract.decorator.d.ts +1 -0
- package/dist/validation/validate-contract.decorator.js +21 -0
- package/dist/validation/validate-contract.decorator.js.map +1 -0
- package/dist/validation/validator.interface.d.ts +4 -0
- package/dist/validation/validator.interface.js +5 -0
- package/dist/validation/validator.interface.js.map +1 -0
- package/package.json +34 -4
package/README.md
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# @jdevel/tnest
|
|
2
|
+
|
|
3
|
+
Type-safe communication between NestJS microservices.
|
|
4
|
+
|
|
5
|
+
Define your message contracts once as TypeScript types, and the compiler enforces them across every producer and consumer — eliminating runtime type mismatches between services.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Compile-time safety** -- mismatched patterns, payloads, or responses are caught before deployment
|
|
10
|
+
- **Transport-agnostic** -- works with any NestJS transport (TCP, Redis, NATS, RabbitMQ, Kafka, gRPC)
|
|
11
|
+
- **NestJS-native** -- standard modules, decorators, and dependency injection
|
|
12
|
+
- **Zero runtime overhead** -- type enforcement happens at compile time; no runtime schema checking unless you opt in
|
|
13
|
+
- **First-class testing** -- `MockTypedClient` and `TestContractModule` included
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @jdevel/tnest
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Peer dependencies:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @nestjs/common @nestjs/microservices reflect-metadata rxjs
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
### 1. Define contracts
|
|
30
|
+
|
|
31
|
+
Contracts describe the messages exchanged between services. There are three kinds:
|
|
32
|
+
|
|
33
|
+
| Type | Purpose | Has response? |
|
|
34
|
+
| --------- | ---------------------------------- | ------------- |
|
|
35
|
+
| `Command` | Write operation (request/response) | Yes |
|
|
36
|
+
| `Query` | Read operation (request/response) | Yes |
|
|
37
|
+
| `Event` | Notification (fire-and-forget) | No |
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// contracts/user.contracts.ts
|
|
41
|
+
import { defineRegistry, command, event, query } from '@jdevel/tnest';
|
|
42
|
+
|
|
43
|
+
interface CreateUserDto {
|
|
44
|
+
email: string;
|
|
45
|
+
name: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface User {
|
|
49
|
+
id: string;
|
|
50
|
+
email: string;
|
|
51
|
+
name: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const userContracts = defineRegistry({
|
|
55
|
+
'user.create': command<CreateUserDto, User>(),
|
|
56
|
+
'user.created': event<{ userId: string; email: string }>(),
|
|
57
|
+
'user.get': query<{ id: string }, User>(),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export type UserContracts = typeof userContracts;
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
You can also define contracts with explicit interfaces if you prefer:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import type { Command, Event, Query } from '@jdevel/tnest';
|
|
67
|
+
|
|
68
|
+
export interface UserContracts {
|
|
69
|
+
'user.create': Command<'user.create', CreateUserDto, User>;
|
|
70
|
+
'user.created': Event<'user.created', { userId: string; email: string }>;
|
|
71
|
+
'user.get': Query<'user.get', { id: string }, User>;
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 2. Register the module
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
// app.module.ts
|
|
79
|
+
import { Module } from '@nestjs/common';
|
|
80
|
+
import { Transport } from '@nestjs/microservices';
|
|
81
|
+
import { TnestModule } from '@jdevel/tnest';
|
|
82
|
+
|
|
83
|
+
@Module({
|
|
84
|
+
imports: [
|
|
85
|
+
TnestModule.forRoot({
|
|
86
|
+
clients: [
|
|
87
|
+
{
|
|
88
|
+
name: 'USER_SERVICE',
|
|
89
|
+
options: {
|
|
90
|
+
transport: Transport.TCP,
|
|
91
|
+
options: { host: 'localhost', port: 3001 },
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
}),
|
|
96
|
+
],
|
|
97
|
+
})
|
|
98
|
+
export class AppModule {}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 3. Send messages (producer)
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
// order.service.ts
|
|
105
|
+
import { Injectable, Inject } from '@nestjs/common';
|
|
106
|
+
import { ClientProxy } from '@nestjs/microservices';
|
|
107
|
+
import { TypedClientFactory, TypedClient, getClientToken } from '@jdevel/tnest';
|
|
108
|
+
import { firstValueFrom } from 'rxjs';
|
|
109
|
+
import type { UserContracts } from './contracts/user.contracts';
|
|
110
|
+
|
|
111
|
+
@Injectable()
|
|
112
|
+
export class OrderService {
|
|
113
|
+
private readonly users: TypedClient<UserContracts>;
|
|
114
|
+
|
|
115
|
+
constructor(
|
|
116
|
+
factory: TypedClientFactory,
|
|
117
|
+
@Inject(getClientToken('USER_SERVICE')) proxy: ClientProxy,
|
|
118
|
+
) {
|
|
119
|
+
this.users = factory.create<UserContracts>(proxy);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async createOrder(userId: string) {
|
|
123
|
+
const user = await firstValueFrom(this.users.send('user.get', { id: userId }));
|
|
124
|
+
|
|
125
|
+
this.users.emit('user.created', {
|
|
126
|
+
userId: user.id,
|
|
127
|
+
email: user.email,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return { orderId: '123', user };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The compiler catches contract violations:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
this.users.send('user.delete', { id: '1' });
|
|
139
|
+
// ~~~~~~~~~~~~ ERROR: not a valid pattern
|
|
140
|
+
|
|
141
|
+
this.users.send('user.get', { name: 'test' });
|
|
142
|
+
// ~~~~~~~~~~~~~~~~ ERROR: expects { id: string }
|
|
143
|
+
|
|
144
|
+
this.users.send('user.created', { userId: '1', email: 'a@b.com' });
|
|
145
|
+
// ~~~~~~~~~~~~~~ ERROR: 'user.created' is an event, use emit()
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 4. Handle messages (consumer)
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
// user.controller.ts
|
|
152
|
+
import { Controller } from '@nestjs/common';
|
|
153
|
+
import { TypedMessagePattern, TypedEventPattern } from '@jdevel/tnest';
|
|
154
|
+
import type { UserContracts } from './contracts/user.contracts';
|
|
155
|
+
|
|
156
|
+
@Controller()
|
|
157
|
+
export class UserController {
|
|
158
|
+
@TypedMessagePattern<UserContracts>('user.create')
|
|
159
|
+
async create(payload: { email: string; name: string }) {
|
|
160
|
+
return { id: crypto.randomUUID(), ...payload };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@TypedMessagePattern<UserContracts>('user.get')
|
|
164
|
+
async get(payload: { id: string }) {
|
|
165
|
+
return { id: payload.id, email: 'user@example.com', name: 'Example' };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
@TypedEventPattern<UserContracts>('user.created')
|
|
169
|
+
async handleCreated(payload: { userId: string; email: string }) {
|
|
170
|
+
console.log(`User created: ${payload.userId}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Testing
|
|
176
|
+
|
|
177
|
+
`MockTypedClient` records every message and returns configurable canned responses:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { MockTypedClient } from '@jdevel/tnest';
|
|
181
|
+
import { firstValueFrom } from 'rxjs';
|
|
182
|
+
import type { UserContracts } from './contracts/user.contracts';
|
|
183
|
+
|
|
184
|
+
const mock = new MockTypedClient<UserContracts>();
|
|
185
|
+
|
|
186
|
+
mock.setResponse('user.get', {
|
|
187
|
+
id: '42',
|
|
188
|
+
email: 'test@example.com',
|
|
189
|
+
name: 'Test User',
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const user = await firstValueFrom(mock.send('user.get', { id: '42' }));
|
|
193
|
+
// user.id === '42'
|
|
194
|
+
|
|
195
|
+
expect(mock.messages).toEqual([{ type: 'send', pattern: 'user.get', payload: { id: '42' } }]);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
For integration tests, `TestContractModule` swaps real clients for mocks:
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
import { Test } from '@nestjs/testing';
|
|
202
|
+
import { MockTypedClient, TestContractModule } from '@jdevel/tnest';
|
|
203
|
+
import type { UserContracts } from './contracts/user.contracts';
|
|
204
|
+
|
|
205
|
+
const mock = new MockTypedClient<UserContracts>();
|
|
206
|
+
|
|
207
|
+
const module = await Test.createTestingModule({
|
|
208
|
+
imports: [TestContractModule.register([{ name: 'USER_SERVICE', mock }])],
|
|
209
|
+
providers: [OrderService],
|
|
210
|
+
}).compile();
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Async Module Configuration
|
|
214
|
+
|
|
215
|
+
Use `forRootAsync` when transport config comes from `ConfigService` or another async source:
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
219
|
+
import { TnestModule } from '@jdevel/tnest';
|
|
220
|
+
import { Transport } from '@nestjs/microservices';
|
|
221
|
+
|
|
222
|
+
TnestModule.forRootAsync({
|
|
223
|
+
imports: [ConfigModule],
|
|
224
|
+
useFactory: (config: ConfigService) => ({
|
|
225
|
+
clients: [
|
|
226
|
+
{
|
|
227
|
+
name: 'USER_SERVICE',
|
|
228
|
+
options: {
|
|
229
|
+
transport: Transport.TCP,
|
|
230
|
+
options: {
|
|
231
|
+
host: config.get('USER_SERVICE_HOST'),
|
|
232
|
+
port: config.get('USER_SERVICE_PORT'),
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
}),
|
|
238
|
+
inject: [ConfigService],
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Runtime Validation (Optional)
|
|
243
|
+
|
|
244
|
+
By default, contracts are enforced at compile time only. To add runtime payload validation, implement the `ContractValidator` interface and apply `@ValidateContract()`:
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
import { Injectable } from '@nestjs/common';
|
|
248
|
+
import {
|
|
249
|
+
CONTRACT_VALIDATOR,
|
|
250
|
+
ValidateContract,
|
|
251
|
+
TypedMessagePattern,
|
|
252
|
+
type ContractValidator,
|
|
253
|
+
} from '@jdevel/tnest';
|
|
254
|
+
|
|
255
|
+
@Injectable()
|
|
256
|
+
class MyValidator implements ContractValidator {
|
|
257
|
+
validate(payload: unknown): void {
|
|
258
|
+
// your validation logic (zod, class-validator, joi, etc.)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Register in module providers
|
|
263
|
+
{ provide: CONTRACT_VALIDATOR, useClass: MyValidator }
|
|
264
|
+
|
|
265
|
+
// Apply to handlers
|
|
266
|
+
@TypedMessagePattern<UserContracts>('user.create')
|
|
267
|
+
@ValidateContract()
|
|
268
|
+
async create(payload: CreateUserDto) {
|
|
269
|
+
// payload has been validated at runtime
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Custom Serialization (Optional)
|
|
274
|
+
|
|
275
|
+
Provide custom payload serialization (protobuf, msgpack, etc.) by implementing `PayloadSerializer` and `PayloadDeserializer`:
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
import {
|
|
279
|
+
PAYLOAD_SERIALIZER,
|
|
280
|
+
PAYLOAD_DESERIALIZER,
|
|
281
|
+
type PayloadSerializer,
|
|
282
|
+
type PayloadDeserializer,
|
|
283
|
+
} from '@jdevel/tnest';
|
|
284
|
+
|
|
285
|
+
@Injectable()
|
|
286
|
+
class MsgpackSerializer implements PayloadSerializer, PayloadDeserializer {
|
|
287
|
+
serialize(payload: unknown) { return msgpack.encode(payload); }
|
|
288
|
+
deserialize(data: unknown) { return msgpack.decode(data as Buffer); }
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Register in module providers
|
|
292
|
+
{ provide: PAYLOAD_SERIALIZER, useClass: MsgpackSerializer }
|
|
293
|
+
{ provide: PAYLOAD_DESERIALIZER, useClass: MsgpackSerializer }
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Utility Types
|
|
297
|
+
|
|
298
|
+
Extract type information from your contract registry:
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
import type {
|
|
302
|
+
PayloadOf,
|
|
303
|
+
ResponseOf,
|
|
304
|
+
CommandPatterns,
|
|
305
|
+
EventPatterns,
|
|
306
|
+
QueryPatterns,
|
|
307
|
+
SendablePatterns,
|
|
308
|
+
CommandsOf,
|
|
309
|
+
EventsOf,
|
|
310
|
+
QueriesOf,
|
|
311
|
+
} from '@jdevel/tnest';
|
|
312
|
+
|
|
313
|
+
type Payload = PayloadOf<UserContracts['user.create']>; // CreateUserDto
|
|
314
|
+
type Response = ResponseOf<UserContracts['user.create']>; // User
|
|
315
|
+
type Cmds = CommandPatterns<UserContracts>; // 'user.create'
|
|
316
|
+
type Evts = EventPatterns<UserContracts>; // 'user.created'
|
|
317
|
+
type Qrys = QueryPatterns<UserContracts>; // 'user.get'
|
|
318
|
+
type Sendable = SendablePatterns<UserContracts>; // 'user.create' | 'user.get'
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## API Reference
|
|
322
|
+
|
|
323
|
+
| Export | Kind | Description |
|
|
324
|
+
| ----------------------------------------------------- | --------- | ----------------------------------------------- |
|
|
325
|
+
| `TnestModule` | Module | Dynamic module with `forRoot` / `forRootAsync` |
|
|
326
|
+
| `TypedClient` | Class | Type-safe wrapper around `ClientProxy` |
|
|
327
|
+
| `TypedClientFactory` | Service | Creates `TypedClient` instances |
|
|
328
|
+
| `TypedMessagePattern` | Decorator | Typed `@MessagePattern` for commands/queries |
|
|
329
|
+
| `TypedEventPattern` | Decorator | Typed `@EventPattern` for events |
|
|
330
|
+
| `MockTypedClient` | Class | Test double that records messages |
|
|
331
|
+
| `TestContractModule` | Module | Registers mock clients for testing |
|
|
332
|
+
| `ValidateContract` | Decorator | Opt-in runtime payload validation |
|
|
333
|
+
| `getClientToken` | Function | Returns injection token for a named client |
|
|
334
|
+
| `defineRegistry` | Function | Builder helper for defining contract registries |
|
|
335
|
+
| `command` / `event` / `query` | Functions | Builder helpers for individual contracts |
|
|
336
|
+
| `Command` / `Event` / `Query` | Type | Contract type interfaces |
|
|
337
|
+
| `ContractRegistry` | Type | Base type for a registry of contracts |
|
|
338
|
+
| `PayloadOf` / `ResponseOf` / `PatternOf` | Type | Extract parts of a contract |
|
|
339
|
+
| `CommandsOf` / `EventsOf` / `QueriesOf` | Type | Filter registry by contract kind |
|
|
340
|
+
| `CommandPatterns` / `EventPatterns` / `QueryPatterns` | Type | Pattern string unions by kind |
|
|
341
|
+
| `SendablePatterns` | Type | Union of command + query patterns |
|
|
342
|
+
| `ContractValidator` | Interface | Implement for runtime validation |
|
|
343
|
+
| `PayloadSerializer` / `PayloadDeserializer` | Interface | Implement for custom serialization |
|
|
344
|
+
| `CONTRACT_VALIDATOR` | Token | Injection token for validator |
|
|
345
|
+
| `PAYLOAD_SERIALIZER` / `PAYLOAD_DESERIALIZER` | Token | Injection tokens for serialization |
|
|
346
|
+
|
|
347
|
+
## License
|
|
348
|
+
|
|
349
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const constants_1 = require("../constants");
|
|
4
|
+
describe('TNEST_OPTIONS', () => {
|
|
5
|
+
it('is a Symbol', () => {
|
|
6
|
+
expect(typeof constants_1.TNEST_OPTIONS).toBe('symbol');
|
|
7
|
+
});
|
|
8
|
+
});
|
|
9
|
+
describe('getClientToken()', () => {
|
|
10
|
+
it('returns a string prefixed with TNEST_CLIENT_PREFIX', () => {
|
|
11
|
+
const token = (0, constants_1.getClientToken)('USER_SERVICE');
|
|
12
|
+
expect(token).toBe(`${constants_1.TNEST_CLIENT_PREFIX}USER_SERVICE`);
|
|
13
|
+
});
|
|
14
|
+
it('returns deterministic tokens for the same name', () => {
|
|
15
|
+
expect((0, constants_1.getClientToken)('SVC')).toBe((0, constants_1.getClientToken)('SVC'));
|
|
16
|
+
});
|
|
17
|
+
it('returns different tokens for different names', () => {
|
|
18
|
+
expect((0, constants_1.getClientToken)('A')).not.toBe((0, constants_1.getClientToken)('B'));
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
//# sourceMappingURL=constants.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.spec.js","sourceRoot":"","sources":["../../src/__tests__/constants.spec.ts"],"names":[],"mappings":";;AAAA,4CAAkF;AAElF,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,aAAa,EAAE,GAAG,EAAE;QACrB,MAAM,CAAC,OAAO,yBAAa,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,KAAK,GAAG,IAAA,0BAAc,EAAC,cAAc,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,+BAAmB,cAAc,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,IAAA,0BAAc,EAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAA,0BAAc,EAAC,KAAK,CAAC,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,IAAA,0BAAc,EAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAA,0BAAc,EAAC,GAAG,CAAC,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const testing_1 = require("@nestjs/testing");
|
|
4
|
+
const tnest_module_1 = require("../tnest.module");
|
|
5
|
+
const client_1 = require("../client");
|
|
6
|
+
const constants_1 = require("../constants");
|
|
7
|
+
describe('TnestModule', () => {
|
|
8
|
+
describe('forRoot()', () => {
|
|
9
|
+
it('provides TypedClientFactory', async () => {
|
|
10
|
+
const module = await testing_1.Test.createTestingModule({
|
|
11
|
+
imports: [tnest_module_1.TnestModule.forRoot()],
|
|
12
|
+
}).compile();
|
|
13
|
+
const factory = module.get(client_1.TypedClientFactory);
|
|
14
|
+
expect(factory).toBeInstanceOf(client_1.TypedClientFactory);
|
|
15
|
+
});
|
|
16
|
+
it('provides TNEST_OPTIONS', async () => {
|
|
17
|
+
const options = { clients: [] };
|
|
18
|
+
const module = await testing_1.Test.createTestingModule({
|
|
19
|
+
imports: [tnest_module_1.TnestModule.forRoot(options)],
|
|
20
|
+
}).compile();
|
|
21
|
+
const resolved = module.get(constants_1.TNEST_OPTIONS);
|
|
22
|
+
expect(resolved).toBe(options);
|
|
23
|
+
});
|
|
24
|
+
it('registers named client tokens from client definitions', async () => {
|
|
25
|
+
const module = await testing_1.Test.createTestingModule({
|
|
26
|
+
imports: [
|
|
27
|
+
tnest_module_1.TnestModule.forRoot({
|
|
28
|
+
clients: [{ name: 'USER_SERVICE', options: { transport: 0 } }],
|
|
29
|
+
}),
|
|
30
|
+
],
|
|
31
|
+
}).compile();
|
|
32
|
+
const token = (0, constants_1.getClientToken)('USER_SERVICE');
|
|
33
|
+
const client = module.get(token);
|
|
34
|
+
expect(client).toBeDefined();
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe('forRootAsync()', () => {
|
|
38
|
+
it('provides TypedClientFactory', async () => {
|
|
39
|
+
const module = await testing_1.Test.createTestingModule({
|
|
40
|
+
imports: [
|
|
41
|
+
tnest_module_1.TnestModule.forRootAsync({
|
|
42
|
+
useFactory: () => ({ clients: [] }),
|
|
43
|
+
}),
|
|
44
|
+
],
|
|
45
|
+
}).compile();
|
|
46
|
+
const factory = module.get(client_1.TypedClientFactory);
|
|
47
|
+
expect(factory).toBeInstanceOf(client_1.TypedClientFactory);
|
|
48
|
+
});
|
|
49
|
+
it('resolves TNEST_OPTIONS from factory', async () => {
|
|
50
|
+
const options = { clients: [] };
|
|
51
|
+
const module = await testing_1.Test.createTestingModule({
|
|
52
|
+
imports: [
|
|
53
|
+
tnest_module_1.TnestModule.forRootAsync({
|
|
54
|
+
useFactory: () => options,
|
|
55
|
+
}),
|
|
56
|
+
],
|
|
57
|
+
}).compile();
|
|
58
|
+
const resolved = module.get(constants_1.TNEST_OPTIONS);
|
|
59
|
+
expect(resolved).toBe(options);
|
|
60
|
+
});
|
|
61
|
+
it('registers named client tokens when clientNames are provided', async () => {
|
|
62
|
+
const module = await testing_1.Test.createTestingModule({
|
|
63
|
+
imports: [
|
|
64
|
+
tnest_module_1.TnestModule.forRootAsync({
|
|
65
|
+
clientNames: ['USER_SERVICE'],
|
|
66
|
+
useFactory: () => ({
|
|
67
|
+
clients: [{ name: 'USER_SERVICE', options: { transport: 0 } }],
|
|
68
|
+
}),
|
|
69
|
+
}),
|
|
70
|
+
],
|
|
71
|
+
}).compile();
|
|
72
|
+
const token = (0, constants_1.getClientToken)('USER_SERVICE');
|
|
73
|
+
const client = module.get(token);
|
|
74
|
+
expect(client).toBeDefined();
|
|
75
|
+
});
|
|
76
|
+
it('throws when clientName is not found in resolved options', async () => {
|
|
77
|
+
await expect(testing_1.Test.createTestingModule({
|
|
78
|
+
imports: [
|
|
79
|
+
tnest_module_1.TnestModule.forRootAsync({
|
|
80
|
+
clientNames: ['MISSING_SERVICE'],
|
|
81
|
+
useFactory: () => ({ clients: [] }),
|
|
82
|
+
}),
|
|
83
|
+
],
|
|
84
|
+
}).compile()).rejects.toThrow(/client "MISSING_SERVICE" was declared in clientNames but not found/);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
//# sourceMappingURL=tnest-module.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tnest-module.spec.js","sourceRoot":"","sources":["../../src/__tests__/tnest-module.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const rxjs_1 = require("rxjs");
|
|
4
|
+
const microservices_1 = require("@nestjs/microservices");
|
|
5
|
+
const typed_client_factory_1 = require("../typed-client-factory");
|
|
6
|
+
const typed_client_1 = require("../typed-client");
|
|
7
|
+
describe('TypedClientFactory', () => {
|
|
8
|
+
let factory;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
factory = new typed_client_factory_1.TypedClientFactory();
|
|
11
|
+
});
|
|
12
|
+
it('creates a TypedClient from a ClientProxy instance', () => {
|
|
13
|
+
const mockProxy = {
|
|
14
|
+
send: jest.fn().mockReturnValue((0, rxjs_1.of)(undefined)),
|
|
15
|
+
emit: jest.fn().mockReturnValue((0, rxjs_1.of)(undefined)),
|
|
16
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
17
|
+
close: jest.fn(),
|
|
18
|
+
};
|
|
19
|
+
const client = factory.create(mockProxy);
|
|
20
|
+
expect(client).toBeInstanceOf(typed_client_1.TypedClient);
|
|
21
|
+
});
|
|
22
|
+
it('creates a TypedClient from ClientOptions using ClientProxyFactory', () => {
|
|
23
|
+
const mockProxy = {
|
|
24
|
+
send: jest.fn(),
|
|
25
|
+
emit: jest.fn(),
|
|
26
|
+
connect: jest.fn(),
|
|
27
|
+
close: jest.fn(),
|
|
28
|
+
};
|
|
29
|
+
jest.spyOn(microservices_1.ClientProxyFactory, 'create').mockReturnValue(mockProxy);
|
|
30
|
+
const client = factory.create({ transport: 0 });
|
|
31
|
+
expect(microservices_1.ClientProxyFactory.create).toHaveBeenCalledWith({ transport: 0 });
|
|
32
|
+
expect(client).toBeInstanceOf(typed_client_1.TypedClient);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=typed-client-factory.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typed-client-factory.spec.js","sourceRoot":"","sources":["../../../src/client/__tests__/typed-client-factory.spec.ts"],"names":[],"mappings":";;AAAA,+BAA0B;AAC1B,yDAAwE;AACxE,kEAA6D;AAC7D,kDAA8C;AAQ9C,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,IAAI,OAA2B,CAAC;IAEhC,UAAU,CAAC,GAAG,EAAE;QACd,OAAO,GAAG,IAAI,yCAAkB,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,SAAS,GAAG;YAChB,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAA,SAAE,EAAC,SAAS,CAAC,CAAC;YAC9C,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAA,SAAE,EAAC,SAAS,CAAC,CAAC;YAC9C,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;YAC/C,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE;SACS,CAAC;QAE5B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAe,SAAS,CAAC,CAAC;QAEvD,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,0BAAW,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,MAAM,SAAS,GAAG;YAChB,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;YACf,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;YACf,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE;YAClB,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE;SACS,CAAC;QAE5B,IAAI,CAAC,KAAK,CAAC,kCAAkB,EAAE,QAAQ,CAAC,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;QAEpE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAe,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QAE9D,MAAM,CAAC,kCAAkB,CAAC,MAAM,CAAC,CAAC,oBAAoB,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QACzE,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,0BAAW,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const expect_type_1 = require("expect-type");
|
|
4
|
+
describe('TypedClient type constraints', () => {
|
|
5
|
+
it('send() returns Observable of correct response type for command', () => {
|
|
6
|
+
(0, expect_type_1.expectTypeOf)().parameter(0).toMatchTypeOf();
|
|
7
|
+
});
|
|
8
|
+
it('send() infers response type from pattern', () => {
|
|
9
|
+
(0, expect_type_1.expectTypeOf)().toMatchTypeOf();
|
|
10
|
+
});
|
|
11
|
+
it('emit() accepts only event patterns', () => {
|
|
12
|
+
(0, expect_type_1.expectTypeOf)().parameter(0).toEqualTypeOf();
|
|
13
|
+
});
|
|
14
|
+
it('emit() returns Observable<undefined>', () => {
|
|
15
|
+
(0, expect_type_1.expectTypeOf)().toEqualTypeOf();
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
//# sourceMappingURL=typed-client-types.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typed-client-types.spec.js","sourceRoot":"","sources":["../../../src/client/__tests__/typed-client-types.spec.ts"],"names":[],"mappings":";;AAAA,6CAA2C;AAgB3C,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,IAAA,0BAAY,GAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,aAAa,EAA8B,CAAC;IAC1F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAElD,IAAA,0BAAY,GAAc,CAAC,aAAa,EAAuB,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,IAAA,0BAAY,GAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,aAAa,EAAkB,CAAC;IAC9E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAE9C,IAAA,0BAAY,GAAc,CAAC,aAAa,EAAyB,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const rxjs_1 = require("rxjs");
|
|
4
|
+
const rxjs_2 = require("rxjs");
|
|
5
|
+
const typed_client_1 = require("../typed-client");
|
|
6
|
+
function createMockClientProxy() {
|
|
7
|
+
return {
|
|
8
|
+
send: jest.fn().mockReturnValue((0, rxjs_1.of)(undefined)),
|
|
9
|
+
emit: jest.fn().mockReturnValue((0, rxjs_1.of)(undefined)),
|
|
10
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
11
|
+
close: jest.fn(),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
describe('TypedClient', () => {
|
|
15
|
+
let mockProxy;
|
|
16
|
+
let client;
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
mockProxy = createMockClientProxy();
|
|
19
|
+
client = new typed_client_1.TypedClient(mockProxy);
|
|
20
|
+
});
|
|
21
|
+
describe('send()', () => {
|
|
22
|
+
it('delegates to ClientProxy.send() with pattern and payload', async () => {
|
|
23
|
+
const payload = { name: 'Alice' };
|
|
24
|
+
const response = { id: '123' };
|
|
25
|
+
mockProxy.send.mockReturnValue((0, rxjs_1.of)(response));
|
|
26
|
+
const result = await (0, rxjs_2.firstValueFrom)(client.send('user.create', payload));
|
|
27
|
+
expect(mockProxy.send).toHaveBeenCalledWith('user.create', payload);
|
|
28
|
+
expect(result).toEqual(response);
|
|
29
|
+
});
|
|
30
|
+
it('works with query patterns', async () => {
|
|
31
|
+
const payload = { id: '123' };
|
|
32
|
+
const response = { name: 'Alice' };
|
|
33
|
+
mockProxy.send.mockReturnValue((0, rxjs_1.of)(response));
|
|
34
|
+
const result = await (0, rxjs_2.firstValueFrom)(client.send('user.get', payload));
|
|
35
|
+
expect(mockProxy.send).toHaveBeenCalledWith('user.get', payload);
|
|
36
|
+
expect(result).toEqual(response);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe('emit()', () => {
|
|
40
|
+
it('delegates to ClientProxy.emit() with pattern and payload', async () => {
|
|
41
|
+
const payload = { userId: '123' };
|
|
42
|
+
await (0, rxjs_2.firstValueFrom)(client.emit('user.created', payload));
|
|
43
|
+
expect(mockProxy.emit).toHaveBeenCalledWith('user.created', payload);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
describe('connect()', () => {
|
|
47
|
+
it('delegates to ClientProxy.connect()', async () => {
|
|
48
|
+
await client.connect();
|
|
49
|
+
expect(mockProxy.connect).toHaveBeenCalled();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe('close()', () => {
|
|
53
|
+
it('delegates to ClientProxy.close()', () => {
|
|
54
|
+
client.close();
|
|
55
|
+
expect(mockProxy.close).toHaveBeenCalled();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=typed-client.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typed-client.spec.js","sourceRoot":"","sources":["../../../src/client/__tests__/typed-client.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TypedClientFactory = exports.TypedClient = void 0;
|
|
4
|
+
var typed_client_1 = require("./typed-client");
|
|
5
|
+
Object.defineProperty(exports, "TypedClient", { enumerable: true, get: function () { return typed_client_1.TypedClient; } });
|
|
6
|
+
var typed_client_factory_1 = require("./typed-client-factory");
|
|
7
|
+
Object.defineProperty(exports, "TypedClientFactory", { enumerable: true, get: function () { return typed_client_factory_1.TypedClientFactory; } });
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":";;;AAAA,+CAA6C;AAApC,2GAAA,WAAW,OAAA;AACpB,+DAA4D;AAAnD,0HAAA,kBAAkB,OAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ClientProxy, type ClientOptions } from '@nestjs/microservices';
|
|
2
|
+
import type { ContractRegistry } from '../contracts';
|
|
3
|
+
import { TypedClient } from './typed-client';
|
|
4
|
+
export declare class TypedClientFactory {
|
|
5
|
+
create<TRegistry extends ContractRegistry>(clientOrOptions: ClientProxy | ClientOptions): TypedClient<TRegistry>;
|
|
6
|
+
}
|