@jsenv/server 15.5.3 → 16.0.1
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.
- package/README.md +79 -86
- package/dist/html/4xx.html +196 -0
- package/dist/html/500.html +17 -0
- package/dist/html/route_inspector.html +2435 -0
- package/dist/js/formidable.js +17 -0
- package/dist/js/vendors.js +2001 -0
- package/dist/js/ws.js +6863 -0
- package/dist/jsenv_server.js +8829 -0
- package/package.json +19 -9
- package/src/content_negotiation/pick_content_version.js +63 -0
- package/src/{fetch_filesystem.js → filesystem/fetch_filesystem.js} +57 -72
- package/src/{serve_directory.js → filesystem/serve_directory.js} +1 -2
- package/src/interfacing_with_node/from_node_request.js +455 -0
- package/src/interfacing_with_node/get_observable_value_type.js +43 -0
- package/src/interfacing_with_node/observable.js +91 -75
- package/src/interfacing_with_node/observable_from.js +34 -0
- package/src/interfacing_with_node/observable_from_file_handle.js +30 -0
- package/src/interfacing_with_node/observable_from_node_stream.js +44 -46
- package/src/interfacing_with_node/observable_from_node_web_readable_stream.js +32 -0
- package/src/interfacing_with_node/observable_from_promise.js +13 -0
- package/src/interfacing_with_node/write_node_response.js +80 -35
- package/src/internal/colorizeResponseStatus.js +2 -0
- package/src/internal/headers_composition.js +42 -15
- package/src/internal/multiple-header.js +27 -12
- package/src/internal/object_composition.js +2 -2
- package/src/internal/response_composition.js +16 -3
- package/src/main.js +15 -11
- package/src/progressive_response.js +38 -0
- package/src/replace_placeholder_in_html.js +12 -0
- package/src/request_body_handling.js +22 -0
- package/src/router/router.js +878 -0
- package/src/server_events.js +431 -0
- package/src/service_controller.js +79 -73
- package/src/services/autoreload_on_server_restart/client/autoreload_on_server_restart.js +125 -0
- package/src/services/autoreload_on_server_restart/jsenv_service_autoreload_on_server_restart.js +45 -0
- package/src/services/cors/jsenv_service_cors.js +10 -18
- package/src/services/default_body_4xx_5xx/client/4xx.html +65 -0
- package/src/services/default_body_4xx_5xx/jsenv_service_default_body_4xx_5xx.js +82 -0
- package/src/services/error_handler/client/500.html +17 -0
- package/src/services/error_handler/jsenv_service_error_handler.js +47 -28
- package/src/services/internal_client_files/jsenv_service_internal_client_files.js +34 -0
- package/src/services/open_file/jsenv_service_open_file.js +37 -0
- package/src/services/route_inspector/client/code_block.js +215 -0
- package/src/services/route_inspector/client/route_inspector.html +1363 -0
- package/src/services/route_inspector/jsenv_service_route_inspector.js +39 -0
- package/src/start_server.js +575 -607
- package/src/web_socket_response.js +53 -0
- package/src/from_fetch_response.js +0 -15
- package/src/interfacing_with_node/body.js +0 -81
- package/src/interfacing_with_node/request_factory.js +0 -125
- package/src/readRequestBody.js +0 -32
- package/src/server_timing/timing_measure.js +0 -26
- package/src/service_composition/path_to_regexp.js +0 -436
- package/src/service_composition/routing.js +0 -31
- package/src/sse/sse_room.js +0 -313
package/src/sse/sse_room.js
DELETED
|
@@ -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
|
-
};
|