@jsenv/server 7.1.0 → 8.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.
Files changed (44) hide show
  1. package/README.md +63 -23
  2. package/main.js +32 -17
  3. package/package.json +20 -15
  4. package/src/{internal → content_negotiation}/applyContentNegotiation.js +5 -1
  5. package/src/{internal → content_negotiation}/checkContentNegotiation.js +3 -3
  6. package/src/{negotiateContentEncoding.js → content_negotiation/negotiateContentEncoding.js} +5 -3
  7. package/src/{negotiateContentLanguage.js → content_negotiation/negotiateContentLanguage.js} +9 -5
  8. package/src/{negotiateContentType.js → content_negotiation/negotiateContentType.js} +10 -5
  9. package/src/content_negotiation/plugin_content_negotiation_check.js +15 -0
  10. package/src/cors/plugin_cors.js +156 -0
  11. package/src/fetchUrl.js +12 -7
  12. package/src/findFreePort.js +2 -1
  13. package/src/{composeResponse.js → internal/composeResponse.js} +5 -3
  14. package/src/internal/compositionMappingToCompose.js +26 -6
  15. package/src/{convertFileSystemErrorToResponseProperties.js → internal/convertFileSystemErrorToResponseProperties.js} +0 -0
  16. package/src/internal/getServerOrigins.js +5 -1
  17. package/src/internal/headersFromObject.js +3 -1
  18. package/src/{headersToObject.js → internal/headersToObject.js} +0 -0
  19. package/src/internal/listenClientError.js +5 -1
  20. package/src/internal/listenEvent.js +6 -1
  21. package/src/internal/listenRequest.js +5 -1
  22. package/src/internal/listenServerConnectionError.js +31 -23
  23. package/src/internal/multiple-header.js +11 -5
  24. package/src/internal/nodeRequestToRequest.js +10 -7
  25. package/src/internal/observable.js +5 -1
  26. package/src/internal/populateNodeResponse.js +11 -2
  27. package/src/internal/server-polyglot.js +11 -2
  28. package/src/internal/trackServerPendingConnections.js +15 -11
  29. package/src/internal/trackServerPendingRequests.js +10 -7
  30. package/src/jsenvServerInternalErrorToResponse.js +7 -3
  31. package/src/plugin_request_waiting_check.js +34 -0
  32. package/src/serveFile.js +88 -41
  33. package/src/server_timing/plugin_server_timing.js +28 -0
  34. package/src/{serverTiming.js → server_timing/timing_header.js} +43 -62
  35. package/src/server_timing/timing_measure.js +26 -0
  36. package/src/service_composition/service_composition.js +65 -0
  37. package/src/{createSSERoom.js → sse/createSSERoom.js} +22 -6
  38. package/src/startServer.js +197 -252
  39. package/src/stopReasons.js +3 -1
  40. package/src/urlToContentType.js +13 -6
  41. package/src/jsenvAccessControlAllowedHeaders.js +0 -1
  42. package/src/jsenvAccessControlAllowedMethods.js +0 -1
  43. package/src/service-composition.js +0 -41
  44. package/src/urlToSearchParamValue.js +0 -3
@@ -1,30 +1,7 @@
1
- import { performance } from "perf_hooks"
2
- import { parseMultipleHeader, stringifyMultipleHeader } from "./internal/multiple-header.js"
3
-
4
- export const timeStart = (name) => {
5
- // as specified in https://w3c.github.io/server-timing/#the-performanceservertiming-interface
6
- // duration is a https://www.w3.org/TR/hr-time-2/#sec-domhighrestimestamp
7
- const startTimestamp = performance.now()
8
- const timeEnd = () => {
9
- const endTimestamp = performance.now()
10
- const timing = {
11
- [name]: endTimestamp - startTimestamp,
12
- }
13
- return timing
14
- }
15
- return timeEnd
16
- }
17
-
18
- export const timeFunction = (name, fn) => {
19
- const timeEnd = timeStart(name)
20
- const returnValue = fn()
21
- if (returnValue && typeof returnValue.then === "function") {
22
- return returnValue.then((value) => {
23
- return [timeEnd(), value]
24
- })
25
- }
26
- return [timeEnd(), returnValue]
27
- }
1
+ import {
2
+ parseMultipleHeader,
3
+ stringifyMultipleHeader,
4
+ } from "../internal/multiple-header.js"
28
5
 
