@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.
Files changed (53) hide show
  1. package/README.md +23 -23
  2. package/package.json +3 -3
  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,9 +1,9 @@
1
- import { createLogger } from "@jsenv/log"
1
+ import { createLogger } from "@jsenv/log";
2
2
 
3
3
  import {
4
4
  createObservable,
5
5
  createCompositeProducer,
6
- } from "@jsenv/server/src/interfacing_with_node/observable.js"
6
+ } from "@jsenv/server/src/interfacing_with_node/observable.js";
7
7
 
8
8
  // https://www.html5rocks.com/en/tutorials/eventsource/basics/
9
9
  export const createSSERoom = ({
@@ -19,36 +19,36 @@ export const createSSERoom = ({
19
19
  welcomeEventEnabled = false,
20
20
  welcomeEventPublic = false, // decides if welcome event are sent to other clients
21
21
  } = {}) => {
22
- const logger = createLogger({ logLevel })
22
+ const logger = createLogger({ logLevel });
23
23
 
24
- const room = {}
25
- const clients = new Set()
26
- const eventHistory = createEventHistory(historyLength)
24
+ const room = {};
25
+ const clients = new Set();
26
+ const eventHistory = createEventHistory(historyLength);
27
27
  // what about previousEventId that keeps growing ?
28
28
  // we could add some limit
29
29
  // one limit could be that an event older than 24h is deleted
30
- let previousEventId = 0
31
- let opened = false
32
- let interval
33
- let cleanupEffect = CLEANUP_NOOP
30
+ let previousEventId = 0;
31
+ let opened = false;
32
+ let interval;
33
+ let cleanupEffect = CLEANUP_NOOP;
34
34
 
35
35
  const join = (request) => {
36
36
  // should we ensure a given request can join a room only once?
37
37
 
38
38
  const lastKnownId =
39
39
  request.headers["last-event-id"] ||
40
- new URL(request.url).searchParams.get("last-event-id")
40
+ new URL(request.url).searchParams.get("last-event-id");
41
41
 
42
42
  if (clients.size >= maxClientAllowed) {
43
43
  return {
44
44
  status: 503,
45
- }
45
+ };
46
46
  }
47
47
 
48
48
  if (!opened) {
49
49
  return {
50
50
  status: 204,
51
- }
51
+ };
52
52
  }
53
53
 
54
54
  const sseRoomObservable = createObservable(({ next, complete }) => {
@@ -56,30 +56,30 @@ export const createSSERoom = ({
56
56
  next,
57
57
  complete,
58
58
  request,
59
- }
59
+ };
60
60
  if (clients.size === 0) {
61
- const effectReturnValue = effect()
61
+ const effectReturnValue = effect();
62
62
  if (typeof effectReturnValue === "function") {
63
- cleanupEffect = effectReturnValue
63
+ cleanupEffect = effectReturnValue;
64
64
  } else {
65
- cleanupEffect = CLEANUP_NOOP
65
+ cleanupEffect = CLEANUP_NOOP;
66
66
  }
67
67
  }
68
- clients.add(client)
68
+ clients.add(client);
69
69
  logger.debug(
70
70
  `A client has joined. Number of client in room: ${clients.size}`,
71
- )
71
+ );
72
72
 
73
73
  if (lastKnownId !== undefined) {
74
- const previousEvents = getAllEventSince(lastKnownId)
75
- const eventMissedCount = previousEvents.length
74
+ const previousEvents = getAllEventSince(lastKnownId);
75
+ const eventMissedCount = previousEvents.length;
76
76
  if (eventMissedCount > 0) {
77
77
  logger.info(
78
78
  `send ${eventMissedCount} event missed by client since event with id "${lastKnownId}"`,
79
- )
79
+ );
80
80
  previousEvents.forEach((previousEvent) => {
81
- next(stringifySourceEvent(previousEvent))
82
- })
81
+ next(stringifySourceEvent(previousEvent));
82
+ });
83
83
  }
84
84
  }
85
85
 
@@ -88,43 +88,45 @@ export const createSSERoom = ({
88
88
  retry: retryDuration,
89
89
  type: "welcome",
90
90
  data: new Date().toLocaleTimeString(),
91
- }
92
- addEventToHistory(welcomeEvent)
91
+ };
92
+ addEventToHistory(welcomeEvent);
93
93
 
94
94
  // send to everyone
95
95
  if (welcomeEventPublic) {
96
96
  sendEventToAllClients(welcomeEvent, {
97
97
  history: false,
98
- })
98
+ });
99
99
  }
100
100
  // send only to this client
101
101
  else {
102
- next(stringifySourceEvent(welcomeEvent))
102
+ next(stringifySourceEvent(welcomeEvent));
103
103
  }
