@jsenv/server 15.5.2 → 16.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 (55) hide show
  1. package/README.md +79 -86
  2. package/dist/html/4xx.html +196 -0
  3. package/dist/html/500.html +17 -0
  4. package/dist/html/route_inspector.html +2435 -0
  5. package/dist/js/formidable.js +17 -0
  6. package/dist/js/vendors.js +2001 -0
  7. package/dist/js/ws.js +6863 -0
  8. package/dist/jsenv_server.js +8829 -0
  9. package/package.json +20 -9
  10. package/src/content_negotiation/pick_content_version.js +63 -0
  11. package/src/{fetch_filesystem.js → filesystem/fetch_filesystem.js} +57 -72
  12. package/src/{serve_directory.js → filesystem/serve_directory.js} +1 -2
  13. package/src/interfacing_with_node/from_node_request.js +455 -0
  14. package/src/interfacing_with_node/get_observable_value_type.js +43 -0
  15. package/src/interfacing_with_node/observable.js +91 -75
  16. package/src/interfacing_with_node/observable_from.js +34 -0
  17. package/src/interfacing_with_node/observable_from_file_handle.js +30 -0
  18. package/src/interfacing_with_node/observable_from_node_stream.js +44 -46
  19. package/src/interfacing_with_node/observable_from_node_web_readable_stream.js +32 -0
  20. package/src/interfacing_with_node/observable_from_promise.js +13 -0
  21. package/src/interfacing_with_node/write_node_response.js +80 -35
  22. package/src/internal/colorizeResponseStatus.js +2 -0
  23. package/src/internal/headers_composition.js +42 -15
  24. package/src/internal/multiple-header.js +27 -12
  25. package/src/internal/object_composition.js +2 -2
  26. package/src/internal/response_composition.js +16 -3
  27. package/src/main.js +15 -11
  28. package/src/progressive_response.js +38 -0
  29. package/src/replace_placeholder_in_html.js +12 -0
  30. package/src/request_body_handling.js +22 -0
  31. package/src/router/router.js +878 -0
  32. package/src/server_events.js +431 -0
  33. package/src/service_controller.js +79 -73
  34. package/src/services/autoreload_on_server_restart/client/autoreload_on_server_restart.js +125 -0
  35. package/src/services/autoreload_on_server_restart/jsenv_service_autoreload_on_server_restart.js +45 -0
  36. package/src/services/cors/jsenv_service_cors.js +10 -18
  37. package/src/services/default_body_4xx_5xx/client/4xx.html +65 -0
  38. package/src/services/default_body_4xx_5xx/jsenv_service_default_body_4xx_5xx.js +82 -0
  39. package/src/services/error_handler/client/500.html +17 -0
  40. package/src/services/error_handler/jsenv_service_error_handler.js +47 -28
  41. package/src/services/internal_client_files/jsenv_service_internal_client_files.js +34 -0
  42. package/src/services/open_file/jsenv_service_open_file.js +37 -0
  43. package/src/services/route_inspector/client/code_block.js +215 -0
  44. package/src/services/route_inspector/client/route_inspector.html +1363 -0
  45. package/src/services/route_inspector/jsenv_service_route_inspector.js +39 -0
  46. package/src/start_server.js +575 -607
  47. package/src/web_socket_response.js +53 -0
  48. package/src/from_fetch_response.js +0 -15
  49. package/src/interfacing_with_node/body.js +0 -81
  50. package/src/interfacing_with_node/request_factory.js +0 -125
  51. package/src/readRequestBody.js +0 -32
  52. package/src/server_timing/timing_measure.js +0 -26
  53. package/src/service_composition/path_to_regexp.js +0 -436
  54. package/src/service_composition/routing.js +0 -31
  55. package/src/sse/sse_room.js +0 -313
