@jsenv/server 17.6.5 → 17.6.6
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/dist/jsenv_server.js +67 -17
- package/package.json +1 -1
- package/src/plugins/cors/server_plugin_cors.js +20 -6
- package/src/start_server.js +47 -11
package/dist/jsenv_server.js
CHANGED
|
@@ -7309,11 +7309,14 @@ const TIMING_NOOP = () => {
|
|
|
7309
7309
|
* - `GET /@jsenv/server/*`, serving this package's own client files;
|
|
7310
7310
|
* - `/.internal/alive.websocket` and `/.internal/alive.eventsource`, which a client
|
|
7311
7311
|
* subscribes to in order to reload when the server restarts.
|
|
7312
|
-
* @param {boolean|Object} [params.serverTiming=false] - `true
|
|
7313
|
-
* `server-timing` response headers: the
|
|
7314
|
-
*
|
|
7315
|
-
*
|
|
7316
|
-
*
|
|
7312
|
+
* @param {boolean|Function|Object} [params.serverTiming=false] - `true`, `(request) =>
|
|
7313
|
+
* boolean` or `{ enabled, minDuration }` to send `server-timing` response headers: the
|
|
7314
|
+
* time to start responding, the routing of each plugin, what routes measure with
|
|
7315
|
+
* `helpers.timing` and the `timing` a response hands back. `enabled` can be a function
|
|
7316
|
+
* too: it is asked once per request, so a server can measure for a client it recognizes
|
|
7317
|
+
* (a header, a cookie, an origin) and stay silent for everyone else. `minDuration` (ms)
|
|
7318
|
+
* drops the entries that took less: 0 keeps everything (what a test wants), a human
|
|
7319
|
+
* reading devtools usually wants the sub-millisecond noise gone.
|
|
7317
7320
|
* @param {number} [params.requestWaitingMs=0] - Call `requestWaitingCallback` when a request
|
|
7318
7321
|
* still has no response after that many ms (0 disables).
|
|
7319
7322
|
* @param {Function} [params.requestWaitingCallback] - `({ request, requestWaitingMs })`, logs a warning by default.
|
|
@@ -7670,13 +7673,16 @@ const startServer = async ({
|
|
|
7670
7673
|
sendResponseOperation.addAbortSignal(receiveRequestOperation.signal);
|
|
7671
7674
|
return [receiveRequestOperation, sendResponseOperation];
|
|
7672
7675
|
};
|
|
7673
|
-
const serverTimingMinDuration =
|
|
7674
|
-
serverTiming
|
|
7675
|
-
? serverTiming.minDuration || 0
|
|
7676
|
-
: 0;
|
|
7676
|
+
const { serverTimingEnabled, serverTimingMinDuration } =
|
|
7677
|
+
resolveServerTiming(serverTiming);
|
|
7677
7678
|
const getResponseProperties = async (request) => {
|
|
7679
|
+
// Asked once per request, on what the client sent: an api can then measure
|
|
7680
|
+
// for everyone and disclose to a token, a cookie, an origin — instead of
|
|
7681
|
+
// choosing between measuring nothing and telling every client what it does
|
|
7682
|
+
// internally.
|
|
7683
|
+
const timingEnabled = serverTimingEnabled(request);
|
|
7678
7684
|
const timings = {};
|
|
7679
|
-
const timing =
|
|
7685
|
+
const timing = timingEnabled
|
|
7680
7686
|
? (name) => {
|
|
7681
7687
|
const start = performance.now();
|
|
7682
7688
|
timings[name] = null;
|
|
@@ -7711,7 +7717,7 @@ const startServer = async ({
|
|
|
7711
7717
|
|
|
7712
7718
|
let headersToInject;
|
|
7713
7719
|
const finalizeResponseProperties = (responseProperties) => {
|
|
7714
|
-
if (
|
|
7720
|
+
if (timingEnabled) {
|
|
7715
7721
|
startRespondingTiming.end();
|
|
7716
7722
|
// A response can hand back measures of its own (a `timing` property —
|
|
7717
7723
|
// durations keyed by description, null for a plain marker): they join
|
|
@@ -8233,6 +8239,36 @@ const PROCESS_TEARDOWN_EVENTS_MAP = {
|
|
|
8233
8239
|
exit: STOP_REASON_PROCESS_EXIT,
|
|
8234
8240
|
};
|
|
8235
8241
|
|
|
8242
|
+
const SERVER_TIMING_DISABLED = () => false;
|
|
8243
|
+
const SERVER_TIMING_ENABLED = () => true;
|
|
8244
|
+
|
|
8245
|
+
const resolveServerTiming = (serverTiming) => {
|
|
8246
|
+
if (typeof serverTiming === "function") {
|
|
8247
|
+
return {
|
|
8248
|
+
serverTimingEnabled: serverTiming,
|
|
8249
|
+
serverTimingMinDuration: 0,
|
|
8250
|
+
};
|
|
8251
|
+
}
|
|
8252
|
+
if (serverTiming && typeof serverTiming === "object") {
|
|
8253
|
+
const { enabled = true, minDuration = 0 } = serverTiming;
|
|
8254
|
+
return {
|
|
8255
|
+
serverTimingEnabled:
|
|
8256
|
+
typeof enabled === "function"
|
|
8257
|
+
? enabled
|
|
8258
|
+
: enabled
|
|
8259
|
+
? SERVER_TIMING_ENABLED
|
|
8260
|
+
: SERVER_TIMING_DISABLED,
|
|
8261
|
+
serverTimingMinDuration: minDuration,
|
|
8262
|
+
};
|
|
8263
|
+
}
|
|
8264
|
+
return {
|
|
8265
|
+
serverTimingEnabled: serverTiming
|
|
8266
|
+
? SERVER_TIMING_ENABLED
|
|
8267
|
+
: SERVER_TIMING_DISABLED,
|
|
8268
|
+
serverTimingMinDuration: 0,
|
|
8269
|
+
};
|
|
8270
|
+
};
|
|
8271
|
+
|
|
8236
8272
|
const internalErrorHtmlFileUrl = import.meta.resolve("./client/error_handler/500.html");
|
|
8237
8273
|
let internalErrorHtmlTemplate;
|
|
8238
8274
|
const readInternalErrorHtmlTemplate = () => {
|
|
@@ -8421,6 +8457,9 @@ class ProgressiveResponse {
|
|
|
8421
8457
|
* so here we just need to add the CORS headers to the response
|
|
8422
8458
|
*/
|
|
8423
8459
|
|
|
8460
|
+
const TIMING_ALLOW_ORIGIN_DISABLED = () => false;
|
|
8461
|
+
const TIMING_ALLOW_ORIGIN_ENABLED = () => true;
|
|
8462
|
+
|
|
8424
8463
|
const jsenvAccessControlAllowedHeaders = ["x-requested-with"];
|
|
8425
8464
|
|
|
8426
8465
|
const jsenvAccessControlAllowedMethods = [
|
|
@@ -8453,8 +8492,10 @@ const jsenvAccessControlAllowedMethods = [
|
|
|
8453
8492
|
* @param {boolean} [params.accessControlAllowCredentials=false] - Send
|
|
8454
8493
|
* `access-control-allow-credentials: true`.
|
|
8455
8494
|
* @param {number} [params.accessControlMaxAge=600] - Seconds a browser may cache the preflight.
|
|
8456
|
-
* @param {boolean} [params.timingAllowOrigin=false] - Send `timing-allow-origin`
|
|
8457
|
-
* allowed origin can read resource timing.
|
|
8495
|
+
* @param {boolean|Function} [params.timingAllowOrigin=false] - Send `timing-allow-origin`
|
|
8496
|
+
* so the allowed origin can read resource timing (the `server-timing` header included).
|
|
8497
|
+
* A `(request) => boolean` is asked once per request, so it can be the very test that
|
|
8498
|
+
* decides `startServer({ serverTiming })`.
|
|
8458
8499
|
* @returns {Object|Array} The plugin, or `[]` when CORS stays disabled.
|
|
8459
8500
|
*/
|
|
8460
8501
|
const serverPluginCORS = ({
|
|
@@ -8482,6 +8523,12 @@ const serverPluginCORS = ({
|
|
|
8482
8523
|
const allowedOriginChecker = createAllowedOriginChecker(
|
|
8483
8524
|
accessControlAllowedOrigins,
|
|
8484
8525
|
);
|
|
8526
|
+
const timingAllowOriginEnabled =
|
|
8527
|
+
typeof timingAllowOrigin === "function"
|
|
8528
|
+
? timingAllowOrigin
|
|
8529
|
+
: timingAllowOrigin
|
|
8530
|
+
? TIMING_ALLOW_ORIGIN_ENABLED
|
|
8531
|
+
: TIMING_ALLOW_ORIGIN_DISABLED;
|
|
8485
8532
|
|
|
8486
8533
|
return {
|
|
8487
8534
|
name: "jsenv:cors",
|
|
@@ -8496,7 +8543,7 @@ const serverPluginCORS = ({
|
|
|
8496
8543
|
accessControlAllowRequestHeaders,
|
|
8497
8544
|
accessControlAllowCredentials,
|
|
8498
8545
|
accessControlMaxAge,
|
|
8499
|
-
|
|
8546
|
+
timingAllowOriginEnabled,
|
|
8500
8547
|
});
|
|
8501
8548
|
return {
|
|
8502
8549
|
headers: accessControlHeaders,
|
|
@@ -8550,7 +8597,7 @@ const originPatternToRegExp = (originPattern) => {
|
|
|
8550
8597
|
// https://www.w3.org/TR/cors/
|
|
8551
8598
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
|
8552
8599
|
const generateAccessControlHeaders = ({
|
|
8553
|
-
request
|
|
8600
|
+
request,
|
|
8554
8601
|
allowedOriginChecker,
|
|
8555
8602
|
accessControlAllowRequestOrigin,
|
|
8556
8603
|
accessControlAllowedMethods,
|
|
@@ -8561,8 +8608,9 @@ const generateAccessControlHeaders = ({
|
|
|
8561
8608
|
// by default OPTIONS request can be cache for a long time, it's not going to change soon ?
|
|
8562
8609
|
// we could put a lot here, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
|
|
8563
8610
|
accessControlMaxAge = 600,
|
|
8564
|
-
|
|
8611
|
+
timingAllowOriginEnabled,
|
|
8565
8612
|
} = {}) => {
|
|
8613
|
+
const { headers } = request;
|
|
8566
8614
|
const vary = [];
|
|
8567
8615
|
|
|
8568
8616
|
// Access-Control-Allow-Origin must be a single value (not a list).
|
|
@@ -8626,7 +8674,9 @@ const generateAccessControlHeaders = ({
|
|
|
8626
8674
|
? { "access-control-allow-credentials": true }
|
|
8627
8675
|
: {}),
|
|
8628
8676
|
"access-control-max-age": accessControlMaxAge,
|
|
8629
|
-
...(
|
|
8677
|
+
...(timingAllowOriginEnabled(request)
|
|
8678
|
+
? { "timing-allow-origin": allowOrigin }
|
|
8679
|
+
: {}),
|
|
8630
8680
|
...(vary.length ? { vary: vary.join(", ") } : {}),
|
|
8631
8681
|
};
|
|
8632
8682
|
};
|
package/package.json
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
* so here we just need to add the CORS headers to the response
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
const TIMING_ALLOW_ORIGIN_DISABLED = () => false;
|
|
7
|
+
const TIMING_ALLOW_ORIGIN_ENABLED = () => true;
|
|
8
|
+
|
|
6
9
|
export const jsenvAccessControlAllowedHeaders = ["x-requested-with"];
|
|
7
10
|
|
|
8
11
|
export const jsenvAccessControlAllowedMethods = [
|
|
@@ -35,8 +38,10 @@ export const jsenvAccessControlAllowedMethods = [
|
|
|
35
38
|
* @param {boolean} [params.accessControlAllowCredentials=false] - Send
|
|
36
39
|
* `access-control-allow-credentials: true`.
|
|
37
40
|
* @param {number} [params.accessControlMaxAge=600] - Seconds a browser may cache the preflight.
|
|
38
|
-
* @param {boolean} [params.timingAllowOrigin=false] - Send `timing-allow-origin`
|
|
39
|
-
* allowed origin can read resource timing.
|
|
41
|
+
* @param {boolean|Function} [params.timingAllowOrigin=false] - Send `timing-allow-origin`
|
|
42
|
+
* so the allowed origin can read resource timing (the `server-timing` header included).
|
|
43
|
+
* A `(request) => boolean` is asked once per request, so it can be the very test that
|
|
44
|
+
* decides `startServer({ serverTiming })`.
|
|
40
45
|
* @returns {Object|Array} The plugin, or `[]` when CORS stays disabled.
|
|
41
46
|
*/
|
|
42
47
|
export const serverPluginCORS = ({
|
|
@@ -64,6 +69,12 @@ export const serverPluginCORS = ({
|
|
|
64
69
|
const allowedOriginChecker = createAllowedOriginChecker(
|
|
65
70
|
accessControlAllowedOrigins,
|
|
66
71
|
);
|
|
72
|
+
const timingAllowOriginEnabled =
|
|
73
|
+
typeof timingAllowOrigin === "function"
|
|
74
|
+
? timingAllowOrigin
|
|
75
|
+
: timingAllowOrigin
|
|
76
|
+
? TIMING_ALLOW_ORIGIN_ENABLED
|
|
77
|
+
: TIMING_ALLOW_ORIGIN_DISABLED;
|
|
67
78
|
|
|
68
79
|
return {
|
|
69
80
|
name: "jsenv:cors",
|
|
@@ -78,7 +89,7 @@ export const serverPluginCORS = ({
|
|
|
78
89
|
accessControlAllowRequestHeaders,
|
|
79
90
|
accessControlAllowCredentials,
|
|
80
91
|
accessControlMaxAge,
|
|
81
|
-
|
|
92
|
+
timingAllowOriginEnabled,
|
|
82
93
|
});
|
|
83
94
|
return {
|
|
84
95
|
headers: accessControlHeaders,
|
|
@@ -132,7 +143,7 @@ const originPatternToRegExp = (originPattern) => {
|
|
|
132
143
|
// https://www.w3.org/TR/cors/
|
|
133
144
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
|
134
145
|
const generateAccessControlHeaders = ({
|
|
135
|
-
request
|
|
146
|
+
request,
|
|
136
147
|
allowedOriginChecker,
|
|
137
148
|
accessControlAllowRequestOrigin,
|
|
138
149
|
accessControlAllowedMethods,
|
|
@@ -143,8 +154,9 @@ const generateAccessControlHeaders = ({
|
|
|
143
154
|
// by default OPTIONS request can be cache for a long time, it's not going to change soon ?
|
|
144
155
|
// we could put a lot here, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
|
|
145
156
|
accessControlMaxAge = 600,
|
|
146
|
-
|
|
157
|
+
timingAllowOriginEnabled,
|
|
147
158
|
} = {}) => {
|
|
159
|
+
const { headers } = request;
|
|
148
160
|
const vary = [];
|
|
149
161
|
|
|
150
162
|
// Access-Control-Allow-Origin must be a single value (not a list).
|
|
@@ -208,7 +220,9 @@ const generateAccessControlHeaders = ({
|
|
|
208
220
|
? { "access-control-allow-credentials": true }
|
|
209
221
|
: {}),
|
|
210
222
|
"access-control-max-age": accessControlMaxAge,
|
|
211
|
-
...(
|
|
223
|
+
...(timingAllowOriginEnabled(request)
|
|
224
|
+
? { "timing-allow-origin": allowOrigin }
|
|
225
|
+
: {}),
|
|
212
226
|
...(vary.length ? { vary: vary.join(", ") } : {}),
|
|
213
227
|
};
|
|
214
228
|
};
|
package/src/start_server.js
CHANGED
|
@@ -129,11 +129,14 @@ const TIMING_NOOP = () => {
|
|
|
129
129
|
* - `GET /@jsenv/server/*`, serving this package's own client files;
|
|
130
130
|
* - `/.internal/alive.websocket` and `/.internal/alive.eventsource`, which a client
|
|
131
131
|
* subscribes to in order to reload when the server restarts.
|
|
132
|
-
* @param {boolean|Object} [params.serverTiming=false] - `true
|
|
133
|
-
* `server-timing` response headers: the
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
132
|
+
* @param {boolean|Function|Object} [params.serverTiming=false] - `true`, `(request) =>
|
|
133
|
+
* boolean` or `{ enabled, minDuration }` to send `server-timing` response headers: the
|
|
134
|
+
* time to start responding, the routing of each plugin, what routes measure with
|
|
135
|
+
* `helpers.timing` and the `timing` a response hands back. `enabled` can be a function
|
|
136
|
+
* too: it is asked once per request, so a server can measure for a client it recognizes
|
|
137
|
+
* (a header, a cookie, an origin) and stay silent for everyone else. `minDuration` (ms)
|
|
138
|
+
* drops the entries that took less: 0 keeps everything (what a test wants), a human
|
|
139
|
+
* reading devtools usually wants the sub-millisecond noise gone.
|
|
137
140
|
* @param {number} [params.requestWaitingMs=0] - Call `requestWaitingCallback` when a request
|
|
138
141
|
* still has no response after that many ms (0 disables).
|
|
139
142
|
* @param {Function} [params.requestWaitingCallback] - `({ request, requestWaitingMs })`, logs a warning by default.
|
|
@@ -490,13 +493,16 @@ export const startServer = async ({
|
|
|
490
493
|
sendResponseOperation.addAbortSignal(receiveRequestOperation.signal);
|
|
491
494
|
return [receiveRequestOperation, sendResponseOperation];
|
|
492
495
|
};
|
|
493
|
-
const serverTimingMinDuration =
|
|
494
|
-
serverTiming
|
|
495
|
-
? serverTiming.minDuration || 0
|
|
496
|
-
: 0;
|
|
496
|
+
const { serverTimingEnabled, serverTimingMinDuration } =
|
|
497
|
+
resolveServerTiming(serverTiming);
|
|
497
498
|
const getResponseProperties = async (request) => {
|
|
499
|
+
// Asked once per request, on what the client sent: an api can then measure
|
|
500
|
+
// for everyone and disclose to a token, a cookie, an origin — instead of
|
|
501
|
+
// choosing between measuring nothing and telling every client what it does
|
|
502
|
+
// internally.
|
|
503
|
+
const timingEnabled = serverTimingEnabled(request);
|
|
498
504
|
const timings = {};
|
|
499
|
-
const timing =
|
|
505
|
+
const timing = timingEnabled
|
|
500
506
|
? (name) => {
|
|
501
507
|
const start = performance.now();
|
|
502
508
|
timings[name] = null;
|
|
@@ -531,7 +537,7 @@ export const startServer = async ({
|
|
|
531
537
|
|
|
532
538
|
let headersToInject;
|
|
533
539
|
const finalizeResponseProperties = (responseProperties) => {
|
|
534
|
-
if (
|
|
540
|
+
if (timingEnabled) {
|
|
535
541
|
startRespondingTiming.end();
|
|
536
542
|
// A response can hand back measures of its own (a `timing` property —
|
|
537
543
|
// durations keyed by description, null for a plain marker): they join
|
|
@@ -1057,3 +1063,33 @@ const PROCESS_TEARDOWN_EVENTS_MAP = {
|
|
|
1057
1063
|
beforeExit: STOP_REASON_PROCESS_BEFORE_EXIT,
|
|
1058
1064
|
exit: STOP_REASON_PROCESS_EXIT,
|
|
1059
1065
|
};
|
|
1066
|
+
|
|
1067
|
+
const SERVER_TIMING_DISABLED = () => false;
|
|
1068
|
+
const SERVER_TIMING_ENABLED = () => true;
|
|
1069
|
+
|
|
1070
|
+
const resolveServerTiming = (serverTiming) => {
|
|
1071
|
+
if (typeof serverTiming === "function") {
|
|
1072
|
+
return {
|
|
1073
|
+
serverTimingEnabled: serverTiming,
|
|
1074
|
+
serverTimingMinDuration: 0,
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
if (serverTiming && typeof serverTiming === "object") {
|
|
1078
|
+
const { enabled = true, minDuration = 0 } = serverTiming;
|
|
1079
|
+
return {
|
|
1080
|
+
serverTimingEnabled:
|
|
1081
|
+
typeof enabled === "function"
|
|
1082
|
+
? enabled
|
|
1083
|
+
: enabled
|
|
1084
|
+
? SERVER_TIMING_ENABLED
|
|
1085
|
+
: SERVER_TIMING_DISABLED,
|
|
1086
|
+
serverTimingMinDuration: minDuration,
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
return {
|
|
1090
|
+
serverTimingEnabled: serverTiming
|
|
1091
|
+
? SERVER_TIMING_ENABLED
|
|
1092
|
+
: SERVER_TIMING_DISABLED,
|
|
1093
|
+
serverTimingMinDuration: 0,
|
|
1094
|
+
};
|
|
1095
|
+
};
|