alink-cli 0.7.2 → 0.7.4

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.
@@ -0,0 +1,826 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { $t as parseHeader, Ct as setHeader, E as toHandled, H as toPersisted, J as fromWebSocket, Mt as TypeId, N as TypeId$2, Nt as inspect, Qt as isEmpty, R as MultipartError, St as setBody, U as decodeField, V as makeConfig, W as make$5, _n as fromInput$1, a as layer$3, at as ResponseError, bt as removeHeader, c as make$6, cn as fromRecordUnsafe, ct as causeResponse, dt as HttpPlatform, fn as raw, ft as make$4, hn as uint8Array, i as RequestInit, in as layerWeak, it as RequestParseError, j as HttpServerRequest, jt as MaxBodySize, ln as merge, n as import_websocket_server, nt as ClientAbort, o as HttpServer, pn as stream$1, r as Mime_default, rn as layer$2, rt as HttpServerError, s as layerTestClient, sn as fromInput, st as ServeError, tn as toSetCookieHeaders, vt as raw$1, z as TypeId$1 } from "./NodeSocket-08w4osAf.mjs";
4
+ import { $c as combine, $i as makeUnsafe, Aa as cached, Cs as void_, Do as matchCauseEffect, Dr as map$1, Es as withFiber, Fr as orDie, Ga as die, Kr as runForEachArray, Qa as flatMap, Qu as add, Sa as andThen, Sc as forkUnsafe, So as map, Tc as provide$1, Xa as failCause, Xo as scope, Ya as fail, Yo as runSync, _f as pipe, aa as runIn, as as suspend, cs as tapCause, dc as provideMerge, el as die$1, fc as succeed$1, gs as try_, hd as fromNullishOr, hf as flow, hs as tryPromise, ja as callback, lc as mergeAll, lu as seconds, ms as timeoutOrElse, ni as unwrap, no as fnUntraced, ns as succeed, os as sync, po as interruptible, qo as runForkWith, rc as effect, sc as fresh, uc as provide, va as acquireRelease, vc as addFinalizer, yc as addFinalizerExit, zd as Class } from "./Schema-B3i-HrZQ.mjs";
5
+ import { c as layer$4, d as pipeThroughDuplex, f as toArrayBuffer, m as toUint8Array, n as layer$5, p as toString, u as fromReadable } from "./NodeServices-DDZTiw5K.mjs";
6
+ import { h as unwrap$1 } from "./Config-Bj2ZPCsP.mjs";
7
+ import { Duplex, Readable } from "node:stream";
8
+ import * as Fs from "node:fs";
9
+ import * as Http from "node:http";
10
+ import * as NodeStreamP from "node:stream/promises";
11
+ import { pipeline } from "node:stream/promises";
12
+ import * as Zlib from "node:zlib";
13
+ //#region ../../node_modules/.pnpm/@effect+platform-node@4.0.0-beta.103_bufferutil@4.1.0_effect@4.0.0-beta.103_patch_hash=_7eec308fcf6351488fccaf9a0edac255/node_modules/@effect/platform-node/dist/NodeHttpIncomingMessage.js
14
+ /**
15
+ * Adapter base for exposing Node `http.IncomingMessage` values as Effect HTTP
16
+ * incoming messages.
17
+ *
18
+ * Server requests and Node client responses both arrive as Node readable
19
+ * streams with raw header objects, socket metadata, and one-shot body
20
+ * consumption. This module's `NodeHttpIncomingMessage` class keeps the original
21
+ * Node message available while presenting Effect's `HttpIncomingMessage` shape:
22
+ * typed headers, remote address lookup, stream access, and text, JSON,
23
+ * URL-encoded, and array-buffer body readers.
24
+ *
25
+ * @since 4.0.0
26
+ */
27
+ /**
28
+ * Adapts a Node `IncomingMessage` to Effect HTTP incoming messages.
29
+ *
30
+ * **When to use**
31
+ *
32
+ * Use to implement Node HTTP request or response adapters that expose the
33
+ * Effect HTTP incoming-message interface.
34
+ *
35
+ * **Details**
36
+ *
37
+ * The adapter exposes headers, remote address, stream access, and cached body
38
+ * decoders. Subclasses provide the error mapping for unknown Node errors.
39
+ *
40
+ * @category constructors
41
+ * @since 4.0.0
42
+ */
43
+ var NodeHttpIncomingMessage = class extends Class {
44
+ /**
45
+ * Marks this value as an HTTP incoming message for runtime guards.
46
+ *
47
+ * @since 4.0.0
48
+ */
49
+ [TypeId];
50
+ source;
51
+ onError;
52
+ remoteAddressOverride;
53
+ constructor(source, onError, remoteAddressOverride) {
54
+ super();
55
+ this[TypeId] = TypeId;
56
+ this.source = source;
57
+ this.onError = onError;
58
+ this.remoteAddressOverride = remoteAddressOverride;
59
+ }
60
+ get headers() {
61
+ return fromInput(this.source.headers);
62
+ }
63
+ get remoteAddress() {
64
+ return this.remoteAddressOverride ?? fromNullishOr(this.source.socket.remoteAddress);
65
+ }
66
+ textEffect;
67
+ get text() {
68
+ if (this.textEffect) return this.textEffect;
69
+ this.textEffect = runSync(cached(flatMap(MaxBodySize, (maxBodySize) => toString(() => this.source, {
70
+ onError: this.onError,
71
+ maxBytes: maxBodySize
72
+ }))));
73
+ this.arrayBufferEffect = map(this.textEffect, (_) => new TextEncoder().encode(_).buffer);
74
+ return this.textEffect;
75
+ }
76
+ get textUnsafe() {
77
+ return runSync(this.text);
78
+ }
79
+ get json() {
80
+ return flatMap(this.text, (text) => try_({
81
+ try: () => text === "" ? null : JSON.parse(text),
82
+ catch: this.onError
83
+ }));
84
+ }
85
+ get jsonUnsafe() {
86
+ return runSync(this.json);
87
+ }
88
+ get urlParamsBody() {
89
+ return flatMap(this.text, (_) => try_({
90
+ try: () => fromInput$1(new URLSearchParams(_)),
91
+ catch: this.onError
92
+ }));
93
+ }
94
+ get stream() {
95
+ return fromReadable({
96
+ evaluate: () => this.source,
97
+ onError: this.onError
98
+ });
99
+ }
100
+ arrayBufferEffect;
101
+ get arrayBuffer() {
102
+ if (this.arrayBufferEffect) return this.arrayBufferEffect;
103
+ this.arrayBufferEffect = withFiber((fiber) => toArrayBuffer(() => this.source, {
104
+ onError: this.onError,
105
+ maxBytes: fiber.getRef(MaxBodySize)
106
+ })).pipe(cached, runSync);
107
+ this.textEffect = map(this.arrayBufferEffect, (_) => new TextDecoder().decode(_));
108
+ return this.arrayBufferEffect;
109
+ }
110
+ };
111
+ //#endregion
112
+ //#region ../../node_modules/.pnpm/@effect+platform-node-shared@4.0.0-beta.103_bufferutil@4.1.0_effect@4.0.0-beta.103_patc_c414c3b6e1e822e230993f828f7aeb31/node_modules/@effect/platform-node-shared/dist/NodeHttpCompression.js
113
+ /**
114
+ * HTTP response compression backed by `node:zlib`, shared by the Node.js, Bun,
115
+ * and Deno platforms.
116
+ *
117
+ * Byte-array bodies are compressed in one shot with the asynchronous
118
+ * `node:zlib` APIs, preserving an exact `Content-Length`. Streaming bodies go
119
+ * through `node:zlib` transform streams that flush each input chunk.
120
+ *
121
+ * @since 4.0.0
122
+ */
123
+ /**
124
+ * The compression algorithms supported by the runtime's `node:zlib`. `zstd`
125
+ * requires Node.js 22.15 or newer.
126
+ *
127
+ * @category constants
128
+ * @since 4.0.0
129
+ */
130
+ const algorithms = /*#__PURE__*/ new Set(typeof Zlib.zstdCompress === "function" ? [
131
+ "gzip",
132
+ "deflate",
133
+ "br",
134
+ "zstd"
135
+ ] : [
136
+ "gzip",
137
+ "deflate",
138
+ "br"
139
+ ]);
140
+ const brotliParams = (level, sizeHint) => {
141
+ const params = {};
142
+ if (level !== void 0) params[Zlib.constants.BROTLI_PARAM_QUALITY] = level;
143
+ if (sizeHint !== void 0) params[Zlib.constants.BROTLI_PARAM_SIZE_HINT] = sizeHint;
144
+ return { params };
145
+ };
146
+ const zstdParams = (level) => level === void 0 || level === 3 ? void 0 : { params: { [Zlib.constants.ZSTD_c_compressionLevel]: level } };
147
+ const compress = (data, algorithm, options) => callback((resume) => {
148
+ const complete = (error, result) => resume(error === null ? succeed(result) : die(error));
149
+ switch (algorithm) {
150
+ case "gzip":
151
+ Zlib.gzip(data, { level: options?.level }, complete);
152
+ break;
153
+ case "deflate":
154
+ Zlib.deflate(data, { level: options?.level }, complete);
155
+ break;
156
+ case "br":
157
+ Zlib.brotliCompress(data, brotliParams(options?.level, data.byteLength), complete);
158
+ break;
159
+ case "zstd": {
160
+ const params = zstdParams(options?.level);
161
+ if (params === void 0) Zlib.zstdCompress(data, complete);
162
+ else Zlib.zstdCompress(data, params, complete);
163
+ break;
164
+ }
165
+ }
166
+ });
167
+ /**
168
+ * Creates a `Compression` that compresses byte-array bodies in one shot with
169
+ * the asynchronous `node:zlib` APIs, setting the exact `Content-Length` of the
170
+ * compressed body. All other bodies are delegated to `fallback`.
171
+ *
172
+ * @category constructors
173
+ * @since 4.0.0
174
+ */
175
+ const make$3 = (fallback) => ({
176
+ algorithms: fallback.algorithms,
177
+ compressResponse(response, algorithm, options) {
178
+ const body = response.body;
179
+ if (body._tag !== "Uint8Array") return fallback.compressResponse(response, algorithm, options);
180
+ return map(compress(body.body, algorithm, options), (result) => setHeader(setBody(response, uint8Array(result, body.contentType)), "content-length", result.byteLength.toString()));
181
+ }
182
+ });
183
+ /**
184
+ * Creates a `node:zlib` compression transform stream that flushes each input
185
+ * chunk, for streaming response bodies.
186
+ *
187
+ * @category constructors
188
+ * @since 4.0.0
189
+ */
190
+ const compressTransform = (algorithm, options) => {
191
+ switch (algorithm) {
192
+ case "gzip": return Zlib.createGzip({
193
+ level: options?.level,
194
+ flush: Zlib.constants.Z_SYNC_FLUSH
195
+ });
196
+ case "deflate": return Zlib.createDeflate({
197
+ level: options?.level,
198
+ flush: Zlib.constants.Z_SYNC_FLUSH
199
+ });
200
+ case "br": return Zlib.createBrotliCompress({
201
+ ...brotliParams(options?.level),
202
+ flush: Zlib.constants.BROTLI_OPERATION_FLUSH
203
+ });
204
+ case "zstd": return Zlib.createZstdCompress({
205
+ ...zstdParams(options?.level),
206
+ flush: Zlib.constants.ZSTD_e_flush
207
+ });
208
+ }
209
+ };
210
+ //#endregion
211
+ //#region ../../node_modules/.pnpm/@effect+platform-node@4.0.0-beta.103_bufferutil@4.1.0_effect@4.0.0-beta.103_patch_hash=_7eec308fcf6351488fccaf9a0edac255/node_modules/@effect/platform-node/dist/NodeHttpPlatform.js
212
+ /**
213
+ * Node.js implementation of the Effect HTTP platform service.
214
+ *
215
+ * This module connects the portable `HttpPlatform` file response helpers to
216
+ * Node runtime primitives. It serves local files through Node readable streams,
217
+ * supports byte ranges, converts Web `File` values to readable streams, and
218
+ * fills in content type and content length headers when needed.
219
+ *
220
+ * @since 4.0.0
221
+ */
222
+ const compressedBody = (response, body) => removeHeader(setBody(response, body), "content-length");
223
+ /**
224
+ * Creates the Node `HttpPlatform`, serving file responses from Node readable
225
+ * streams and adding MIME type and content-length headers when needed.
226
+ *
227
+ * @category constructors
228
+ * @since 4.0.0
229
+ */
230
+ const make$2 = /*#__PURE__*/ make$4({
231
+ platform: "node",
232
+ compression: /* @__PURE__ */ make$3({
233
+ algorithms,
234
+ compressResponse(response, algorithm, options) {
235
+ const body = response.body;
236
+ switch (body._tag) {
237
+ case "Stream": return succeed(compressedBody(response, stream$1(pipeThroughDuplex(body.stream, { evaluate: () => compressTransform(algorithm, options) }), body.contentType)));
238
+ case "Raw": {
239
+ const readable = body.body instanceof Readable ? body.body : Readable.fromWeb(new Response(body.body).body);
240
+ const transform = compressTransform(algorithm, options);
241
+ readable.on("error", (cause) => transform.destroy(cause));
242
+ transform.on("error", (cause) => readable.destroy(cause));
243
+ transform.on("close", () => readable.destroy());
244
+ return succeed(compressedBody(response, raw(readable.pipe(transform), { contentType: body.contentType })));
245
+ }
246
+ default: return succeed(response);
247
+ }
248
+ }
249
+ }),
250
+ fileResponse(path, status, statusText, headers, start, end, contentLength) {
251
+ return raw$1(contentLength === 0 ? Readable.from([]) : Fs.createReadStream(path, {
252
+ start,
253
+ end: end === void 0 ? void 0 : end - 1
254
+ }), {
255
+ headers: {
256
+ ...headers,
257
+ "content-type": headers["content-type"] ?? Mime_default.getType(path) ?? "application/octet-stream",
258
+ "content-length": contentLength.toString()
259
+ },
260
+ status,
261
+ statusText
262
+ });
263
+ },
264
+ fileWebResponse(file, status, statusText, headers, _options) {
265
+ return raw$1(Readable.fromWeb(file.stream()), {
266
+ headers: merge(headers, fromRecordUnsafe({
267
+ "content-type": headers["content-type"] ?? Mime_default.getType(file.name) ?? "application/octet-stream",
268
+ "content-length": file.size.toString()
269
+ })),
270
+ status,
271
+ statusText
272
+ });
273
+ }
274
+ });
275
+ /**
276
+ * Provides the Node `HttpPlatform` together with the filesystem and ETag
277
+ * services it needs for file responses.
278
+ *
279
+ * @category layers
280
+ * @since 4.0.0
281
+ */
282
+ const layer$1 = /*#__PURE__*/ pipe(/*#__PURE__*/ effect(HttpPlatform)(make$2), /*#__PURE__*/ provide(layer$4), /*#__PURE__*/ provide(layer$2));
283
+ //#endregion
284
+ //#region ../../node_modules/.pnpm/multipasta@0.2.8/node_modules/multipasta/dist/esm/node.js
285
+ var MultipastaStream = class extends Duplex {
286
+ _parser;
287
+ _canWrite = true;
288
+ _writeCallback;
289
+ constructor(config) {
290
+ super({ readableObjectMode: true });
291
+ let currentError;
292
+ let currentFile;
293
+ this._parser = make$5({
294
+ ...config,
295
+ onField: (info, value) => {
296
+ if (currentError !== void 0) return;
297
+ const field = {
298
+ _tag: "Field",
299
+ info,
300
+ value
301
+ };
302
+ this.push(field);
303
+ this.emit("field", field);
304
+ },
305
+ onFile: (info) => {
306
+ if (currentError !== void 0) return (_) => {};
307
+ const file = new FileStream(info, this);
308
+ currentFile = file;
309
+ this.push(file);
310
+ this.emit("file", file);
311
+ return (chunk) => {
312
+ this._canWrite = file.push(chunk);
313
+ if (chunk === null && !this._canWrite) {
314
+ currentFile = void 0;
315
+ this._resume();
316
+ }
317
+ };
318
+ },
319
+ onError: (error) => {
320
+ this.emit("error", error);
321
+ currentFile?.emit("error", error);
322
+ currentError = error;
323
+ },
324
+ onDone: () => {
325
+ this.push(null);
326
+ }
327
+ });
328
+ }
329
+ _resume() {
330
+ this._canWrite = true;
331
+ if (this._writeCallback !== void 0) {
332
+ const callback = this._writeCallback;
333
+ this._writeCallback = void 0;
334
+ callback();
335
+ }
336
+ }
337
+ _read(_size) {}
338
+ _write(chunk, encoding, callback) {
339
+ this._parser.write(chunk instanceof Uint8Array ? chunk : Buffer.from(chunk, encoding));
340
+ if (this._canWrite) callback();
341
+ else this._writeCallback = callback;
342
+ }
343
+ _final(callback) {
344
+ this._parser.end();
345
+ callback();
346
+ }
347
+ };
348
+ const make$1 = (config) => new MultipastaStream(config);
349
+ var FileStream = class extends Readable {
350
+ info;
351
+ _parent;
352
+ _tag = "File";
353
+ filename;
354
+ constructor(info, _parent) {
355
+ super();
356
+ this.info = info;
357
+ this._parent = _parent;
358
+ this.filename = info.filename;
359
+ }
360
+ _read(_size) {
361
+ if (this._parent._canWrite === false) this._parent._resume();
362
+ }
363
+ };
364
+ //#endregion
365
+ //#region ../../node_modules/.pnpm/@effect+platform-node@4.0.0-beta.103_bufferutil@4.1.0_effect@4.0.0-beta.103_patch_hash=_7eec308fcf6351488fccaf9a0edac255/node_modules/@effect/platform-node/dist/NodeMultipart.js
366
+ /**
367
+ * Node.js multipart parsing for HTTP `multipart/form-data` request bodies.
368
+ *
369
+ * `NodeMultipart` adapts a Node `Readable` plus incoming HTTP headers into
370
+ * Effect's shared multipart model. It can expose form parts as a stream or
371
+ * collect a complete persisted form by writing file uploads to scoped temporary
372
+ * files through the current `FileSystem` and `Path` services. `fileToReadable`
373
+ * returns the underlying Node readable stream for file parts produced by this
374
+ * parser.
375
+ *
376
+ * @since 4.0.0
377
+ */
378
+ /**
379
+ * Parses multipart data from a Node readable request body and headers into a
380
+ * stream of `Multipart.Part` values, converting parser failures to
381
+ * `MultipartError`.
382
+ *
383
+ * @category constructors
384
+ * @since 4.0.0
385
+ */
386
+ const stream = (source, headers) => makeConfig(headers).pipe(map((config) => fromReadable({
387
+ evaluate() {
388
+ const parser = make$1(config);
389
+ source.pipe(parser);
390
+ return parser;
391
+ },
392
+ onError: (error) => convertError(error)
393
+ })), unwrap, map$1(convertPart));
394
+ /**
395
+ * Parses multipart data from a Node readable request body and persists file
396
+ * parts using the current `FileSystem`, `Path`, and `Scope` services.
397
+ *
398
+ * @category constructors
399
+ * @since 4.0.0
400
+ */
401
+ const persisted = (source, headers) => toPersisted(stream(source, headers), (path, file) => tryPromise({
402
+ try: (signal) => NodeStreamP.pipeline(file.file, Fs.createWriteStream(path), { signal }),
403
+ catch: (cause) => MultipartError.fromReason("InternalError", cause)
404
+ }));
405
+ const convertPart = (part) => part._tag === "Field" ? new FieldImpl(part.info, part.value) : new FileImpl(part);
406
+ var PartBase = class extends Class {
407
+ [TypeId$1];
408
+ constructor() {
409
+ super();
410
+ this[TypeId$1] = TypeId$1;
411
+ }
412
+ };
413
+ var FieldImpl = class extends PartBase {
414
+ _tag = "Field";
415
+ key;
416
+ contentType;
417
+ value;
418
+ constructor(info, value) {
419
+ super();
420
+ this.key = info.name;
421
+ this.contentType = info.contentType;
422
+ this.value = decodeField(info, value);
423
+ }
424
+ toJSON() {
425
+ return {
426
+ _id: "@effect/platform/Multipart/Part",
427
+ _tag: "Field",
428
+ key: this.key,
429
+ value: this.value,
430
+ contentType: this.contentType
431
+ };
432
+ }
433
+ };
434
+ var FileImpl = class extends PartBase {
435
+ _tag = "File";
436
+ key;
437
+ name;
438
+ contentType;
439
+ content;
440
+ contentEffect;
441
+ file;
442
+ constructor(file) {
443
+ super();
444
+ this.file = file;
445
+ this.key = file.info.name;
446
+ this.name = file.filename ?? file.info.name;
447
+ this.contentType = file.info.contentType;
448
+ this.content = fromReadable({
449
+ evaluate: () => file,
450
+ onError: (cause) => MultipartError.fromReason("InternalError", cause)
451
+ });
452
+ this.contentEffect = toUint8Array(() => file, { onError: (cause) => MultipartError.fromReason("InternalError", cause) });
453
+ }
454
+ toJSON() {
455
+ return {
456
+ _id: "@effect/platform/Multipart/Part",
457
+ _tag: "File",
458
+ key: this.key,
459
+ name: this.name,
460
+ contentType: this.contentType
461
+ };
462
+ }
463
+ };
464
+ function convertError(cause) {
465
+ switch (cause._tag) {
466
+ case "ReachedLimit": switch (cause.limit) {
467
+ case "MaxParts": return MultipartError.fromReason("TooManyParts", cause);
468
+ case "MaxFieldSize": return MultipartError.fromReason("FieldTooLarge", cause);
469
+ case "MaxPartSize": return MultipartError.fromReason("FileTooLarge", cause);
470
+ case "MaxTotalSize": return MultipartError.fromReason("BodyTooLarge", cause);
471
+ }
472
+ default: return MultipartError.fromReason("Parse", cause);
473
+ }
474
+ }
475
+ //#endregion
476
+ //#region ../../node_modules/.pnpm/@effect+platform-node@4.0.0-beta.103_bufferutil@4.1.0_effect@4.0.0-beta.103_patch_hash=_7eec308fcf6351488fccaf9a0edac255/node_modules/@effect/platform-node/dist/NodeHttpServer.js
477
+ /**
478
+ * Node.js implementation of the Effect `HttpServer`.
479
+ *
480
+ * This module adapts a supplied Node `http.Server` into Effect's
481
+ * platform-independent HTTP server service. It starts the server with Node
482
+ * `listen` options, converts `request` events into `HttpServerRequest` values,
483
+ * writes `HttpServerResponse` bodies through Node's `ServerResponse`, and
484
+ * handles `upgrade` events by exposing the upgraded socket through
485
+ * `HttpServerRequest.upgrade`. It also exports request and upgrade handler
486
+ * constructors plus layers for the server alone, HTTP support services, the
487
+ * combined server, configurable options, and tests.
488
+ *
489
+ * @since 4.0.0
490
+ */
491
+ /**
492
+ * Creates a scoped `HttpServer` from a Node `http.Server`, starts listening
493
+ * with the supplied options, registers request and upgrade handling, and closes
494
+ * the server during scope finalization with optional graceful-shutdown control.
495
+ *
496
+ * @category constructors
497
+ * @since 4.0.0
498
+ */
499
+ const make = /*#__PURE__*/ fnUntraced(function* (evaluate, options) {
500
+ const scope$2 = yield* scope;
501
+ const server = evaluate();
502
+ const shutdown = yield* callback((resume) => {
503
+ if (!server.listening) return resume(void_);
504
+ server.close((error) => {
505
+ if (error) resume(die(error));
506
+ else resume(void_);
507
+ });
508
+ }).pipe(cached);
509
+ const preemptiveShutdown = options.disablePreemptiveShutdown ? void_ : timeoutOrElse(shutdown, {
510
+ duration: options.gracefulShutdownTimeout ?? seconds(20),
511
+ orElse: () => void_
512
+ });
513
+ yield* addFinalizer(scope$2, shutdown);
514
+ yield* callback((resume) => {
515
+ function onError(cause) {
516
+ resume(fail(new ServeError({ cause })));
517
+ }
518
+ server.on("error", onError);
519
+ server.listen(options, () => {
520
+ server.off("error", onError);
521
+ resume(void_);
522
+ });
523
+ });
524
+ const address = server.address();
525
+ const wss = yield* acquireRelease(sync(() => new import_websocket_server.default({
526
+ ...options.websocket,
527
+ noServer: true
528
+ })), (wss) => callback((resume) => {
529
+ wss.close(() => resume(void_));
530
+ })).pipe(provide$1(scope$2), cached);
531
+ return make$6({
532
+ address: typeof address === "string" ? {
533
+ _tag: "UnixAddress",
534
+ path: address
535
+ } : {
536
+ _tag: "TcpAddress",
537
+ hostname: address.address === "::" ? "0.0.0.0" : address.address,
538
+ port: address.port
539
+ },
540
+ serve: fnUntraced(function* (httpApp, middleware) {
541
+ const serveScope = yield* scope;
542
+ const scope$1 = forkUnsafe(serveScope, "parallel");
543
+ const handler = yield* makeHandler(httpApp, {
544
+ middleware,
545
+ scope: scope$1
546
+ });
547
+ const upgradeHandler = yield* makeUpgradeHandler(wss, httpApp, {
548
+ middleware,
549
+ scope: scope$1
550
+ });
551
+ yield* addFinalizerExit(serveScope, () => {
552
+ server.off("request", handler);
553
+ server.off("upgrade", upgradeHandler);
554
+ return preemptiveShutdown;
555
+ });
556
+ server.on("request", handler);
557
+ server.on("upgrade", upgradeHandler);
558
+ })
559
+ });
560
+ });
561
+ /**
562
+ * Creates a Node `request` event handler for an Effect HTTP application,
563
+ * injecting a `HttpServerRequest` and interrupting the request fiber if the
564
+ * client closes the response before it finishes.
565
+ *
566
+ * @category handlers
567
+ * @since 4.0.0
568
+ */
569
+ const makeHandler = (httpEffect, options) => {
570
+ const handled = toHandled(httpEffect, handleResponse, options.middleware);
571
+ return withFiber((parent) => {
572
+ const services = parent.context;
573
+ return succeed(function handler(nodeRequest, nodeResponse) {
574
+ const fiber = runIn(runForkWith(add(services, HttpServerRequest, new ServerRequestImpl(nodeRequest, nodeResponse)))(handled), options.scope);
575
+ nodeResponse.on("close", () => {
576
+ if (!nodeResponse.writableEnded) fiber.interruptUnsafe(parent.id, ClientAbort.annotation);
577
+ });
578
+ });
579
+ });
580
+ };
581
+ /**
582
+ * Creates a Node `upgrade` event handler for an Effect HTTP application,
583
+ * exposing the upgraded WebSocket as the request's `upgrade` effect and
584
+ * interrupting the request fiber when the socket closes early.
585
+ *
586
+ * @category handlers
587
+ * @since 4.0.0
588
+ */
589
+ const makeUpgradeHandler = (lazyWss, httpEffect, options) => {
590
+ const handledApp = toHandled(httpEffect, handleResponse, options.middleware);
591
+ return withFiber((parent) => {
592
+ const services = parent.context;
593
+ return succeed(function handler(nodeRequest, socket, head) {
594
+ let nodeResponse_ = void 0;
595
+ const nodeResponse = () => {
596
+ if (nodeResponse_ === void 0) {
597
+ nodeResponse_ = new Http.ServerResponse(nodeRequest);
598
+ nodeResponse_.assignSocket(socket);
599
+ nodeResponse_.on("finish", () => {
600
+ socket.end();
601
+ });
602
+ }
603
+ return nodeResponse_;
604
+ };
605
+ const upgradeEffect = fromWebSocket(flatMap(lazyWss, (wss) => acquireRelease(callback((resume) => wss.handleUpgrade(nodeRequest, socket, head, (ws) => {
606
+ resume(succeed(ws));
607
+ })), (ws) => sync(() => ws.close()))));
608
+ const fiber = runIn(runForkWith(add(services, HttpServerRequest, new ServerRequestImpl(nodeRequest, nodeResponse, upgradeEffect)))(handledApp), options.scope);
609
+ socket.on("close", () => {
610
+ if (!socket.writableEnded) fiber.interruptUnsafe(parent.id, ClientAbort.annotation);
611
+ });
612
+ });
613
+ });
614
+ };
615
+ var ServerRequestImpl = class ServerRequestImpl extends NodeHttpIncomingMessage {
616
+ [TypeId$2];
617
+ response;
618
+ upgradeEffect;
619
+ url;
620
+ headersOverride;
621
+ constructor(source, response, upgradeEffect, url = source.url, headersOverride, remoteAddressOverride) {
622
+ super(source, (cause) => new HttpServerError({ reason: new RequestParseError({
623
+ request: this,
624
+ cause
625
+ }) }), remoteAddressOverride);
626
+ this[TypeId$2] = TypeId$2;
627
+ this.response = response;
628
+ this.upgradeEffect = upgradeEffect;
629
+ this.url = url;
630
+ this.headersOverride = headersOverride;
631
+ }
632
+ cachedCookies;
633
+ get cookies() {
634
+ if (this.cachedCookies) return this.cachedCookies;
635
+ return this.cachedCookies = parseHeader(this.headers.cookie ?? "");
636
+ }
637
+ get resolvedResponse() {
638
+ return typeof this.response === "function" ? this.response() : this.response;
639
+ }
640
+ modify(options) {
641
+ return new ServerRequestImpl(this.source, this.response, this.upgradeEffect, options.url ?? this.url, options.headers ?? this.headersOverride, "remoteAddress" in options ? options.remoteAddress : this.remoteAddressOverride);
642
+ }
643
+ get originalUrl() {
644
+ return this.source.url;
645
+ }
646
+ cachedMethod;
647
+ get method() {
648
+ return this.cachedMethod ??= this.source.method.toUpperCase();
649
+ }
650
+ get headers() {
651
+ this.headersOverride ??= this.source.headers;
652
+ return this.headersOverride;
653
+ }
654
+ multipartEffect;
655
+ get multipart() {
656
+ if (this.multipartEffect) return this.multipartEffect;
657
+ this.multipartEffect = runSync(cached(persisted(this.source, this.source.headers)));
658
+ return this.multipartEffect;
659
+ }
660
+ get multipartStream() {
661
+ return stream(this.source, this.source.headers);
662
+ }
663
+ get upgrade() {
664
+ return this.upgradeEffect ?? fail(new HttpServerError({ reason: new RequestParseError({
665
+ request: this,
666
+ description: "not an upgradeable ServerRequest"
667
+ }) }));
668
+ }
669
+ toString() {
670
+ return `ServerRequest(${this.method} ${this.url})`;
671
+ }
672
+ toJSON() {
673
+ return inspect(this, {
674
+ _id: "HttpServerRequest",
675
+ method: this.method,
676
+ url: this.originalUrl
677
+ });
678
+ }
679
+ };
680
+ /**
681
+ * Provides an `HttpServer` by creating and managing a scoped Node
682
+ * `http.Server` with the supplied listen and shutdown options.
683
+ *
684
+ * @category layers
685
+ * @since 4.0.0
686
+ */
687
+ const layerServer = /*#__PURE__*/ flow(make, /*#__PURE__*/ effect(HttpServer));
688
+ /**
689
+ * Provides the Node HTTP support services used by `NodeHttpServer`, including
690
+ * the HTTP platform, ETag generator, and core Node platform services.
691
+ *
692
+ * @category layers
693
+ * @since 4.0.0
694
+ */
695
+ const layerHttpServices = /*#__PURE__*/ mergeAll(layer$1, layerWeak, layer$5);
696
+ /**
697
+ * Provides a Node `HttpServer` together with the Node HTTP platform, ETag, and
698
+ * core platform services required to serve requests.
699
+ *
700
+ * @category layers
701
+ * @since 4.0.0
702
+ */
703
+ const layer = (evaluate, options) => mergeAll(layerServer(evaluate, options), layerHttpServices);
704
+ /**
705
+ * Provides a Node `HttpServer` together with the Node HTTP platform, ETag,
706
+ * and core Node platform services, reading the listen and shutdown options from
707
+ * a `Config` value.
708
+ *
709
+ * @category layers
710
+ * @since 4.0.0
711
+ */
712
+ const layerConfig = (evaluate, options) => mergeAll(effect(HttpServer)(flatMap(unwrap$1(options), (options) => make(evaluate, options))), layerHttpServices);
713
+ /**
714
+ * Provides a test HTTP server listening on an ephemeral port together with a
715
+ * Fetch-backed `HttpClient` configured for server integration tests.
716
+ *
717
+ * @category testing
718
+ * @since 4.0.0
719
+ */
720
+ const layerTest = /*#__PURE__*/ layerTestClient.pipe(/*#__PURE__*/ provide(/*#__PURE__*/ fresh(layer$3).pipe(/*#__PURE__*/ provide(/*#__PURE__*/ succeed$1(RequestInit)({ keepalive: false })))), /*#__PURE__*/ provideMerge(/*#__PURE__*/ layer(Http.createServer, { port: 0 })));
721
+ const handleResponse = (request, response) => {
722
+ const nodeResponse = request.resolvedResponse;
723
+ if (nodeResponse.writableEnded) return void_;
724
+ let headers = response.headers;
725
+ if (!isEmpty(response.cookies)) {
726
+ headers = { ...headers };
727
+ const toSet = toSetCookieHeaders(response.cookies);
728
+ if (headers["set-cookie"] !== void 0) toSet.push(headers["set-cookie"]);
729
+ headers["set-cookie"] = toSet;
730
+ }
731
+ if (request.method === "HEAD") {
732
+ nodeResponse.writeHead(response.status, headers);
733
+ return andThen(cancelResponseBody(response.body), callback((resume) => {
734
+ let completed = false;
735
+ const done = () => {
736
+ if (completed) return;
737
+ completed = true;
738
+ nodeResponse.off("close", done);
739
+ resume(void_);
740
+ };
741
+ nodeResponse.once("close", done);
742
+ nodeResponse.end(done);
743
+ }));
744
+ }
745
+ const body = response.body;
746
+ switch (body._tag) {
747
+ case "Empty":
748
+ nodeResponse.writeHead(response.status, headers);
749
+ nodeResponse.end();
750
+ return void_;
751
+ case "Raw":
752
+ nodeResponse.writeHead(response.status, headers);
753
+ if (typeof body.body === "object" && body.body !== null && "pipe" in body.body && typeof body.body.pipe === "function") return tryPromise({
754
+ try: (signal) => pipeline(body.body, nodeResponse, {
755
+ signal,
756
+ end: true
757
+ }),
758
+ catch: (cause) => new HttpServerError({ reason: new ResponseError({
759
+ request,
760
+ response,
761
+ description: "Error writing raw response",
762
+ cause
763
+ }) })
764
+ }).pipe(interruptible, tapCause(handleCause(nodeResponse, response)));
765
+ return callback((resume) => {
766
+ nodeResponse.end(body.body, () => resume(void_));
767
+ });
768
+ case "Uint8Array":
769
+ nodeResponse.writeHead(response.status, headers);
770
+ if (body.body.length < 1024 * 1024) {
771
+ nodeResponse.end(body.body);
772
+ return void_;
773
+ }
774
+ return callback((resume) => {
775
+ nodeResponse.end(body.body, () => resume(void_));
776
+ });
777
+ case "FormData": return suspend(() => {
778
+ const r = new globalThis.Response(body.formData);
779
+ nodeResponse.writeHead(response.status, {
780
+ ...headers,
781
+ ...Object.fromEntries(r.headers)
782
+ });
783
+ return callback((resume, signal) => {
784
+ Readable.fromWeb(r.body, { signal }).pipe(nodeResponse).on("error", (cause) => {
785
+ resume(fail(new HttpServerError({ reason: new ResponseError({
786
+ request,
787
+ response,
788
+ description: "Error writing FormData response",
789
+ cause
790
+ }) })));
791
+ }).once("finish", () => {
792
+ resume(void_);
793
+ });
794
+ }).pipe(interruptible, tapCause(handleCause(nodeResponse, response)));
795
+ });
796
+ case "Stream": {
797
+ nodeResponse.writeHead(response.status, headers);
798
+ const drainLatch = makeUnsafe();
799
+ nodeResponse.on("drain", () => drainLatch.openUnsafe());
800
+ return body.stream.pipe(orDie, runForEachArray((array) => {
801
+ const chunk = array.length > 1 ? Buffer.concat(array) : array[0];
802
+ if (nodeResponse.write(chunk)) return void_;
803
+ drainLatch.closeUnsafe();
804
+ return drainLatch.await;
805
+ }), interruptible, matchCauseEffect({
806
+ onSuccess: () => sync(() => nodeResponse.end()),
807
+ onFailure: handleCause(nodeResponse, response)
808
+ }));
809
+ }
810
+ }
811
+ };
812
+ const cancelResponseBody = (body) => {
813
+ const stream = body._tag === "Raw" ? body.body : void 0;
814
+ if (stream instanceof Readable) return sync(() => stream.destroy());
815
+ return void_;
816
+ };
817
+ const handleCause = (nodeResponse, originalResponse) => (originalCause) => flatMap(causeResponse(originalCause), ([response, cause]) => {
818
+ const headersSent = nodeResponse.headersSent;
819
+ if (!headersSent) nodeResponse.writeHead(response.status);
820
+ if (!nodeResponse.writableEnded) nodeResponse.end();
821
+ return failCause(headersSent ? combine(originalCause, die$1(originalResponse)) : cause);
822
+ });
823
+ //#endregion
824
+ export { layer, layerConfig, layerHttpServices, layerServer, layerTest, make, makeHandler, makeUpgradeHandler };
825
+
826
+ //# sourceMappingURL=NodeHttpServer-BGJlR_Kf.mjs.map