@@ -1,313 +0,0 @@
1
- import { createLogger } from "@jsenv/humanize";
2
-
3
- import {
4
- createCompositeProducer,
5
- createObservable,
6
- } from "@jsenv/server/src/interfacing_with_node/observable.js";
7
-
8
- // https://www.html5rocks.com/en/tutorials/eventsource/basics/
9
- export const createSSERoom = ({
10
- logLevel,
11
- effect = () => {},
12
- // do not keep process alive because of rooms, something else must keep it alive
13
- keepProcessAlive = false,
14
- keepaliveDuration = 30 * 1000,
15
- retryDuration = 1 * 1000,
16
- historyLength = 1 * 1000,
17
- maxClientAllowed = 100, // max 100 clients accepted
18
- computeEventId = (event, lastEventId) => lastEventId + 1,
19
- welcomeEventEnabled = false,
20
- welcomeEventPublic = false, // decides if welcome event are sent to other clients
21
- } = {}) => {
22
- const logger = createLogger({ logLevel });
23
-
24
- const room = {};
25
- const clients = new Set();
26
- const eventHistory = createEventHistory(historyLength);
27
- // what about previousEventId that keeps growing ?
28
- // we could add some limit
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;
34
-
35
- const join = (request) => {
36
- // should we ensure a given request can join a room only once?
37
-
38
- const lastKnownId =
39
- request.headers["last-event-id"] ||
40
- new URL(request.url).searchParams.get("last-event-id");
41
-
42
- if (clients.size >= maxClientAllowed) {
43
- return {
44
- status: 503,
45
- };
46
- }
47
-
48
- if (!opened) {
49
- return {
50
- status: 204,
51
- };
52
- }
53
-
54
- const sseRoomObservable = createObservable(({ next, complete }) => {
55
- const client = {
56
- next,
57
- complete,
58
- request,
59
- };
60
- if (clients.size === 0) {
61
- const effectReturnValue = effect();
62
- if (typeof effectReturnValue === "function") {
63
- cleanupEffect = effectReturnValue;
64
- } else {
65
- cleanupEffect = CLEANUP_NOOP;
66
- }
67
- }
68
- clients.add(client);
69
- logger.debug(
70
- `A client has joined. Number of client in room: ${clients.size}`,
71
- );
72
-
73
- if (lastKnownId !== undefined) {
74
- const previousEvents = getAllEventSince(lastKnownId);
75
- const eventMissedCount = previousEvents.length;
76
- if (eventMissedCount > 0) {
77
- logger.info(
78
- `send ${eventMissedCount} event missed by client since event with id "${lastKnownId}"`,
79
- );
80
- previousEvents.forEach((previousEvent) => {
81
- next(stringifySourceEvent(previousEvent));
82
- });
83
- }
84
- }
85
-
86
- if (welcomeEventEnabled) {
87
- const welcomeEvent = {
88
- retry: retryDuration,
89
- type: "welcome",
90
- data: new Date().toLocaleTimeString(),
91
- };
92
- addEventToHistory(welcomeEvent);
93
-
94
- // send to everyone
95
- if (welcomeEventPublic) {
96
- sendEventToAllClients(welcomeEvent, {
97
- history: false,
98
- });
99
- }
100
- // send only to this client
101
- else {
102
- next(stringifySourceEvent(welcomeEvent));
103
- }
104
- } else {
105
- const firstEvent = {
106
- retry: retryDuration,
107
- type: "comment",
108
- data: new Date().toLocaleTimeString(),
109
- };
110
- next(stringifySourceEvent(firstEvent));
111
- }
112
-
113
- return () => {
114
- clients.delete(client);
115
- if (clients.size === 0) {
116
- cleanupEffect();
117
- cleanupEffect = CLEANUP_NOOP;
118
- }
119
- logger.debug(
120
- `A client left. Number of client in room: ${clients.size}`,
121
- );
122
- };
123
- });
124
-
125
- const requestSSEObservable = connectRequestAndRoom(
126
- request,
127
- room,
128
- sseRoomObservable,
129
- );
130
-
131
- return {
132
- status: 200,
133
- headers: {
134
- "content-type": "text/event-stream",
135
- "cache-control": "no-store",
136
- "connection": "keep-alive",
137
- },
138
- body: requestSSEObservable,
139
- };
140
- };
141
-
142
- const leave = (request) => {
143
- disconnectRequestFromRoom(request, room);
144
- };
145
-
146
- const addEventToHistory = (event) => {
147
- if (typeof event.id === "undefined") {
148
- event.id = computeEventId(event, previousEventId);
149
- }
150
- previousEventId = event.id;
151
- eventHistory.add(event);
152
- };
153
-
154
- const sendEventToAllClients = (event, { history = true } = {}) => {
155
- if (history) {
156
- addEventToHistory(event);
157
- }
158
- logger.debug(
159
- `send "${event.type}" event to ${clients.size} client in the room`,
160
- );
161
- const eventString = stringifySourceEvent(event);
162
- clients.forEach((client) => {
163
- client.next(eventString);
164
- });
165
- };
166
-
167
- const getAllEventSince = (id) => {
168
- const events = eventHistory.since(id);
169
- if (welcomeEventEnabled && !welcomeEventPublic) {
170
- return events.filter((event) => event.type !== "welcome");
171
- }
172
- return events;
173
- };
174
-
175
- const keepAlive = () => {
176
- // maybe that, when an event occurs, we can delay the keep alive event
177
- logger.debug(
178
- `send keep alive event, number of client listening event source: ${clients.size}`,
179
- );
180
- sendEventToAllClients(
181
- {
182
- type: "comment",
183
- data: new Date().toLocaleTimeString(),
184
- },
185
- { history: false },
186
- );
187
- };
188
-
189
- const open = () => {
190
- if (opened) return;
191
- opened = true;
192
- interval = setInterval(keepAlive, keepaliveDuration);
193
- if (!keepProcessAlive) {
194
- interval.unref();
195
- }
196
- };
197
-
198
- const close = () => {
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
- };
207
-
208
- open();
209
-
210
- Object.assign(room, {
211
- // main api:
212
- // - ability to sendEvent to clients in the room
213
- // - ability to join the room
214
- // - ability to leave the room
215
- sendEventToAllClients,
216
- join,
217
- leave,
218
-
219
- // should rarely be necessary, get information about the room
220
- getAllEventSince,
221
- getRoomClientCount: () => clients.size,
222
-
223
- // should rarely be used
224
- close,
225
- open,
226
- });
227
- return room;
228
- };
229
-
230
- const CLEANUP_NOOP = () => {};
231
-
232
- const requestMap = new Map();
233
-
234
- const connectRequestAndRoom = (request, room, roomObservable) => {
235
- let sseProducer;
236
- let roomObservableMap;
237
- const requestInfo = requestMap.get(request);
238
- if (requestInfo) {
239
- sseProducer = requestInfo.sseProducer;
240
- roomObservableMap = requestInfo.roomObservableMap;
241
- } else {
242
- sseProducer = createCompositeProducer({
243
- cleanup: () => {
244
- requestMap.delete(request);
245
- },
246
- });
247
- roomObservableMap = new Map();
248
- requestMap.set(request, { sseProducer, roomObservableMap });
249
- }
250
-
251
- roomObservableMap.set(room, roomObservable);
252
- sseProducer.addObservable(roomObservable);
253
-
254
- return createObservable(sseProducer);
255
- };
256
-
257
- const disconnectRequestFromRoom = (request, room) => {
258
- const requestInfo = requestMap.get(request);
259
- if (!requestInfo) {
260
- return;
261
- }
262
- const { sseProducer, roomObservableMap } = requestInfo;
263
- const roomObservable = roomObservableMap.get(room);
264
- roomObservableMap.delete(room);
265
- sseProducer.removeObservable(roomObservable);
266
- };
267
-
268
- // https://github.com/dmail-old/project/commit/da7d2c88fc8273850812972885d030a22f9d7448
269
- // https://github.com/dmail-old/project/commit/98b3ae6748d461ac4bd9c48944a551b1128f4459
270
- // https://github.com/dmail-old/http-eventsource/blob/master/lib/event-source.js
271
- // http://html5doctor.com/server-sent-events/
272
- const stringifySourceEvent = ({ data, type = "message", id, retry }) => {
273
- let string = "";
274
-
275
- if (id !== undefined) {
276
- string += `id:${id}\n`;
277
- }
278
-
279
- if (retry) {
280
- string += `retry:${retry}\n`;
281
- }
282
-
283
- if (type !== "message") {
284
- string += `event:${type}\n`;
285
- }
286
-
287
- string += `data:${data}\n\n`;
288
-
289
- return string;
290
- };
291
-
292
- const createEventHistory = (limit) => {
293
- const events = [];
294
-
295
- const add = (data) => {
296
- events.push(data);
297
-
298
- if (events.length >= limit) {
299
- events.shift();
300
- }
301
- };
302
-
303
- const since = (id) => {
304
- const index = events.findIndex((event) => String(event.id) === id);
305
- return index === -1 ? [] : events.slice(index + 1);
306
- };
307
-
308
- const reset = () => {
309
- events.length = 0;
310
- };
311
-
312
- return { add, since, reset };
313
- };