srvx 0.2.1 → 0.2.3

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.
@@ -1,5 +1,6 @@
1
1
  import { ServerOptions, Server, FetchHandler, NodeHttpHandler } from '../types.mjs';
2
2
  import NodeHttp__default from 'node:http';
3
+ import { Readable } from 'node:stream';
3
4
  import 'node:net';
4
5
  import 'bun';
5
6
  import '@cloudflare/workers-types';
@@ -7,8 +8,8 @@ import '@cloudflare/workers-types';
7
8
  type NodeFastResponse = InstanceType<typeof NodeFastResponse>;
8
9
  declare const NodeFastResponse: {
9
10
  new (body?: BodyInit | null, init?: ResponseInit): {
10
- "__#4200@#body"?: BodyInit | null;
11
- "__#4200@#init"?: ResponseInit;
11
+ "__#4201@#body"?: BodyInit | null;
12
+ "__#4201@#init"?: ResponseInit;
12
13
  /**
13
14
  * Prepare Node.js response object
14
15
  */
@@ -16,14 +17,14 @@ declare const NodeFastResponse: {
16
17
  status: number;
17
18
  statusText: string;
18
19
  headers: NodeHttp__default.OutgoingHttpHeader[];
19
- body: string | Uint8Array<ArrayBufferLike> | ReadableStream<Uint8Array<ArrayBufferLike>> | Buffer<ArrayBufferLike> | DataView<ArrayBufferLike> | null | undefined;
20
+ body: string | Uint8Array<ArrayBufferLike> | ReadableStream<Uint8Array<ArrayBufferLike>> | Readable | Buffer<ArrayBufferLike> | DataView<ArrayBufferLike> | null | undefined;
20
21
  };
21
22
  /** Lazy initialized response instance */
22
- "__#4200@#responseObj"?: Response;
23
+ "__#4201@#responseObj"?: Response;
23
24
  /** Lazy initialized headers instance */
24
- "__#4200@#headersObj"?: Headers;
25
+ "__#4201@#headersObj"?: Headers;
25
26
  clone(): Response;
26
- readonly "__#4200@#response": Response;
27
+ readonly "__#4201@#response": Response;
27
28
  readonly headers: Headers;
28
29
  readonly ok: boolean;
29
30
  readonly redirected: boolean;
@@ -31,7 +32,7 @@ declare const NodeFastResponse: {
31
32
  readonly statusText: string;
32
33
  readonly type: ResponseType;
33
34
  readonly url: string;
34
- "__#4200@#fastBody"<T extends object>(as: new (...args: any[]) => T): T | null | false;
35
+ "__#4201@#fastBody"<T extends object>(as: new (...args: any[]) => T): T | null | false;
35
36
  readonly body: ReadableStream<Uint8Array> | null;
36
37
  readonly bodyUsed: boolean;
37
38
  arrayBuffer(): Promise<ArrayBuffer>;
@@ -10,11 +10,18 @@ async function sendNodeResponse(nodeRes, webRes) {
10
10
  }
11
11
  if (webRes.nodeResponse) {
12
12
  const res = webRes.nodeResponse();
13
- nodeRes.writeHead(res.status, res.statusText, res.headers);
14
- if (res.body instanceof ReadableStream) {
15
- return streamBody(res.body, nodeRes);
13
+ if (!nodeRes.headersSent) {
14
+ nodeRes.writeHead(res.status, res.statusText, res.headers);
15
+ }
16
+ if (res.body) {
17
+ if (res.body instanceof ReadableStream) {
18
+ return streamBody(res.body, nodeRes);
19
+ } else if (typeof res.body?.pipe === "function") {
20
+ res.body.pipe(nodeRes);
21
+ return new Promise((resolve) => nodeRes.on("close", resolve));
22
+ }
23
+ nodeRes.write(res.body);
16
24
  }
17
- nodeRes.write(res.body);
18
25
  return endNodeResponse(nodeRes);
19
26
  }
20
27
  const headerEntries = [];
@@ -27,7 +34,9 @@ async function sendNodeResponse(nodeRes, webRes) {
27
34
  headerEntries.push([key, value]);
28
35
  }
29
36
  }
30
- nodeRes.writeHead(webRes.status || 200, webRes.statusText, headerEntries);
37
+ if (!nodeRes.headersSent) {
38
+ nodeRes.writeHead(webRes.status || 200, webRes.statusText, headerEntries);
39
+ }
31
40
  return webRes.body ? streamBody(webRes.body, nodeRes) : endNodeResponse(nodeRes);
32
41
  }
33
42
  function endNodeResponse(nodeRes) {
@@ -79,103 +88,107 @@ const kNodeInspect = /* @__PURE__ */ Symbol.for(
79
88
  "nodejs.util.inspect.custom"
80
89
  );
81
90
 
82
- const NodeReqHeadersProxy = /* @__PURE__ */ (() => class NodeReqHeadersProxy {
83
- constructor(req) {
84
- this[kNodeReq] = req;
85
- }
86
- append(name, value) {
87
- name = name.toLowerCase();
88
- const _headers = this[kNodeReq].headers;
89
- const _current = _headers[name];
90
- if (_current) {
91
- if (Array.isArray(_current)) {
92
- _current.push(value);
91
+ const NodeReqHeadersProxy = /* @__PURE__ */ (() => {
92
+ class NodeReqHeadersProxy2 {
93
+ constructor(req) {
94
+ this[kNodeReq] = req;
95
+ }
96
+ append(name, value) {
97
+ name = name.toLowerCase();
98
+ const _headers = this[kNodeReq].headers;
99
+ const _current = _headers[name];
100
+ if (_current) {
101
+ if (Array.isArray(_current)) {
102
+ _current.push(value);
103
+ } else {
104
+ _headers[name] = [_current, value];
105
+ }
93
106
  } else {
94
- _headers[name] = [_current, value];
107
+ _headers[name] = value;
95
108
  }
96
- } else {
97
- _headers[name] = value;
98
109
  }
99
- }
100
- delete(name) {
101
- name = name.toLowerCase();
102
- this[kNodeReq].headers[name] = void 0;
103
- }
104
- get(name) {
105
- name = name.toLowerCase();
106
- return _normalizeValue(this[kNodeReq].headers[name]);
107
- }
108
- getSetCookie() {
109
- const setCookie = this[kNodeReq].headers["set-cookie"];
110
- if (!setCookie || setCookie.length === 0) {
111
- return [];
110
+ delete(name) {
111
+ name = name.toLowerCase();
112
+ this[kNodeReq].headers[name] = void 0;
112
113
  }
113
- return splitSetCookieString(setCookie);
114
- }
115
- has(name) {
116
- name = name.toLowerCase();
117
- return !!this[kNodeReq].headers[name];
118
- }
119
- set(name, value) {
120
- name = name.toLowerCase();
121
- this[kNodeReq].headers[name] = value;
122
- }
123
- get count() {
124
- throw new Error("Method not implemented.");
125
- }
126
- getAll(_name) {
127
- throw new Error("Method not implemented.");
128
- }
129
- toJSON() {
130
- const _headers = this[kNodeReq].headers;
131
- const result = {};
132
- for (const key in _headers) {
133
- if (_headers[key]) {
134
- result[key] = _normalizeValue(_headers[key]);
135
- }
136
- }
137
- return result;
138
- }
139
- forEach(cb, thisArg) {
140
- const _headers = this[kNodeReq].headers;
141
- for (const key in _headers) {
142
- if (_headers[key]) {
143
- cb.call(
144
- thisArg,
145
- _normalizeValue(_headers[key]),
146
- key,
147
- this
148
- );
114
+ get(name) {
115
+ name = name.toLowerCase();
116
+ return _normalizeValue(this[kNodeReq].headers[name]);
117
+ }
118
+ getSetCookie() {
119
+ const setCookie = this[kNodeReq].headers["set-cookie"];
120
+ if (!setCookie || setCookie.length === 0) {
121
+ return [];
149
122
  }
123
+ return splitSetCookieString(setCookie);
150
124
  }
151
- }
152
- *entries() {
153
- const _headers = this[kNodeReq].headers;
154
- for (const key in _headers) {
155
- yield [key, _normalizeValue(_headers[key])];
125
+ has(name) {
126
+ name = name.toLowerCase();
127
+ return !!this[kNodeReq].headers[name];
156
128
  }
157
- }
158
- *keys() {
159
- const keys = Object.keys(this[kNodeReq].headers);
160
- for (const key of keys) {
161
- yield key;
129
+ set(name, value) {
130
+ name = name.toLowerCase();
131
+ this[kNodeReq].headers[name] = value;
162
132
  }
163
- }
164
- *values() {
165
- const values = Object.values(this[kNodeReq].headers);
166
- for (const value of values) {
167
- yield _normalizeValue(value);
133
+ get count() {
134
+ throw new Error("Method not implemented.");
135
+ }
136
+ getAll(_name) {
137
+ throw new Error("Method not implemented.");
138
+ }
139
+ toJSON() {
140
+ const _headers = this[kNodeReq].headers;
141
+ const result = {};
142
+ for (const key in _headers) {
143
+ if (_headers[key]) {
144
+ result[key] = _normalizeValue(_headers[key]);
145
+ }
146
+ }
147
+ return result;
148
+ }
149
+ forEach(cb, thisArg) {
150
+ const _headers = this[kNodeReq].headers;
151
+ for (const key in _headers) {
152
+ if (_headers[key]) {
153
+ cb.call(
154
+ thisArg,
155
+ _normalizeValue(_headers[key]),
156
+ key,
157
+ this
158
+ );
159
+ }
160
+ }
161
+ }
162
+ *entries() {
163
+ const _headers = this[kNodeReq].headers;
164
+ for (const key in _headers) {
165
+ yield [key, _normalizeValue(_headers[key])];
166
+ }
167
+ }
168
+ *keys() {
169
+ const keys = Object.keys(this[kNodeReq].headers);
170
+ for (const key of keys) {
171
+ yield key;
172
+ }
173
+ }
174
+ *values() {
175
+ const values = Object.values(this[kNodeReq].headers);
176
+ for (const value of values) {
177
+ yield _normalizeValue(value);
178
+ }
179
+ }
180
+ [(Symbol.iterator)]() {
181
+ return this.entries()[Symbol.iterator]();
182
+ }
183
+ get [Symbol.toStringTag]() {
184
+ return "Headers";
185
+ }
186
+ [kNodeInspect]() {
187
+ return Object.fromEntries(this.entries());
168
188
  }
169
189
  }
170
- [(Symbol.iterator)]() {
171
- return this.entries()[Symbol.iterator]();
172
- }
173
- get [Symbol.toStringTag]() {
174
- return "Headers";
175
- }
176
- [kNodeInspect]() {
177
- return Object.fromEntries(this.entries());
178
- }
190
+ Object.setPrototypeOf(NodeReqHeadersProxy2.prototype, Headers.prototype);
191
+ return NodeReqHeadersProxy2;
179
192
  })();
180
193
  function _normalizeValue(value) {
181
194
  if (Array.isArray(value)) {
@@ -341,148 +354,155 @@ function parseHost(host) {
341
354
  return [s[0], String(Number.parseInt(s[1]) || "")];
342
355
  }
343
356
 
344
- const NodeRequestProxy = /* @__PURE__ */ (() => class NodeRequestProxy2 {
345
- constructor(nodeReq) {
346
- this.cache = "default";
347
- this.credentials = "same-origin";
348
- this.destination = "";
349
- this.integrity = "";
350
- this.keepalive = false;
351
- this.mode = "cors";
352
- this.redirect = "follow";
353
- this.referrer = "about:client";
354
- this.referrerPolicy = "";
355
- this.bodyUsed = false;
356
- this[kNodeReq] = nodeReq;
357
- this._url = new NodeReqURLProxy(nodeReq);
358
- this.headers = new NodeReqHeadersProxy(nodeReq);
359
- }
360
- #abortSignal;
361
- #hasBody;
362
- #bodyBytes;
363
- #blobBody;
364
- #formDataBody;
365
- #jsonBody;
366
- #textBody;
367
- #bodyStream;
368
- get remoteAddress() {
369
- return this[kNodeReq].socket?.remoteAddress;
370
- }
371
- clone() {
372
- return new NodeRequestProxy2(this[kNodeReq]);
373
- }
374
- get url() {
375
- return this._url.href;
376
- }
377
- get method() {
378
- return this[kNodeReq].method || "GET";
379
- }
380
- get signal() {
381
- if (!this.#abortSignal) {
382
- this.#abortSignal = new AbortController();
357
+ const NodeRequestProxy = /* @__PURE__ */ (() => {
358
+ class NodeRequestProxy2 {
359
+ #url;
360
+ #headers;
361
+ #bodyUsed = false;
362
+ #abortSignal;
363
+ #hasBody;
364
+ #bodyBytes;
365
+ #blobBody;
366
+ #formDataBody;
367
+ #jsonBody;
368
+ #textBody;
369
+ #bodyStream;
370
+ constructor(nodeReq) {
371
+ this[kNodeReq] = nodeReq;
383
372
  }
384
- return this.#abortSignal.signal;
385
- }
386
- get _hasBody() {
387
- if (this.#hasBody !== void 0) {
388
- return this.#hasBody;
373
+ get headers() {
374
+ if (!this.#headers) {
375
+ this.#headers = new NodeReqHeadersProxy(this[kNodeReq]);
376
+ }
377
+ return this.#headers;
389
378
  }
390
- const method = this[kNodeReq].method?.toUpperCase();
391
- if (!method || !(method === "PATCH" || method === "POST" || method === "PUT" || method === "DELETE")) {
392
- this.#hasBody = false;
393
- return false;
379
+ get remoteAddress() {
380
+ return this[kNodeReq].socket?.remoteAddress;
381
+ }
382
+ clone() {
383
+ return new NodeRequestProxy2(this[kNodeReq]);
384
+ }
385
+ get url() {
386
+ if (!this.#url) {
387
+ this.#url = new NodeReqURLProxy(this[kNodeReq]);
388
+ }
389
+ return this.#url.href;
390
+ }
391
+ get method() {
392
+ return this[kNodeReq].method || "GET";
394
393
  }
395
- if (!Number.parseInt(this[kNodeReq].headers["content-length"] || "")) {
396
- const isChunked = (this[kNodeReq].headers["transfer-encoding"] || "").split(",").map((e) => e.trim()).filter(Boolean).includes("chunked");
397
- if (!isChunked) {
394
+ get signal() {
395
+ if (!this.#abortSignal) {
396
+ this.#abortSignal = new AbortController();
397
+ }
398
+ return this.#abortSignal.signal;
399
+ }
400
+ get bodyUsed() {
401
+ return this.#bodyUsed;
402
+ }
403
+ get _hasBody() {
404
+ if (this.#hasBody !== void 0) {
405
+ return this.#hasBody;
406
+ }
407
+ const method = this[kNodeReq].method?.toUpperCase();
408
+ if (!method || !(method === "PATCH" || method === "POST" || method === "PUT" || method === "DELETE")) {
398
409
  this.#hasBody = false;
399
410
  return false;
400
411
  }
401
- }
402
- return true;
403
- }
404
- get body() {
405
- if (!this._hasBody) {
406
- return null;
407
- }
408
- if (!this.#bodyStream) {
409
- this.bodyUsed = true;
410
- this.#bodyStream = new ReadableStream({
411
- start: (controller) => {
412
- this[kNodeReq].on("data", (chunk) => {
413
- controller.enqueue(chunk);
414
- }).once("error", (error) => {
415
- controller.error(error);
416
- this.#abortSignal?.abort();
417
- }).once("close", () => {
418
- this.#abortSignal?.abort();
419
- }).once("end", () => {
420
- controller.close();
421
- });
412
+ if (!Number.parseInt(this[kNodeReq].headers["content-length"] || "")) {
413
+ const isChunked = (this[kNodeReq].headers["transfer-encoding"] || "").split(",").map((e) => e.trim()).filter(Boolean).includes("chunked");
414
+ if (!isChunked) {
415
+ this.#hasBody = false;
416
+ return false;
422
417
  }
418
+ }
419
+ return true;
420
+ }
421
+ get body() {
422
+ if (!this._hasBody) {
423
+ return null;
424
+ }
425
+ if (!this.#bodyStream) {
426
+ this.#bodyUsed = true;
427
+ this.#bodyStream = new ReadableStream({
428
+ start: (controller) => {
429
+ this[kNodeReq].on("data", (chunk) => {
430
+ controller.enqueue(chunk);
431
+ }).once("error", (error) => {
432
+ controller.error(error);
433
+ this.#abortSignal?.abort();
434
+ }).once("close", () => {
435
+ this.#abortSignal?.abort();
436
+ }).once("end", () => {
437
+ controller.close();
438
+ });
439
+ }
440
+ });
441
+ }
442
+ return this.#bodyStream;
443
+ }
444
+ bytes() {
445
+ if (!this.#bodyBytes) {
446
+ const _bodyStream = this.body;
447
+ this.#bodyBytes = _bodyStream ? _readStream(_bodyStream) : Promise.resolve(new Uint8Array());
448
+ }
449
+ return this.#bodyBytes;
450
+ }
451
+ arrayBuffer() {
452
+ return this.bytes().then((buff) => {
453
+ return buff.buffer.slice(
454
+ buff.byteOffset,
455
+ buff.byteOffset + buff.byteLength
456
+ );
423
457
  });
424
458
  }
425
- return this.#bodyStream;
426
- }
427
- bytes() {
428
- if (!this.#bodyBytes) {
429
- const _bodyStream = this.body;
430
- this.#bodyBytes = _bodyStream ? _readStream(_bodyStream) : Promise.resolve(new Uint8Array());
459
+ blob() {
460
+ if (!this.#blobBody) {
461
+ this.#blobBody = this.bytes().then((bytes) => {
462
+ return new Blob([bytes], {
463
+ type: this[kNodeReq].headers["content-type"]
464
+ });
465
+ });
466
+ }
467
+ return this.#blobBody;
431
468
  }
432
- return this.#bodyBytes;
433
- }
434
- arrayBuffer() {
435
- return this.bytes().then((buff) => {
436
- return buff.buffer.slice(
437
- buff.byteOffset,
438
- buff.byteOffset + buff.byteLength
439
- );
440
- });
441
- }
442
- blob() {
443
- if (!this.#blobBody) {
444
- this.#blobBody = this.bytes().then((bytes) => {
445
- return new Blob([bytes], {
446
- type: this[kNodeReq].headers["content-type"]
469
+ formData() {
470
+ if (!this.#formDataBody) {
471
+ this.#formDataBody = new Response(this.body, {
472
+ headers: this.headers
473
+ }).formData();
474
+ }
475
+ return this.#formDataBody;
476
+ }
477
+ text() {
478
+ if (!this.#textBody) {
479
+ this.#textBody = this.bytes().then((bytes) => {
480
+ return new TextDecoder().decode(bytes);
447
481
  });
448
- });
482
+ }
483
+ return this.#textBody;
449
484
  }
450
- return this.#blobBody;
451
- }
452
- formData() {
453
- if (!this.#formDataBody) {
454
- this.#formDataBody = new Response(this.body, {
455
- headers: this.headers
456
- }).formData();
485
+ json() {
486
+ if (!this.#jsonBody) {
487
+ this.#jsonBody = this.text().then((txt) => {
488
+ return JSON.parse(txt);
489
+ });
490
+ }
491
+ return this.#jsonBody;
457
492
  }
458
- return this.#formDataBody;
459
- }
460
- text() {
461
- if (!this.#textBody) {
462
- this.#textBody = this.bytes().then((bytes) => {
463
- return new TextDecoder().decode(bytes);
464
- });
493
+ get [(Symbol.toStringTag)]() {
494
+ return "Request";
465
495
  }
466
- return this.#textBody;
467
- }
468
- json() {
469
- if (!this.#jsonBody) {
470
- this.#jsonBody = this.text().then((txt) => {
471
- return JSON.parse(txt);
472
- });
496
+ [kNodeInspect]() {
497
+ return {
498
+ method: this.method,
499
+ url: this.url,
500
+ headers: this.headers
501
+ };
473
502
  }
474
- return this.#jsonBody;
475
- }
476
- get [(Symbol.toStringTag)]() {
477
- return "Request";
478
- }
479
- [kNodeInspect]() {
480
- return {
481
- method: this.method,
482
- url: this.url,
483
- headers: this.headers
484
- };
485
503
  }
504
+ Object.setPrototypeOf(NodeRequestProxy2.prototype, Request.prototype);
505
+ return NodeRequestProxy2;
486
506
  })();
487
507
  async function _readStream(stream) {
488
508
  const chunks = [];
@@ -516,12 +536,10 @@ const NodeFastResponse = /* @__PURE__ */ (() => (
516
536
  const status = this.#init?.status ?? 200;
517
537
  const statusText = this.#init?.statusText ?? "";
518
538
  const headers = [];
519
- let headersInit = this.#init?.headers;
539
+ const headersInit = this.#init?.headers;
520
540
  if (headersInit) {
521
- if (typeof headersInit === "object") {
522
- headersInit = Object.entries(headersInit);
523
- }
524
- for (const [key, value] of headersInit) {
541
+ const headerEntries = headersInit.entries ? headersInit.entries() : Object.entries(headersInit);
542
+ for (const [key, value] of headerEntries) {
525
543
  if (key === "set-cookie") {
526
544
  for (const setCookie of splitSetCookieString(value)) {
527
545
  headers.push(["set-cookie", setCookie]);
@@ -549,6 +567,8 @@ const NodeFastResponse = /* @__PURE__ */ (() => (
549
567
  if (bodyInit.type) {
550
568
  headers.push(["content-type", bodyInit.type]);
551
569
  }
570
+ } else if (typeof bodyInit.pipe === "function") {
571
+ body = bodyInit;
552
572
  } else {
553
573
  const res = new Response(bodyInit);
554
574
  body = res.body;
package/dist/types.d.mts CHANGED
@@ -4,6 +4,7 @@ import * as Bun from 'bun';
4
4
  import * as CF from '@cloudflare/workers-types';
5
5
 
6
6
  type MaybePromise<T> = T | Promise<T>;
7
+ declare const Response: globalThis.Response;
7
8
  /**
8
9
  * Create a new server instance.
9
10
  */
@@ -172,4 +173,4 @@ type DenoFetchHandler = (request: Request, info?: Deno.ServeHandlerInfo<Deno.Net
172
173
  type NodeHttpHandler = (nodeReq: NodeHttp.IncomingMessage, nodeRes: NodeHttp.ServerResponse) => void | Promise<void>;
173
174
  type CloudflareFetchHandler = CF.ExportedHandlerFetchHandler;
174
175
 
175
- export { type BunFetchandler, type CloudflareFetchHandler, type DenoFetchHandler, type FetchHandler, type NodeHttpHandler, type Server, type ServerHandler, type ServerOptions, type ServerPlugin, type ServerPluginInstance, type ServerRequest, serve };
176
+ export { type BunFetchandler, type CloudflareFetchHandler, type DenoFetchHandler, type FetchHandler, type NodeHttpHandler, Response, type Server, type ServerHandler, type ServerOptions, type ServerPlugin, type ServerPluginInstance, type ServerRequest, serve };
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "srvx",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Universal Server API based on web platform standards. Works seamlessly with Deno, Bun and Node.js.",
5
5
  "repository": "unjs/srvx",
6
6
  "license": "MIT",
7
7
  "sideEffects": false,
8
8
  "type": "module",
9
+ "types": "./dist/types.d.mts",
9
10
  "exports": {
10
11
  "./types": "./dist/types.d.mts",
11
12
  "./deno": "./dist/adapters/deno.mjs",
@@ -16,7 +17,8 @@
16
17
  "deno": "./dist/adapters/deno.mjs",
17
18
  "bun": "./dist/adapters/bun.mjs",
18
19
  "workerd": "./dist/adapters/cloudflare.mjs",
19
- "node": "./dist/adapters/node.mjs"
20
+ "node": "./dist/adapters/node.mjs",
21
+ "types": "./dist/types.d.mts"
20
22
  }
21
23
  },
22
24
  "files": [