@venizia/ignis-docs 0.0.5 → 0.0.6-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/package.json +1 -1
  2. package/wiki/best-practices/architecture-decisions.md +0 -8
  3. package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
  4. package/wiki/best-practices/performance-optimization.md +3 -3
  5. package/wiki/best-practices/security-guidelines.md +2 -2
  6. package/wiki/best-practices/troubleshooting-tips.md +1 -1
  7. package/wiki/guides/core-concepts/components-guide.md +1 -1
  8. package/wiki/guides/core-concepts/components.md +2 -2
  9. package/wiki/guides/core-concepts/dependency-injection.md +1 -1
  10. package/wiki/guides/core-concepts/services.md +1 -1
  11. package/wiki/guides/tutorials/building-a-crud-api.md +1 -1
  12. package/wiki/guides/tutorials/ecommerce-api.md +2 -2
  13. package/wiki/guides/tutorials/realtime-chat.md +6 -6
  14. package/wiki/guides/tutorials/testing.md +1 -1
  15. package/wiki/references/base/bootstrapping.md +0 -2
  16. package/wiki/references/base/components.md +2 -2
  17. package/wiki/references/base/controllers.md +0 -1
  18. package/wiki/references/base/datasources.md +1 -1
  19. package/wiki/references/base/dependency-injection.md +1 -1
  20. package/wiki/references/base/filter-system/quick-reference.md +0 -14
  21. package/wiki/references/base/middlewares.md +0 -8
  22. package/wiki/references/base/providers.md +0 -9
  23. package/wiki/references/base/services.md +0 -1
  24. package/wiki/references/components/authentication/api.md +444 -0
  25. package/wiki/references/components/authentication/errors.md +177 -0
  26. package/wiki/references/components/authentication/index.md +571 -0
  27. package/wiki/references/components/authentication/usage.md +781 -0
  28. package/wiki/references/components/health-check.md +292 -103
  29. package/wiki/references/components/index.md +14 -12
  30. package/wiki/references/components/mail/api.md +505 -0
  31. package/wiki/references/components/mail/errors.md +176 -0
  32. package/wiki/references/components/mail/index.md +535 -0
  33. package/wiki/references/components/mail/usage.md +404 -0
  34. package/wiki/references/components/request-tracker.md +229 -25
  35. package/wiki/references/components/socket-io/api.md +1051 -0
  36. package/wiki/references/components/socket-io/errors.md +119 -0
  37. package/wiki/references/components/socket-io/index.md +410 -0
  38. package/wiki/references/components/socket-io/usage.md +322 -0
  39. package/wiki/references/components/static-asset/api.md +261 -0
  40. package/wiki/references/components/static-asset/errors.md +89 -0
  41. package/wiki/references/components/static-asset/index.md +617 -0
  42. package/wiki/references/components/static-asset/usage.md +364 -0
  43. package/wiki/references/components/swagger.md +390 -110
  44. package/wiki/references/components/template/api-page.md +125 -0
  45. package/wiki/references/components/template/errors-page.md +100 -0
  46. package/wiki/references/components/template/index.md +104 -0
  47. package/wiki/references/components/template/setup-page.md +134 -0
  48. package/wiki/references/components/template/single-page.md +132 -0
  49. package/wiki/references/components/template/usage-page.md +127 -0
  50. package/wiki/references/components/websocket/api.md +508 -0
  51. package/wiki/references/components/websocket/errors.md +123 -0
  52. package/wiki/references/components/websocket/index.md +453 -0
  53. package/wiki/references/components/websocket/usage.md +475 -0
  54. package/wiki/references/helpers/cron/index.md +224 -0
  55. package/wiki/references/helpers/crypto/index.md +537 -0
  56. package/wiki/references/helpers/env/index.md +214 -0
  57. package/wiki/references/helpers/error/index.md +232 -0
  58. package/wiki/references/helpers/index.md +16 -15
  59. package/wiki/references/helpers/inversion/index.md +608 -0
  60. package/wiki/references/helpers/logger/index.md +600 -0
  61. package/wiki/references/helpers/network/api.md +986 -0
  62. package/wiki/references/helpers/network/index.md +620 -0
  63. package/wiki/references/helpers/queue/index.md +589 -0
  64. package/wiki/references/helpers/redis/index.md +495 -0
  65. package/wiki/references/helpers/socket-io/api.md +497 -0
  66. package/wiki/references/helpers/socket-io/index.md +513 -0
  67. package/wiki/references/helpers/storage/api.md +705 -0
  68. package/wiki/references/helpers/storage/index.md +583 -0
  69. package/wiki/references/helpers/template/index.md +66 -0
  70. package/wiki/references/helpers/template/single-page.md +126 -0
  71. package/wiki/references/helpers/testing/index.md +510 -0
  72. package/wiki/references/helpers/types/index.md +512 -0
  73. package/wiki/references/helpers/uid/index.md +272 -0
  74. package/wiki/references/helpers/websocket/api.md +736 -0
  75. package/wiki/references/helpers/websocket/index.md +574 -0
  76. package/wiki/references/helpers/worker-thread/index.md +470 -0
  77. package/wiki/references/quick-reference.md +3 -18
  78. package/wiki/references/utilities/jsx.md +1 -8
  79. package/wiki/references/utilities/statuses.md +0 -7
  80. package/wiki/references/components/authentication.md +0 -476
  81. package/wiki/references/components/mail.md +0 -687
  82. package/wiki/references/components/socket-io.md +0 -562
  83. package/wiki/references/components/static-asset.md +0 -1277
  84. package/wiki/references/helpers/cron.md +0 -108
  85. package/wiki/references/helpers/crypto.md +0 -132
  86. package/wiki/references/helpers/env.md +0 -83
  87. package/wiki/references/helpers/error.md +0 -97
  88. package/wiki/references/helpers/inversion.md +0 -176
  89. package/wiki/references/helpers/logger.md +0 -296
  90. package/wiki/references/helpers/network.md +0 -396
  91. package/wiki/references/helpers/queue.md +0 -150
  92. package/wiki/references/helpers/redis.md +0 -142
  93. package/wiki/references/helpers/socket-io.md +0 -932
  94. package/wiki/references/helpers/storage.md +0 -665
  95. package/wiki/references/helpers/testing.md +0 -133
  96. package/wiki/references/helpers/types.md +0 -167
  97. package/wiki/references/helpers/uid.md +0 -167
  98. package/wiki/references/helpers/worker-thread.md +0 -178
