@jsenv/server 12.7.5 → 13.1.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.
Files changed (28) hide show
  1. package/README.md +66 -81
  2. package/package.json +3 -2
  3. package/src/content_negotiation/{applyContentNegotiation.js → pick_accepted_content.js} +1 -3
  4. package/src/content_negotiation/{negotiateContentEncoding.js → pick_content_encoding.js} +3 -3
  5. package/src/content_negotiation/{negotiateContentLanguage.js → pick_content_language.js} +3 -3
  6. package/src/content_negotiation/{negotiateContentType.js → pick_content_type.js} +3 -3
  7. package/src/fetch_filesystem.js +2 -2
  8. package/src/internal/dns_resolution.js +17 -0
  9. package/src/internal/getServerOrigins.js +43 -14
  10. package/src/internal/listen.js +15 -13
  11. package/src/internal/request_factory.js +3 -1
  12. package/src/internal/server-polyglot.js +1 -0
  13. package/src/main.js +11 -13
  14. package/src/serve_directory.js +2 -2
  15. package/src/service_controller.js +174 -0
  16. package/src/{cors/plugin_cors.js → services/cors/jsenv_service_cors.js} +29 -45
  17. package/src/services/error_handler/jsenv_service_error_handler.js +98 -0
  18. package/src/{content_negotiation/checkContentNegotiation.js → services/response_acceptance_check/jsenv_service_response_acceptance_check.js} +16 -7
  19. package/src/services/ressource_aliases/jsenv_service_ressource_aliases.js +40 -0
  20. package/src/sse/{createSSERoom.js → sse_room.js} +18 -16
  21. package/src/start_server.js +962 -0
  22. package/src/content_negotiation/plugin_content_negotiation_check.js +0 -15
  23. package/src/error_to_response_default.js +0 -94
  24. package/src/plugin_request_waiting_check.js +0 -32
  25. package/src/ressource_aliases/plugin_ressource_aliases.js +0 -44
  26. package/src/server_timing/plugin_server_timing.js +0 -28
  27. package/src/service_composition/service_composition.js +0 -55
  28. package/src/startServer.js +0 -868
package/README.md CHANGED
@@ -8,42 +8,23 @@ import { startServer } from "@jsenv/server"
8
8
  await startServer({
9
9
  protocol: "http",
10
10
  port: 8080,
11
- requestToResponse: () => {
12
- return {
13
- status: 200,
14
- headers: {
15
- "content-type": "text/plain",
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
- body: "Hello world",
18
- }
19
- },
22
+ },
23
+ ],
20
24
  })
21
25
  ```
22
26
 
23
- # Example
24
-
25
- _Code starting a server and using [node-fetch](https://github.com/node-fetch/node-fetch) to send a request on that server:_
26
-
27
- ```js
28
- import fetch from "node-fetch"
29
- import { startServer } from "@jsenv/server"
30
-
31
- const server = await startServer({
32
- requestToResponse: () => {
33
- return {
34
- status: 200,
35
- headers: {
36
- "content-type": "text/plain",
37
- },
38
- body: "Hello world",
39
- }
40
- },
41
- })
42
-
43
- const response = await fetch(server.origin)
44
- const responseBodyAsText = await response.text()
45
- console.log(responseBodyAsText) // "Hello world"
46
- ```
27
+ # Examples
47
28
 
48
29
  _Code starting a server with 2 request handlers:_
49
30
 
@@ -57,22 +38,24 @@ _Code starting a server with 2 request handlers:_
57
38
  */
58
39
  import { startServer, composeServices } from "@jsenv/server"
59
40
 
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
41
  const server = await startServer({
72
- requestToResponse: composeServices({
73
- index: indexService,
74
- otherwise: notFoundService,
75
- }),
42
+ services: [
43
+ {
44
+ name: "index",
45
+ handleRequest: (request) => {
46
+ if (request.ressource === "/") {
47
+ return { status: 200 }
48
+ }
49
+ return null
50
+ },
51
+ },
52
+ {
53
+ name: "otherwise",
54
+ handleRequest: () => {
55
+ return { status: 404 }
56
+ },
57
+ },
58
+ ],
76
59
  })
77
60
 
78
61
  const fetch = await import("node-fetch")
@@ -91,26 +74,25 @@ import { startServer } from "@jsenv/server"
91
74
 
92
75
  await startServer({
93
76
  protocol: "https",
94
- certificate: readFileSyncAsString("./server.crt"),
95
- privateKey: readFileSyncAsString("./server.key"),
77
+ certificate: readFileSync(new URL("./server.crt", import.meta.url), "utf8"),
78
+ privateKey: readFileSync(new URL("./server.key", import.meta.url), "utf8"),
96
79
  allowHttpRequestOnHttps: true,
97
- requestToResponse: (request) => {
98
- const clientUsesHttp = request.origin.startsWith("http:")
99
-
100
- return {
101
- status: 200,
102
- headers: {
103
- "content-type": "text/plain",
80
+ services: [
81
+ {
82
+ handleRequest: (request) => {
83
+ const clientUsesHttp = request.origin.startsWith("http:")
84
+
85
+ return {
86
+ status: 200,
87
+ headers: {
88
+ "content-type": "text/plain",
89
+ },
90
+ body: clientUsesHttp ? `Welcome http user` : `Welcome https user`,
91
+ }
104
92
  },
105
- body: clientUsesHttp ? `Welcome http user` : `Welcome https user`,
106
- }
107
- },
93
+ },
94
+ ],
108
95
  })
109
-
110
- function readFileSyncAsString(relativeUrl) {
111
- const fileUrl = new URL(relativeUrl, import.meta.url)
112
- return String(readFileSync(fileUrl))
113
- }
114
96
  ```
