@jsenv/server 15.0.1 → 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.
Files changed (53) hide show
  1. package/README.md +23 -23
  2. package/package.json +4 -4
  3. package/src/content_negotiation/pick_accepted_content.js +13 -13
  4. package/src/content_negotiation/pick_content_encoding.js +25 -25
  5. package/src/content_negotiation/pick_content_language.js +35 -34
  6. package/src/content_negotiation/pick_content_type.js +29 -29
  7. package/src/fetch_filesystem.js +110 -107
  8. package/src/from_fetch_response.js +6 -6
  9. package/src/interfacing_with_node/body.js +22 -22
  10. package/src/interfacing_with_node/observable.js +80 -80
  11. package/src/interfacing_with_node/observable_from_node_stream.js +35 -35
  12. package/src/interfacing_with_node/request_factory.js +26 -26
  13. package/src/interfacing_with_node/write_node_response.js +82 -82
  14. package/src/internal/colorizeResponseStatus.js +26 -26
  15. package/src/internal/convertFileSystemErrorToResponseProperties.js +10 -10
  16. package/src/internal/dns_resolution.js +7 -7
  17. package/src/internal/etag.js +11 -11
  18. package/src/internal/filesystem.js +19 -19
  19. package/src/internal/headersFromObject.js +8 -8
  20. package/src/internal/headersFromString.js +11 -11
  21. package/src/internal/headersToObject.js +5 -5
  22. package/src/internal/headersToString.js +7 -7
  23. package/src/internal/headers_composition.js +9 -9
  24. package/src/internal/hostname_parser.js +9 -9
  25. package/src/internal/listen.js +47 -47
  26. package/src/internal/listenClientError.js +10 -10
  27. package/src/internal/listenEvent.js +5 -5
  28. package/src/internal/listenRequest.js +8 -8
  29. package/src/internal/listenServerConnectionError.js +19 -19
  30. package/src/internal/multiple-header.js +50 -50
  31. package/src/internal/normalizeHeaderName.js +4 -4
  32. package/src/internal/normalizeHeaderValue.js +2 -2
  33. package/src/internal/object_composition.js +41 -39
  34. package/src/internal/response_composition.js +6 -6
  35. package/src/internal/server-polyglot.js +42 -42
  36. package/src/internal/server_ips.js +20 -20
  37. package/src/internal/trackServerPendingConnections.js +23 -23
  38. package/src/internal/trackServerPendingRequests.js +28 -28
  39. package/src/main.js +18 -18
  40. package/src/readRequestBody.js +15 -15
  41. package/src/serve_directory.js +16 -16
  42. package/src/server_timing/timing_header.js +24 -24
  43. package/src/server_timing/timing_measure.js +14 -14
  44. package/src/service_composition/path_to_regexp.js +173 -168
  45. package/src/service_composition/routing.js +12 -12
  46. package/src/service_controller.js +77 -77
  47. package/src/services/cors/jsenv_service_cors.js +29 -29
  48. package/src/services/error_handler/jsenv_service_error_handler.js +18 -18
  49. package/src/services/request_aliases/jsenv_service_request_aliases.js +20 -20
  50. package/src/services/response_acceptance_check/jsenv_service_response_acceptance_check.js +16 -16
  51. package/src/sse/sse_room.js +117 -115
  52. package/src/start_server.js +328 -328
  53. package/src/stopReasons.js +9 -9
@@ -1,4 +1,4 @@
1
- import { isIP } from "node:net"
1
+ import { isIP } from "node:net";
2
2
 
3
3
  export const parseHostname = (hostname) => {
4
4
  if (hostname === "0.0.0.0") {
@@ -6,7 +6,7 @@ export const parseHostname = (hostname) => {
6
6
  type: "ip",
7
7
  label: "unspecified",
8
8
  version: 4,
9
- }
9
+ };
10
10
  }
11
11
  if (
12
12
  hostname === "::" ||
@@ -16,14 +16,14 @@ export const parseHostname = (hostname) => {
16
16
  type: "ip",
17
17
  label: "unspecified",
18
18
  version: 6,
19
- }
19
+ };
20
20
  }
21
21
  if (hostname === "127.0.0.1") {
22
22
  return {
23
23
  type: "ip",
24
24
  label: "loopback",
25
25
  version: 4,
26
- }
26
+ };
27
27
  }
