@jsenv/server 15.0.2 → 15.0.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.
- package/README.md +23 -23
- package/package.json +3 -3
- package/src/content_negotiation/pick_accepted_content.js +13 -13
- package/src/content_negotiation/pick_content_encoding.js +25 -25
- package/src/content_negotiation/pick_content_language.js +35 -34
- package/src/content_negotiation/pick_content_type.js +29 -29
- package/src/fetch_filesystem.js +110 -107
- package/src/from_fetch_response.js +6 -6
- package/src/interfacing_with_node/body.js +22 -22
- package/src/interfacing_with_node/observable.js +80 -80
- package/src/interfacing_with_node/observable_from_node_stream.js +35 -35
- package/src/interfacing_with_node/request_factory.js +26 -26
- package/src/interfacing_with_node/write_node_response.js +82 -82
- package/src/internal/colorizeResponseStatus.js +26 -26
- package/src/internal/convertFileSystemErrorToResponseProperties.js +10 -10
- package/src/internal/dns_resolution.js +7 -7
- package/src/internal/etag.js +11 -11
- package/src/internal/filesystem.js +19 -19
- package/src/internal/headersFromObject.js +8 -8
- package/src/internal/headersFromString.js +11 -11
- package/src/internal/headersToObject.js +5 -5
- package/src/internal/headersToString.js +7 -7
- package/src/internal/headers_composition.js +9 -9
- package/src/internal/hostname_parser.js +9 -9
- package/src/internal/listen.js +47 -47
- package/src/internal/listenClientError.js +10 -10
- package/src/internal/listenEvent.js +5 -5
- package/src/internal/listenRequest.js +8 -8
- package/src/internal/listenServerConnectionError.js +19 -19
- package/src/internal/multiple-header.js +50 -50
- package/src/internal/normalizeHeaderName.js +4 -4
- package/src/internal/normalizeHeaderValue.js +2 -2
- package/src/internal/object_composition.js +41 -39
- package/src/internal/response_composition.js +6 -6
- package/src/internal/server-polyglot.js +42 -42
- package/src/internal/server_ips.js +20 -20
- package/src/internal/trackServerPendingConnections.js +23 -23
- package/src/internal/trackServerPendingRequests.js +28 -28
- package/src/main.js +18 -18
- package/src/readRequestBody.js +15 -15
- package/src/serve_directory.js +16 -16
- package/src/server_timing/timing_header.js +24 -24
- package/src/server_timing/timing_measure.js +14 -14
- package/src/service_composition/path_to_regexp.js +173 -168
- package/src/service_composition/routing.js +12 -12
- package/src/service_controller.js +77 -77
- package/src/services/cors/jsenv_service_cors.js +29 -29
- package/src/services/error_handler/jsenv_service_error_handler.js +18 -18
- package/src/services/request_aliases/jsenv_service_request_aliases.js +20 -20
- package/src/services/response_acceptance_check/jsenv_service_response_acceptance_check.js +16 -16
- package/src/sse/sse_room.js +117 -115
- package/src/start_server.js +328 -328
- package/src/stopReasons.js +9 -9
package/src/readRequestBody.js
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
export const readRequestBody = (request, { as = "string" } = {}) => {
|
|
2
2
|
return new Promise((resolve, reject) => {
|
|
3
|
-
const bufferArray = []
|
|
3
|
+
const bufferArray = [];
|
|
4
4
|
request.body.subscribe({
|
|
5
5
|
error: reject,
|
|
6
6
|
next: (buffer) => {
|
|
7
|
-
bufferArray.push(buffer)
|
|
7
|
+
bufferArray.push(buffer);
|
|
8
8
|
},
|
|
9
9
|
complete: () => {
|
|
10
|
-
const bodyAsBuffer = Buffer.concat(bufferArray)
|
|
10
|
+
const bodyAsBuffer = Buffer.concat(bufferArray);
|
|
11
11
|
|
|
12
12
|
if (as === "buffer") {
|
|
13
|
-
resolve(bodyAsBuffer)
|
|
14
|
-
return
|
|
13
|
+
resolve(bodyAsBuffer);
|
|
14
|
+
return;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
if (as === "string") {
|
|
18
|
-
const bodyAsString = bodyAsBuffer.toString()
|
|
19
|
-
resolve(bodyAsString)
|
|
20
|
-
return
|
|
18
|
+
const bodyAsString = bodyAsBuffer.toString();
|
|
19
|
+
resolve(bodyAsString);
|
|
20
|
+
return;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
if (as === "json") {
|
|
24
|
-
const bodyAsString = bodyAsBuffer.toString()
|
|
25
|
-
const bodyAsJSON = JSON.parse(bodyAsString)
|
|
26
|
-
resolve(bodyAsJSON)
|
|
27
|
-
return
|
|
24
|
+
const bodyAsString = bodyAsBuffer.toString();
|
|
25
|
+
const bodyAsJSON = JSON.parse(bodyAsString);
|
|
26
|
+
resolve(bodyAsJSON);
|
|
27
|
+
return;
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
|
-
})
|
|
31
|
-
})
|
|
32
|
-
}
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
};
|
package/src/serve_directory.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { readdirSync } from "node:fs"
|
|
1
|
+
import { readdirSync } from "node:fs";
|
|
2
2
|
|
|
3
|
-
import { pickContentType } from "./content_negotiation/pick_content_type.js"
|
|
3
|
+
import { pickContentType } from "./content_negotiation/pick_content_type.js";
|
|
4
4
|
|
|
5
5
|
export const serveDirectory = (
|
|
6
6
|
url,
|
|
7
7
|
{ headers = {}, rootDirectoryUrl } = {},
|
|
8
8
|
) => {
|
|
9
|
-
url = String(url)
|
|
10
|
-
url = url[url.length - 1] === "/" ? url : `${url}
|
|
11
|
-
const directoryContentArray = readdirSync(new URL(url))
|
|
9
|
+
url = String(url);
|
|
10
|
+
url = url[url.length - 1] === "/" ? url : `${url}/`;
|
|
11
|
+
const directoryContentArray = readdirSync(new URL(url));
|
|
12
12
|
const responseProducers = {
|
|
13
13
|
"application/json": () => {
|
|
14
|
-
const directoryContentJson = JSON.stringify(directoryContentArray)
|
|
14
|
+
const directoryContentJson = JSON.stringify(directoryContentArray);
|
|
15
15
|
return {
|
|
16
16
|
status: 200,
|
|
17
17
|
headers: {
|
|
@@ -19,7 +19,7 @@ export const serveDirectory = (
|
|
|
19
19
|
"content-length": directoryContentJson.length,
|
|
20
20
|
},
|
|
21
21
|
body: directoryContentJson,
|
|
22
|
-
}
|
|
22
|
+
};
|
|
23
23
|
},
|
|
24
24
|
"text/html": () => {
|
|
25
25
|
const directoryAsHtml = `<!DOCTYPE html>
|
|
@@ -34,18 +34,18 @@ export const serveDirectory = (
|
|
|
34
34
|
<h1>Content of directory ${url}</h1>
|
|
35
35
|
<ul>
|
|
36
36
|
${directoryContentArray.map((filename) => {
|
|
37
|
-
const fileUrl = String(new URL(filename, url))
|
|
37
|
+
const fileUrl = String(new URL(filename, url));
|
|
38
38
|
const fileUrlRelativeToServer = fileUrl.slice(
|
|
39
39
|
String(rootDirectoryUrl).length,
|
|
40
|
-
)
|
|
40
|
+
);
|
|
41
41
|
return `<li>
|
|
42
42
|
<a href="/${fileUrlRelativeToServer}">${fileUrlRelativeToServer}</a>
|
|
43
|
-
</li
|
|
43
|
+
</li>`;
|
|
44
44
|
}).join(`
|
|
45
45
|
`)}
|
|
46
46
|
</ul>
|
|
47
47
|
</body>
|
|
48
|
-
</html
|
|
48
|
+
</html>`;
|
|
49
49
|
|
|
50
50
|
return {
|
|
51
51
|
status: 200,
|
|
@@ -54,12 +54,12 @@ export const serveDirectory = (
|
|
|
54
54
|
"content-length": Buffer.byteLength(directoryAsHtml),
|
|
55
55
|
},
|
|
56
56
|
body: directoryAsHtml,
|
|
57
|
-
}
|
|
57
|
+
};
|
|
58
58
|
},
|
|
59
|
-
}
|
|
59
|
+
};
|
|
60
60
|
const bestContentType = pickContentType(
|
|
61
61
|
{ headers },
|
|
62
62
|
Object.keys(responseProducers),
|
|
63
|
-
)
|
|
64
|
-
return responseProducers[bestContentType || "application/json"]()
|
|
65
|
-
}
|
|
63
|
+
);
|
|
64
|
+
return responseProducers[bestContentType || "application/json"]();
|
|
65
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseMultipleHeader,
|
|
3
3
|
stringifyMultipleHeader,
|
|
4
|
-
} from "../internal/multiple-header.js"
|
|
4
|
+
} from "../internal/multiple-header.js";
|
|
5
5
|
|
|
6
6
|
// to predict order in chrome devtools we should put a,b,c,d,e or something
|
|
7
7
|
// because in chrome dev tools they are shown in alphabetic order
|
|
@@ -10,19 +10,19 @@ import {
|
|
|
10
10
|
// so response can return a new timing object
|
|
11
11
|
// yes it's awful, feel free to PR with a better approach :)
|
|
12
12
|
export const timingToServerTimingResponseHeaders = (timing) => {
|
|
13
|
-
const serverTimingHeader = {}
|
|
13
|
+
const serverTimingHeader = {};
|
|
14
14
|
Object.keys(timing).forEach((key, index) => {
|
|
15
|
-
const name = letters[index] || "zz"
|
|
15
|
+
const name = letters[index] || "zz";
|
|
16
16
|
serverTimingHeader[name] = {
|
|
17
17
|
desc: key,
|
|
18
18
|
dur: timing[key],
|
|
19
|
-
}
|
|
20
|
-
})
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
21
|
const serverTimingHeaderString =
|
|
22
|
-
stringifyServerTimingHeader(serverTimingHeader)
|
|
22
|
+
stringifyServerTimingHeader(serverTimingHeader);
|
|
23
23
|
|
|
24
|
-
return { "server-timing": serverTimingHeaderString }
|
|
25
|
-
}
|
|
24
|
+
return { "server-timing": serverTimingHeaderString };
|
|
25
|
+
};
|
|
26
26
|
|
|
27
27
|
export const parseServerTimingHeader = (serverTimingHeaderString) => {
|
|
28
28
|
const serverTimingHeaderObject = parseMultipleHeader(
|
|
@@ -30,27 +30,27 @@ export const parseServerTimingHeader = (serverTimingHeaderString) => {
|
|
|
30
30
|
{
|
|
31
31
|
validateName: validateServerTimingName,
|
|
32
32
|
validateProperty: ({ name }) => {
|
|
33
|
-
return name === "desc" || name === "dur"
|
|
33
|
+
return name === "desc" || name === "dur";
|
|
34
34
|
},
|
|
35
35
|
},
|
|
36
|
-
)
|
|
36
|
+
);
|
|
37
37
|
|
|
38
|
-
const serverTiming = {}
|
|
38
|
+
const serverTiming = {};
|
|
39
39
|
Object.keys(serverTimingHeaderObject).forEach((key) => {
|
|
40
|
-
const { desc, dur } = serverTimingHeaderObject[key]
|
|
40
|
+
const { desc, dur } = serverTimingHeaderObject[key];
|
|
41
41
|
serverTiming[key] = {
|
|
42
42
|
...(desc ? { description: desc } : {}),
|
|
43
43
|
...(dur ? { duration: dur } : {}),
|
|
44
|
-
}
|
|
45
|
-
})
|
|
46
|
-
return serverTiming
|
|
47
|
-
}
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
return serverTiming;
|
|
47
|
+
};
|
|
48
48
|
|
|
49
49
|
export const stringifyServerTimingHeader = (serverTimingHeader) => {
|
|
50
50
|
return stringifyMultipleHeader(serverTimingHeader, {
|
|
51
51
|
validateName: validateServerTimingName,
|
|
52
|
-
})
|
|
53
|
-
}
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
54
|
|
|
55
55
|
// (),/:;<=>?@[\]{}" Don't allowed
|
|
56
56
|
// Minimal length is one symbol
|
|
@@ -59,13 +59,13 @@ export const stringifyServerTimingHeader = (serverTimingHeader) => {
|
|
|
59
59
|
// https://www.w3.org/TR/2019/WD-server-timing-20190307/#the-server-timing-header-field
|
|
60
60
|
// https://tools.ietf.org/html/rfc7230#section-3.2.6
|
|
61
61
|
const validateServerTimingName = (name) => {
|
|
62
|
-
const valid = /^[!#$%&'*+\-.^_`|~0-9a-z]+$/gi.test(name)
|
|
62
|
+
const valid = /^[!#$%&'*+\-.^_`|~0-9a-z]+$/gi.test(name);
|
|
63
63
|
if (!valid) {
|
|
64
|
-
console.warn(`server timing contains invalid symbols`)
|
|
65
|
-
return false
|
|
64
|
+
console.warn(`server timing contains invalid symbols`);
|
|
65
|
+
return false;
|
|
66
66
|
}
|
|
67
|
-
return true
|
|
68
|
-
}
|
|
67
|
+
return true;
|
|
68
|
+
};
|
|
69
69
|
|
|
70
70
|
const letters = [
|
|
71
71
|
"a",
|
|
@@ -94,4 +94,4 @@ const letters = [
|
|
|
94
94
|
"x",
|
|
95
95
|
"y",
|
|
96
96
|
"z",
|
|
97
|
-
]
|
|
97
|
+
];
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import { performance } from "node:perf_hooks"
|
|
1
|
+
import { performance } from "node:perf_hooks";
|
|
2
2
|
|
|
3
3
|
export const timeStart = (name) => {
|
|
4
4
|
// as specified in https://w3c.github.io/server-timing/#the-performanceservertiming-interface
|
|
5
5
|
// duration is a https://www.w3.org/TR/hr-time-2/#sec-domhighrestimestamp
|
|
6
|
-
const startTimestamp = performance.now()
|
|
6
|
+
const startTimestamp = performance.now();
|
|
7
7
|
const timeEnd = () => {
|
|
8
|
-
const endTimestamp = performance.now()
|
|
8
|
+
const endTimestamp = performance.now();
|
|
9
9
|
const timing = {
|
|
10
10
|
[name]: endTimestamp - startTimestamp,
|
|
11
|
-
}
|
|
12
|
-
return timing
|
|
13
|
-
}
|
|
14
|
-
return timeEnd
|
|
15
|
-
}
|
|
11
|
+
};
|
|
12
|
+
return timing;
|
|
13
|
+
};
|
|
14
|
+
return timeEnd;
|
|
15
|
+
};
|
|
16
16
|
|
|
17
17
|
export const timeFunction = (name, fn) => {
|
|
18
|
-
const timeEnd = timeStart(name)
|
|
19
|
-
const returnValue = fn()
|
|
18
|
+
const timeEnd = timeStart(name);
|
|
19
|
+
const returnValue = fn();
|
|
20
20
|
if (returnValue && typeof returnValue.then === "function") {
|
|
21
21
|
return returnValue.then((value) => {
|
|
22
|
-
return [timeEnd(), value]
|
|
23
|
-
})
|
|
22
|
+
return [timeEnd(), value];
|
|
23
|
+
});
|
|
24
24
|
}
|
|
25
|
-
return [timeEnd(), returnValue]
|
|
26
|
-
}
|
|
25
|
+
return [timeEnd(), returnValue];
|
|
26
|
+
};
|