almostnode 0.1.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/LICENSE +21 -0
- package/README.md +731 -0
- package/dist/__sw__.js +394 -0
- package/dist/ai-chatbot-demo-entry.d.ts +6 -0
- package/dist/ai-chatbot-demo-entry.d.ts.map +1 -0
- package/dist/ai-chatbot-demo.d.ts +42 -0
- package/dist/ai-chatbot-demo.d.ts.map +1 -0
- package/dist/assets/runtime-worker-D9x_Ddwz.js +60543 -0
- package/dist/assets/runtime-worker-D9x_Ddwz.js.map +1 -0
- package/dist/convex-app-demo-entry.d.ts +6 -0
- package/dist/convex-app-demo-entry.d.ts.map +1 -0
- package/dist/convex-app-demo.d.ts +68 -0
- package/dist/convex-app-demo.d.ts.map +1 -0
- package/dist/cors-proxy.d.ts +46 -0
- package/dist/cors-proxy.d.ts.map +1 -0
- package/dist/create-runtime.d.ts +42 -0
- package/dist/create-runtime.d.ts.map +1 -0
- package/dist/demo.d.ts +6 -0
- package/dist/demo.d.ts.map +1 -0
- package/dist/dev-server.d.ts +97 -0
- package/dist/dev-server.d.ts.map +1 -0
- package/dist/frameworks/next-dev-server.d.ts +202 -0
- package/dist/frameworks/next-dev-server.d.ts.map +1 -0
- package/dist/frameworks/vite-dev-server.d.ts +85 -0
- package/dist/frameworks/vite-dev-server.d.ts.map +1 -0
- package/dist/index.cjs +14965 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +14867 -0
- package/dist/index.mjs.map +1 -0
- package/dist/next-demo.d.ts +49 -0
- package/dist/next-demo.d.ts.map +1 -0
- package/dist/npm/index.d.ts +71 -0
- package/dist/npm/index.d.ts.map +1 -0
- package/dist/npm/registry.d.ts +66 -0
- package/dist/npm/registry.d.ts.map +1 -0
- package/dist/npm/resolver.d.ts +52 -0
- package/dist/npm/resolver.d.ts.map +1 -0
- package/dist/npm/tarball.d.ts +29 -0
- package/dist/npm/tarball.d.ts.map +1 -0
- package/dist/runtime-interface.d.ts +90 -0
- package/dist/runtime-interface.d.ts.map +1 -0
- package/dist/runtime.d.ts +103 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/sandbox-helpers.d.ts +43 -0
- package/dist/sandbox-helpers.d.ts.map +1 -0
- package/dist/sandbox-runtime.d.ts +65 -0
- package/dist/sandbox-runtime.d.ts.map +1 -0
- package/dist/server-bridge.d.ts +89 -0
- package/dist/server-bridge.d.ts.map +1 -0
- package/dist/shims/assert.d.ts +51 -0
- package/dist/shims/assert.d.ts.map +1 -0
- package/dist/shims/async_hooks.d.ts +37 -0
- package/dist/shims/async_hooks.d.ts.map +1 -0
- package/dist/shims/buffer.d.ts +20 -0
- package/dist/shims/buffer.d.ts.map +1 -0
- package/dist/shims/child_process-browser.d.ts +92 -0
- package/dist/shims/child_process-browser.d.ts.map +1 -0
- package/dist/shims/child_process.d.ts +93 -0
- package/dist/shims/child_process.d.ts.map +1 -0
- package/dist/shims/chokidar.d.ts +55 -0
- package/dist/shims/chokidar.d.ts.map +1 -0
- package/dist/shims/cluster.d.ts +52 -0
- package/dist/shims/cluster.d.ts.map +1 -0
- package/dist/shims/crypto.d.ts +122 -0
- package/dist/shims/crypto.d.ts.map +1 -0
- package/dist/shims/dgram.d.ts +34 -0
- package/dist/shims/dgram.d.ts.map +1 -0
- package/dist/shims/diagnostics_channel.d.ts +80 -0
- package/dist/shims/diagnostics_channel.d.ts.map +1 -0
- package/dist/shims/dns.d.ts +87 -0
- package/dist/shims/dns.d.ts.map +1 -0
- package/dist/shims/domain.d.ts +25 -0
- package/dist/shims/domain.d.ts.map +1 -0
- package/dist/shims/esbuild.d.ts +105 -0
- package/dist/shims/esbuild.d.ts.map +1 -0
- package/dist/shims/events.d.ts +37 -0
- package/dist/shims/events.d.ts.map +1 -0
- package/dist/shims/fs.d.ts +115 -0
- package/dist/shims/fs.d.ts.map +1 -0
- package/dist/shims/fsevents.d.ts +67 -0
- package/dist/shims/fsevents.d.ts.map +1 -0
- package/dist/shims/http.d.ts +217 -0
- package/dist/shims/http.d.ts.map +1 -0
- package/dist/shims/http2.d.ts +81 -0
- package/dist/shims/http2.d.ts.map +1 -0
- package/dist/shims/https.d.ts +36 -0
- package/dist/shims/https.d.ts.map +1 -0
- package/dist/shims/inspector.d.ts +25 -0
- package/dist/shims/inspector.d.ts.map +1 -0
- package/dist/shims/module.d.ts +22 -0
- package/dist/shims/module.d.ts.map +1 -0
- package/dist/shims/net.d.ts +100 -0
- package/dist/shims/net.d.ts.map +1 -0
- package/dist/shims/os.d.ts +159 -0
- package/dist/shims/os.d.ts.map +1 -0
- package/dist/shims/path.d.ts +72 -0
- package/dist/shims/path.d.ts.map +1 -0
- package/dist/shims/perf_hooks.d.ts +50 -0
- package/dist/shims/perf_hooks.d.ts.map +1 -0
- package/dist/shims/process.d.ts +93 -0
- package/dist/shims/process.d.ts.map +1 -0
- package/dist/shims/querystring.d.ts +23 -0
- package/dist/shims/querystring.d.ts.map +1 -0
- package/dist/shims/readdirp.d.ts +52 -0
- package/dist/shims/readdirp.d.ts.map +1 -0
- package/dist/shims/readline.d.ts +62 -0
- package/dist/shims/readline.d.ts.map +1 -0
- package/dist/shims/rollup.d.ts +34 -0
- package/dist/shims/rollup.d.ts.map +1 -0
- package/dist/shims/sentry.d.ts +163 -0
- package/dist/shims/sentry.d.ts.map +1 -0
- package/dist/shims/stream.d.ts +181 -0
- package/dist/shims/stream.d.ts.map +1 -0
- package/dist/shims/tls.d.ts +53 -0
- package/dist/shims/tls.d.ts.map +1 -0
- package/dist/shims/tty.d.ts +30 -0
- package/dist/shims/tty.d.ts.map +1 -0
- package/dist/shims/url.d.ts +64 -0
- package/dist/shims/url.d.ts.map +1 -0
- package/dist/shims/util.d.ts +106 -0
- package/dist/shims/util.d.ts.map +1 -0
- package/dist/shims/v8.d.ts +73 -0
- package/dist/shims/v8.d.ts.map +1 -0
- package/dist/shims/vfs-adapter.d.ts +126 -0
- package/dist/shims/vfs-adapter.d.ts.map +1 -0
- package/dist/shims/vm.d.ts +45 -0
- package/dist/shims/vm.d.ts.map +1 -0
- package/dist/shims/worker_threads.d.ts +66 -0
- package/dist/shims/worker_threads.d.ts.map +1 -0
- package/dist/shims/ws.d.ts +66 -0
- package/dist/shims/ws.d.ts.map +1 -0
- package/dist/shims/zlib.d.ts +161 -0
- package/dist/shims/zlib.d.ts.map +1 -0
- package/dist/transform.d.ts +24 -0
- package/dist/transform.d.ts.map +1 -0
- package/dist/virtual-fs.d.ts +226 -0
- package/dist/virtual-fs.d.ts.map +1 -0
- package/dist/vite-demo.d.ts +35 -0
- package/dist/vite-demo.d.ts.map +1 -0
- package/dist/vite-sw.js +132 -0
- package/dist/worker/runtime-worker.d.ts +8 -0
- package/dist/worker/runtime-worker.d.ts.map +1 -0
- package/dist/worker-runtime.d.ts +50 -0
- package/dist/worker-runtime.d.ts.map +1 -0
- package/package.json +85 -0
- package/src/ai-chatbot-demo-entry.ts +244 -0
- package/src/ai-chatbot-demo.ts +509 -0
- package/src/convex-app-demo-entry.ts +1107 -0
- package/src/convex-app-demo.ts +1316 -0
- package/src/cors-proxy.ts +81 -0
- package/src/create-runtime.ts +147 -0
- package/src/demo.ts +304 -0
- package/src/dev-server.ts +274 -0
- package/src/frameworks/next-dev-server.ts +2224 -0
- package/src/frameworks/vite-dev-server.ts +702 -0
- package/src/index.ts +101 -0
- package/src/next-demo.ts +1784 -0
- package/src/npm/index.ts +347 -0
- package/src/npm/registry.ts +152 -0
- package/src/npm/resolver.ts +385 -0
- package/src/npm/tarball.ts +209 -0
- package/src/runtime-interface.ts +103 -0
- package/src/runtime.ts +1046 -0
- package/src/sandbox-helpers.ts +173 -0
- package/src/sandbox-runtime.ts +252 -0
- package/src/server-bridge.ts +426 -0
- package/src/shims/assert.ts +664 -0
- package/src/shims/async_hooks.ts +86 -0
- package/src/shims/buffer.ts +75 -0
- package/src/shims/child_process-browser.ts +217 -0
- package/src/shims/child_process.ts +463 -0
- package/src/shims/chokidar.ts +313 -0
- package/src/shims/cluster.ts +67 -0
- package/src/shims/crypto.ts +830 -0
- package/src/shims/dgram.ts +47 -0
- package/src/shims/diagnostics_channel.ts +196 -0
- package/src/shims/dns.ts +172 -0
- package/src/shims/domain.ts +58 -0
- package/src/shims/esbuild.ts +805 -0
- package/src/shims/events.ts +195 -0
- package/src/shims/fs.ts +803 -0
- package/src/shims/fsevents.ts +63 -0
- package/src/shims/http.ts +904 -0
- package/src/shims/http2.ts +96 -0
- package/src/shims/https.ts +86 -0
- package/src/shims/inspector.ts +30 -0
- package/src/shims/module.ts +82 -0
- package/src/shims/net.ts +359 -0
- package/src/shims/os.ts +195 -0
- package/src/shims/path.ts +199 -0
- package/src/shims/perf_hooks.ts +92 -0
- package/src/shims/process.ts +346 -0
- package/src/shims/querystring.ts +97 -0
- package/src/shims/readdirp.ts +228 -0
- package/src/shims/readline.ts +110 -0
- package/src/shims/rollup.ts +80 -0
- package/src/shims/sentry.ts +133 -0
- package/src/shims/stream.ts +1126 -0
- package/src/shims/tls.ts +95 -0
- package/src/shims/tty.ts +64 -0
- package/src/shims/url.ts +171 -0
- package/src/shims/util.ts +312 -0
- package/src/shims/v8.ts +113 -0
- package/src/shims/vfs-adapter.ts +402 -0
- package/src/shims/vm.ts +83 -0
- package/src/shims/worker_threads.ts +111 -0
- package/src/shims/ws.ts +382 -0
- package/src/shims/zlib.ts +289 -0
- package/src/transform.ts +313 -0
- package/src/types/external.d.ts +67 -0
- package/src/virtual-fs.ts +903 -0
- package/src/vite-demo.ts +577 -0
- package/src/worker/runtime-worker.ts +128 -0
- package/src/worker-runtime.ts +145 -0
|
@@ -0,0 +1,904 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js http module shim
|
|
3
|
+
* Provides IncomingMessage, ServerResponse, and Server for virtual HTTP handling
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { EventEmitter, type EventListener } from './events';
|
|
7
|
+
import { Readable, Writable, Buffer } from './stream';
|
|
8
|
+
import { Socket, Server as NetServer, AddressInfo } from './net';
|
|
9
|
+
|
|
10
|
+
export type RequestListener = (req: IncomingMessage, res: ServerResponse) => void;
|
|
11
|
+
|
|
12
|
+
export interface RequestOptions {
|
|
13
|
+
method?: string;
|
|
14
|
+
path?: string;
|
|
15
|
+
headers?: Record<string, string | string[]>;
|
|
16
|
+
hostname?: string;
|
|
17
|
+
port?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Incoming HTTP request (Node.js compatible)
|
|
22
|
+
*/
|
|
23
|
+
export class IncomingMessage extends Readable {
|
|
24
|
+
httpVersion: string = '1.1';
|
|
25
|
+
httpVersionMajor: number = 1;
|
|
26
|
+
httpVersionMinor: number = 1;
|
|
27
|
+
complete: boolean = false;
|
|
28
|
+
headers: Record<string, string | string[] | undefined> = {};
|
|
29
|
+
rawHeaders: string[] = [];
|
|
30
|
+
trailers: Record<string, string | undefined> = {};
|
|
31
|
+
rawTrailers: string[] = [];
|
|
32
|
+
method?: string;
|
|
33
|
+
url?: string;
|
|
34
|
+
statusCode?: number;
|
|
35
|
+
statusMessage?: string;
|
|
36
|
+
socket: Socket;
|
|
37
|
+
|
|
38
|
+
private _body: Buffer | null = null;
|
|
39
|
+
|
|
40
|
+
constructor(socket?: Socket) {
|
|
41
|
+
super();
|
|
42
|
+
this.socket = socket || new Socket();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
setTimeout(msecs: number, callback?: () => void): this {
|
|
46
|
+
if (callback) {
|
|
47
|
+
this.once('timeout', callback);
|
|
48
|
+
}
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
destroy(error?: Error): this {
|
|
53
|
+
super.destroy(error);
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Internal: set body data
|
|
58
|
+
_setBody(body: Buffer | string | null): void {
|
|
59
|
+
if (body === null) {
|
|
60
|
+
this._body = null;
|
|
61
|
+
} else {
|
|
62
|
+
this._body = typeof body === 'string' ? Buffer.from(body) : body;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (this._body) {
|
|
66
|
+
this.push(this._body);
|
|
67
|
+
}
|
|
68
|
+
this.push(null);
|
|
69
|
+
this.complete = true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Internal: initialize from raw request
|
|
73
|
+
static fromRequest(
|
|
74
|
+
method: string,
|
|
75
|
+
url: string,
|
|
76
|
+
headers: Record<string, string>,
|
|
77
|
+
body?: Buffer | string
|
|
78
|
+
): IncomingMessage {
|
|
79
|
+
const msg = new IncomingMessage();
|
|
80
|
+
msg.method = method;
|
|
81
|
+
msg.url = url;
|
|
82
|
+
msg.headers = { ...headers };
|
|
83
|
+
|
|
84
|
+
// Build raw headers
|
|
85
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
86
|
+
msg.rawHeaders.push(key, value);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (body) {
|
|
90
|
+
msg._setBody(body);
|
|
91
|
+
} else {
|
|
92
|
+
msg.push(null);
|
|
93
|
+
msg.complete = true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return msg;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Outgoing HTTP response (Node.js compatible)
|
|
102
|
+
*/
|
|
103
|
+
export class ServerResponse extends Writable {
|
|
104
|
+
statusCode: number = 200;
|
|
105
|
+
statusMessage: string = 'OK';
|
|
106
|
+
headersSent: boolean = false;
|
|
107
|
+
finished: boolean = false;
|
|
108
|
+
sendDate: boolean = true;
|
|
109
|
+
socket: Socket | null;
|
|
110
|
+
|
|
111
|
+
private _headers: Map<string, string | string[]> = new Map();
|
|
112
|
+
private _body: Uint8Array[] = [];
|
|
113
|
+
private _resolve?: (response: ResponseData) => void;
|
|
114
|
+
|
|
115
|
+
constructor(req: IncomingMessage) {
|
|
116
|
+
super();
|
|
117
|
+
this.socket = req.socket;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Internal: set resolver for async response handling
|
|
121
|
+
_setResolver(resolve: (response: ResponseData) => void): void {
|
|
122
|
+
this._resolve = resolve;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
setHeader(name: string, value: string | string[] | number): this {
|
|
126
|
+
if (this.headersSent) {
|
|
127
|
+
throw new Error('Cannot set headers after they are sent');
|
|
128
|
+
}
|
|
129
|
+
this._headers.set(name.toLowerCase(), String(value));
|
|
130
|
+
return this;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
getHeader(name: string): string | string[] | undefined {
|
|
134
|
+
return this._headers.get(name.toLowerCase());
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
getHeaders(): Record<string, string | string[]> {
|
|
138
|
+
const headers: Record<string, string | string[]> = {};
|
|
139
|
+
for (const [key, value] of this._headers) {
|
|
140
|
+
headers[key] = value;
|
|
141
|
+
}
|
|
142
|
+
return headers;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
getHeaderNames(): string[] {
|
|
146
|
+
return [...this._headers.keys()];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
hasHeader(name: string): boolean {
|
|
150
|
+
return this._headers.has(name.toLowerCase());
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
removeHeader(name: string): void {
|
|
154
|
+
if (this.headersSent) {
|
|
155
|
+
throw new Error('Cannot remove headers after they are sent');
|
|
156
|
+
}
|
|
157
|
+
this._headers.delete(name.toLowerCase());
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
writeHead(
|
|
161
|
+
statusCode: number,
|
|
162
|
+
statusMessageOrHeaders?: string | Record<string, string | string[] | number>,
|
|
163
|
+
headers?: Record<string, string | string[] | number>
|
|
164
|
+
): this {
|
|
165
|
+
this.statusCode = statusCode;
|
|
166
|
+
|
|
167
|
+
if (typeof statusMessageOrHeaders === 'string') {
|
|
168
|
+
this.statusMessage = statusMessageOrHeaders;
|
|
169
|
+
if (headers) {
|
|
170
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
171
|
+
this.setHeader(key, value);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
} else if (statusMessageOrHeaders) {
|
|
175
|
+
for (const [key, value] of Object.entries(statusMessageOrHeaders)) {
|
|
176
|
+
this.setHeader(key, value);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return this;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
write(
|
|
184
|
+
chunk: Uint8Array | string,
|
|
185
|
+
encodingOrCallback?: BufferEncoding | ((error?: Error | null) => void),
|
|
186
|
+
callback?: (error?: Error | null) => void
|
|
187
|
+
): boolean {
|
|
188
|
+
this.headersSent = true;
|
|
189
|
+
const buffer = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;
|
|
190
|
+
this._body.push(buffer);
|
|
191
|
+
|
|
192
|
+
const cb = typeof encodingOrCallback === 'function' ? encodingOrCallback : callback;
|
|
193
|
+
if (cb) {
|
|
194
|
+
queueMicrotask(() => cb(null));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
end(
|
|
201
|
+
chunkOrCallback?: Uint8Array | string | (() => void),
|
|
202
|
+
encodingOrCallback?: BufferEncoding | (() => void),
|
|
203
|
+
callback?: () => void
|
|
204
|
+
): this {
|
|
205
|
+
if (typeof chunkOrCallback === 'function') {
|
|
206
|
+
callback = chunkOrCallback;
|
|
207
|
+
} else if (chunkOrCallback !== undefined) {
|
|
208
|
+
this.write(chunkOrCallback as Uint8Array | string);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (typeof encodingOrCallback === 'function') {
|
|
212
|
+
callback = encodingOrCallback;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
this.headersSent = true;
|
|
216
|
+
this.finished = true;
|
|
217
|
+
|
|
218
|
+
// Resolve with response data
|
|
219
|
+
if (this._resolve) {
|
|
220
|
+
const headers: Record<string, string> = {};
|
|
221
|
+
for (const [key, value] of this._headers) {
|
|
222
|
+
headers[key] = Array.isArray(value) ? value.join(', ') : value;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
this._resolve({
|
|
226
|
+
statusCode: this.statusCode,
|
|
227
|
+
statusMessage: this.statusMessage,
|
|
228
|
+
headers,
|
|
229
|
+
body: Buffer.concat(this._body),
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
queueMicrotask(() => {
|
|
234
|
+
this.emit('finish');
|
|
235
|
+
if (callback) callback();
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
return this;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Convenience method for simple responses
|
|
242
|
+
send(data: string | Buffer | object): this {
|
|
243
|
+
if (typeof data === 'object' && !Buffer.isBuffer(data)) {
|
|
244
|
+
this.setHeader('Content-Type', 'application/json');
|
|
245
|
+
data = JSON.stringify(data);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (!this.hasHeader('Content-Type')) {
|
|
249
|
+
this.setHeader('Content-Type', 'text/html');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
this.write(typeof data === 'string' ? data : data);
|
|
253
|
+
return this.end();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Express compatibility
|
|
257
|
+
json(data: unknown): this {
|
|
258
|
+
this.setHeader('Content-Type', 'application/json');
|
|
259
|
+
return this.end(JSON.stringify(data));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
status(code: number): this {
|
|
263
|
+
this.statusCode = code;
|
|
264
|
+
return this;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
redirect(urlOrStatus: string | number, url?: string): void {
|
|
268
|
+
if (typeof urlOrStatus === 'number') {
|
|
269
|
+
this.statusCode = urlOrStatus;
|
|
270
|
+
this.setHeader('Location', url!);
|
|
271
|
+
} else {
|
|
272
|
+
this.statusCode = 302;
|
|
273
|
+
this.setHeader('Location', urlOrStatus);
|
|
274
|
+
}
|
|
275
|
+
this.end();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Get body for testing/debugging
|
|
279
|
+
_getBody(): Buffer {
|
|
280
|
+
return Buffer.concat(this._body);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
_getBodyAsString(): string {
|
|
284
|
+
return this._getBody().toString('utf8');
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export interface ResponseData {
|
|
289
|
+
statusCode: number;
|
|
290
|
+
statusMessage: string;
|
|
291
|
+
headers: Record<string, string>;
|
|
292
|
+
body: Buffer;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* HTTP Server (Node.js compatible)
|
|
297
|
+
*/
|
|
298
|
+
export class Server extends EventEmitter {
|
|
299
|
+
private _netServer: NetServer;
|
|
300
|
+
private _requestListener?: RequestListener;
|
|
301
|
+
private _pendingRequests: Map<string, {
|
|
302
|
+
resolve: (response: ResponseData) => void;
|
|
303
|
+
reject: (error: Error) => void;
|
|
304
|
+
}> = new Map();
|
|
305
|
+
|
|
306
|
+
listening: boolean = false;
|
|
307
|
+
maxHeadersCount: number | null = null;
|
|
308
|
+
timeout: number = 0;
|
|
309
|
+
keepAliveTimeout: number = 5000;
|
|
310
|
+
headersTimeout: number = 60000;
|
|
311
|
+
requestTimeout: number = 0;
|
|
312
|
+
|
|
313
|
+
constructor(requestListener?: RequestListener) {
|
|
314
|
+
super();
|
|
315
|
+
this._requestListener = requestListener;
|
|
316
|
+
this._netServer = new NetServer();
|
|
317
|
+
|
|
318
|
+
this._netServer.on('listening', () => {
|
|
319
|
+
this.listening = true;
|
|
320
|
+
this.emit('listening');
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
this._netServer.on('close', () => {
|
|
324
|
+
this.listening = false;
|
|
325
|
+
this.emit('close');
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
this._netServer.on('error', (err) => {
|
|
329
|
+
this.emit('error', err);
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
listen(
|
|
334
|
+
portOrOptions?: number | { port?: number; host?: string },
|
|
335
|
+
hostOrCallback?: string | (() => void),
|
|
336
|
+
callback?: () => void
|
|
337
|
+
): this {
|
|
338
|
+
let port: number | undefined;
|
|
339
|
+
let host: string | undefined;
|
|
340
|
+
let cb: (() => void) | undefined;
|
|
341
|
+
|
|
342
|
+
if (typeof portOrOptions === 'number') {
|
|
343
|
+
port = portOrOptions;
|
|
344
|
+
if (typeof hostOrCallback === 'string') {
|
|
345
|
+
host = hostOrCallback;
|
|
346
|
+
cb = callback;
|
|
347
|
+
} else {
|
|
348
|
+
cb = hostOrCallback;
|
|
349
|
+
}
|
|
350
|
+
} else if (portOrOptions) {
|
|
351
|
+
port = portOrOptions.port;
|
|
352
|
+
host = portOrOptions.host;
|
|
353
|
+
cb = typeof hostOrCallback === 'function' ? hostOrCallback : callback;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Wrap callback to register server after listening
|
|
357
|
+
const originalCb = cb;
|
|
358
|
+
const self = this;
|
|
359
|
+
cb = function() {
|
|
360
|
+
const addr = self._netServer.address();
|
|
361
|
+
if (addr) {
|
|
362
|
+
_registerServer(addr.port, self);
|
|
363
|
+
}
|
|
364
|
+
if (originalCb) originalCb();
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
this._netServer.listen(port, host, cb);
|
|
368
|
+
|
|
369
|
+
return this;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
close(callback?: (err?: Error) => void): this {
|
|
373
|
+
const addr = this._netServer.address();
|
|
374
|
+
if (addr) {
|
|
375
|
+
_unregisterServer(addr.port);
|
|
376
|
+
}
|
|
377
|
+
this._netServer.close(callback);
|
|
378
|
+
return this;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
address(): AddressInfo | null {
|
|
382
|
+
return this._netServer.address();
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
setTimeout(msecs?: number, callback?: () => void): this {
|
|
386
|
+
this.timeout = msecs || 0;
|
|
387
|
+
if (callback) {
|
|
388
|
+
this.on('timeout', callback);
|
|
389
|
+
}
|
|
390
|
+
return this;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
ref(): this {
|
|
394
|
+
this._netServer.ref();
|
|
395
|
+
return this;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
unref(): this {
|
|
399
|
+
this._netServer.unref();
|
|
400
|
+
return this;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Handle an incoming request (used by server bridge)
|
|
405
|
+
*/
|
|
406
|
+
async handleRequest(
|
|
407
|
+
method: string,
|
|
408
|
+
url: string,
|
|
409
|
+
headers: Record<string, string>,
|
|
410
|
+
body?: Buffer | string
|
|
411
|
+
): Promise<ResponseData> {
|
|
412
|
+
return new Promise((resolve, reject) => {
|
|
413
|
+
const req = IncomingMessage.fromRequest(method, url, headers, body);
|
|
414
|
+
const res = new ServerResponse(req);
|
|
415
|
+
|
|
416
|
+
res._setResolver(resolve);
|
|
417
|
+
|
|
418
|
+
// Set timeout
|
|
419
|
+
const timeoutId = this.timeout
|
|
420
|
+
? setTimeout(() => {
|
|
421
|
+
reject(new Error('Request timeout'));
|
|
422
|
+
}, this.timeout)
|
|
423
|
+
: null;
|
|
424
|
+
|
|
425
|
+
res.on('finish', () => {
|
|
426
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
try {
|
|
430
|
+
this.emit('request', req, res);
|
|
431
|
+
|
|
432
|
+
if (this._requestListener) {
|
|
433
|
+
this._requestListener(req, res);
|
|
434
|
+
}
|
|
435
|
+
} catch (error) {
|
|
436
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
437
|
+
reject(error);
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Create an HTTP server
|
|
445
|
+
*/
|
|
446
|
+
export function createServer(requestListener?: RequestListener): Server {
|
|
447
|
+
return new Server(requestListener);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* HTTP status codes
|
|
452
|
+
*/
|
|
453
|
+
export const STATUS_CODES: Record<number, string> = {
|
|
454
|
+
100: 'Continue',
|
|
455
|
+
101: 'Switching Protocols',
|
|
456
|
+
200: 'OK',
|
|
457
|
+
201: 'Created',
|
|
458
|
+
202: 'Accepted',
|
|
459
|
+
204: 'No Content',
|
|
460
|
+
301: 'Moved Permanently',
|
|
461
|
+
302: 'Found',
|
|
462
|
+
304: 'Not Modified',
|
|
463
|
+
400: 'Bad Request',
|
|
464
|
+
401: 'Unauthorized',
|
|
465
|
+
403: 'Forbidden',
|
|
466
|
+
404: 'Not Found',
|
|
467
|
+
405: 'Method Not Allowed',
|
|
468
|
+
408: 'Request Timeout',
|
|
469
|
+
500: 'Internal Server Error',
|
|
470
|
+
501: 'Not Implemented',
|
|
471
|
+
502: 'Bad Gateway',
|
|
472
|
+
503: 'Service Unavailable',
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* HTTP methods
|
|
477
|
+
*/
|
|
478
|
+
export const METHODS = [
|
|
479
|
+
'GET',
|
|
480
|
+
'POST',
|
|
481
|
+
'PUT',
|
|
482
|
+
'DELETE',
|
|
483
|
+
'PATCH',
|
|
484
|
+
'HEAD',
|
|
485
|
+
'OPTIONS',
|
|
486
|
+
'CONNECT',
|
|
487
|
+
'TRACE',
|
|
488
|
+
];
|
|
489
|
+
|
|
490
|
+
// CORS proxy getter - checks localStorage for configured proxy
|
|
491
|
+
function getCorsProxy(): string | null {
|
|
492
|
+
if (typeof localStorage !== 'undefined') {
|
|
493
|
+
return localStorage.getItem('__corsProxyUrl') || null;
|
|
494
|
+
}
|
|
495
|
+
return null;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* HTTP Client Request - makes real HTTP requests using fetch()
|
|
500
|
+
*/
|
|
501
|
+
export class ClientRequest extends Writable {
|
|
502
|
+
method: string;
|
|
503
|
+
path: string;
|
|
504
|
+
headers: Record<string, string>;
|
|
505
|
+
|
|
506
|
+
private _options: RequestOptions;
|
|
507
|
+
private _protocol: 'http' | 'https';
|
|
508
|
+
private _bodyChunks: Uint8Array[] = [];
|
|
509
|
+
private _aborted: boolean = false;
|
|
510
|
+
private _timeout: number | null = null;
|
|
511
|
+
private _timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
512
|
+
private _requestEnded: boolean = false;
|
|
513
|
+
|
|
514
|
+
constructor(options: RequestOptions, protocol: 'http' | 'https' = 'http') {
|
|
515
|
+
super();
|
|
516
|
+
this._options = options;
|
|
517
|
+
this._protocol = protocol;
|
|
518
|
+
this.method = options.method || 'GET';
|
|
519
|
+
this.path = options.path || '/';
|
|
520
|
+
this.headers = {};
|
|
521
|
+
|
|
522
|
+
if (options.headers) {
|
|
523
|
+
for (const [key, value] of Object.entries(options.headers)) {
|
|
524
|
+
this.headers[key.toLowerCase()] = Array.isArray(value) ? value.join(', ') : value;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
setHeader(name: string, value: string): void {
|
|
530
|
+
this.headers[name.toLowerCase()] = value;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
getHeader(name: string): string | undefined {
|
|
534
|
+
return this.headers[name.toLowerCase()];
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
removeHeader(name: string): void {
|
|
538
|
+
delete this.headers[name.toLowerCase()];
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
write(
|
|
542
|
+
chunk: Uint8Array | string,
|
|
543
|
+
encodingOrCallback?: BufferEncoding | ((error?: Error | null) => void),
|
|
544
|
+
callback?: (error?: Error | null) => void
|
|
545
|
+
): boolean {
|
|
546
|
+
const buffer = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;
|
|
547
|
+
this._bodyChunks.push(buffer);
|
|
548
|
+
|
|
549
|
+
const cb = typeof encodingOrCallback === 'function' ? encodingOrCallback : callback;
|
|
550
|
+
if (cb) {
|
|
551
|
+
queueMicrotask(() => cb(null));
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
return true;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
end(
|
|
558
|
+
dataOrCallback?: Uint8Array | string | (() => void),
|
|
559
|
+
encodingOrCallback?: BufferEncoding | (() => void),
|
|
560
|
+
callback?: () => void
|
|
561
|
+
): this {
|
|
562
|
+
if (this._requestEnded) return this;
|
|
563
|
+
this._requestEnded = true;
|
|
564
|
+
|
|
565
|
+
// Handle overloaded arguments
|
|
566
|
+
let finalCallback = callback;
|
|
567
|
+
if (typeof dataOrCallback === 'function') {
|
|
568
|
+
finalCallback = dataOrCallback;
|
|
569
|
+
} else if (dataOrCallback !== undefined) {
|
|
570
|
+
this.write(dataOrCallback as Uint8Array | string);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (typeof encodingOrCallback === 'function') {
|
|
574
|
+
finalCallback = encodingOrCallback;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Perform the actual request
|
|
578
|
+
this._performRequest().then(() => {
|
|
579
|
+
if (finalCallback) finalCallback();
|
|
580
|
+
}).catch((error) => {
|
|
581
|
+
this.emit('error', error);
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
return this;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
abort(): void {
|
|
588
|
+
this._aborted = true;
|
|
589
|
+
if (this._timeoutId) {
|
|
590
|
+
clearTimeout(this._timeoutId);
|
|
591
|
+
}
|
|
592
|
+
this.emit('abort');
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
setTimeout(ms: number, callback?: () => void): this {
|
|
596
|
+
this._timeout = ms;
|
|
597
|
+
if (callback) {
|
|
598
|
+
this.once('timeout', callback);
|
|
599
|
+
}
|
|
600
|
+
return this;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
private async _performRequest(): Promise<void> {
|
|
604
|
+
if (this._aborted) return;
|
|
605
|
+
|
|
606
|
+
try {
|
|
607
|
+
// Build URL
|
|
608
|
+
const protocol = this._protocol === 'https' ? 'https:' : 'http:';
|
|
609
|
+
const hostname = this._options.hostname || 'localhost';
|
|
610
|
+
const port = this._options.port ? `:${this._options.port}` : '';
|
|
611
|
+
const path = this._options.path || '/';
|
|
612
|
+
const url = `${protocol}//${hostname}${port}${path}`;
|
|
613
|
+
|
|
614
|
+
// Use CORS proxy if configured
|
|
615
|
+
const corsProxy = getCorsProxy();
|
|
616
|
+
const fetchUrl = corsProxy
|
|
617
|
+
? corsProxy + encodeURIComponent(url)
|
|
618
|
+
: url;
|
|
619
|
+
|
|
620
|
+
// Build fetch options
|
|
621
|
+
const fetchOptions: RequestInit = {
|
|
622
|
+
method: this.method,
|
|
623
|
+
headers: this.headers,
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
// Add body if we have one (not for GET/HEAD)
|
|
627
|
+
if (this._bodyChunks.length > 0 && this.method !== 'GET' && this.method !== 'HEAD') {
|
|
628
|
+
fetchOptions.body = Buffer.concat(this._bodyChunks);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// Set up timeout with AbortController
|
|
632
|
+
const controller = new AbortController();
|
|
633
|
+
fetchOptions.signal = controller.signal;
|
|
634
|
+
|
|
635
|
+
if (this._timeout) {
|
|
636
|
+
this._timeoutId = setTimeout(() => {
|
|
637
|
+
controller.abort();
|
|
638
|
+
this.emit('timeout');
|
|
639
|
+
}, this._timeout);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Make the request
|
|
643
|
+
const response = await fetch(fetchUrl, fetchOptions);
|
|
644
|
+
|
|
645
|
+
// Clear timeout
|
|
646
|
+
if (this._timeoutId) {
|
|
647
|
+
clearTimeout(this._timeoutId);
|
|
648
|
+
this._timeoutId = null;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
if (this._aborted) return;
|
|
652
|
+
|
|
653
|
+
// Convert response to IncomingMessage
|
|
654
|
+
const incomingMessage = await this._responseToIncomingMessage(response);
|
|
655
|
+
|
|
656
|
+
// Emit response event
|
|
657
|
+
this.emit('response', incomingMessage);
|
|
658
|
+
|
|
659
|
+
} catch (error) {
|
|
660
|
+
if (this._timeoutId) {
|
|
661
|
+
clearTimeout(this._timeoutId);
|
|
662
|
+
}
|
|
663
|
+
if (this._aborted) return;
|
|
664
|
+
|
|
665
|
+
// Wrap abort errors
|
|
666
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
667
|
+
// Already emitted timeout event
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
this.emit('error', error);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
private async _responseToIncomingMessage(response: Response): Promise<IncomingMessage> {
|
|
676
|
+
const msg = new IncomingMessage();
|
|
677
|
+
|
|
678
|
+
// Set status
|
|
679
|
+
msg.statusCode = response.status;
|
|
680
|
+
msg.statusMessage = response.statusText || STATUS_CODES[response.status] || '';
|
|
681
|
+
|
|
682
|
+
// Copy headers
|
|
683
|
+
response.headers.forEach((value, key) => {
|
|
684
|
+
msg.headers[key.toLowerCase()] = value;
|
|
685
|
+
msg.rawHeaders.push(key, value);
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
// Read body and push to stream
|
|
689
|
+
const body = await response.arrayBuffer();
|
|
690
|
+
msg._setBody(Buffer.from(body));
|
|
691
|
+
|
|
692
|
+
return msg;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Helper to parse URL/options arguments for request()
|
|
698
|
+
*/
|
|
699
|
+
function parseRequestArgs(
|
|
700
|
+
urlOrOptions: string | URL | RequestOptions,
|
|
701
|
+
optionsOrCallback?: RequestOptions | ((res: IncomingMessage) => void),
|
|
702
|
+
callback?: (res: IncomingMessage) => void
|
|
703
|
+
): { options: RequestOptions; callback?: (res: IncomingMessage) => void } {
|
|
704
|
+
let options: RequestOptions;
|
|
705
|
+
let cb = callback;
|
|
706
|
+
|
|
707
|
+
if (typeof urlOrOptions === 'string' || urlOrOptions instanceof URL) {
|
|
708
|
+
const parsed = new URL(urlOrOptions.toString());
|
|
709
|
+
options = {
|
|
710
|
+
hostname: parsed.hostname,
|
|
711
|
+
port: parsed.port ? parseInt(parsed.port) : undefined,
|
|
712
|
+
path: parsed.pathname + parsed.search,
|
|
713
|
+
method: 'GET',
|
|
714
|
+
};
|
|
715
|
+
if (typeof optionsOrCallback === 'function') {
|
|
716
|
+
cb = optionsOrCallback;
|
|
717
|
+
} else if (optionsOrCallback) {
|
|
718
|
+
options = { ...options, ...optionsOrCallback };
|
|
719
|
+
}
|
|
720
|
+
} else {
|
|
721
|
+
options = urlOrOptions;
|
|
722
|
+
if (typeof optionsOrCallback === 'function') {
|
|
723
|
+
cb = optionsOrCallback;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
return { options, callback: cb };
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Create an HTTP client request
|
|
732
|
+
*/
|
|
733
|
+
export function request(
|
|
734
|
+
urlOrOptions: string | URL | RequestOptions,
|
|
735
|
+
optionsOrCallback?: RequestOptions | ((res: IncomingMessage) => void),
|
|
736
|
+
callback?: (res: IncomingMessage) => void
|
|
737
|
+
): ClientRequest {
|
|
738
|
+
const { options, callback: cb } = parseRequestArgs(urlOrOptions, optionsOrCallback, callback);
|
|
739
|
+
const req = new ClientRequest(options, 'http');
|
|
740
|
+
if (cb) {
|
|
741
|
+
req.once('response', cb as unknown as EventListener);
|
|
742
|
+
}
|
|
743
|
+
return req;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* Make an HTTP GET request
|
|
748
|
+
*/
|
|
749
|
+
export function get(
|
|
750
|
+
urlOrOptions: string | URL | RequestOptions,
|
|
751
|
+
optionsOrCallback?: RequestOptions | ((res: IncomingMessage) => void),
|
|
752
|
+
callback?: (res: IncomingMessage) => void
|
|
753
|
+
): ClientRequest {
|
|
754
|
+
const { options, callback: cb } = parseRequestArgs(urlOrOptions, optionsOrCallback, callback);
|
|
755
|
+
const req = new ClientRequest({ ...options, method: 'GET' }, 'http');
|
|
756
|
+
if (cb) {
|
|
757
|
+
req.once('response', cb as unknown as EventListener);
|
|
758
|
+
}
|
|
759
|
+
req.end();
|
|
760
|
+
return req;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Internal: create client request with specified protocol
|
|
765
|
+
* Used by https module
|
|
766
|
+
*/
|
|
767
|
+
export function _createClientRequest(
|
|
768
|
+
urlOrOptions: string | URL | RequestOptions,
|
|
769
|
+
optionsOrCallback: RequestOptions | ((res: IncomingMessage) => void) | undefined,
|
|
770
|
+
callback: ((res: IncomingMessage) => void) | undefined,
|
|
771
|
+
protocol: 'http' | 'https'
|
|
772
|
+
): ClientRequest {
|
|
773
|
+
const { options, callback: cb } = parseRequestArgs(urlOrOptions, optionsOrCallback, callback);
|
|
774
|
+
const req = new ClientRequest(options, protocol);
|
|
775
|
+
if (cb) {
|
|
776
|
+
req.once('response', cb as unknown as EventListener);
|
|
777
|
+
}
|
|
778
|
+
return req;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Server registry for tracking listening servers
|
|
783
|
+
* Used by server bridge to route requests
|
|
784
|
+
*/
|
|
785
|
+
export type ServerRegistryCallback = (port: number, server: Server) => void;
|
|
786
|
+
|
|
787
|
+
const serverRegistry = new Map<number, Server>();
|
|
788
|
+
let onServerListenCallback: ServerRegistryCallback | null = null;
|
|
789
|
+
let onServerCloseCallback: ((port: number) => void) | null = null;
|
|
790
|
+
|
|
791
|
+
export function _registerServer(port: number, server: Server): void {
|
|
792
|
+
serverRegistry.set(port, server);
|
|
793
|
+
if (onServerListenCallback) {
|
|
794
|
+
onServerListenCallback(port, server);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
export function _unregisterServer(port: number): void {
|
|
799
|
+
serverRegistry.delete(port);
|
|
800
|
+
if (onServerCloseCallback) {
|
|
801
|
+
onServerCloseCallback(port);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
export function getServer(port: number): Server | undefined {
|
|
806
|
+
return serverRegistry.get(port);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
export function getAllServers(): Map<number, Server> {
|
|
810
|
+
return new Map(serverRegistry);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
export function setServerListenCallback(callback: ServerRegistryCallback | null): void {
|
|
814
|
+
onServerListenCallback = callback;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
export function setServerCloseCallback(callback: ((port: number) => void) | null): void {
|
|
818
|
+
onServerCloseCallback = callback;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* HTTP Agent - manages connection persistence and reuse
|
|
823
|
+
* This is a stub implementation for browser environment
|
|
824
|
+
*/
|
|
825
|
+
export interface AgentOptions {
|
|
826
|
+
keepAlive?: boolean;
|
|
827
|
+
keepAliveMsecs?: number;
|
|
828
|
+
maxSockets?: number;
|
|
829
|
+
maxTotalSockets?: number;
|
|
830
|
+
maxFreeSockets?: number;
|
|
831
|
+
scheduling?: 'fifo' | 'lifo';
|
|
832
|
+
timeout?: number;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
export class Agent extends EventEmitter {
|
|
836
|
+
maxSockets: number;
|
|
837
|
+
maxFreeSockets: number;
|
|
838
|
+
maxTotalSockets: number;
|
|
839
|
+
sockets: Record<string, Socket[]>;
|
|
840
|
+
freeSockets: Record<string, Socket[]>;
|
|
841
|
+
requests: Record<string, IncomingMessage[]>;
|
|
842
|
+
options: AgentOptions;
|
|
843
|
+
|
|
844
|
+
constructor(opts?: AgentOptions) {
|
|
845
|
+
super();
|
|
846
|
+
this.options = opts || {};
|
|
847
|
+
this.maxSockets = opts?.maxSockets ?? Infinity;
|
|
848
|
+
this.maxFreeSockets = opts?.maxFreeSockets ?? 256;
|
|
849
|
+
this.maxTotalSockets = opts?.maxTotalSockets ?? Infinity;
|
|
850
|
+
this.sockets = {};
|
|
851
|
+
this.freeSockets = {};
|
|
852
|
+
this.requests = {};
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
createConnection(
|
|
856
|
+
_options: Record<string, unknown>,
|
|
857
|
+
callback?: (err: Error | null, socket: Socket) => void
|
|
858
|
+
): Socket {
|
|
859
|
+
const socket = new Socket();
|
|
860
|
+
if (callback) {
|
|
861
|
+
callback(null, socket);
|
|
862
|
+
}
|
|
863
|
+
return socket;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
getName(options: { host?: string; port?: number; localAddress?: string }): string {
|
|
867
|
+
const host = options.host || 'localhost';
|
|
868
|
+
const port = options.port || 80;
|
|
869
|
+
return `${host}:${port}:${options.localAddress || ''}`;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
addRequest(_req: ClientRequest, _options: Record<string, unknown>): void {
|
|
873
|
+
// Stub - in browser we use fetch instead
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
destroy(): void {
|
|
877
|
+
// Clean up - stub
|
|
878
|
+
this.sockets = {};
|
|
879
|
+
this.freeSockets = {};
|
|
880
|
+
this.requests = {};
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// Global agent instance
|
|
885
|
+
export const globalAgent = new Agent();
|
|
886
|
+
|
|
887
|
+
export default {
|
|
888
|
+
Server,
|
|
889
|
+
IncomingMessage,
|
|
890
|
+
ServerResponse,
|
|
891
|
+
ClientRequest,
|
|
892
|
+
createServer,
|
|
893
|
+
request,
|
|
894
|
+
get,
|
|
895
|
+
STATUS_CODES,
|
|
896
|
+
METHODS,
|
|
897
|
+
getServer,
|
|
898
|
+
getAllServers,
|
|
899
|
+
setServerListenCallback,
|
|
900
|
+
setServerCloseCallback,
|
|
901
|
+
_createClientRequest,
|
|
902
|
+
Agent,
|
|
903
|
+
globalAgent,
|
|
904
|
+
};
|