115
97
 
116
98
  _Code starting a server for static files:_
@@ -119,28 +101,31 @@ _Code starting a server for static files:_
119
101
  import { startServer, fetchFileSystem } from "@jsenv/server"
120
102
 
121
103
  await startServer({
122
- requestToResponse: async (request) => {
123
- const fileUrl = new URL(request.ressource.slice(1), import.meta.url)
124
- const response = await fetchFileSystem(fileUrl, {
125
- ...request,
126
- })
127
- return response
128
- },
104
+ services: [
105
+ {
106
+ handleRequest: async (request) => {
107
+ const fileUrl = new URL(request.ressource.slice(1), import.meta.url)
108
+ const response = await fetchFileSystem(fileUrl, request)
109
+ return response
110
+ },
111
+ },
112
+ ],
129
113
  })
130
114
  ```
131
115
 
132
116
  # Documentation
133
117
 
134
- - [https](./docs/https/https.md)
135
- - [Serving files](./docs/serving_files/serving_files.md)
136
- - [Handling requests](./docs/handling_requests/handling_requests.md)
137
- - [Handling errors](./docs/handling_errors/handling_errors.md)
138
- - [Server timing](./docs/server_timing/server_timing.md)
139
- - [CORS](./docs/cors/cors.md)
140
- - [Content negotiation](./docs/content_negotiation/content_negotiation.md)
141
- - [Server Sent Events](./docs/sse/sse.md)
142
- - [Cluster](./docs/cluster/cluster.md)
143
- - [Http2 push](./docs/http2_push/http2_push.md)
118
+ - [Handling requests](./docs/handling_requests.md)
119
+ - [Handling errors](./docs/handling_errors.md)
120
+ - [Server timing](./docs/server_timing.md)
121
+ - [CORS](./docs/cors.md)
122
+ - [https](./docs/https.md)
123
+ - [Serving files](./docs/serving_files.md)
124
+ - [Content negotiation](./docs/content_negotiation.md)
125
+ - [Websocket](./docs/websocket.md)
126
+ - [Server Sent Events](./docs/server_sent_events.md)
127
+ - [Cluster](./docs/cluster.md)
128
+ - [Http2 push](./docs/http2_push.md)
144
129
 
145
130
  # Installation
146
131
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/server",
3
- "version": "12.7.5",
3
+ "version": "13.1.0",
4
4
  "description": "Write your Node.js server using pure functions",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -37,7 +37,8 @@
37
37
  "@jsenv/abort": "4.2.3",
38
38
  "@jsenv/log": "3.1.0",
39
39
  "@jsenv/url-meta": "7.0.0",
40
- "@jsenv/utils": "2.0.1"
40
+ "@jsenv/utils": "2.0.1",
41
+ "ws": "8.8.1"
41
42
  },
42
43
  "devDependencies": {
43
44
  "@jsenv/fetch": "../fetch/",
@@ -1,11 +1,10 @@
1
- export const applyContentNegotiation = ({
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 { applyContentNegotiation } from "./applyContentNegotiation.js"
2
+ import { pickAcceptedContent } from "./pick_accepted_content.js"
3
3
 
4
- export const negotiateContentEncoding = (request, availableEncodings) => {
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 applyContentNegotiation({
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 { applyContentNegotiation } from "./applyContentNegotiation.js"
2
+ import { pickAcceptedContent } from "./pick_accepted_content.js"
3
3
 
4
- export const negotiateContentLanguage = (request, availableLanguages) => {
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 applyContentNegotiation({
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 { applyContentNegotiation } from "./applyContentNegotiation.js"
2
+ import { pickAcceptedContent } from "./pick_accepted_content.js"
3
3
 
4
- export const negotiateContentType = (request, availableContentTypes) => {
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 applyContentNegotiation({
12
+ return pickAcceptedContent({
13
13
  accepteds: contentTypesAccepted,
14
14
  availables: availableContentTypes,
15
15
  getAcceptanceScore: getContentTypeAcceptanceScore,
@@ -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 { negotiateContentEncoding } from "./content_negotiation/negotiateContentEncoding.js"
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 = negotiateContentEncoding(
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
- export const getServerOrigins = ({ protocol, ip, port }) => {
4
- const isInternalIp = ip === "127.0.0.1"
5
- const internalOrigin = createServerOrigin({
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: "localhost",
10
+ hostname:
11
+ localhostDnsResolution.address === "127.0.0.1"
12
+ ? "localhost"
13
+ : "127.0.0.1",
8
14
  port,
9
15
  })
10
- if (isInternalIp) {
11
- return { internal: internalOrigin }
16
+ if (isLocal) {
17
+ return { local: localOrigin }
12
18
  }
13
- const isAnyIp = !ip || ip === "::" || ip === "0.0.0.0"
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
- internal: internalOrigin,
16
- external: createServerOrigin({
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.family !== "IPv4") return false
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
+ }
@@ -6,7 +6,7 @@ const listen = async ({
6
6
  server,
7
7
  port,
8
8
  portHint,
9
- ip,
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
- ip,
20
+ host,
21
21
  })
22
22
  }
23
23
  listeningOperation.throwIfAborted()
24
- port = await startListening({ server, port, ip })
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
- ip = "127.0.0.1",
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, ip) => {
49
+ const testUntil = async (port, host) => {
50
50
  findFreePortOperation.throwIfAborted()
51
- const free = await portIsFree(port, ip)
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(`${ip} has no available port between ${min} and ${max}`)
58
+ throw new Error(
59
+ `${host} has no available port between ${min} and ${max}`,
60
+ )
59
61
  }
60
- return testUntil(nextPort, ip)
62
+ return testUntil(nextPort, host)
61
63
  }
62
- const freePort = await testUntil(initialPort, ip)
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, ip) => {
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
- ip,
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, ip }) => {
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, ip)
102
+ server.listen(port, host)
101
103
  })
102
104
  }
103
105
 
@@ -6,7 +6,9 @@ export const fromNodeRequest = (nodeRequest, { serverOrigin, signal }) => {
6
6
  const body = nodeStreamToObservable(nodeRequest)
7
7
 
8
8
  let requestOrigin
9
- if (nodeRequest.authority) {
9
+ if (nodeRequest.upgrade) {
10
+ requestOrigin = serverOrigin
11
+ } else if (nodeRequest.authority) {
10
12
  requestOrigin = nodeRequest.connection.encrypted
11
13
  ? `https://${nodeRequest.authority}`