104
104
  } else {
105
105
  const firstEvent = {
106
106
  retry: retryDuration,
107
107
  type: "comment",
108
108
  data: new Date().toLocaleTimeString(),
109
- }
110
- next(stringifySourceEvent(firstEvent))
109
+ };
110
+ next(stringifySourceEvent(firstEvent));
111
111
  }
112
112
 
113
113
  return () => {
114
- clients.delete(client)
114
+ clients.delete(client);
115
115
  if (clients.size === 0) {
116
- cleanupEffect()
117
- cleanupEffect = CLEANUP_NOOP
116
+ cleanupEffect();
117
+ cleanupEffect = CLEANUP_NOOP;
118
118
  }
119
- logger.debug(`A client left. Number of client in room: ${clients.size}`)
120
- }
121
- })
119
+ logger.debug(
120
+ `A client left. Number of client in room: ${clients.size}`,
121
+ );
122
+ };
123
+ });
122
124
 
123
125
  const requestSSEObservable = connectRequestAndRoom(
124
126
  request,
125
127
  room,
126
128
  sseRoomObservable,
127
- )
129
+ );
128
130
 
129
131
  return {
130
132
  status: 200,
@@ -134,76 +136,76 @@ export const createSSERoom = ({
134
136
  "connection": "keep-alive",
135
137
  },
136
138
  body: requestSSEObservable,
137
- }
138
- }
139
+ };
140
+ };
139
141
 
140
142
  const leave = (request) => {
141
- disconnectRequestFromRoom(request, room)
142
- }
143
+ disconnectRequestFromRoom(request, room);
144
+ };
143
145
 
144
146
  const addEventToHistory = (event) => {
145
147
  if (typeof event.id === "undefined") {
146
- event.id = computeEventId(event, previousEventId)
148
+ event.id = computeEventId(event, previousEventId);
147
149
  }
148
- previousEventId = event.id
149
- eventHistory.add(event)
150
- }
150
+ previousEventId = event.id;
151
+ eventHistory.add(event);
152
+ };
151
153
 
152
154
  const sendEventToAllClients = (event, { history = true } = {}) => {
153
155
  if (history) {
154
- addEventToHistory(event)
156
+ addEventToHistory(event);
155
157
  }
156
158
  logger.debug(
157
159
  `send "${event.type}" event to ${clients.size} client in the room`,
158
- )
159
- const eventString = stringifySourceEvent(event)
160
+ );
161
+ const eventString = stringifySourceEvent(event);
160
162
  clients.forEach((client) => {
161
- client.next(eventString)
162
- })
163
- }
163
+ client.next(eventString);
164
+ });
165
+ };
164
166
 
165
167
  const getAllEventSince = (id) => {
166
- const events = eventHistory.since(id)
168
+ const events = eventHistory.since(id);
167
169
  if (welcomeEventEnabled && !welcomeEventPublic) {
168
- return events.filter((event) => event.type !== "welcome")
170
+ return events.filter((event) => event.type !== "welcome");
169
171
  }
170
- return events
171
- }
172
+ return events;
173
+ };
172
174
 
173
175
  const keepAlive = () => {
174
176
  // maybe that, when an event occurs, we can delay the keep alive event
175
177
  logger.debug(
176
178
  `send keep alive event, number of client listening event source: ${clients.size}`,
177
- )
179
+ );
178
180
  sendEventToAllClients(
179
181
  {
180
182
  type: "comment",
181
183
  data: new Date().toLocaleTimeString(),
182
184
  },
183
185
  { history: false },
184
- )
185
- }
186
+ );
187
+ };
186
188
 
187
189
  const open = () => {
188
- if (opened) return
189
- opened = true
190
- interval = setInterval(keepAlive, keepaliveDuration)
190
+ if (opened) return;
191
+ opened = true;
192
+ interval = setInterval(keepAlive, keepaliveDuration);
191
193
  if (!keepProcessAlive) {
192
- interval.unref()
194
+ interval.unref();
193
195
  }
194
- }
196
+ };
195
197
 
196
198
  const close = () => {
197
- if (!opened) return
198
- logger.debug(`closing room, number of client in the room: ${clients.size}`)
199
- clients.forEach((client) => client.complete())
200
- clients.clear()
201
- clearInterval(interval)
202
- eventHistory.reset()
203
- opened = false
204
- }
199
+ if (!opened) return;
200
+ logger.debug(`closing room, number of client in the room: ${clients.size}`);
201
+ clients.forEach((client) => client.complete());
202
+ clients.clear();
203
+ clearInterval(interval);
204
+ eventHistory.reset();
205
+ opened = false;
206
+ };
205
207
 
206
- open()
208
+ open();
207
209
 