28
28
  if (
29
29
  hostname === "::1" ||
@@ -33,16 +33,16 @@ export const parseHostname = (hostname) => {
33
33
  type: "ip",
34
34
  label: "loopback",
35
35
  version: 6,
36
- }
36
+ };
37
37
  }
38
- const ipVersion = isIP(hostname)
38
+ const ipVersion = isIP(hostname);
39
39
  if (ipVersion === 0) {
40
40
  return {
41
41
  type: "hostname",
42
- }
42
+ };
43
43
  }
44
44
  return {
45
45
  type: "ip",
46
46
  version: ipVersion,
47
- }
48
- }
47
+ };
48
+ };
@@ -1,5 +1,5 @@
1
- import { createServer } from "node:net"
2
- import { Abort } from "@jsenv/abort"
1
+ import { createServer } from "node:net";
2
+ import { Abort } from "@jsenv/abort";
3
3
 
4
4
  const listen = async ({
5
5
  signal = new AbortController().signal,
@@ -8,28 +8,28 @@ const listen = async ({
8
8
  portHint,
9
9
  hostname,
10
10
  }) => {
11
- const listeningOperation = Abort.startOperation()
11
+ const listeningOperation = Abort.startOperation();
12
12
 
13
13
  try {
14
- listeningOperation.addAbortSignal(signal)
14
+ listeningOperation.addAbortSignal(signal);
15
15
 
16
16
  if (portHint) {
17
- listeningOperation.throwIfAborted()
17
+ listeningOperation.throwIfAborted();
18
18
  port = await findFreePort(portHint, {
19
19
  signal: listeningOperation.signal,
20
20
  hostname,
21
- })
21
+ });
22
22
  }
23
- listeningOperation.throwIfAborted()
24
- port = await startListening({ server, port, hostname })
25
- listeningOperation.addAbortCallback(() => stopListening(server))
26
- listeningOperation.throwIfAborted()
23
+ listeningOperation.throwIfAborted();
24
+ port = await startListening({ server, port, hostname });
25
+ listeningOperation.addAbortCallback(() => stopListening(server));
26
+ listeningOperation.throwIfAborted();
27
27
 
28
- return port
28
+ return port;
29
29
  } finally {
30
- await listeningOperation.end()
30
+ await listeningOperation.end();
31
31
  }
32
- }
32
+ };
33
33
 
34
34
  export const findFreePort = async (
35
35
  initialPort = 1,
@@ -41,75 +41,75 @@ export const findFreePort = async (
41
41
  next = (port) => port + 1,
42
42
  } = {},
43
43
  ) => {
44
- const findFreePortOperation = Abort.startOperation()
44
+ const findFreePortOperation = Abort.startOperation();
45
45
  try {
46
- findFreePortOperation.addAbortSignal(signal)
47
- findFreePortOperation.throwIfAborted()
46
+ findFreePortOperation.addAbortSignal(signal);
47
+ findFreePortOperation.throwIfAborted();
48
48
 
49
49
  const testUntil = async (port, host) => {
50
- findFreePortOperation.throwIfAborted()
51
- const free = await portIsFree(port, host)
50
+ findFreePortOperation.throwIfAborted();
51
+ const free = await portIsFree(port, host);
52
52
  if (free) {
53
- return port
53
+ return port;
54
54
  }
55
55
 
56
- const nextPort = next(port)
56
+ const nextPort = next(port);
57
57
  if (nextPort > max) {
58
58
  throw new Error(
59
59
  `${hostname} has no available port between ${min} and ${max}`,
60
- )
60
+ );
61
61
  }
62
- return testUntil(nextPort, hostname)
63
- }
64
- const freePort = await testUntil(initialPort, hostname)
65
- return freePort
62
+ return testUntil(nextPort, hostname);
63
+ };
64
+ const freePort = await testUntil(initialPort, hostname);
65
+ return freePort;
66
66
  } finally {
67
- await findFreePortOperation.end()
67
+ await findFreePortOperation.end();
68
68
  }
69
- }
69
+ };
70
70
 
71
71
  const portIsFree = async (port, hostname) => {
72
- const server = createServer()
72
+ const server = createServer();
73
73
 
74
74
  try {
75
75
  await startListening({
76
76
  server,
77
77
  port,
78
78
  hostname,
79
- })
79
+ });
80
80
  } catch (error) {
81
81
  if (error && error.code === "EADDRINUSE") {
82
- return false
82
+ return false;
83
83
  }
84
84
  if (error && error.code === "EACCES") {
85
- return false
85
+ return false;
86
86
  }
87
- throw error
87
+ throw error;
88
88
  }