12
14
  : `http://${nodeRequest.authority}`
@@ -62,6 +62,7 @@ export const createPolyglotServer = async ({
62
62
 
63
63
  // The async part is just to lazyly import "http2" or "https"
64
64
  // so that these module are parsed only if used.
65
+ // https://nodejs.org/api/tls.html#tlscreatesecurecontextoptions
65
66
  const createSecureServer = async ({
66
67
  certificate,
67
68
  privateKey,
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 "./startServer.js"
8
- export { composeServices } from "./service_composition/service_composition.js"
7
+ export { startServer } from "./start_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
- pluginCORS,
24
+ jsenvServiceCORS,
25
25
  jsenvAccessControlAllowedHeaders,
26
26
  jsenvAccessControlAllowedMethods,
27
- } from "./cors/plugin_cors.js"
27
+ } from "./services/cors/jsenv_service_cors.js"
28
28
 
29
- // server timings
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/createSSERoom.js"
33
+ export { createSSERoom } from "./sse/sse_room.js"
35
34
 
36
35
  // content-negotiation
37
- export { negotiateContentType } from "./content_negotiation/negotiateContentType.js"
38
- export { negotiateContentEncoding } from "./content_negotiation/negotiateContentEncoding.js"
39
- export { negotiateContentLanguage } from "./content_negotiation/negotiateContentLanguage.js"
40
- export { pluginContentNegotiationCheck } from "./content_negotiation/plugin_content_negotiation_check.js"
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 { pluginRessourceAliases } from "./ressource_aliases/plugin_ressource_aliases.js"
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"
@@ -1,6 +1,6 @@
1
1
  import { readdirSync } from "node:fs"
2
2
 
3
- import { negotiateContentType } from "./content_negotiation/negotiateContentType.js"
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 = negotiateContentType(
60
+ const bestContentType = pickContentType(
61
61
  { headers },
62
62
  Object.keys(responseProducers),
63
63
  )