@jsenv/server 12.7.4 → 13.0.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/README.md +68 -50
- package/package.json +2 -2
- package/src/content_negotiation/{applyContentNegotiation.js → pick_accepted_content.js} +1 -3
- package/src/content_negotiation/{negotiateContentEncoding.js → pick_content_encoding.js} +3 -3
- package/src/content_negotiation/{negotiateContentLanguage.js → pick_content_language.js} +3 -3
- package/src/content_negotiation/{negotiateContentType.js → pick_content_type.js} +3 -3
- package/src/fetch_filesystem.js +2 -2
- package/src/internal/dns_resolution.js +17 -0
- package/src/internal/getServerOrigins.js +43 -14
- package/src/internal/listen.js +15 -13
- package/src/main.js +11 -13
- package/src/serve_directory.js +2 -2
- package/src/{startServer.js → server.js} +150 -122
- package/src/service_controller.js +173 -0
- package/src/{cors/plugin_cors.js → services/cors/jsenv_service_cors.js} +29 -45
- package/src/services/error_handler/jsenv_service_error_handler.js +98 -0
- package/src/{content_negotiation/checkContentNegotiation.js → services/response_acceptance_check/jsenv_service_response_acceptance_check.js} +16 -7
- package/src/services/ressource_aliases/jsenv_service_ressource_aliases.js +40 -0
- package/src/sse/{createSSERoom.js → sse_room.js} +18 -16
- package/src/content_negotiation/plugin_content_negotiation_check.js +0 -15
- package/src/error_to_response_default.js +0 -94
- package/src/plugin_request_waiting_check.js +0 -32
- package/src/ressource_aliases/plugin_ressource_aliases.js +0 -44
- package/src/server_timing/plugin_server_timing.js +0 -28
- package/src/service_composition/service_composition.js +0 -55
package/README.md
CHANGED
|
@@ -8,15 +8,19 @@ import { startServer } from "@jsenv/server"
|
|
|
8
8
|
await startServer({
|
|
9
9
|
protocol: "http",
|
|
10
10
|
port: 8080,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
services: [
|
|
12
|
+
{
|
|
13
|
+
handleRequest: () => {
|
|
14
|
+
return {
|
|
15
|
+
status: 200,
|
|
16
|
+
headers: {
|
|
17
|
+
"content-type": "text/plain",
|
|
18
|
+
},
|
|
19
|
+
body: "Hello world",
|
|
20
|
+
}
|
|
16
21
|
},
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
},
|
|
22
|
+
},
|
|
23
|
+
],
|
|
20
24
|
})
|
|
21
25
|
```
|
|
22
26
|
|
|
@@ -29,15 +33,19 @@ import fetch from "node-fetch"
|
|
|
29
33
|
import { startServer } from "@jsenv/server"
|
|
30
34
|
|
|
31
35
|
const server = await startServer({
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
services: [
|
|
37
|
+
{
|
|
38
|
+
handleRequest: () => {
|
|
39
|
+
return {
|
|
40
|
+
status: 200,
|
|
41
|
+
headers: {
|
|
42
|
+
"content-type": "text/plain",
|
|
43
|
+
},
|
|
44
|
+
body: "Hello world",
|
|
45
|
+
}
|
|
37
46
|
},
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
},
|
|
47
|
+
},
|
|
48
|
+
],
|
|
41
49
|
})
|
|
42
50
|
|
|
43
51
|
const response = await fetch(server.origin)
|
|
@@ -57,22 +65,24 @@ _Code starting a server with 2 request handlers:_
|
|
|
57
65
|
*/
|
|
58
66
|
import { startServer, composeServices } from "@jsenv/server"
|
|
59
67
|
|
|
60
|
-
const indexService = (request) => {
|
|
61
|
-
if (request.ressource === "/") {
|
|
62
|
-
return { status: 200 }
|
|
63
|
-
}
|
|
64
|
-
return null // means "I don't handle that request"
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const notFoundService = (request) => {
|
|
68
|
-
return { status: 404 }
|
|
69
|
-
}
|
|
70
|
-
|
|
71
68
|
const server = await startServer({
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
69
|
+
services: [
|
|
70
|
+
{
|
|
71
|
+
name: "index",
|
|
72
|
+
handleRequest: (request) => {
|
|
73
|
+
if (request.ressource === "/") {
|
|
74
|
+
return { status: 200 }
|
|
75
|
+
}
|
|
76
|
+
return null // means "I don't handle that request"
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "otherwise",
|
|
81
|
+
handleRequest: () => {
|
|
82
|
+
return { status: 404 }
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
],
|
|
76
86
|
})
|
|
77
87
|
|
|
78
88
|
const fetch = await import("node-fetch")
|
|
@@ -94,17 +104,21 @@ await startServer({
|
|
|
94
104
|
certificate: readFileSyncAsString("./server.crt"),
|
|
95
105
|
privateKey: readFileSyncAsString("./server.key"),
|
|
96
106
|
allowHttpRequestOnHttps: true,
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
107
|
+
services: [
|
|
108
|
+
{
|
|
109
|
+
handleRequest: (request) => {
|
|
110
|
+
const clientUsesHttp = request.origin.startsWith("http:")
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
status: 200,
|
|
114
|
+
headers: {
|
|
115
|
+
"content-type": "text/plain",
|
|
116
|
+
},
|
|
117
|
+
body: clientUsesHttp ? `Welcome http user` : `Welcome https user`,
|
|
118
|
+
}
|
|
104
119
|
},
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
},
|
|
120
|
+
},
|
|
121
|
+
],
|
|
108
122
|
})
|
|
109
123
|
|
|
110
124
|
function readFileSyncAsString(relativeUrl) {
|
|
@@ -119,24 +133,28 @@ _Code starting a server for static files:_
|
|
|
119
133
|
import { startServer, fetchFileSystem } from "@jsenv/server"
|
|
120
134
|
|
|
121
135
|
await startServer({
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
136
|
+
services: [
|
|
137
|
+
{
|
|
138
|
+
handleRequest: async (request) => {
|
|
139
|
+
const fileUrl = new URL(request.ressource.slice(1), import.meta.url)
|
|
140
|
+
const response = await fetchFileSystem(fileUrl, {
|
|
141
|
+
...request,
|
|
142
|
+
})
|
|
143
|
+
return response
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
],
|
|
129
147
|
})
|
|
130
148
|
```
|
|
131
149
|
|
|
132
150
|
# Documentation
|
|
133
151
|
|
|
134
|
-
- [https](./docs/https/https.md)
|
|
135
|
-
- [Serving files](./docs/serving_files/serving_files.md)
|
|
136
152
|
- [Handling requests](./docs/handling_requests/handling_requests.md)
|
|
137
153
|
- [Handling errors](./docs/handling_errors/handling_errors.md)
|
|
138
154
|
- [Server timing](./docs/server_timing/server_timing.md)
|
|
139
155
|
- [CORS](./docs/cors/cors.md)
|
|
156
|
+
- [https](./docs/https/https.md)
|
|
157
|
+
- [Serving files](./docs/serving_files/serving_files.md)
|
|
140
158
|
- [Content negotiation](./docs/content_negotiation/content_negotiation.md)
|
|
141
159
|
- [Server Sent Events](./docs/sse/sse.md)
|
|
142
160
|
- [Cluster](./docs/cluster/cluster.md)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "13.0.0",
|
|
4
4
|
"description": "Write your Node.js server using pure functions",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@jsenv/abort": "4.2.3",
|
|
38
|
-
"@jsenv/log": "3.0
|
|
38
|
+
"@jsenv/log": "3.1.0",
|
|
39
39
|
"@jsenv/url-meta": "7.0.0",
|
|
40
40
|
"@jsenv/utils": "2.0.1"
|
|
41
41
|
},
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
export const
|
|
1
|
+
export const pickAcceptedContent = ({
|
|
2
2
|
availables,
|
|
3
3
|
accepteds,
|
|
4
4
|
getAcceptanceScore,
|
|
5
5
|
}) => {
|
|
6
6
|
let highestScore = -1
|
|
7
7
|
let availableWithHighestScore = null
|
|
8
|
-
|
|
9
8
|
let availableIndex = 0
|
|
10
9
|
while (availableIndex < availables.length) {
|
|
11
10
|
const available = availables[availableIndex]
|
|
@@ -23,6 +22,5 @@ export const applyContentNegotiation = ({
|
|
|
23
22
|
}
|
|
24
23
|
}
|
|
25
24
|
}
|
|
26
|
-
|
|
27
25
|
return availableWithHighestScore
|
|
28
26
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parseMultipleHeader } from "../internal/multiple-header.js"
|
|
2
|
-
import {
|
|
2
|
+
import { pickAcceptedContent } from "./pick_accepted_content.js"
|
|
3
3
|
|
|
4
|
-
export const
|
|
4
|
+
export const pickContentEncoding = (request, availableEncodings) => {
|
|
5
5
|
const { headers = {} } = request
|
|
6
6
|
const requestAcceptEncodingHeader = headers["accept-encoding"]
|
|
7
7
|
if (!requestAcceptEncodingHeader) {
|
|
@@ -11,7 +11,7 @@ export const negotiateContentEncoding = (request, availableEncodings) => {
|
|
|
11
11
|
const encodingsAccepted = parseAcceptEncodingHeader(
|
|
12
12
|
requestAcceptEncodingHeader,
|
|
13
13
|
)
|
|
14
|
-
return
|
|
14
|
+
return pickAcceptedContent({
|
|
15
15
|
accepteds: encodingsAccepted,
|
|
16
16
|
availables: availableEncodings,
|
|
17
17
|
getAcceptanceScore: getEncodingAcceptanceScore,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parseMultipleHeader } from "../internal/multiple-header.js"
|
|
2
|
-
import {
|
|
2
|
+
import { pickAcceptedContent } from "./pick_accepted_content.js"
|
|
3
3
|
|
|
4
|
-
export const
|
|
4
|
+
export const pickContentLanguage = (request, availableLanguages) => {
|
|
5
5
|
const { headers = {} } = request
|
|
6
6
|
const requestAcceptLanguageHeader = headers["accept-language"]
|
|
7
7
|
if (!requestAcceptLanguageHeader) {
|
|
@@ -11,7 +11,7 @@ export const negotiateContentLanguage = (request, availableLanguages) => {
|
|
|
11
11
|
const languagesAccepted = parseAcceptLanguageHeader(
|
|
12
12
|
requestAcceptLanguageHeader,
|
|
13
13
|
)
|
|
14
|
-
return
|
|
14
|
+
return pickAcceptedContent({
|
|
15
15
|
accepteds: languagesAccepted,
|
|
16
16
|
availables: availableLanguages,
|
|
17
17
|
getAcceptanceScore: getLanguageAcceptanceScore,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parseMultipleHeader } from "../internal/multiple-header.js"
|
|
2
|
-
import {
|
|
2
|
+
import { pickAcceptedContent } from "./pick_accepted_content.js"
|
|
3
3
|
|
|
4
|
-
export const
|
|
4
|
+
export const pickContentType = (request, availableContentTypes) => {
|
|
5
5
|
const { headers = {} } = request
|
|
6
6
|
const requestAcceptHeader = headers.accept
|
|
7
7
|
if (!requestAcceptHeader) {
|
|
@@ -9,7 +9,7 @@ export const negotiateContentType = (request, availableContentTypes) => {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const contentTypesAccepted = parseAcceptHeader(requestAcceptHeader)
|
|
12
|
-
return
|
|
12
|
+
return pickAcceptedContent({
|
|
13
13
|
accepteds: contentTypesAccepted,
|
|
14
14
|
availables: availableContentTypes,
|
|
15
15
|
getAcceptanceScore: getContentTypeAcceptanceScore,
|
package/src/fetch_filesystem.js
CHANGED
|
@@ -12,7 +12,7 @@ import { bufferToEtag } from "./internal/etag.js"
|
|
|
12
12
|
import { composeTwoResponses } from "./internal/response_composition.js"
|
|
13
13
|
import { convertFileSystemErrorToResponseProperties } from "./internal/convertFileSystemErrorToResponseProperties.js"
|
|
14
14
|
import { timeFunction } from "./server_timing/timing_measure.js"
|
|
15
|
-
import {
|
|
15
|
+
import { pickContentEncoding } from "./content_negotiation/pick_content_encoding.js"
|
|
16
16
|
import { serveDirectory } from "./serve_directory.js"
|
|
17
17
|
|
|
18
18
|
export const fetchFileSystem = async (
|
|
@@ -360,7 +360,7 @@ const getMtimeResponse = async ({ headers, sourceStat }) => {
|
|
|
360
360
|
}
|
|
361
361
|
|
|
362
362
|
const getCompressedResponse = async ({ sourceUrl, headers }) => {
|
|
363
|
-
const acceptedCompressionFormat =
|
|
363
|
+
const acceptedCompressionFormat = pickContentEncoding(
|
|
364
364
|
{ headers },
|
|
365
365
|
Object.keys(availableCompressionFormats),
|
|
366
366
|
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { lookup } from "node:dns"
|
|
2
|
+
|
|
3
|
+
export const applyDnsResolution = async (
|
|
4
|
+
hostname,
|
|
5
|
+
{ verbatim = false } = {},
|
|
6
|
+
) => {
|
|
7
|
+
const dnsResolution = await new Promise((resolve, reject) => {
|
|
8
|
+
lookup(hostname, { verbatim }, (error, address, family) => {
|
|
9
|
+
if (error) {
|
|
10
|
+
reject(error)
|
|
11
|
+
} else {
|
|
12
|
+
resolve({ address, family })
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
return dnsResolution
|
|
17
|
+
}
|
|
@@ -1,26 +1,47 @@
|
|
|
1
1
|
import { networkInterfaces } from "node:os"
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { applyDnsResolution } from "./dns_resolution.js"
|
|
4
|
+
|
|
5
|
+
export const getServerOrigins = async ({ protocol, host, port }) => {
|
|
6
|
+
const isLocal = LOOPBACK_HOSTNAMES.includes(host)
|
|
7
|
+
const localhostDnsResolution = await applyDnsResolution("localhost")
|
|
8
|
+
const localOrigin = createServerOrigin({
|
|
6
9
|
protocol,
|
|
7
|
-
hostname:
|
|
10
|
+
hostname:
|
|
11
|
+
localhostDnsResolution.address === "127.0.0.1"
|
|
12
|
+
? "localhost"
|
|
13
|
+
: "127.0.0.1",
|
|
8
14
|
port,
|
|
9
15
|
})
|
|
10
|
-
if (
|
|
11
|
-
return {
|
|
16
|
+
if (isLocal) {
|
|
17
|
+
return { local: localOrigin }
|
|
12
18
|
}
|
|
13
|
-
const isAnyIp =
|
|
19
|
+
const isAnyIp = WILDCARD_HOSTNAMES.includes(host)
|
|
20
|
+
const networkOrigin = createServerOrigin({
|
|
21
|
+
protocol,
|
|
22
|
+
hostname: isAnyIp ? getExternalIp() : host,
|
|
23
|
+
port,
|
|
24
|
+
})
|
|
14
25
|
return {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
protocol,
|
|
18
|
-
hostname: isAnyIp ? getExternalIp(ip) : ip,
|
|
19
|
-
port,
|
|
20
|
-
}),
|
|
26
|
+
local: localOrigin,
|
|
27
|
+
network: networkOrigin,
|
|
21
28
|
}
|
|
22
29
|
}
|
|
23
30
|
|
|
31
|
+
const LOOPBACK_HOSTNAMES = [
|
|
32
|
+
"localhost",
|
|
33
|
+
"127.0.0.1",
|
|
34
|
+
"::1",
|
|
35
|
+
"0000:0000:0000:0000:0000:0000:0000:0001",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
const WILDCARD_HOSTNAMES = [
|
|
39
|
+
undefined,
|
|
40
|
+
"0.0.0.0",
|
|
41
|
+
"::",
|
|
42
|
+
"0000:0000:0000:0000:0000:0000:0000:0000",
|
|
43
|
+
]
|
|
44
|
+
|
|
24
45
|
const createServerOrigin = ({ protocol, hostname, port }) => {
|
|
25
46
|
const url = new URL("https://127.0.0.1:80")
|
|
26
47
|
url.protocol = protocol
|
|
@@ -36,10 +57,18 @@ const getExternalIp = () => {
|
|
|
36
57
|
const networkAddressArray = networkInterfaceMap[key]
|
|
37
58
|
return networkAddressArray.find((networkAddress) => {
|
|
38
59
|
if (networkAddress.internal) return false
|
|
39
|
-
if (networkAddress
|
|
60
|
+
if (!isIpV4(networkAddress)) return false
|
|
40
61
|
internalIPV4NetworkAddress = networkAddress
|
|
41
62
|
return true
|
|
42
63
|
})
|
|
43
64
|
})
|
|
44
65
|
return internalIPV4NetworkAddress ? internalIPV4NetworkAddress.address : null
|
|
45
66
|
}
|
|
67
|
+
|
|
68
|
+
const isIpV4 = (networkAddress) => {
|
|
69
|
+
// node 18+
|
|
70
|
+
if (typeof networkAddress.family === "number") {
|
|
71
|
+
return networkAddress.family === 4
|
|
72
|
+
}
|
|
73
|
+
return networkAddress.family === "IPv4"
|
|
74
|
+
}
|
package/src/internal/listen.js
CHANGED
|
@@ -6,7 +6,7 @@ const listen = async ({
|
|
|
6
6
|
server,
|
|
7
7
|
port,
|
|
8
8
|
portHint,
|
|
9
|
-
|
|
9
|
+
host,
|
|
10
10
|
}) => {
|
|
11
11
|
const listeningOperation = Abort.startOperation()
|
|
12
12
|
|
|
@@ -17,11 +17,11 @@ const listen = async ({
|
|
|
17
17
|
listeningOperation.throwIfAborted()
|
|
18
18
|
port = await findFreePort(portHint, {
|
|
19
19
|
signal: listeningOperation.signal,
|
|
20
|
-
|
|
20
|
+
host,
|
|
21
21
|
})
|
|
22
22
|
}
|
|
23
23
|
listeningOperation.throwIfAborted()
|
|
24
|
-
port = await startListening({ server, port,
|
|
24
|
+
port = await startListening({ server, port, host })
|
|
25
25
|
listeningOperation.addAbortCallback(() => stopListening(server))
|
|
26
26
|
listeningOperation.throwIfAborted()
|
|
27
27
|
|
|
@@ -35,7 +35,7 @@ export const findFreePort = async (
|
|
|
35
35
|
initialPort = 1,
|
|
36
36
|
{
|
|
37
37
|
signal = new AbortController().signal,
|
|
38
|
-
|
|
38
|
+
host = "127.0.0.1",
|
|
39
39
|
min = 1,
|
|
40
40
|
max = 65534,
|
|
41
41
|
next = (port) => port + 1,
|
|
@@ -46,34 +46,36 @@ export const findFreePort = async (
|
|
|
46
46
|
findFreePortOperation.addAbortSignal(signal)
|
|
47
47
|
findFreePortOperation.throwIfAborted()
|
|
48
48
|
|
|
49
|
-
const testUntil = async (port,
|
|
49
|
+
const testUntil = async (port, host) => {
|
|
50
50
|
findFreePortOperation.throwIfAborted()
|
|
51
|
-
const free = await portIsFree(port,
|
|
51
|
+
const free = await portIsFree(port, host)
|
|
52
52
|
if (free) {
|
|
53
53
|
return port
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
const nextPort = next(port)
|
|
57
57
|
if (nextPort > max) {
|
|
58
|
-
throw new Error(
|
|
58
|
+
throw new Error(
|
|
59
|
+
`${host} has no available port between ${min} and ${max}`,
|
|
60
|
+
)
|
|
59
61
|
}
|
|
60
|
-
return testUntil(nextPort,
|
|
62
|
+
return testUntil(nextPort, host)
|
|
61
63
|
}
|
|
62
|
-
const freePort = await testUntil(initialPort,
|
|
64
|
+
const freePort = await testUntil(initialPort, host)
|
|
63
65
|
return freePort
|
|
64
66
|
} finally {
|
|
65
67
|
await findFreePortOperation.end()
|
|
66
68
|
}
|
|
67
69
|
}
|
|
68
70
|
|
|
69
|
-
const portIsFree = async (port,
|
|
71
|
+
const portIsFree = async (port, host) => {
|
|
70
72
|
const server = createServer()
|
|
71
73
|
|
|
72
74
|
try {
|
|
73
75
|
await startListening({
|
|
74
76
|
server,
|
|
75
77
|
port,
|
|
76
|
-
|
|
78
|
+
host,
|
|
77
79
|
})
|
|
78
80
|
} catch (error) {
|
|
79
81
|
if (error && error.code === "EADDRINUSE") {
|
|
@@ -89,7 +91,7 @@ const portIsFree = async (port, ip) => {
|
|
|
89
91
|
return true
|
|
90
92
|
}
|
|
91
93
|
|
|
92
|
-
const startListening = ({ server, port,
|
|
94
|
+
const startListening = ({ server, port, host }) => {
|
|
93
95
|
return new Promise((resolve, reject) => {
|
|
94
96
|
server.on("error", reject)
|
|
95
97
|
server.on("listening", () => {
|
|
@@ -97,7 +99,7 @@ const startListening = ({ server, port, ip }) => {
|
|
|
97
99
|
// https://nodejs.org/api/net.html#net_server_listen_port_host_backlog_callback
|
|
98
100
|
resolve(server.address().port)
|
|
99
101
|
})
|
|
100
|
-
server.listen(port,
|
|
102
|
+
server.listen(port, host)
|
|
101
103
|
})
|
|
102
104
|
}
|
|
103
105
|
|
package/src/main.js
CHANGED
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
* Ideally this file should be kept simple to help discovering codebase progressively.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export { startServer } from "./
|
|
8
|
-
export { composeServices } from "./service_composition/service_composition.js"
|
|
7
|
+
export { startServer } from "./server.js"
|
|
9
8
|
export { setupRoutes } from "./service_composition/routing.js"
|
|
10
9
|
export { readRequestBody } from "./readRequestBody.js"
|
|
11
10
|
export { fetchFileSystem } from "./fetch_filesystem.js"
|
|
@@ -18,31 +17,30 @@ export {
|
|
|
18
17
|
STOP_REASON_PROCESS_EXIT,
|
|
19
18
|
STOP_REASON_NOT_SPECIFIED,
|
|
20
19
|
} from "./stopReasons.js"
|
|
20
|
+
export { jsenvServiceErrorHandler } from "./services/error_handler/jsenv_service_error_handler.js"
|
|
21
21
|
|
|
22
22
|
// CORS
|
|
23
23
|
export {
|
|
24
|
-
|
|
24
|
+
jsenvServiceCORS,
|
|
25
25
|
jsenvAccessControlAllowedHeaders,
|
|
26
26
|
jsenvAccessControlAllowedMethods,
|
|
27
|
-
} from "./cors/
|
|
27
|
+
} from "./services/cors/jsenv_service_cors.js"
|
|
28
28
|
|
|
29
|
-
// server
|
|
30
|
-
export { pluginServerTiming } from "./server_timing/plugin_server_timing.js"
|
|
29
|
+
// server timing
|
|
31
30
|
export { timeFunction, timeStart } from "./server_timing/timing_measure.js"
|
|
32
31
|
|
|
33
32
|
// SSE
|
|
34
|
-
export { createSSERoom } from "./sse/
|
|
33
|
+
export { createSSERoom } from "./sse/sse_room.js"
|
|
35
34
|
|
|
36
35
|
// content-negotiation
|
|
37
|
-
export {
|
|
38
|
-
export {
|
|
39
|
-
export {
|
|
40
|
-
export {
|
|
36
|
+
export { pickContentType } from "./content_negotiation/pick_content_type.js"
|
|
37
|
+
export { pickContentEncoding } from "./content_negotiation/pick_content_encoding.js"
|
|
38
|
+
export { pickContentLanguage } from "./content_negotiation/pick_content_language.js"
|
|
39
|
+
export { jsenvServiceResponseAcceptanceCheck } from "./services/response_acceptance_check/jsenv_service_response_acceptance_check.js"
|
|
41
40
|
|
|
42
41
|
// others
|
|
43
42
|
export { serveDirectory } from "./serve_directory.js"
|
|
44
43
|
export { fromFetchResponse } from "./from_fetch_response.js"
|
|
45
44
|
export { composeTwoResponses } from "./internal/response_composition.js"
|
|
46
|
-
export {
|
|
47
|
-
export { pluginRequestWaitingCheck } from "./plugin_request_waiting_check.js"
|
|
45
|
+
export { jsenvServiceRessourceAliases } from "./services/ressource_aliases/jsenv_service_ressource_aliases.js"
|
|
48
46
|
export { findFreePort } from "./internal/listen.js"
|
package/src/serve_directory.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readdirSync } from "node:fs"
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { pickContentType } from "./content_negotiation/pick_content_type.js"
|
|
4
4
|
|
|
5
5
|
export const serveDirectory = (
|
|
6
6
|
url,
|
|
@@ -57,7 +57,7 @@ export const serveDirectory = (
|
|
|
57
57
|
}
|
|
58
58
|
},
|
|
59
59
|
}
|
|
60
|
-
const bestContentType =
|
|
60
|
+
const bestContentType = pickContentType(
|
|
61
61
|
{ headers },
|
|
62
62
|
Object.keys(responseProducers),
|
|
63
63
|
)
|