89
89
 
90
- await stopListening(server)
91
- return true
92
- }
90
+ await stopListening(server);
91
+ return true;
92
+ };
93
93
 
94
94
  const startListening = ({ server, port, hostname }) => {
95
95
  return new Promise((resolve, reject) => {
96
- server.on("error", reject)
96
+ server.on("error", reject);
97
97
  server.on("listening", () => {
98
98
  // in case port is 0 (randomly assign an available port)
99
99
  // https://nodejs.org/api/net.html#net_server_listen_port_host_backlog_callback
100
- resolve(server.address().port)
101
- })
102
- server.listen(port, hostname)
103
- })
104
- }
100
+ resolve(server.address().port);
101
+ });
102
+ server.listen(port, hostname);
103
+ });
104
+ };
105
105
 
106
106
  export const stopListening = (server) => {
107
107
  return new Promise((resolve, reject) => {
108
- server.on("error", reject)
109
- server.on("close", resolve)
110
- server.close()
111
- })
112
- }
108
+ server.on("error", reject);
109
+ server.on("close", resolve);
110
+ server.close();
111
+ });
112
+ };
113
113
 
114
114
  // unit test exports
115
- export { listen, portIsFree }
115
+ export { listen, portIsFree };
@@ -1,4 +1,4 @@
1
- import { listenEvent } from "./listenEvent.js"
1
+ import { listenEvent } from "./listenEvent.js";
2
2
 
3
3
  export const listenClientError = (nodeServer, clientErrorCallback) => {
4
4
  if (nodeServer._httpServer) {
@@ -6,22 +6,22 @@ export const listenClientError = (nodeServer, clientErrorCallback) => {
6
6
  nodeServer,
7
7
  "clientError",
8
8
  clientErrorCallback,
9
- )
9
+ );
10
10
  const removeHttpClientError = listenEvent(
11
11
  nodeServer._httpServer,
12
12
  "clientError",
13
13
  clientErrorCallback,
14
- )
14
+ );
15
15
  const removeTlsClientError = listenEvent(
16
16
  nodeServer._tlsServer,
17
17
  "clientError",
18
18
  clientErrorCallback,
19
- )
19
+ );
20
20
  return () => {
21
- removeNetClientError()
22
- removeHttpClientError()
23
- removeTlsClientError()
24
- }
21
+ removeNetClientError();
22
+ removeHttpClientError();
23
+ removeTlsClientError();
24
+ };
25
25
  }
26
- return listenEvent(nodeServer, "clientError", clientErrorCallback)
27
- }
26
+ return listenEvent(nodeServer, "clientError", clientErrorCallback);
27
+ };
@@ -5,11 +5,11 @@ export const listenEvent = (
5
5
  { once = false } = {},
6
6
  ) => {
7
7
  if (once) {
8
- objectWithEventEmitter.once(eventName, callback)
8
+ objectWithEventEmitter.once(eventName, callback);
9
9
  } else {
10
- objectWithEventEmitter.addListener(eventName, callback)
10
+ objectWithEventEmitter.addListener(eventName, callback);
11
11
  }
12
12
  return () => {
13
- objectWithEventEmitter.removeListener(eventName, callback)
14
- }
15
- }
13
+ objectWithEventEmitter.removeListener(eventName, callback);
14
+ };
15
+ };
@@ -1,4 +1,4 @@
1
- import { listenEvent } from "./listenEvent.js"
1
+ import { listenEvent } from "./listenEvent.js";
2
2
 
3
3
  export const listenRequest = (nodeServer, requestCallback) => {
4
4
  if (nodeServer._httpServer) {
@@ -6,16 +6,16 @@ export const listenRequest = (nodeServer, requestCallback) => {
6
6
  nodeServer._httpServer,
7
7
  "request",
8
8
  requestCallback,
9
- )
9
+ );
10
10
  const removeTlsRequestListener = listenEvent(
11
11
  nodeServer._tlsServer,
12
12
  "request",
13
13
  requestCallback,
14
- )
14
+ );
15
15
  return () => {
16
- removeHttpRequestListener()
17
- removeTlsRequestListener()
18
- }
16
+ removeHttpRequestListener();
17
+ removeTlsRequestListener();
18
+ };
19
19
  }