@@ -0,0 +1,620 @@
1
+ # Network
2
+
3
+ Multi-protocol network communication helpers for HTTP requests, TCP/TLS sockets, and UDP datagrams with scoped logging, auto-reconnect, and client authentication support.
4
+
5
+ ## Quick Reference
6
+
7
+ | Class | Extends | Protocol | Type |
8
+ |-------|---------|----------|------|
9
+ | `AxiosNetworkRequest` | `BaseNetworkRequest<'axios'>` | HTTP/HTTPS | Client |
10
+ | `NodeFetchNetworkRequest` | `BaseNetworkRequest<'node-fetch'>` | HTTP/HTTPS | Client |
11
+ | `NetworkTcpClient` | `BaseNetworkTcpClient` | TCP | Client |
12
+ | `NetworkTcpServer` | `BaseNetworkTcpServer` | TCP | Server |
13
+ | `NetworkTlsTcpClient` | `BaseNetworkTcpClient` | TLS/SSL | Client |
14
+ | `NetworkTlsTcpServer` | `BaseNetworkTcpServer` | TLS/SSL | Server |
15
+ | `NetworkUdpClient` | `BaseHelper` | UDP | Client |
16
+
17
+ #### Import Paths
18
+
19
+ ```typescript
20
+ // Main package -- TCP, UDP, and Node Fetch
21
+ import {
22
+ NodeFetchNetworkRequest,
23
+ BaseNetworkRequest,
24
+ NetworkTcpClient,
25
+ NetworkTcpServer,
26
+ NetworkTlsTcpClient,
27
+ NetworkTlsTcpServer,
28
+ BaseNetworkTcpClient,
29
+ BaseNetworkTcpServer,
30
+ NetworkUdpClient,
31
+ } from '@venizia/ignis-helpers';
32
+
33
+ // Axios (separate export -- optional peer dependency)
34
+ import {
35
+ AxiosNetworkRequest,
36
+ AxiosFetcher,
37
+ type IAxiosNetworkRequestOptions,
38
+ type IAxiosRequestOptions,
39
+ } from '@venizia/ignis-helpers/axios';
40
+
41
+ // Types
42
+ import type {
43
+ INodeFetchNetworkRequestOptions,
44
+ INodeFetchRequestOptions,
45
+ INetworkTcpClientProps,
46
+ ITcpSocketServerOptions,
47
+ ITcpSocketClient,
48
+ IRequestOptions,
49
+ IFetchable,
50
+ TFetcherVariant,
51
+ } from '@venizia/ignis-helpers';
52
+ ```
53
+
54
+ ## Creating an Instance
55
+
56
+ ### HTTP Request
57
+
58
+ HTTP helpers follow a two-layer design: a `BaseNetworkRequest` that holds the base URL and delegates to an underlying `IFetchable` fetcher (either Axios or native `fetch`). You typically extend one of the concrete classes to create a typed API client.
59
+
60
+ #### Axios
61
+
62
+ ```typescript
63
+ import { AxiosNetworkRequest } from '@venizia/ignis-helpers/axios';
64
+
65
+ class PaymentGateway extends AxiosNetworkRequest {
66
+ constructor() {
67
+ super({
68
+ name: 'PaymentGateway',
69
+ networkOptions: {
70
+ baseUrl: 'https://api.payments.com',
71
+ timeout: 30000,
72
+ headers: {
73
+ 'X-API-Key': process.env.PAYMENT_API_KEY,
74
+ },
75
+ },
76
+ });
77
+ }
78
+ }
79
+ ```
80
+
81
+ #### `IAxiosNetworkRequestOptions`
82
+
83
+ | Option | Type | Default | Description |
84
+ |--------|------|---------|-------------|
85
+ | `name` | `string` | -- | Helper name, used for scoped logging |
86
+ | `networkOptions.baseUrl` | `string` | `undefined` | Base URL prepended to all request paths |
87
+ | `networkOptions.timeout` | `number` | `60000` | Request timeout in milliseconds |
88
+ | `networkOptions.headers` | `object` | `{ 'content-type': 'application/json; charset=utf-8' }` | Default headers; your values override the default content-type |
89
+ | `networkOptions.*` | `AxiosRequestConfig` | -- | All other Axios config options are accepted |
90
+
91
+ > [!TIP]
92
+ > `AxiosNetworkRequest` sets sensible defaults: `Content-Type: application/json`, `withCredentials: true`, `validateStatus: status < 500`, and `timeout: 60000` (1 minute). Your options override these defaults.
93
+
94
+ #### Node.js Fetch
95
+
96
+ ```typescript
97
+ import { NodeFetchNetworkRequest } from '@venizia/ignis-helpers';
98
+
99
+ class MyApiClient extends NodeFetchNetworkRequest {
100
+ constructor() {
101
+ super({
102
+ name: 'MyApiClient',
103
+ networkOptions: {
104
+ baseUrl: 'https://api.example.com',
105
+ headers: {
106
+ 'Authorization': 'Bearer my-token',
107
+ },
108
+ credentials: 'include',
109
+ mode: 'cors',
110
+ },
111
+ });
112
+ }
113
+ }
114
+ ```
115
+
116
+ #### `INodeFetchNetworkRequestOptions`
117
+
118
+ | Option | Type | Default | Description |
119
+ |--------|------|---------|-------------|
120
+ | `name` | `string` | -- | Helper name, used for scoped logging |
121
+ | `networkOptions.baseUrl` | `string` | `undefined` | Base URL prepended to all request paths |
122
+ | `networkOptions.headers` | `HeadersInit` | `{ 'content-type': 'application/json; charset=utf-8' }` | Default headers; supports `Headers` object or plain object |
123
+ | `networkOptions.*` | `RequestInit` | -- | All other `fetch` options are accepted |
124
+
125
+ ### TCP Client / Server
126
+
127
+ #### TCP Client
128
+
129
+ ```typescript
130
+ import { NetworkTcpClient } from '@venizia/ignis-helpers';
131
+
132
+ const tcpClient = new NetworkTcpClient({
133
+ identifier: 'sensor-reader',
134
+ options: {
135
+ host: 'localhost',
136
+ port: 8080,
137
+ },
138
+ reconnect: true,
139
+ maxRetry: 10,
140
+ encoding: 'utf8',
141
+ onConnected: ({ client }) => { console.log('Connected'); },
142
+ onData: ({ identifier, message }) => { console.log('Data:', message.toString()); },
143
+ onClosed: ({ client }) => { console.log('Closed'); },
144
+ onError: (error) => { console.error('Error:', error); },
145
+ });
146
+ ```
147
+
148
+ #### TCP Client Options (`INetworkTcpClientProps`)
149
+
150
+ | Option | Type | Default | Description |
151
+ |--------|------|---------|-------------|
152
+ | `identifier` | `string` | -- | Unique client identifier for logging |
153
+ | `scope` | `string` | `identifier` | Logger scope name |
154
+ | `options` | `TcpSocketConnectOpts` | -- | Node.js `net.connect` options (`host`, `port`, etc.) |
155
+ | `reconnect` | `boolean` | `false` | Enable automatic reconnection on error |
156
+ | `maxRetry` | `number` | `5` | Maximum reconnection attempts |
157
+ | `encoding` | `BufferEncoding` | `undefined` | Socket encoding (e.g., `'utf8'`) |
158
+ | `onConnected` | `(opts: { client }) => void` | Internal logger | Called when connection is established |
159
+ | `onData` | `(opts: { identifier, message }) => void` | No-op | Called when data is received |
160
+ | `onClosed` | `(opts: { client }) => void` | Internal logger | Called when connection closes |
161
+ | `onError` | `(error: any) => void` | Internal handler with auto-reconnect | Called on connection errors |
162
+
163
+ #### TCP Server
164
+
165
+ ```typescript
166
+ import { NetworkTcpServer } from '@venizia/ignis-helpers';
167
+
168
+ const tcpServer = new NetworkTcpServer({
169
+ identifier: 'my-tcp-server',
170
+ serverOptions: {},
171
+ listenOptions: { port: 8080, host: '0.0.0.0' },
172
+ authenticateOptions: { required: true, duration: 5000 },
173
+ onServerReady: ({ server }) => { console.log('Listening'); },
174
+ onClientConnected: ({ id, socket }) => { console.log(`Client ${id} connected`); },
175
+ onClientData: ({ id, socket, data }) => { console.log(`Data from ${id}:`, data.toString()); },
176
+ onClientClose: ({ id, socket }) => { console.log(`Client ${id} disconnected`); },
177
+ onClientError: ({ id, socket, error }) => { console.error(`Error from ${id}:`, error); },
178
+ });
179
+ ```
180
+
181
+ > [!IMPORTANT]
182
+ > When `authenticateOptions.required` is `true`, you **must** provide a positive `duration` value. Clients that do not authenticate within this duration are automatically disconnected with an "Unauthorized Client" message. Use `doAuthenticate()` in your `onClientData` handler to transition the client's state.
183
+
184
+ #### TCP Server Options (`ITcpSocketServerOptions`)
185
+
186
+ | Option | Type | Default | Description |
187
+ |--------|------|---------|-------------|
188
+ | `identifier` | `string` | -- | Server identifier for logging |
189
+ | `scope` | `string` | `identifier` | Logger scope name |
190
+ | `serverOptions` | `Partial<ServerOpts>` | -- | Node.js `net.Server` options |
191
+ | `listenOptions` | `Partial<ListenOptions>` | -- | Listen options (`port`, `host`, `backlog`, etc.) |
192
+ | `authenticateOptions.required` | `boolean` | -- | Whether clients must authenticate |
193
+ | `authenticateOptions.duration` | `number` | `undefined` | Auth timeout in ms (required when `required: true`) |
194
+ | `extraEvents` | `Record<string, (opts) => void>` | `{}` | Additional socket events to register per client |
195
+ | `onServerReady` | `(opts: { server }) => void` | `undefined` | Called when server starts listening |
196
+ | `onClientConnected` | `(opts: { id, socket }) => void` | `undefined` | Called on new client connection |
197
+ | `onClientData` | `(opts: { id, socket, data }) => void` | `undefined` | Called when data is received from a client |
198
+ | `onClientClose` | `(opts: { id, socket }) => void` | `undefined` | Called when a client disconnects |
199
+ | `onClientError` | `(opts: { id, socket, error }) => void` | `undefined` | Called on client socket error |
200
+
201
+ ### TLS Client / Server
202
+
203
+ TLS variants are identical to their TCP counterparts but use `node:tls` under the hood for encrypted connections. The constructor options accept TLS-specific fields (`cert`, `key`, `ca`, `rejectUnauthorized`, etc.) in addition to all the same handler options.
204
+
205
+ #### TLS Client
206
+
207
+ ```typescript
208
+ import { NetworkTlsTcpClient } from '@venizia/ignis-helpers';
209
+ import fs from 'node:fs';
210
+
211
+ const tlsClient = new NetworkTlsTcpClient({
212
+ identifier: 'secure-client',
213
+ options: {
214
+ host: 'secure.example.com',
215
+ port: 8443,
216
+ rejectUnauthorized: true,
217
+ ca: fs.readFileSync('ca.crt'),
218
+ cert: fs.readFileSync('client.crt'),
219
+ key: fs.readFileSync('client.key'),
220
+ },
221
+ reconnect: true,
222
+ maxRetry: 3,
223
+ onData: ({ message }) => { console.log('Secure data:', message); },
224
+ });
225
+
226
+ tlsClient.connect({ resetReconnectCounter: true });
227
+ ```
228
+
229
+ > [!NOTE]
230
+ > `NetworkTlsTcpClient` accepts `ConnectionOptions` from `node:tls` in the `options` field. All other options (`reconnect`, `maxRetry`, callbacks) are identical to `NetworkTcpClient`.
231
+
232
+ #### TLS Server
233
+
234
+ ```typescript
235
+ import { NetworkTlsTcpServer } from '@venizia/ignis-helpers';
236
+ import fs from 'node:fs';
237
+
238
+ const tlsServer = new NetworkTlsTcpServer({
239
+ identifier: 'secure-tcp-server',
240
+ serverOptions: {
241
+ cert: fs.readFileSync('server.crt'),
242
+ key: fs.readFileSync('server.key'),
243
+ ca: [fs.readFileSync('ca.crt')],
244
+ requestCert: true,
245
+ },
246
+ listenOptions: { port: 8443, host: '0.0.0.0' },
247
+ authenticateOptions: { required: false },
248
+ onClientData: ({ id, data }) => { console.log(`Secure data from ${id}:`, data.toString()); },
249
+ });
250
+ ```
251
+
252
+ > [!NOTE]
253
+ > `NetworkTlsTcpServer` accepts `TlsOptions` from `node:tls` in `serverOptions`. All handlers and methods are identical to `NetworkTcpServer`.
254
+
255
+ ### UDP Client
256
+
257
+ ```typescript
258
+ import { NetworkUdpClient } from '@venizia/ignis-helpers';
259
+
260
+ const udpClient = NetworkUdpClient.newInstance({
261
+ identifier: 'my-udp-client',
262
+ port: 8081,
263
+ host: '0.0.0.0',
264
+ reuseAddr: true,
265
+ multicastAddress: {
266
+ groups: ['239.1.2.3'],
267
+ interface: '0.0.0.0',
268
+ },
269
+ onData: ({ message, remoteInfo }) => {
270
+ console.log(`From ${remoteInfo.address}:${remoteInfo.port}:`, message.toString());
271
+ },
272
+ onBind: async ({ socket, multicastAddress }) => {
273
+ if (multicastAddress?.groups) {
274
+ for (const group of multicastAddress.groups) {
275
+ socket.addMembership(group, multicastAddress.interface);
276
+ }
277
+ }
278
+ },
279
+ });
280
+ ```
281
+
282
+ #### UDP Client Options (`INetworkUdpClientProps`)
283
+
284
+ | Option | Type | Default | Description |
285
+ |--------|------|---------|-------------|
286
+ | `identifier` | `string` | -- | Unique client identifier for logging |
287
+ | `host` | `string` | `undefined` | Bind address |
288
+ | `port` | `number` | -- | Bind port |
289
+ | `reuseAddr` | `boolean` | `undefined` | Allow address reuse (`SO_REUSEADDR`) |
290
+ | `multicastAddress.groups` | `string[]` | `undefined` | Multicast group addresses to join |
291
+ | `multicastAddress.interface` | `string` | `undefined` | Network interface for multicast |
292
+ | `onConnected` | `(opts: { identifier, host?, port }) => void` | Internal logger | Called when socket starts listening |
293
+ | `onData` | `(opts: { identifier, message, remoteInfo }) => void` | Internal logger | Called when data is received |
294
+ | `onClosed` | `(opts: { identifier, host?, port }) => void` | Internal logger | Called when socket is closed |
295
+ | `onError` | `(opts: { identifier, host?, port, error }) => void` | Internal logger | Called on socket error |
296
+ | `onBind` | `(opts: { identifier, socket, host?, port, reuseAddr?, multicastAddress? }) => void` | `undefined` | Called after socket is bound; use to join multicast groups |
297
+
298
+ ## Usage
299
+
300
+ ### HTTP Requests
301
+
302
+ Access the underlying fetcher via `getNetworkService()`, then use `send()` or the convenience methods (`get`, `post`, `put`, `patch`, `delete`):
303
+
304
+ ```typescript
305
+ class PaymentGateway extends AxiosNetworkRequest {
306
+ constructor() {
307
+ super({
308
+ name: 'PaymentGateway',
309
+ networkOptions: {
310
+ baseUrl: process.env.PAYMENT_API_URL,
311
+ timeout: 30000,
312
+ headers: { 'X-API-Key': process.env.PAYMENT_API_KEY },
313
+ },
314
+ });
315
+ }
316
+
317
+ async charge(amount: number, currency: string) {
318
+ const url = this.getRequestUrl({ paths: ['v1', 'charges'] });
319
+ const response = await this.getNetworkService().send({
320
+ url,
321
+ method: 'post',
322
+ body: { amount, currency },
323
+ });
324
+ this.logger.for('charge').info('Payment processed: %s', response.data.id);
325
+ return response.data;
326
+ }
327
+
328
+ async getTransaction(id: string) {
329
+ const url = this.getRequestUrl({ paths: ['v1', 'transactions', id] });
330
+ return this.getNetworkService().get({ url });
331
+ }
332
+ }
333
+ ```
334
+
335
+ #### Convenience Methods
336
+
337
+ ```typescript
338
+ const fetcher = this.getNetworkService();
339
+
340
+ // These are equivalent:
341
+ await fetcher.send({ url: '/users', method: 'get' });
342
+ await fetcher.get({ url: '/users' });
343
+
344
+ // POST with body
345
+ await fetcher.post({ url: '/users', body: { name: 'Alice' } });
346
+
347
+ // All methods: get(), post(), put(), patch(), delete()
348
+ ```
349
+
350
+ #### HTTPS with Axios
351
+
352
+ For HTTPS requests, the `AxiosFetcher` automatically creates an `https.Agent`. By default, `rejectUnauthorized` is `false`. Override it per request:
353
+
354
+ ```typescript
355
+ await fetcher.send({
356
+ url: 'https://strict-api.example.com/data',
357
+ method: 'get',
358
+ rejectUnauthorized: true,
359
+ });
360
+ ```
361
+
362
+ #### Timeout with Node Fetch
363
+
364
+ The `NodeFetcher` implements timeout via `AbortController`. Pass `timeout` in each `send()` call:
365
+
366
+ ```typescript
367
+ await fetcher.send({
368
+ url: '/slow-endpoint',
369
+ method: 'get',
370
+ timeout: 5000, // Aborts after 5 seconds
371
+ });
372
+ ```
373
+
374
+ ### TCP Communication
375
+
376
+ ```typescript
377
+ import { NetworkTcpClient, NetworkTcpServer } from '@venizia/ignis-helpers';
378
+
379
+ // --- Server ---
380
+ const server = new NetworkTcpServer({
381
+ identifier: 'echo-server',
382
+ serverOptions: {},
383
+ listenOptions: { port: 9000, host: '0.0.0.0' },
384
+ authenticateOptions: { required: false },
385
+ onClientData: ({ id, data }) => {
386
+ // Echo back to the sender
387
+ server.emit({ clientId: id, payload: data });
388
+ },
389
+ });
390
+
391
+ // --- Client ---
392
+ const client = new NetworkTcpClient({
393
+ identifier: 'echo-client',
394
+ options: { host: 'localhost', port: 9000 },
395
+ reconnect: true,
396
+ maxRetry: 5,
397
+ encoding: 'utf8',
398
+ onData: ({ message }) => { console.log('Echo:', message); },
399
+ });
400
+
401
+ client.connect({ resetReconnectCounter: true });
402
+ client.emit({ payload: 'Hello, Server!' });
403
+ ```
404
+
405
+ #### Server with Authentication Flow
406
+
407
+ ```typescript
408
+ const server = new NetworkTcpServer({
409
+ identifier: 'auth-tcp-server',
410
+ serverOptions: {},
411
+ listenOptions: { port: 9000, host: '0.0.0.0' },
412
+ authenticateOptions: { required: true, duration: 5000 },
413
+
414
+ onClientData: ({ id, data }) => {
415
+ const message = data.toString();
416
+ const client = server.getClient({ id });
417
+
418
+ if (client?.state === 'unauthorized') {
419
+ if (message === 'secret-token') {
420
+ server.doAuthenticate({ id, state: 'authenticated' });
421
+ server.emit({ clientId: id, payload: 'Authenticated!' });
422
+ } else {
423
+ server.emit({ clientId: id, payload: 'Invalid credentials' });
424
+ }
425
+ return;
426
+ }
427
+
428
+ // Handle authenticated client messages
429
+ console.log(`[${id}] ${message}`);
430
+ },
431
+ });
432
+ ```
433
+
434
+ #### Client State Tracking
435
+
436
+ Each connected client is tracked as an `ITcpSocketClient`:
437
+
438
+ ```typescript
439
+ interface ITcpSocketClient<SocketClientType> {
440
+ id: string;
441
+ socket: SocketClientType;
442
+ state: 'unauthorized' | 'authenticating' | 'authenticated';
443
+ subscriptions: Set<string>;
444
+ storage: {
445
+ connectedAt: dayjs.Dayjs;
446
+ authenticatedAt: dayjs.Dayjs | null;
447
+ [additionField: symbol | string]: any; // Extensible storage
448
+ };
449
+ }
450
+ ```
451
+
452
+ ### TLS Encrypted Communication
453
+
454
+ TLS classes share the same API as their TCP counterparts. Provide TLS certificates in the `options` or `serverOptions` field:
455
+
456
+ ```typescript
457
+ import { NetworkTlsTcpServer, NetworkTlsTcpClient } from '@venizia/ignis-helpers';
458
+ import fs from 'node:fs';
459
+
460
+ // --- TLS Server ---
461
+ const tlsServer = new NetworkTlsTcpServer({
462
+ identifier: 'secure-server',
463
+ serverOptions: {
464
+ cert: fs.readFileSync('server.crt'),
465
+ key: fs.readFileSync('server.key'),
466
+ ca: [fs.readFileSync('ca.crt')],
467
+ requestCert: true,
468
+ },
469
+ listenOptions: { port: 8443, host: '0.0.0.0' },
470
+ authenticateOptions: { required: false },
471
+ onClientData: ({ id, data }) => {
472
+ console.log(`Secure data from ${id}:`, data.toString());
473
+ },
474
+ });
475
+
476
+ // --- TLS Client ---
477
+ const tlsClient = new NetworkTlsTcpClient({
478
+ identifier: 'secure-client',
479
+ options: {
480
+ host: 'localhost',
481
+ port: 8443,
482
+ rejectUnauthorized: true,
483
+ ca: fs.readFileSync('ca.crt'),
484
+ cert: fs.readFileSync('client.crt'),
485
+ key: fs.readFileSync('client.key'),
486
+ },
487
+ onData: ({ message }) => { console.log('Secure:', message); },
488
+ });
489
+
490
+ tlsClient.connect({ resetReconnectCounter: true });
491
+ tlsClient.emit({ payload: 'Hello over TLS!' });
492
+ ```
493
+
494
+ ### UDP Communication
495
+
496
+ ```typescript
497
+ import { NetworkUdpClient } from '@venizia/ignis-helpers';
498
+
499
+ const udpClient = NetworkUdpClient.newInstance({
500
+ identifier: 'multicast-listener',
501
+ port: 5000,
502
+ host: '0.0.0.0',
503
+ reuseAddr: true,
504
+ multicastAddress: {
505
+ groups: ['239.1.2.3'],
506
+ interface: '0.0.0.0',
507
+ },
508
+ onData: ({ message, remoteInfo }) => {
509
+ console.log(`From ${remoteInfo.address}:${remoteInfo.port}:`, message.toString());
510
+ },
511
+ onBind: async ({ socket, multicastAddress }) => {
512
+ // Join multicast groups after socket is bound
513
+ if (multicastAddress?.groups) {
514
+ for (const group of multicastAddress.groups) {
515
+ socket.addMembership(group, multicastAddress.interface);
516
+ }
517
+ }
518
+ },
519
+ });
520
+
521
+ // Start listening
522
+ udpClient.connect();
523
+
524
+ // Access the underlying dgram.Socket
525
+ const socket = udpClient.getClient();
526
+
527
+ // Check if bound
528
+ if (udpClient.isConnected()) {
529
+ // Send a datagram via the raw socket
530
+ socket.send('Hello', 5001, '239.1.2.3');
531
+ }
532
+
533
+ // Stop listening
534
+ udpClient.disconnect();
535
+ ```
536
+
537
+ ## Troubleshooting
538
+
539
+ ### HTTP: "[getRequestUrl] Invalid configuration for third party request base url!"
540
+
541
+ **Cause:** `getRequestUrl()` was called but no `baseUrl` was provided at construction time or in the call's `opts.baseUrl` parameter.
542
+
543
+ **Fix:** Provide a `baseUrl` either in the constructor's `networkOptions` or pass it in the `opts` parameter of `getRequestUrl()`:
544
+
545
+ ```typescript
546
+ // At construction
547
+ const client = new AxiosNetworkRequest({
548
+ name: 'MyClient',
549
+ networkOptions: { baseUrl: 'https://api.example.com' },
550
+ });
551
+
552
+ // Or per call
553
+ const url = client.getRequestUrl({
554
+ baseUrl: 'https://api.example.com',
555
+ paths: ['v1', 'users'],
556
+ });
557
+ ```
558
+
559
+ ### HTTP: Timeout not working with NodeFetchNetworkRequest
560
+
561
+ **Cause:** The `timeout` option on the constructor's `networkOptions` is not automatically applied to individual requests. Timeout must be set per-request.
562
+
563
+ **Fix:** Pass `timeout` in each `send()` call:
564
+
565
+ ```typescript
566
+ await this.getNetworkService().send({
567
+ url: '/slow-endpoint',
568
+ method: 'get',
569
+ timeout: 5000,
570
+ });
571
+ ```
572
+
573
+ The `NodeFetcher` internally creates an `AbortController` and aborts the request after the specified timeout.
574
+
575
+ ### TCP Server: "Invalid authenticate duration"
576
+
577
+ **Cause:** `authenticateOptions.required` is `true` but `duration` is missing, zero, or negative.
578
+
579
+ **Fix:** Provide a positive `duration` value when authentication is required:
580
+
581
+ ```typescript
582
+ // Correct
583
+ authenticateOptions: { required: true, duration: 5000 }
584
+
585
+ // Wrong -- throws at construction time
586
+ authenticateOptions: { required: true }
587
+ authenticateOptions: { required: true, duration: -1 }
588
+ ```
589
+
590
+ ### TCP Client: Reconnect loop never stops
591
+
592
+ **Cause:** `maxRetry` is set to `-1` (infinite retries) and the target server is unreachable. The reconnect delay is fixed at 5 seconds between attempts.
593
+
594
+ **Fix:** Use a finite `maxRetry` value, or set `reconnect: false` if you handle reconnection externally:
595
+
596
+ ```typescript
597
+ const client = new NetworkTcpClient({
598
+ identifier: 'my-client',
599
+ options: { host: 'localhost', port: 8080 },
600
+ reconnect: true,
601
+ maxRetry: 5, // Stop after 5 attempts
602
+ });
603
+ ```
604
+
605
+ ### TCP Server: Client emit does nothing
606
+
607
+ **Cause:** The target client socket is not writable (already closed or half-closed), or the payload is empty. The server logs a warning but does not throw.
608
+
609
+ **Fix:** Check the client state before emitting:
610
+
611
+ ```typescript
612
+ const client = server.getClient({ id: clientId });
613
+ if (client && client.socket.writable) {
614
+ server.emit({ clientId, payload: 'Hello' });
615
+ }
616
+ ```
617
+
618
+ ## See Also
619
+
620
+ - [API Reference](./api) -- Full method signatures and types