29
6
  // to predict order in chrome devtools we should put a,b,c,d,e or something
30
7
  // because in chrome dev tools they are shown in alphabetic order
@@ -41,47 +18,22 @@ export const timingToServerTimingResponseHeaders = (timing) => {
41
18
  dur: timing[key],
42
19
  }
43
20
  })
44
- const serverTimingHeaderString = stringifyServerTimingHeader(serverTimingHeader)
21
+ const serverTimingHeaderString =
22
+ stringifyServerTimingHeader(serverTimingHeader)
45
23
 
46
24
  return { "server-timing": serverTimingHeaderString }
47
25
  }
48
26
 
49
- const letters = [
50
- "a",
51
- "b",
52
- "c",
53
- "d",
54
- "e",
55
- "f",
56
- "g",
57
- "h",
58
- "i",
59
- "j",
60
- "k",
61
- "l",
62
- "m",
63
- "n",
64
- "o",
65
- "p",
66
- "q",
67
- "r",
68
- "s",
69
- "t",
70
- "u",
71
- "v",
72
- "w",
73
- "x",
74
- "y",
75
- "z",
76
- ]
77
-
78
27
  export const parseServerTimingHeader = (serverTimingHeaderString) => {
79
- const serverTimingHeaderObject = parseMultipleHeader(serverTimingHeaderString, {
80
- validateName: validateServerTimingName,
81
- validateProperty: ({ name }) => {
82
- return name === "desc" || name === "dur"
28
+ const serverTimingHeaderObject = parseMultipleHeader(
29
+ serverTimingHeaderString,
30
+ {
31
+ validateName: validateServerTimingName,
32
+ validateProperty: ({ name }) => {
33
+ return name === "desc" || name === "dur"
34
+ },
83
35
  },
84
- })
36
+ )
85
37
 
86
38
  const serverTiming = {}
87
39
  Object.keys(serverTimingHeaderObject).forEach((key) => {
@@ -114,3 +66,32 @@ const validateServerTimingName = (name) => {
114
66
  }
115
67
  return true
116
68
  }
69
+
70
+ const letters = [
71
+ "a",
72
+ "b",
73
+ "c",
74
+ "d",
75
+ "e",
76
+ "f",
77
+ "g",
78
+ "h",
79
+ "i",
80
+ "j",
81
+ "k",
82
+ "l",
83
+ "m",
84
+ "n",
85
+ "o",
86
+ "p",
87
+ "q",
88
+ "r",
89
+ "s",
90
+ "t",
91
+ "u",
92
+ "v",
93
+ "w",
94
+ "x",
95
+ "y",
96
+ "z",
97
+ ]
@@ -0,0 +1,26 @@
1
+ import { performance } from "perf_hooks"
2
+
3
+ export const timeStart = (name) => {
4
+ // as specified in https://w3c.github.io/server-timing/#the-performanceservertiming-interface
5
+ // duration is a https://www.w3.org/TR/hr-time-2/#sec-domhighrestimestamp
6
+ const startTimestamp = performance.now()
7
+ const timeEnd = () => {
8
+ const endTimestamp = performance.now()
9
+ const timing = {
10
+ [name]: endTimestamp - startTimestamp,
11
+ }
12
+ return timing
13
+ }
14
+ return timeEnd
15
+ }
16
+
17
+ export const timeFunction = (name, fn) => {
18
+ const timeEnd = timeStart(name)
19
+ const returnValue = fn()
20
+ if (returnValue && typeof returnValue.then === "function") {
21
+ return returnValue.then((value) => {
22
+ return [timeEnd(), value]
23
+ })
24
+ }
25
+ return [timeEnd(), returnValue]
26
+ }
@@ -0,0 +1,65 @@
1
+ import { firstOperationMatching } from "@jsenv/cancellation"
2
+
3
+ import { timeFunction } from "@jsenv/server/src/server_timing/timing_measure.js"
4
+ import { composeResponse } from "@jsenv/server/src/internal/composeResponse.js"
5
+
6
+ const nextSymbol = Symbol("next")
7
+
8
+ export const nextService = (nextRequest) => {
9
+ return {
10
+ [nextSymbol]: nextRequest,
11
+ }
12
+ }
13
+
14
+ export const composeServices = (...callbacks) => {
15
+ return (request) => {
16
+ return firstOperationMatching({
17
+ array: callbacks,
18
+ start: (callback) => callback(request),
19
+ predicate: (returnValue) => {
20
+ if (returnValue === null || typeof returnValue !== "object") {
21
+ return false
22
+ }
23
+ const nextRequest = returnValue[nextSymbol]
24
+ if (nextRequest) {
25
+ request = nextRequest
26
+ return false
27
+ }
28
+ return true
29
+ },
30
+ })
31
+ }
32
+ }
33
+
34
+ export const composeServicesWithTiming = (namedServices) => {
35
+ return async (request) => {
36
+ const servicesTiming = {}
37
+ const response = await firstOperationMatching({
38
+ array: Object.keys(namedServices).map((serviceName) => {
39
+ return { serviceName, serviceFn: namedServices[serviceName] }
40
+ }),
41
+ start: async ({ serviceName, serviceFn }) => {
42
+ const [serviceTiming, value] = await timeFunction(serviceName, () =>
43
+ serviceFn(request),
44
+ )
45
+ Object.assign(servicesTiming, serviceTiming)
46
+ return value
47
+ },
48
+ predicate: (returnValue) => {
49
+ if (returnValue === null || typeof returnValue !== "object") {
50
+ return false
51
+ }
52
+ const nextRequest = returnValue[nextSymbol]
53
+ if (nextRequest) {
54
+ request = nextRequest
55
+ return false
56
+ }
57
+ return true
58
+ },
59
+ })
60
+ if (response) {
61
+ return composeResponse({ timing: servicesTiming }, response)
62
+ }
63
+ return null
64
+ }
65
+ }
@@ -1,5 +1,9 @@
1
1
  import { createLogger } from "@jsenv/logger"
2
- import { createObservable, createCompositeProducer } from "./internal/observable.js"
2
+
3
+ import {
4
+ createObservable,
5
+ createCompositeProducer,
6
+ } from "@jsenv/server/src/internal/observable.js"
3
7
 
4
8
  // https://www.html5rocks.com/en/tutorials/eventsource/basics/
5
9
  export const createSSERoom = ({
@@ -35,7 +39,9 @@ export const createSSERoom = ({
35
39
 
36
40
  const lastKnownId =
37
41
  request.headers["last-event-id"] ||
38
- new URL(request.ressource, request.origin).searchParams.get("last-event-id")
42
+ new URL(request.ressource, request.origin).searchParams.get(
43
+ "last-event-id",
44
+ )
39
45
 
40
46
  if (clients.size >= maxClientAllowed) {
41
47
  return {
@@ -55,7 +61,9 @@ export const createSSERoom = ({
55
61
  complete,
56
62
  }
57
63
  clients.add(client)
58
- logger.debug(`A client has joined. Number of client in room: ${clients.size}`)
64
+ logger.debug(
65
+ `A client has joined. Number of client in room: ${clients.size}`,
66
+ )
59
67
 
60
68
  if (lastKnownId !== undefined) {
61
69
  const previousEvents = getAllEventSince(lastKnownId)
@@ -101,7 +109,11 @@ export const createSSERoom = ({
101
109
  }
102
110
  })
103
111
 
104
- const requestSSEObservable = connectRequestAndRoom(request, room, sseRoomObservable)
112
+ const requestSSEObservable = connectRequestAndRoom(
113
+ request,
114
+ room,
115
+ sseRoomObservable,
116
+ )
105
117
 
106
118
  return {
107
119
  status: 200,
@@ -128,7 +140,9 @@ export const createSSERoom = ({
128
140
 
129
141
  const sendEvent = (event) => {
130
142
  if (event.type !== "comment") {
131
- logger.debug(`send ${event.type} event to ${clients.size} client in the room`)
143
+ logger.debug(
144
+ `send ${event.type} event to ${clients.size} client in the room`,
145
+ )
132
146
  addEventToHistory(event)
133
147
  }
134
148
  write(stringifySourceEvent(event))
@@ -150,7 +164,9 @@ export const createSSERoom = ({
150
164
 
151
165
  const keepAlive = () => {
152
166
  // maybe that, when an event occurs, we can delay the keep alive event
153
- logger.debug(`send keep alive event, number of client listening event source: ${clients.size}`)
167
+ logger.debug(
168
+ `send keep alive event, number of client listening event source: ${clients.size}`,
169
+ )
154
170
  sendEvent({
155
171
  type: "comment",
156
172
  data: new Date().toLocaleTimeString(),