20
- return listenEvent(nodeServer, "request", requestCallback)
21
- }
20
+ return listenEvent(nodeServer, "request", requestCallback);
21
+ };
@@ -1,11 +1,11 @@
1
- import { listenEvent } from "./listenEvent.js"
1
+ import { listenEvent } from "./listenEvent.js";
2
2
 
3
3
  export const listenServerConnectionError = (
4
4
  nodeServer,
5
5
  connectionErrorCallback,
6
6
  { ignoreErrorAfterConnectionIsDestroyed = true } = {},
7
7
  ) => {
8
- const cleanupSet = new Set()
8
+ const cleanupSet = new Set();
9
9
 
10
10
  const removeConnectionListener = listenEvent(
11
11
  nodeServer,
@@ -16,34 +16,34 @@ export const listenServerConnectionError = (
16
16
  "error",
17
17
  (error) => {
18
18
  if (ignoreErrorAfterConnectionIsDestroyed && socket.destroyed) {
19
- return
19
+ return;
20
20
  }
21
- connectionErrorCallback(error, socket)
21
+ connectionErrorCallback(error, socket);
22
22
  },
23
- )
23
+ );
24
24
  const removeOnceSocketCloseListener = listenEvent(
25
25
  socket,
26
26
  "close",
27
27
  () => {
28
- removeSocketErrorListener()
29
- cleanupSet.delete(cleanup)
28
+ removeSocketErrorListener();
29
+ cleanupSet.delete(cleanup);
30
30
  },
31
31
  {
32
32
  once: true,
33
33
  },
34
- )
34
+ );
35
35
  const cleanup = () => {
36
- removeSocketErrorListener()
37
- removeOnceSocketCloseListener()
38
- }
39
- cleanupSet.add(cleanup)
36
+ removeSocketErrorListener();
37
+ removeOnceSocketCloseListener();
38
+ };
39
+ cleanupSet.add(cleanup);
40
40
  },
41
- )
41
+ );
42
42
  return () => {
43
- removeConnectionListener()
43
+ removeConnectionListener();
44
44
  cleanupSet.forEach((cleanup) => {
45
- cleanup()
46
- })
47
- cleanupSet.clear()
48
- }
49
- }
45
+ cleanup();
46
+ });
47
+ cleanupSet.clear();
48
+ };
49
+ };
@@ -15,54 +15,54 @@ export const parseMultipleHeader = (
15
15
  multipleHeaderString,
16
16
  { validateName = () => true, validateProperty = () => true } = {},
17
17
  ) => {
18
- const values = multipleHeaderString.split(",")
19
- const multipleHeader = {}
18
+ const values = multipleHeaderString.split(",");
19
+ const multipleHeader = {};
20
20
  values.forEach((value) => {
21
- const valueTrimmed = value.trim()
22
- const valueParts = valueTrimmed.split(";")
23
- const name = valueParts[0]
24
- const nameValidation = validateName(name)
21
+ const valueTrimmed = value.trim();
22
+ const valueParts = valueTrimmed.split(";");
23
+ const name = valueParts[0];
24
+ const nameValidation = validateName(name);
25
25
  if (!nameValidation) {
26
- return
26
+ return;
27
27
  }
28
28
 
29
29
  const properties = parseHeaderProperties(valueParts.slice(1), {
30
30
  validateProperty,
31
- })
32
- multipleHeader[name] = properties
33
- })
34
- return multipleHeader
35
- }
31
+ });
32
+ multipleHeader[name] = properties;
33
+ });
34
+ return multipleHeader;
35
+ };
36
36
 
37
37
  const parseHeaderProperties = (headerProperties, { validateProperty }) => {
38
38
  const properties = headerProperties.reduce((previous, valuePart) => {
39
- const [propertyName, propertyValueString] = valuePart.split("=")
40
- const propertyValue = parseHeaderPropertyValue(propertyValueString)
41
- const property = { name: propertyName, value: propertyValue }
42
- const propertyValidation = validateProperty(property)
39
+ const [propertyName, propertyValueString] = valuePart.split("=");
40
+ const propertyValue = parseHeaderPropertyValue(propertyValueString);
41
+ const property = { name: propertyName, value: propertyValue };
42
+ const propertyValidation = validateProperty(property);
43
43
  if (!propertyValidation) {
44
- return previous
44
+ return previous;
45
45
  }
46
46
  return {
47
47
  ...previous,
48
48
  [property.name]: property.value,
49
- }
50
- }, {})
51
- return properties
52
- }
49
+ };
50
+ }, {});
51
+ return properties;
52
+ };
53
53
 