208
210
  Object.assign(room, {
209
211
  // main api:
@@ -221,91 +223,91 @@ export const createSSERoom = ({
221
223
  // should rarely be used
222
224
  close,
223
225
  open,
224
- })
225
- return room
226
- }
226
+ });
227
+ return room;
228
+ };
227
229
 
228
- const CLEANUP_NOOP = () => {}
230
+ const CLEANUP_NOOP = () => {};
229
231
 
230
- const requestMap = new Map()
232
+ const requestMap = new Map();
231
233
 
232
234
  const connectRequestAndRoom = (request, room, roomObservable) => {
233
- let sseProducer
234
- let roomObservableMap
235
- const requestInfo = requestMap.get(request)
235
+ let sseProducer;
236
+ let roomObservableMap;
237
+ const requestInfo = requestMap.get(request);
236
238
  if (requestInfo) {
237
- sseProducer = requestInfo.sseProducer
238
- roomObservableMap = requestInfo.roomObservableMap
239
+ sseProducer = requestInfo.sseProducer;
240
+ roomObservableMap = requestInfo.roomObservableMap;
239
241
  } else {
240
242
  sseProducer = createCompositeProducer({
241
243
  cleanup: () => {
242
- requestMap.delete(request)
244
+ requestMap.delete(request);
243
245
  },
244
- })
245
- roomObservableMap = new Map()
246
- requestMap.set(request, { sseProducer, roomObservableMap })
246
+ });
247
+ roomObservableMap = new Map();
248
+ requestMap.set(request, { sseProducer, roomObservableMap });
247
249
  }
248
250
 
249
- roomObservableMap.set(room, roomObservable)
250
- sseProducer.addObservable(roomObservable)
251
+ roomObservableMap.set(room, roomObservable);
252
+ sseProducer.addObservable(roomObservable);
251
253
 
252
- return createObservable(sseProducer)
253
- }
254
+ return createObservable(sseProducer);
255
+ };
254
256
 
255
257
  const disconnectRequestFromRoom = (request, room) => {
256
- const requestInfo = requestMap.get(request)
258
+ const requestInfo = requestMap.get(request);
257
259
  if (!requestInfo) {
258
- return
260
+ return;
259
261
  }
260
- const { sseProducer, roomObservableMap } = requestInfo
261
- const roomObservable = roomObservableMap.get(room)
262
- roomObservableMap.delete(room)
263
- sseProducer.removeObservable(roomObservable)
264
- }
262
+ const { sseProducer, roomObservableMap } = requestInfo;
263
+ const roomObservable = roomObservableMap.get(room);
264
+ roomObservableMap.delete(room);
265
+ sseProducer.removeObservable(roomObservable);
266
+ };
265
267
 
266
268
  // https://github.com/dmail-old/project/commit/da7d2c88fc8273850812972885d030a22f9d7448
267
269
  // https://github.com/dmail-old/project/commit/98b3ae6748d461ac4bd9c48944a551b1128f4459
268
270
  // https://github.com/dmail-old/http-eventsource/blob/master/lib/event-source.js
269
271
  // http://html5doctor.com/server-sent-events/
270
272
  const stringifySourceEvent = ({ data, type = "message", id, retry }) => {
271
- let string = ""
273
+ let string = "";
272
274
 
273
275
  if (id !== undefined) {
274
- string += `id:${id}\n`
276
+ string += `id:${id}\n`;
275
277
  }
276
278
 
277
279
  if (retry) {
278
- string += `retry:${retry}\n`
280
+ string += `retry:${retry}\n`;
279
281
  }
280
282
 
281
283
  if (type !== "message") {
282
- string += `event:${type}\n`
284
+ string += `event:${type}\n`;
283
285
  }
284
286
 
285
- string += `data:${data}\n\n`
287
+ string += `data:${data}\n\n`;
286
288
 
287
- return string
288
- }
289
+ return string;
290
+ };
289
291
 
290
292
  const createEventHistory = (limit) => {
291
- const events = []
293
+ const events = [];
292
294
 
293
295
  const add = (data) => {
294
- events.push(data)
296
+ events.push(data);
295
297
 
296
298
  if (events.length >= limit) {
297
- events.shift()
299
+ events.shift();
298
300
  }
299
- }
301
+ };
300
302
 
301
303
  const since = (id) => {
302
- const index = events.findIndex((event) => String(event.id) === id)
303
- return index === -1 ? [] : events.slice(index + 1)
304
- }
304
+ const index = events.findIndex((event) => String(event.id) === id);
305
+ return index === -1 ? [] : events.slice(index + 1);
306
+ };
305
307
 
306
308
  const reset = () => {
307
- events.length = 0
308
- }
309
+ events.length = 0;
310
+ };
309
311
 
310
- return { add, since, reset }
311
- }
312
+ return { add, since, reset };
313
+ };