@server/next 0.25.10 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -3
- package/src/helpers/createWebsocket.js +21 -0
- package/src/helpers/index.js +3 -0
- package/src/helpers/iteratorAsyncToReadable.js +20 -0
- package/src/helpers/iteratorToReadable.js +10 -0
- package/src/index.js +4 -22
- package/src/parseResponse.js +19 -3
- package/src/reply.js +5 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@server/next",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "A fully-fledged web server with routing, file uploads, sessions, static files, schema validation, websockets, testing, etc.",
|
|
5
5
|
"homepage": "https://server-js.com/",
|
|
6
6
|
"repository": "https://github.com/franciscop/server-next.git",
|
|
@@ -32,8 +32,7 @@
|
|
|
32
32
|
"main": "src/index.js",
|
|
33
33
|
"types": "src/index.d.ts",
|
|
34
34
|
"files": [
|
|
35
|
-
"src/"
|
|
36
|
-
"jsx-dev-runtime.js"
|
|
35
|
+
"src/"
|
|
37
36
|
],
|
|
38
37
|
"exports": {
|
|
39
38
|
".": "./src/index.js",
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export default function createWebsocket(sockets, handlers) {
|
|
2
|
+
return {
|
|
3
|
+
message: async (socket, body) => {
|
|
4
|
+
handlers.socket
|
|
5
|
+
?.filter((s) => s[1] === "message")
|
|
6
|
+
?.map((s) => s[2]({ socket, sockets, body }));
|
|
7
|
+
},
|
|
8
|
+
open: (socket) => {
|
|
9
|
+
sockets.push(socket);
|
|
10
|
+
handlers.socket
|
|
11
|
+
?.filter((s) => s[1] === "open")
|
|
12
|
+
?.map((s) => s[2]({ socket, sockets, body }));
|
|
13
|
+
},
|
|
14
|
+
close: (socket) => {
|
|
15
|
+
sockets.splice(sockets.indexOf(socket), 1);
|
|
16
|
+
handlers.socket
|
|
17
|
+
?.filter((s) => s[1] === "close")
|
|
18
|
+
?.map((s) => s[2]({ socket, sockets, body }));
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
package/src/helpers/index.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
export { default as createCookies } from "./createCookies.js";
|
|
2
2
|
export { default as createId } from "./createId.js";
|
|
3
|
+
export { default as createWebsocket } from "./createWebsocket.js";
|
|
3
4
|
export { default as config } from "./config.js";
|
|
4
5
|
export { default as cors } from "./cors.js";
|
|
5
6
|
export { default as define } from "./define.js";
|
|
6
7
|
export { default as getMachine } from "./getMachine.js";
|
|
7
8
|
export { default as handleRequest } from "./handleRequest.js";
|
|
8
9
|
export { default as iterate } from "./iterate.js";
|
|
10
|
+
export { default as iteratorToReadable } from "./iteratorToReadable.js";
|
|
11
|
+
export { default as iteratorAsyncToReadable } from "./iteratorAsyncToReadable.js";
|
|
9
12
|
export { default as parseHeaders } from "./parseHeaders.js";
|
|
10
13
|
export { default as toWeb } from "./toWeb.js";
|
|
11
14
|
export { default as types } from "./types.js";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export default function iteratorAsyncToReadable(asyncGenerator) {
|
|
2
|
+
return new ReadableStream({
|
|
3
|
+
async pull(controller) {
|
|
4
|
+
try {
|
|
5
|
+
const { value, done } = await asyncGenerator.next();
|
|
6
|
+
if (done) {
|
|
7
|
+
controller.close();
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
controller.enqueue(new TextEncoder().encode(value));
|
|
11
|
+
} catch (err) {
|
|
12
|
+
console.error("Stream error:", err);
|
|
13
|
+
controller.error(err);
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
cancel() {
|
|
17
|
+
console.log("Stream cancelled");
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
}
|
package/src/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import createNodeContext from "./context/node.js";
|
|
|
5
5
|
import createWinterContext from "./context/winter.js";
|
|
6
6
|
import {
|
|
7
7
|
config,
|
|
8
|
+
createWebsocket,
|
|
8
9
|
getMachine,
|
|
9
10
|
handleRequest,
|
|
10
11
|
iterate,
|
|
@@ -31,9 +32,6 @@ export default function server(options = {}) {
|
|
|
31
32
|
this.opts = config(options);
|
|
32
33
|
this.platform = getMachine();
|
|
33
34
|
|
|
34
|
-
// TODO: find a way to remove this hack
|
|
35
|
-
this.extended = false;
|
|
36
|
-
|
|
37
35
|
// Skip "forbidden methods" https://fetch.spec.whatwg.org/#concept-method
|
|
38
36
|
this.handlers = {
|
|
39
37
|
socket: [],
|
|
@@ -46,27 +44,11 @@ export default function server(options = {}) {
|
|
|
46
44
|
options: [],
|
|
47
45
|
};
|
|
48
46
|
|
|
47
|
+
// Keep a reference of the currently connected sockets
|
|
49
48
|
this.sockets = [];
|
|
49
|
+
|
|
50
50
|
// Note: required by Bun
|
|
51
|
-
this.websocket =
|
|
52
|
-
message: async (socket, body) => {
|
|
53
|
-
this.handlers.socket
|
|
54
|
-
?.filter((s) => s[1] === "message")
|
|
55
|
-
?.map((s) => s[2]({ socket, sockets: this.sockets, body }));
|
|
56
|
-
},
|
|
57
|
-
open: (ws) => {
|
|
58
|
-
this.sockets.push(ws);
|
|
59
|
-
this.handlers.socket
|
|
60
|
-
?.filter((s) => s[1] === "open")
|
|
61
|
-
?.map((s) => s[2]({ socket, sockets: this.sockets, body }));
|
|
62
|
-
},
|
|
63
|
-
close: (ws) => {
|
|
64
|
-
this.sockets.splice(this.sockets.indexOf(ws), 1);
|
|
65
|
-
this.handlers.socket
|
|
66
|
-
?.filter((s) => s[1] === "close")
|
|
67
|
-
?.map((s) => s[2]({ socket, sockets: this.sockets, body }));
|
|
68
|
-
},
|
|
69
|
-
};
|
|
51
|
+
this.websocket = createWebsocket(this.sockets, this.handlers);
|
|
70
52
|
|
|
71
53
|
// Initialize it right away for Node.js
|
|
72
54
|
if (this.platform.runtime === "node") {
|
package/src/parseResponse.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
cors,
|
|
3
|
+
createId,
|
|
4
|
+
iteratorToReadable,
|
|
5
|
+
iteratorAsyncToReadable,
|
|
6
|
+
} from "./helpers/index.js";
|
|
2
7
|
import { json } from "./reply.js";
|
|
3
8
|
import ServerError from "./ServerError.js";
|
|
4
9
|
|
|
@@ -34,14 +39,25 @@ export default async function parseResponse(out, ctx) {
|
|
|
34
39
|
out = json(out);
|
|
35
40
|
}
|
|
36
41
|
|
|
42
|
+
// Sync and Async iterators
|
|
43
|
+
if (out[Symbol.iterator]) {
|
|
44
|
+
out = new Response(iteratorToReadable(out));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// The ReadableStream seems to be an asyncIterator, but we don't want to handle that yet
|
|
48
|
+
if (out[Symbol.asyncIterator] && !(out instanceof Response)) {
|
|
49
|
+
out = new Response(iteratorAsyncToReadable(out));
|
|
50
|
+
}
|
|
51
|
+
|
|
37
52
|
// The output from fetch(), create a copy of it into a new response
|
|
38
53
|
if (out instanceof Response && out.url && out.body) {
|
|
39
54
|
out = new Response(out.body, {
|
|
40
|
-
status:
|
|
55
|
+
status: out.status,
|
|
41
56
|
headers: out.headers,
|
|
42
57
|
});
|
|
58
|
+
|
|
59
|
+
// Compression not supported for streaming response, stripping header
|
|
43
60
|
if (/^(br|gzip)$/.test(out.headers.get("content-encoding"))) {
|
|
44
|
-
console.warn("Compression not yet supported for response");
|
|
45
61
|
out.headers.delete("content-encoding");
|
|
46
62
|
}
|
|
47
63
|
}
|
package/src/reply.js
CHANGED
|
@@ -76,11 +76,11 @@ Reply.prototype.redirect = function (Location) {
|
|
|
76
76
|
return this.headers({ Location }).status(302).send();
|
|
77
77
|
};
|
|
78
78
|
|
|
79
|
-
Reply.prototype.file = async function (path) {
|
|
79
|
+
Reply.prototype.file = async function (path, renderer = (data) => data) {
|
|
80
80
|
try {
|
|
81
81
|
const data = await fs.readFile(path);
|
|
82
82
|
const ext = path.split(".").pop();
|
|
83
|
-
return this.type(ext).send(data);
|
|
83
|
+
return this.type(ext).send(await renderer(data));
|
|
84
84
|
} catch (error) {
|
|
85
85
|
if (error.code === "ENOENT") {
|
|
86
86
|
return status(404).send();
|
|
@@ -89,14 +89,14 @@ Reply.prototype.file = async function (path) {
|
|
|
89
89
|
}
|
|
90
90
|
};
|
|
91
91
|
|
|
92
|
-
Reply.prototype.view = async function (path) {
|
|
92
|
+
Reply.prototype.view = async function (path, renderer = (data) => data) {
|
|
93
93
|
return async (ctx) => {
|
|
94
94
|
if (!ctx.options.views) {
|
|
95
95
|
throw new Error("Views not enabled");
|
|
96
96
|
}
|
|
97
97
|
const data = await ctx.options.views.read(path);
|
|
98
|
-
if (data) return this.
|
|
99
|
-
return this.
|
|
98
|
+
if (!data) return this.status(404).send();
|
|
99
|
+
return this.type(path.split(".").pop()).send(await renderer(data));
|
|
100
100
|
};
|
|
101
101
|
};
|
|
102
102
|
|