54
54
  const parseHeaderPropertyValue = (headerPropertyValueString) => {
55
- const firstChar = headerPropertyValueString[0]
55
+ const firstChar = headerPropertyValueString[0];
56
56
  const lastChar =
57
- headerPropertyValueString[headerPropertyValueString.length - 1]
57
+ headerPropertyValueString[headerPropertyValueString.length - 1];
58
58
  if (firstChar === '"' && lastChar === '"') {
59
- return headerPropertyValueString.slice(1, -1)
59
+ return headerPropertyValueString.slice(1, -1);
60
60
  }
61
61
  if (isNaN(headerPropertyValueString)) {
62
- return headerPropertyValueString
62
+ return headerPropertyValueString;
63
63
  }
64
- return parseFloat(headerPropertyValueString)
65
- }
64
+ return parseFloat(headerPropertyValueString);
65
+ };
66
66
 
67
67
  export const stringifyMultipleHeader = (
68
68
  multipleHeader,
@@ -70,34 +70,34 @@ export const stringifyMultipleHeader = (
70
70
  ) => {
71
71
  return Object.keys(multipleHeader)
72
72
  .filter((name) => {
73
- const headerProperties = multipleHeader[name]
73
+ const headerProperties = multipleHeader[name];
74
74
  if (!headerProperties) {
75
- return false
75
+ return false;
76
76
  }
77
77
  if (typeof headerProperties !== "object") {
78
- return false
78
+ return false;
79
79
  }
80
- const nameValidation = validateName(name)
80
+ const nameValidation = validateName(name);
81
81
  if (!nameValidation) {
82
- return false
82
+ return false;
83
83
  }
84
- return true
84
+ return true;
85
85
  })
86
86
  .map((name) => {
87
- const headerProperties = multipleHeader[name]
87
+ const headerProperties = multipleHeader[name];
88
88
  const headerPropertiesString = stringifyHeaderProperties(
89
89
  headerProperties,
90
90
  {
91
91
  validateProperty,
92
92
  },
93
- )
93
+ );
94
94
  if (headerPropertiesString.length) {
95
- return `${name};${headerPropertiesString}`
95
+ return `${name};${headerPropertiesString}`;
96
96
  }
97
- return name
97
+ return name;
98
98
  })
99
- .join(", ")
100
- }
99
+ .join(", ");
100
+ };
101
101
 
102
102
  const stringifyHeaderProperties = (headerProperties, { validateProperty }) => {
103
103
  const headerPropertiesString = Object.keys(headerProperties)
@@ -105,24 +105,24 @@ const stringifyHeaderProperties = (headerProperties, { validateProperty }) => {
105
105
  const property = {
106
106
  name,
107
107
  value: headerProperties[name],
108
- }
109
- return property
108
+ };
109
+ return property;
110
110
  })
111
111
  .filter((property) => {
112
- const propertyValidation = validateProperty(property)
112
+ const propertyValidation = validateProperty(property);
113
113
  if (!propertyValidation) {
114
- return false
114
+ return false;
115
115
  }
116
- return true
116
+ return true;
117
117
  })
118
118
  .map(stringifyHeaderProperty)
119
- .join(";")
120
- return headerPropertiesString
121
- }
119
+ .join(";");
120
+ return headerPropertiesString;
121
+ };
122
122
 
123
123
  const stringifyHeaderProperty = ({ name, value }) => {
124
124
  if (typeof value === "string") {
125
- return `${name}="${value}"`
125
+ return `${name}="${value}"`;
126
126
  }
127
- return `${name}=${value}`
128
- }
127
+ return `${name}=${value}`;
128
+ };
@@ -1,8 +1,8 @@
1
1
  export const normalizeHeaderName = (headerName) => {
2
- headerName = String(headerName)
2
+ headerName = String(headerName);
3
3
  if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(headerName)) {
4
- throw new TypeError("Invalid character in header field name")
4
+ throw new TypeError("Invalid character in header field name");
5
5
  }
6
6
 
7
- return headerName.toLowerCase()
8
- }
7
+ return headerName.toLowerCase();
8
+ };
@@ -1,3 +1,3 @@
1
1
  export const normalizeHeaderValue = (headerValue) => {
2
- return String(headerValue)
3
- }
2
+ return String(headerValue);
3
+ };