@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.
- package/package.json +1 -1
- package/wiki/best-practices/architecture-decisions.md +0 -8
- package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
- package/wiki/best-practices/performance-optimization.md +3 -3
- package/wiki/best-practices/security-guidelines.md +2 -2
- package/wiki/best-practices/troubleshooting-tips.md +1 -1
- package/wiki/guides/core-concepts/components-guide.md +1 -1
- package/wiki/guides/core-concepts/components.md +2 -2
- package/wiki/guides/core-concepts/dependency-injection.md +1 -1
- package/wiki/guides/core-concepts/services.md +1 -1
- package/wiki/guides/tutorials/building-a-crud-api.md +1 -1
- package/wiki/guides/tutorials/ecommerce-api.md +2 -2
- package/wiki/guides/tutorials/realtime-chat.md +6 -6
- package/wiki/guides/tutorials/testing.md +1 -1
- package/wiki/references/base/bootstrapping.md +0 -2
- package/wiki/references/base/components.md +2 -2
- package/wiki/references/base/controllers.md +0 -1
- package/wiki/references/base/datasources.md +1 -1
- package/wiki/references/base/dependency-injection.md +1 -1
- package/wiki/references/base/filter-system/quick-reference.md +0 -14
- package/wiki/references/base/middlewares.md +0 -8
- package/wiki/references/base/providers.md +0 -9
- package/wiki/references/base/services.md +0 -1
- package/wiki/references/components/authentication/api.md +444 -0
- package/wiki/references/components/authentication/errors.md +177 -0
- package/wiki/references/components/authentication/index.md +571 -0
- package/wiki/references/components/authentication/usage.md +781 -0
- package/wiki/references/components/health-check.md +292 -103
- package/wiki/references/components/index.md +14 -12
- package/wiki/references/components/mail/api.md +505 -0
- package/wiki/references/components/mail/errors.md +176 -0
- package/wiki/references/components/mail/index.md +535 -0
- package/wiki/references/components/mail/usage.md +404 -0
- package/wiki/references/components/request-tracker.md +229 -25
- package/wiki/references/components/socket-io/api.md +1051 -0
- package/wiki/references/components/socket-io/errors.md +119 -0
- package/wiki/references/components/socket-io/index.md +410 -0
- package/wiki/references/components/socket-io/usage.md +322 -0
- package/wiki/references/components/static-asset/api.md +261 -0
- package/wiki/references/components/static-asset/errors.md +89 -0
- package/wiki/references/components/static-asset/index.md +617 -0
- package/wiki/references/components/static-asset/usage.md +364 -0
- package/wiki/references/components/swagger.md +390 -110
- package/wiki/references/components/template/api-page.md +125 -0
- package/wiki/references/components/template/errors-page.md +100 -0
- package/wiki/references/components/template/index.md +104 -0
- package/wiki/references/components/template/setup-page.md +134 -0
- package/wiki/references/components/template/single-page.md +132 -0
- package/wiki/references/components/template/usage-page.md +127 -0
- package/wiki/references/components/websocket/api.md +508 -0
- package/wiki/references/components/websocket/errors.md +123 -0
- package/wiki/references/components/websocket/index.md +453 -0
- package/wiki/references/components/websocket/usage.md +475 -0
- package/wiki/references/helpers/cron/index.md +224 -0
- package/wiki/references/helpers/crypto/index.md +537 -0
- package/wiki/references/helpers/env/index.md +214 -0
- package/wiki/references/helpers/error/index.md +232 -0
- package/wiki/references/helpers/index.md +16 -15
- package/wiki/references/helpers/inversion/index.md +608 -0
- package/wiki/references/helpers/logger/index.md +600 -0
- package/wiki/references/helpers/network/api.md +986 -0
- package/wiki/references/helpers/network/index.md +620 -0
- package/wiki/references/helpers/queue/index.md +589 -0
- package/wiki/references/helpers/redis/index.md +495 -0
- package/wiki/references/helpers/socket-io/api.md +497 -0
- package/wiki/references/helpers/socket-io/index.md +513 -0
- package/wiki/references/helpers/storage/api.md +705 -0
- package/wiki/references/helpers/storage/index.md +583 -0
- package/wiki/references/helpers/template/index.md +66 -0
- package/wiki/references/helpers/template/single-page.md +126 -0
- package/wiki/references/helpers/testing/index.md +510 -0
- package/wiki/references/helpers/types/index.md +512 -0
- package/wiki/references/helpers/uid/index.md +272 -0
- package/wiki/references/helpers/websocket/api.md +736 -0
- package/wiki/references/helpers/websocket/index.md +574 -0
- package/wiki/references/helpers/worker-thread/index.md +470 -0
- package/wiki/references/quick-reference.md +3 -18
- package/wiki/references/utilities/jsx.md +1 -8
- package/wiki/references/utilities/statuses.md +0 -7
- package/wiki/references/components/authentication.md +0 -476
- package/wiki/references/components/mail.md +0 -687
- package/wiki/references/components/socket-io.md +0 -562
- package/wiki/references/components/static-asset.md +0 -1277
- package/wiki/references/helpers/cron.md +0 -108
- package/wiki/references/helpers/crypto.md +0 -132
- package/wiki/references/helpers/env.md +0 -83
- package/wiki/references/helpers/error.md +0 -97
- package/wiki/references/helpers/inversion.md +0 -176
- package/wiki/references/helpers/logger.md +0 -296
- package/wiki/references/helpers/network.md +0 -396
- package/wiki/references/helpers/queue.md +0 -150
- package/wiki/references/helpers/redis.md +0 -142
- package/wiki/references/helpers/socket-io.md +0 -932
- package/wiki/references/helpers/storage.md +0 -665
- package/wiki/references/helpers/testing.md +0 -133
- package/wiki/references/helpers/types.md +0 -167
- package/wiki/references/helpers/uid.md +0 -167
- 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
|