recker 1.0.4 → 1.0.6
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 +1 -1
- package/dist/ai/adaptive-timeout.d.ts +51 -0
- package/dist/ai/adaptive-timeout.d.ts.map +1 -0
- package/dist/ai/adaptive-timeout.js +208 -0
- package/dist/ai/client.d.ts +24 -0
- package/dist/ai/client.d.ts.map +1 -0
- package/dist/ai/client.js +289 -0
- package/dist/ai/index.d.ts +10 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +6 -0
- package/dist/ai/providers/anthropic.d.ts +64 -0
- package/dist/ai/providers/anthropic.d.ts.map +1 -0
- package/dist/ai/providers/anthropic.js +367 -0
- package/dist/ai/providers/base.d.ts +49 -0
- package/dist/ai/providers/base.d.ts.map +1 -0
- package/dist/ai/providers/base.js +145 -0
- package/dist/ai/providers/index.d.ts +7 -0
- package/dist/ai/providers/index.d.ts.map +1 -0
- package/dist/ai/providers/index.js +3 -0
- package/dist/ai/providers/openai.d.ts +65 -0
- package/dist/ai/providers/openai.d.ts.map +1 -0
- package/dist/ai/providers/openai.js +298 -0
- package/dist/ai/rate-limiter.d.ts +44 -0
- package/dist/ai/rate-limiter.d.ts.map +1 -0
- package/dist/ai/rate-limiter.js +212 -0
- package/dist/bench/generator.d.ts +19 -0
- package/dist/bench/generator.d.ts.map +1 -0
- package/dist/bench/generator.js +86 -0
- package/dist/bench/stats.d.ts +35 -0
- package/dist/bench/stats.d.ts.map +1 -0
- package/dist/bench/stats.js +60 -0
- package/dist/cache/memory-storage.d.ts.map +1 -1
- package/dist/cache/memory-storage.js +0 -5
- package/dist/cli/handler.d.ts +11 -0
- package/dist/cli/handler.d.ts.map +1 -0
- package/dist/cli/handler.js +92 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +255 -0
- package/dist/cli/presets.d.ts +2 -0
- package/dist/cli/presets.d.ts.map +1 -0
- package/dist/cli/presets.js +67 -0
- package/dist/cli/tui/ai-chat.d.ts +3 -0
- package/dist/cli/tui/ai-chat.d.ts.map +1 -0
- package/dist/cli/tui/ai-chat.js +100 -0
- package/dist/cli/tui/load-dashboard.d.ts +3 -0
- package/dist/cli/tui/load-dashboard.d.ts.map +1 -0
- package/dist/cli/tui/load-dashboard.js +117 -0
- package/dist/cli/tui/shell.d.ts +27 -0
- package/dist/cli/tui/shell.d.ts.map +1 -0
- package/dist/cli/tui/shell.js +386 -0
- package/dist/cli/tui/websocket.d.ts +2 -0
- package/dist/cli/tui/websocket.d.ts.map +1 -0
- package/dist/cli/tui/websocket.js +87 -0
- package/dist/contract/index.d.ts +2 -2
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/core/client.d.ts +1 -0
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +4 -2
- package/dist/core/request-promise.d.ts +1 -1
- package/dist/core/request-promise.d.ts.map +1 -1
- package/dist/mcp/contract.d.ts +1 -1
- package/dist/mcp/contract.d.ts.map +1 -1
- package/dist/plugins/cache.d.ts.map +1 -1
- package/dist/plugins/cache.js +0 -7
- package/dist/plugins/scrape.d.ts.map +1 -1
- package/dist/plugins/scrape.js +9 -14
- package/dist/protocols/ftp.d.ts +28 -5
- package/dist/protocols/ftp.d.ts.map +1 -1
- package/dist/protocols/ftp.js +549 -136
- package/dist/protocols/sftp.d.ts +4 -2
- package/dist/protocols/sftp.d.ts.map +1 -1
- package/dist/protocols/sftp.js +16 -2
- package/dist/protocols/telnet.d.ts +37 -5
- package/dist/protocols/telnet.d.ts.map +1 -1
- package/dist/protocols/telnet.js +434 -58
- package/dist/runner/request-runner.d.ts.map +1 -1
- package/dist/runner/request-runner.js +0 -1
- package/dist/scrape/document.d.ts +3 -2
- package/dist/scrape/document.d.ts.map +1 -1
- package/dist/scrape/document.js +15 -3
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +1 -0
- package/dist/testing/mock-udp-server.d.ts +44 -0
- package/dist/testing/mock-udp-server.d.ts.map +1 -0
- package/dist/testing/mock-udp-server.js +188 -0
- package/dist/transport/base-udp.d.ts +36 -0
- package/dist/transport/base-udp.d.ts.map +1 -0
- package/dist/transport/base-udp.js +188 -0
- package/dist/transport/udp-response.d.ts +65 -0
- package/dist/transport/udp-response.d.ts.map +1 -0
- package/dist/transport/udp-response.js +269 -0
- package/dist/transport/udp.d.ts +22 -0
- package/dist/transport/udp.d.ts.map +1 -0
- package/dist/transport/udp.js +260 -0
- package/dist/types/ai.d.ts +268 -0
- package/dist/types/ai.d.ts.map +1 -0
- package/dist/types/ai.js +1 -0
- package/dist/types/udp.d.ts +138 -0
- package/dist/types/udp.d.ts.map +1 -0
- package/dist/types/udp.js +1 -0
- package/dist/udp/index.d.ts +6 -0
- package/dist/udp/index.d.ts.map +1 -0
- package/dist/udp/index.js +3 -0
- package/dist/utils/chart.d.ts +15 -0
- package/dist/utils/chart.d.ts.map +1 -0
- package/dist/utils/chart.js +94 -0
- package/dist/utils/colors.d.ts +27 -0
- package/dist/utils/colors.d.ts.map +1 -0
- package/dist/utils/colors.js +50 -0
- package/dist/utils/optional-require.d.ts +20 -0
- package/dist/utils/optional-require.d.ts.map +1 -0
- package/dist/utils/optional-require.js +105 -0
- package/package.json +53 -12
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-udp-server.d.ts","sourceRoot":"","sources":["../../src/testing/mock-udp-server.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,oBAAoB;IAKnC,IAAI,CAAC,EAAE,MAAM,CAAC;IAMd,IAAI,CAAC,EAAE,MAAM,CAAC;IAMd,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAMvB,IAAI,CAAC,EAAE,OAAO,CAAC;IAMf,KAAK,CAAC,EAAE,MAAM,CAAC;IAMf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD,qBAAa,aAAc,SAAQ,YAAY;IAC7C,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,SAAS,CAA8F;IAC/G,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,iBAAiB,CAAyB;IAClD,OAAO,CAAC,QAAQ,CAAkB;gBAEtB,OAAO,GAAE,oBAAyB;IAgB9C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAKD,IAAI,OAAO,IAAI,MAAM,CAEpB;IAKD,IAAI,SAAS,IAAI,OAAO,CAEvB;IAKD,IAAI,gBAAgB,IAAI,eAAe,EAAE,CAExC;IAKD,IAAI,YAAY,IAAI,MAAM,CAEzB;IAKK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA2DtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB3B,WAAW,CACT,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,UAAU,KAAK,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,GAC7F,IAAI;IAkBP,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAO7B,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAO/B,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAO/B,aAAa,IAAI,IAAI;IAOrB,cAAc,IAAI,IAAI;IAOtB,KAAK,IAAI,IAAI;IAWb,OAAO,CAAC,WAAW;IAyBb,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,MAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBzF,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,MAAa,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;WAgB3E,MAAM,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,aAAa,CAAC;CAKhF;AAKD,wBAAsB,mBAAmB,CACvC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EAC3C,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,aAAa,CAAC,CAYxB"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import dgram from 'node:dgram';
|
|
2
|
+
import { EventEmitter } from 'node:events';
|
|
3
|
+
export class MockUDPServer extends EventEmitter {
|
|
4
|
+
socket = null;
|
|
5
|
+
options;
|
|
6
|
+
responses = new Map();
|
|
7
|
+
_port = 0;
|
|
8
|
+
_receivedMessages = [];
|
|
9
|
+
_started = false;
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
super();
|
|
12
|
+
this.options = {
|
|
13
|
+
port: 0,
|
|
14
|
+
host: '127.0.0.1',
|
|
15
|
+
type: 'udp4',
|
|
16
|
+
echo: true,
|
|
17
|
+
delay: 0,
|
|
18
|
+
dropRate: 0,
|
|
19
|
+
...options,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
get port() {
|
|
23
|
+
return this._port;
|
|
24
|
+
}
|
|
25
|
+
get address() {
|
|
26
|
+
return this.options.host;
|
|
27
|
+
}
|
|
28
|
+
get isRunning() {
|
|
29
|
+
return this._started;
|
|
30
|
+
}
|
|
31
|
+
get receivedMessages() {
|
|
32
|
+
return [...this._receivedMessages];
|
|
33
|
+
}
|
|
34
|
+
get messageCount() {
|
|
35
|
+
return this._receivedMessages.length;
|
|
36
|
+
}
|
|
37
|
+
async start() {
|
|
38
|
+
if (this._started) {
|
|
39
|
+
throw new Error('Server already started');
|
|
40
|
+
}
|
|
41
|
+
this.socket = dgram.createSocket(this.options.type);
|
|
42
|
+
this.socket.on('message', async (msg, rinfo) => {
|
|
43
|
+
this._receivedMessages.push({
|
|
44
|
+
data: msg,
|
|
45
|
+
rinfo,
|
|
46
|
+
timestamp: Date.now(),
|
|
47
|
+
});
|
|
48
|
+
this.emit('message', msg, rinfo);
|
|
49
|
+
if (this.options.dropRate > 0 && Math.random() < this.options.dropRate) {
|
|
50
|
+
this.emit('dropped', msg, rinfo);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (this.options.delay > 0) {
|
|
54
|
+
await new Promise((resolve) => setTimeout(resolve, this.options.delay));
|
|
55
|
+
}
|
|
56
|
+
const response = this.getResponse(msg, rinfo);
|
|
57
|
+
if (response) {
|
|
58
|
+
this.socket.send(response, rinfo.port, rinfo.address, (err) => {
|
|
59
|
+
if (err) {
|
|
60
|
+
this.emit('error', err);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
this.emit('sent', response, rinfo);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
this.socket.on('error', (err) => {
|
|
69
|
+
this.emit('error', err);
|
|
70
|
+
});
|
|
71
|
+
return new Promise((resolve) => {
|
|
72
|
+
this.socket.bind(this.options.port, this.options.host, () => {
|
|
73
|
+
const addr = this.socket.address();
|
|
74
|
+
this._port = typeof addr === 'string' ? 0 : addr.port;
|
|
75
|
+
this._started = true;
|
|
76
|
+
this.emit('listening', this._port);
|
|
77
|
+
resolve();
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async stop() {
|
|
82
|
+
if (!this._started || !this.socket) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
return new Promise((resolve) => {
|
|
86
|
+
this.socket.close(() => {
|
|
87
|
+
this._started = false;
|
|
88
|
+
this.socket = null;
|
|
89
|
+
this.emit('close');
|
|
90
|
+
resolve();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
setResponse(pattern, response) {
|
|
95
|
+
const key = pattern instanceof RegExp ? pattern.source : pattern;
|
|
96
|
+
const handler = typeof response === 'function'
|
|
97
|
+
? (msg, rinfo) => {
|
|
98
|
+
const result = response(msg, rinfo);
|
|
99
|
+
if (result === null)
|
|
100
|
+
return null;
|
|
101
|
+
return typeof result === 'string' ? Buffer.from(result) : result;
|
|
102
|
+
}
|
|
103
|
+
: typeof response === 'string'
|
|
104
|
+
? Buffer.from(response)
|
|
105
|
+
: response;
|
|
106
|
+
this.responses.set(key, handler);
|
|
107
|
+
}
|
|
108
|
+
setDelay(delay) {
|
|
109
|
+
this.options.delay = delay;
|
|
110
|
+
}
|
|
111
|
+
setDropRate(rate) {
|
|
112
|
+
this.options.dropRate = Math.max(0, Math.min(1, rate));
|
|
113
|
+
}
|
|
114
|
+
setEcho(enabled) {
|
|
115
|
+
this.options.echo = enabled;
|
|
116
|
+
}
|
|
117
|
+
clearMessages() {
|
|
118
|
+
this._receivedMessages = [];
|
|
119
|
+
}
|
|
120
|
+
clearResponses() {
|
|
121
|
+
this.responses.clear();
|
|
122
|
+
}
|
|
123
|
+
reset() {
|
|
124
|
+
this.clearMessages();
|
|
125
|
+
this.clearResponses();
|
|
126
|
+
this.options.delay = 0;
|
|
127
|
+
this.options.dropRate = 0;
|
|
128
|
+
this.options.echo = true;
|
|
129
|
+
}
|
|
130
|
+
getResponse(msg, rinfo) {
|
|
131
|
+
const msgStr = msg.toString();
|
|
132
|
+
for (const [pattern, response] of this.responses) {
|
|
133
|
+
const regex = new RegExp(pattern);
|
|
134
|
+
if (regex.test(msgStr)) {
|
|
135
|
+
if (typeof response === 'function') {
|
|
136
|
+
return response(msg, rinfo);
|
|
137
|
+
}
|
|
138
|
+
return response;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (this.options.echo) {
|
|
142
|
+
return msg;
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
async sendTo(data, port, address = '127.0.0.1') {
|
|
147
|
+
if (!this.socket || !this._started) {
|
|
148
|
+
throw new Error('Server not started');
|
|
149
|
+
}
|
|
150
|
+
const buffer = typeof data === 'string' ? Buffer.from(data) : data;
|
|
151
|
+
return new Promise((resolve, reject) => {
|
|
152
|
+
this.socket.send(buffer, port, address, (err) => {
|
|
153
|
+
if (err) {
|
|
154
|
+
reject(err);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
resolve();
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
async waitForMessages(count, timeout = 5000) {
|
|
163
|
+
const startTime = Date.now();
|
|
164
|
+
while (this._receivedMessages.length < count) {
|
|
165
|
+
if (Date.now() - startTime > timeout) {
|
|
166
|
+
throw new Error(`Timeout waiting for ${count} messages (received ${this._receivedMessages.length})`);
|
|
167
|
+
}
|
|
168
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
169
|
+
}
|
|
170
|
+
return this._receivedMessages.slice(0, count);
|
|
171
|
+
}
|
|
172
|
+
static async create(options = {}) {
|
|
173
|
+
const server = new MockUDPServer(options);
|
|
174
|
+
await server.start();
|
|
175
|
+
return server;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
export async function createMockUDPServer(responses, options) {
|
|
179
|
+
const server = new MockUDPServer(options);
|
|
180
|
+
if (responses) {
|
|
181
|
+
for (const [pattern, response] of Object.entries(responses)) {
|
|
182
|
+
server.setResponse(pattern, response);
|
|
183
|
+
}
|
|
184
|
+
server.setEcho(false);
|
|
185
|
+
}
|
|
186
|
+
await server.start();
|
|
187
|
+
return server;
|
|
188
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import dgram from 'node:dgram';
|
|
2
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
3
|
+
import type { ReckerRequest, ReckerResponse } from '../types/index.js';
|
|
4
|
+
import type { BaseUDPTransportOptions, UDPTimings, UDPConnection } from '../types/udp.js';
|
|
5
|
+
export interface UDPRequestContext {
|
|
6
|
+
startTime: number;
|
|
7
|
+
queuedTime?: number;
|
|
8
|
+
sendTime?: number;
|
|
9
|
+
receiveTime?: number;
|
|
10
|
+
retransmissions: number;
|
|
11
|
+
localAddress?: string;
|
|
12
|
+
localPort?: number;
|
|
13
|
+
remoteAddress?: string;
|
|
14
|
+
remotePort?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare const udpRequestStorage: AsyncLocalStorage<UDPRequestContext>;
|
|
17
|
+
export declare const DEFAULT_UDP_OPTIONS: Required<BaseUDPTransportOptions>;
|
|
18
|
+
export declare abstract class BaseUDPTransport {
|
|
19
|
+
protected options: Required<BaseUDPTransportOptions>;
|
|
20
|
+
constructor(options?: BaseUDPTransportOptions);
|
|
21
|
+
abstract dispatch(req: ReckerRequest): Promise<ReckerResponse>;
|
|
22
|
+
protected createSocket(type?: 'udp4' | 'udp6'): dgram.Socket;
|
|
23
|
+
protected sendWithRetry(socket: dgram.Socket, data: Buffer, port: number, address: string, signal?: AbortSignal): Promise<Buffer>;
|
|
24
|
+
protected sendOnce(socket: dgram.Socket, data: Buffer, port: number, address: string, signal?: AbortSignal): Promise<Buffer>;
|
|
25
|
+
protected collectTimings(): UDPTimings;
|
|
26
|
+
protected collectConnection(socket: dgram.Socket): UDPConnection;
|
|
27
|
+
protected parseUrl(url: string): {
|
|
28
|
+
host: string;
|
|
29
|
+
port: number;
|
|
30
|
+
path: string;
|
|
31
|
+
};
|
|
32
|
+
protected validatePacketSize(data: Buffer): void;
|
|
33
|
+
protected sleep(ms: number): Promise<void>;
|
|
34
|
+
close(): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=base-udp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-udp.d.ts","sourceRoot":"","sources":["../../src/transport/base-udp.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EACV,uBAAuB,EACvB,UAAU,EACV,aAAa,EACd,MAAM,iBAAiB,CAAC;AAMzB,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAKD,eAAO,MAAM,iBAAiB,sCAA6C,CAAC;AAK5E,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,uBAAuB,CAOjE,CAAC;AAKF,8BAAsB,gBAAgB;IACpC,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC;gBAEzC,OAAO,GAAE,uBAA4B;IAUjD,QAAQ,CAAC,QAAQ,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IAK9D,SAAS,CAAC,YAAY,CAAC,IAAI,GAAE,MAAM,GAAG,MAAe,GAAG,KAAK,CAAC,MAAM;cAiBpD,aAAa,CAC3B,MAAM,EAAE,KAAK,CAAC,MAAM,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,CAAC;IAsClB,SAAS,CAAC,QAAQ,CAChB,MAAM,EAAE,KAAK,CAAC,MAAM,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,CAAC;IAmFlB,SAAS,CAAC,cAAc,IAAI,UAAU;IAwCtC,SAAS,CAAC,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,aAAa;IAgBhE,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;IAqB7E,SAAS,CAAC,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAWhD,SAAS,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOpC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import dgram from 'node:dgram';
|
|
2
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
3
|
+
import { TimeoutError, NetworkError } from '../core/errors.js';
|
|
4
|
+
export const udpRequestStorage = new AsyncLocalStorage();
|
|
5
|
+
export const DEFAULT_UDP_OPTIONS = {
|
|
6
|
+
timeout: 5000,
|
|
7
|
+
retransmissions: 3,
|
|
8
|
+
maxPacketSize: 65507,
|
|
9
|
+
observability: true,
|
|
10
|
+
localAddress: '',
|
|
11
|
+
localPort: 0,
|
|
12
|
+
};
|
|
13
|
+
export class BaseUDPTransport {
|
|
14
|
+
options;
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
this.options = {
|
|
17
|
+
...DEFAULT_UDP_OPTIONS,
|
|
18
|
+
...options,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
createSocket(type = 'udp4') {
|
|
22
|
+
const socket = dgram.createSocket({
|
|
23
|
+
type,
|
|
24
|
+
reuseAddr: true,
|
|
25
|
+
});
|
|
26
|
+
if (this.options.localAddress || this.options.localPort) {
|
|
27
|
+
socket.bind(this.options.localPort, this.options.localAddress || undefined);
|
|
28
|
+
}
|
|
29
|
+
return socket;
|
|
30
|
+
}
|
|
31
|
+
async sendWithRetry(socket, data, port, address, signal) {
|
|
32
|
+
const context = udpRequestStorage.getStore();
|
|
33
|
+
let attempts = 0;
|
|
34
|
+
const maxAttempts = this.options.retransmissions + 1;
|
|
35
|
+
while (attempts < maxAttempts) {
|
|
36
|
+
try {
|
|
37
|
+
if (signal?.aborted) {
|
|
38
|
+
throw new Error('Request aborted');
|
|
39
|
+
}
|
|
40
|
+
const response = await this.sendOnce(socket, data, port, address, signal);
|
|
41
|
+
return response;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
attempts++;
|
|
45
|
+
if (context) {
|
|
46
|
+
context.retransmissions = attempts - 1;
|
|
47
|
+
}
|
|
48
|
+
if (attempts >= maxAttempts) {
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
const delay = Math.min(1000 * Math.pow(2, attempts - 1), 5000);
|
|
52
|
+
await this.sleep(delay);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
throw new TimeoutError({ url: `udp://${address}:${port}` }, { phase: 'response', timeout: this.options.timeout });
|
|
56
|
+
}
|
|
57
|
+
sendOnce(socket, data, port, address, signal) {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
const context = udpRequestStorage.getStore();
|
|
60
|
+
let timeoutId;
|
|
61
|
+
let resolved = false;
|
|
62
|
+
const cleanup = () => {
|
|
63
|
+
if (timeoutId) {
|
|
64
|
+
clearTimeout(timeoutId);
|
|
65
|
+
}
|
|
66
|
+
socket.removeListener('message', onMessage);
|
|
67
|
+
socket.removeListener('error', onError);
|
|
68
|
+
};
|
|
69
|
+
const onMessage = (msg, rinfo) => {
|
|
70
|
+
if (resolved)
|
|
71
|
+
return;
|
|
72
|
+
resolved = true;
|
|
73
|
+
if (context && this.options.observability) {
|
|
74
|
+
context.receiveTime = performance.now();
|
|
75
|
+
context.remoteAddress = rinfo.address;
|
|
76
|
+
context.remotePort = rinfo.port;
|
|
77
|
+
}
|
|
78
|
+
cleanup();
|
|
79
|
+
resolve(msg);
|
|
80
|
+
};
|
|
81
|
+
const onError = (err) => {
|
|
82
|
+
if (resolved)
|
|
83
|
+
return;
|
|
84
|
+
resolved = true;
|
|
85
|
+
cleanup();
|
|
86
|
+
reject(new NetworkError(err.message, err.code || 'UNKNOWN', {}));
|
|
87
|
+
};
|
|
88
|
+
const onTimeout = () => {
|
|
89
|
+
if (resolved)
|
|
90
|
+
return;
|
|
91
|
+
resolved = true;
|
|
92
|
+
cleanup();
|
|
93
|
+
reject(new TimeoutError({ url: `udp://${address}:${port}` }, {
|
|
94
|
+
phase: 'response',
|
|
95
|
+
timeout: this.options.timeout,
|
|
96
|
+
}));
|
|
97
|
+
};
|
|
98
|
+
if (signal) {
|
|
99
|
+
signal.addEventListener('abort', () => {
|
|
100
|
+
if (resolved)
|
|
101
|
+
return;
|
|
102
|
+
resolved = true;
|
|
103
|
+
cleanup();
|
|
104
|
+
reject(new Error('Request aborted'));
|
|
105
|
+
}, { once: true });
|
|
106
|
+
}
|
|
107
|
+
socket.on('message', onMessage);
|
|
108
|
+
socket.on('error', onError);
|
|
109
|
+
timeoutId = setTimeout(onTimeout, this.options.timeout);
|
|
110
|
+
if (context && this.options.observability) {
|
|
111
|
+
context.sendTime = performance.now();
|
|
112
|
+
}
|
|
113
|
+
socket.send(data, 0, data.length, port, address, (err) => {
|
|
114
|
+
if (err) {
|
|
115
|
+
if (resolved)
|
|
116
|
+
return;
|
|
117
|
+
resolved = true;
|
|
118
|
+
cleanup();
|
|
119
|
+
reject(new NetworkError(err.message, err.code || 'SEND_ERROR', {}));
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
collectTimings() {
|
|
125
|
+
const context = udpRequestStorage.getStore();
|
|
126
|
+
if (!context || !this.options.observability) {
|
|
127
|
+
return {
|
|
128
|
+
queued: 0,
|
|
129
|
+
send: 0,
|
|
130
|
+
receive: 0,
|
|
131
|
+
retransmissions: 0,
|
|
132
|
+
total: 0,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const now = performance.now();
|
|
136
|
+
const queued = context.queuedTime
|
|
137
|
+
? context.sendTime - context.queuedTime
|
|
138
|
+
: context.sendTime
|
|
139
|
+
? context.sendTime - context.startTime
|
|
140
|
+
: 0;
|
|
141
|
+
const send = context.sendTime && context.receiveTime
|
|
142
|
+
? 0.1
|
|
143
|
+
: 0;
|
|
144
|
+
const receive = context.sendTime && context.receiveTime
|
|
145
|
+
? context.receiveTime - context.sendTime
|
|
146
|
+
: 0;
|
|
147
|
+
return {
|
|
148
|
+
queued: Math.round(queued * 100) / 100,
|
|
149
|
+
send: Math.round(send * 100) / 100,
|
|
150
|
+
receive: Math.round(receive * 100) / 100,
|
|
151
|
+
retransmissions: context.retransmissions,
|
|
152
|
+
total: Math.round((now - context.startTime) * 100) / 100,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
collectConnection(socket) {
|
|
156
|
+
const context = udpRequestStorage.getStore();
|
|
157
|
+
const address = socket.address();
|
|
158
|
+
return {
|
|
159
|
+
protocol: 'udp',
|
|
160
|
+
localAddress: typeof address === 'string' ? address : address.address,
|
|
161
|
+
localPort: typeof address === 'string' ? 0 : address.port,
|
|
162
|
+
remoteAddress: context?.remoteAddress || '',
|
|
163
|
+
remotePort: context?.remotePort || 0,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
parseUrl(url) {
|
|
167
|
+
let cleanUrl = url;
|
|
168
|
+
if (url.startsWith('udp://')) {
|
|
169
|
+
cleanUrl = url.slice(6);
|
|
170
|
+
}
|
|
171
|
+
const pathIndex = cleanUrl.indexOf('/');
|
|
172
|
+
const hostPort = pathIndex > -1 ? cleanUrl.slice(0, pathIndex) : cleanUrl;
|
|
173
|
+
const path = pathIndex > -1 ? cleanUrl.slice(pathIndex) : '/';
|
|
174
|
+
const [host, portStr] = hostPort.split(':');
|
|
175
|
+
const port = portStr ? parseInt(portStr, 10) : 0;
|
|
176
|
+
return { host, port, path };
|
|
177
|
+
}
|
|
178
|
+
validatePacketSize(data) {
|
|
179
|
+
if (data.length > this.options.maxPacketSize) {
|
|
180
|
+
throw new Error(`Packet size ${data.length} exceeds maximum ${this.options.maxPacketSize} bytes`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
sleep(ms) {
|
|
184
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
185
|
+
}
|
|
186
|
+
async close() {
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { SSEEvent, ProgressEvent } from '../types/index.js';
|
|
2
|
+
import type { UDPTimings, UDPConnection, UDPResponse } from '../types/udp.js';
|
|
3
|
+
export interface UDPResponseOptions {
|
|
4
|
+
timings?: UDPTimings;
|
|
5
|
+
connection?: UDPConnection;
|
|
6
|
+
url?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class UDPResponseImpl implements UDPResponse {
|
|
9
|
+
private _buffer;
|
|
10
|
+
private _timings;
|
|
11
|
+
private _connection;
|
|
12
|
+
private _url;
|
|
13
|
+
private _bodyUsed;
|
|
14
|
+
readonly status: number;
|
|
15
|
+
readonly statusText: string;
|
|
16
|
+
readonly headers: Headers;
|
|
17
|
+
readonly ok: boolean;
|
|
18
|
+
readonly raw: Response;
|
|
19
|
+
constructor(data: Buffer, options?: UDPResponseOptions);
|
|
20
|
+
get url(): string;
|
|
21
|
+
get timings(): UDPTimings;
|
|
22
|
+
get connection(): UDPConnection;
|
|
23
|
+
buffer(): Promise<Buffer>;
|
|
24
|
+
json<R = unknown>(): Promise<R>;
|
|
25
|
+
text(): Promise<string>;
|
|
26
|
+
cleanText(): Promise<string>;
|
|
27
|
+
blob(): Promise<Blob>;
|
|
28
|
+
read(): ReadableStream<Uint8Array> | null;
|
|
29
|
+
clone(): UDPResponseImpl;
|
|
30
|
+
sse(): AsyncGenerator<SSEEvent>;
|
|
31
|
+
download(): AsyncGenerator<ProgressEvent>;
|
|
32
|
+
packets(): AsyncGenerator<Buffer>;
|
|
33
|
+
[Symbol.asyncIterator](): AsyncGenerator<Uint8Array>;
|
|
34
|
+
}
|
|
35
|
+
export declare class StreamingUDPResponse implements UDPResponse {
|
|
36
|
+
private _packets;
|
|
37
|
+
private _timings;
|
|
38
|
+
private _connection;
|
|
39
|
+
private _url;
|
|
40
|
+
private _complete;
|
|
41
|
+
private _waiters;
|
|
42
|
+
readonly status: number;
|
|
43
|
+
readonly statusText: string;
|
|
44
|
+
readonly headers: Headers;
|
|
45
|
+
readonly ok: boolean;
|
|
46
|
+
readonly raw: Response;
|
|
47
|
+
constructor(options?: UDPResponseOptions);
|
|
48
|
+
get url(): string;
|
|
49
|
+
get timings(): UDPTimings;
|
|
50
|
+
get connection(): UDPConnection;
|
|
51
|
+
pushPacket(packet: Buffer): void;
|
|
52
|
+
complete(): void;
|
|
53
|
+
buffer(): Promise<Buffer>;
|
|
54
|
+
json<R = unknown>(): Promise<R>;
|
|
55
|
+
text(): Promise<string>;
|
|
56
|
+
cleanText(): Promise<string>;
|
|
57
|
+
blob(): Promise<Blob>;
|
|
58
|
+
read(): ReadableStream<Uint8Array> | null;
|
|
59
|
+
clone(): StreamingUDPResponse;
|
|
60
|
+
sse(): AsyncGenerator<SSEEvent>;
|
|
61
|
+
download(): AsyncGenerator<ProgressEvent>;
|
|
62
|
+
packets(): AsyncGenerator<Buffer>;
|
|
63
|
+
[Symbol.asyncIterator](): AsyncGenerator<Uint8Array>;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=udp-response.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"udp-response.d.ts","sourceRoot":"","sources":["../../src/transport/udp-response.ts"],"names":[],"mappings":"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"}
|