@jsenv/server 15.0.2 → 15.0.4
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 +23 -23
- package/package.json +4 -4
- package/src/content_negotiation/pick_accepted_content.js +13 -13
- package/src/content_negotiation/pick_content_encoding.js +25 -25
- package/src/content_negotiation/pick_content_language.js +35 -34
- package/src/content_negotiation/pick_content_type.js +29 -29
- package/src/fetch_filesystem.js +110 -107
- package/src/from_fetch_response.js +6 -6
- package/src/interfacing_with_node/body.js +22 -22
- package/src/interfacing_with_node/observable.js +80 -80
- package/src/interfacing_with_node/observable_from_node_stream.js +35 -35
- package/src/interfacing_with_node/request_factory.js +26 -26
- package/src/interfacing_with_node/write_node_response.js +82 -82
- package/src/internal/colorizeResponseStatus.js +26 -26
- package/src/internal/convertFileSystemErrorToResponseProperties.js +10 -10
- package/src/internal/dns_resolution.js +7 -7
- package/src/internal/etag.js +11 -11
- package/src/internal/filesystem.js +19 -19
- package/src/internal/headersFromObject.js +8 -8
- package/src/internal/headersFromString.js +11 -11
- package/src/internal/headersToObject.js +5 -5
- package/src/internal/headersToString.js +7 -7
- package/src/internal/headers_composition.js +9 -9
- package/src/internal/hostname_parser.js +9 -9
- package/src/internal/listen.js +47 -47
- package/src/internal/listenClientError.js +10 -10
- package/src/internal/listenEvent.js +5 -5
- package/src/internal/listenRequest.js +8 -8
- package/src/internal/listenServerConnectionError.js +19 -19
- package/src/internal/multiple-header.js +50 -50
- package/src/internal/normalizeHeaderName.js +4 -4
- package/src/internal/normalizeHeaderValue.js +2 -2
- package/src/internal/object_composition.js +41 -39
- package/src/internal/response_composition.js +6 -6
- package/src/internal/server-polyglot.js +42 -42
- package/src/internal/server_ips.js +20 -20
- package/src/internal/trackServerPendingConnections.js +23 -23
- package/src/internal/trackServerPendingRequests.js +28 -28
- package/src/main.js +18 -18
- package/src/readRequestBody.js +15 -15
- package/src/serve_directory.js +16 -16
- package/src/server_timing/timing_header.js +24 -24
- package/src/server_timing/timing_measure.js +14 -14
- package/src/service_composition/path_to_regexp.js +173 -168
- package/src/service_composition/routing.js +12 -12
- package/src/service_controller.js +77 -77
- package/src/services/cors/jsenv_service_cors.js +29 -29
- package/src/services/error_handler/jsenv_service_error_handler.js +18 -18
- package/src/services/request_aliases/jsenv_service_request_aliases.js +20 -20
- package/src/services/response_acceptance_check/jsenv_service_response_acceptance_check.js +16 -16
- package/src/sse/sse_room.js +117 -115
- package/src/start_server.js +328 -328
- package/src/stopReasons.js +9 -9
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { timeStart } from "./server_timing/timing_measure.js"
|
|
1
|
+
import { timeStart } from "./server_timing/timing_measure.js";
|
|
2
2
|
|
|
3
3
|
const HOOK_NAMES = [
|
|
4
4
|
"serverListening",
|
|
@@ -10,136 +10,136 @@ const HOOK_NAMES = [
|
|
|
10
10
|
"injectResponseHeaders",
|
|
11
11
|
"responseReady",
|
|
12
12
|
"serverStopped",
|
|
13
|
-
]
|
|
13
|
+
];
|
|
14
14
|
|
|
15
15
|
export const createServiceController = (services) => {
|
|
16
|
-
const flatServices = flattenAndFilterServices(services)
|
|
17
|
-
const hookGroups = {}
|
|
16
|
+
const flatServices = flattenAndFilterServices(services);
|
|
17
|
+
const hookGroups = {};
|
|
18
18
|
|
|
19
19
|
const addService = (service) => {
|
|
20
20
|
Object.keys(service).forEach((key) => {
|
|
21
|
-
if (key === "name") return
|
|
22
|
-
const isHook = HOOK_NAMES.includes(key)
|
|
21
|
+
if (key === "name") return;
|
|
22
|
+
const isHook = HOOK_NAMES.includes(key);
|
|
23
23
|
if (!isHook) {
|
|
24
24
|
console.warn(
|
|
25
25
|
`Unexpected "${key}" property on "${service.name}" service`,
|
|
26
|
-
)
|
|
26
|
+
);
|
|
27
27
|
}
|
|
28
|
-
const hookName = key
|
|
29
|
-
const hookValue = service[hookName]
|
|
28
|
+
const hookName = key;
|
|
29
|
+
const hookValue = service[hookName];
|
|
30
30
|
if (hookValue) {
|
|
31
|
-
const group = hookGroups[hookName] || (hookGroups[hookName] = [])
|
|
31
|
+
const group = hookGroups[hookName] || (hookGroups[hookName] = []);
|
|
32
32
|
group.push({
|
|
33
33
|
service,
|
|
34
34
|
name: hookName,
|
|
35
35
|
value: hookValue,
|
|
36
|
-
})
|
|
36
|
+
});
|
|
37
37
|
}
|
|
38
|
-
})
|
|
39
|
-
}
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
40
|
flatServices.forEach((service) => {
|
|
41
|
-
addService(service)
|
|
42
|
-
})
|
|
41
|
+
addService(service);
|
|
42
|
+
});
|
|
43
43
|
|
|
44
|
-
let currentService = null
|
|
45
|
-
let currentHookName = null
|
|
44
|
+
let currentService = null;
|
|
45
|
+
let currentHookName = null;
|
|
46
46
|
const callHook = (hook, info, context) => {
|
|
47
|
-
const hookFn = hook.value
|
|
47
|
+
const hookFn = hook.value;
|
|
48
48
|
if (!hookFn) {
|
|
49
|
-
return null
|
|
49
|
+
return null;
|
|
50
50
|
}
|
|
51
|
-
currentService = hook.service
|
|
52
|
-
currentHookName = hook.name
|
|
53
|
-
let timeEnd
|
|
51
|
+
currentService = hook.service;
|
|
52
|
+
currentHookName = hook.name;
|
|
53
|
+
let timeEnd;
|
|
54
54
|
if (context && context.timing) {
|
|
55
55
|
timeEnd = timeStart(
|
|
56
56
|
`${currentService.name.replace("jsenv:", "")}.${currentHookName}`,
|
|
57
|
-
)
|
|
57
|
+
);
|
|
58
58
|
}
|
|
59
|
-
let valueReturned = hookFn(info, context)
|
|
59
|
+
let valueReturned = hookFn(info, context);
|
|
60
60
|
if (context && context.timing) {
|
|
61
|
-
Object.assign(context.timing, timeEnd())
|
|
61
|
+
Object.assign(context.timing, timeEnd());
|
|
62
62
|
}
|
|
63
|
-
currentService = null
|
|
64
|
-
currentHookName = null
|
|
65
|
-
return valueReturned
|
|
66
|
-
}
|
|
63
|
+
currentService = null;
|
|
64
|
+
currentHookName = null;
|
|
65
|
+
return valueReturned;
|
|
66
|
+
};
|
|
67
67
|
const callAsyncHook = async (hook, info, context) => {
|
|
68
|
-
const hookFn = hook.value
|
|
68
|
+
const hookFn = hook.value;
|
|
69
69
|
if (!hookFn) {
|
|
70
|
-
return null
|
|
70
|
+
return null;
|
|
71
71
|
}
|
|
72
|
-
currentService = hook.service
|
|
73
|
-
currentHookName = hook.name
|
|
74
|
-
let timeEnd
|
|
72
|
+
currentService = hook.service;
|
|
73
|
+
currentHookName = hook.name;
|
|
74
|
+
let timeEnd;
|
|
75
75
|
if (context && context.timing) {
|
|
76
76
|
timeEnd = timeStart(
|
|
77
77
|
`${currentService.name.replace("jsenv:", "")}.${currentHookName}`,
|
|
78
|
-
)
|
|
78
|
+
);
|
|
79
79
|
}
|
|
80
|
-
let valueReturned = await hookFn(info, context)
|
|
80
|
+
let valueReturned = await hookFn(info, context);
|
|
81
81
|
if (context && context.timing) {
|
|
82
|
-
Object.assign(context.timing, timeEnd())
|
|
82
|
+
Object.assign(context.timing, timeEnd());
|
|
83
83
|
}
|
|
84
|
-
currentService = null
|
|
85
|
-
currentHookName = null
|
|
86
|
-
return valueReturned
|
|
87
|
-
}
|
|
84
|
+
currentService = null;
|
|
85
|
+
currentHookName = null;
|
|
86
|
+
return valueReturned;
|
|
87
|
+
};
|
|
88
88
|
|
|
89
89
|
const callHooks = (hookName, info, context, callback = () => {}) => {
|
|
90
|
-
const hooks = hookGroups[hookName]
|
|
90
|
+
const hooks = hookGroups[hookName];
|
|
91
91
|
if (hooks) {
|
|
92
92
|
for (const hook of hooks) {
|
|
93
|
-
const returnValue = callHook(hook, info, context)
|
|
93
|
+
const returnValue = callHook(hook, info, context);
|
|
94
94
|
if (returnValue) {
|
|
95
|
-
callback(returnValue)
|
|
95
|
+
callback(returnValue);
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
-
}
|
|
99
|
+
};
|
|
100
100
|
const callHooksUntil = (
|
|
101
101
|
hookName,
|
|
102
102
|
info,
|
|
103
103
|
context,
|
|
104
104
|
until = (returnValue) => returnValue,
|
|
105
105
|
) => {
|
|
106
|
-
const hooks = hookGroups[hookName]
|
|
106
|
+
const hooks = hookGroups[hookName];
|
|
107
107
|
if (hooks) {
|
|
108
108
|
for (const hook of hooks) {
|
|
109
|
-
const returnValue = callHook(hook, info, context)
|
|
110
|
-
const untilReturnValue = until(returnValue)
|
|
109
|
+
const returnValue = callHook(hook, info, context);
|
|
110
|
+
const untilReturnValue = until(returnValue);
|
|
111
111
|
if (untilReturnValue) {
|
|
112
|
-
return untilReturnValue
|
|
112
|
+
return untilReturnValue;
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
-
return null
|
|
117
|
-
}
|
|
116
|
+
return null;
|
|
117
|
+
};
|
|
118
118
|
const callAsyncHooksUntil = (hookName, info, context) => {
|
|
119
|
-
const hooks = hookGroups[hookName]
|
|
119
|
+
const hooks = hookGroups[hookName];
|
|
120
120
|
if (!hooks) {
|
|
121
|
-
return null
|
|
121
|
+
return null;
|
|
122
122
|
}
|
|
123
123
|
if (hooks.length === 0) {
|
|
124
|
-
return null
|
|
124
|
+
return null;
|
|
125
125
|
}
|
|
126
126
|
return new Promise((resolve, reject) => {
|
|
127
127
|
const visit = (index) => {
|
|
128
128
|
if (index >= hooks.length) {
|
|
129
|
-
return resolve()
|
|
129
|
+
return resolve();
|
|
130
130
|
}
|
|
131
|
-
const hook = hooks[index]
|
|
132
|
-
const returnValue = callAsyncHook(hook, info, context)
|
|
131
|
+
const hook = hooks[index];
|
|
132
|
+
const returnValue = callAsyncHook(hook, info, context);
|
|
133
133
|
return Promise.resolve(returnValue).then((output) => {
|
|
134
134
|
if (output) {
|
|
135
|
-
return resolve(output)
|
|
135
|
+
return resolve(output);
|
|
136
136
|
}
|
|
137
|
-
return visit(index + 1)
|
|
138
|
-
}, reject)
|
|
139
|
-
}
|
|
140
|
-
visit(0)
|
|
141
|
-
})
|
|
142
|
-
}
|
|
137
|
+
return visit(index + 1);
|
|
138
|
+
}, reject);
|
|
139
|
+
};
|
|
140
|
+
visit(0);
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
143
|
|
|
144
144
|
return {
|
|
145
145
|
services: flatServices,
|
|
@@ -150,25 +150,25 @@ export const createServiceController = (services) => {
|
|
|
150
150
|
|
|
151
151
|
getCurrentService: () => currentService,
|
|
152
152
|
getCurrentHookName: () => currentHookName,
|
|
153
|
-
}
|
|
154
|
-
}
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
155
|
|
|
156
156
|
const flattenAndFilterServices = (services) => {
|
|
157
|
-
const flatServices = []
|
|
157
|
+
const flatServices = [];
|
|
158
158
|
const visitServiceEntry = (serviceEntry) => {
|
|
159
159
|
if (Array.isArray(serviceEntry)) {
|
|
160
|
-
serviceEntry.forEach((value) => visitServiceEntry(value))
|
|
161
|
-
return
|
|
160
|
+
serviceEntry.forEach((value) => visitServiceEntry(value));
|
|
161
|
+
return;
|
|
162
162
|
}
|
|
163
163
|
if (typeof serviceEntry === "object" && serviceEntry !== null) {
|
|
164
164
|
if (!serviceEntry.name) {
|
|
165
|
-
serviceEntry.name = "anonymous"
|
|
165
|
+
serviceEntry.name = "anonymous";
|
|
166
166
|
}
|
|
167
|
-
flatServices.push(serviceEntry)
|
|
168
|
-
return
|
|
167
|
+
flatServices.push(serviceEntry);
|
|
168
|
+
return;
|
|
169
169
|
}
|
|
170
|
-
throw new Error(`services must be objects, got ${serviceEntry}`)
|
|
171
|
-
}
|
|
172
|
-
services.forEach((serviceEntry) => visitServiceEntry(serviceEntry))
|
|
173
|
-
return flatServices
|
|
174
|
-
}
|
|
170
|
+
throw new Error(`services must be objects, got ${serviceEntry}`);
|
|
171
|
+
};
|
|
172
|
+
services.forEach((serviceEntry) => visitServiceEntry(serviceEntry));
|
|
173
|
+
return flatServices;
|
|
174
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const jsenvAccessControlAllowedHeaders = ["x-requested-with"]
|
|
1
|
+
export const jsenvAccessControlAllowedHeaders = ["x-requested-with"];
|
|
2
2
|
|
|
3
3
|
export const jsenvAccessControlAllowedMethods = [
|
|
4
4
|
"GET",
|
|
@@ -6,7 +6,7 @@ export const jsenvAccessControlAllowedMethods = [
|
|
|
6
6
|
"PUT",
|
|
7
7
|
"DELETE",
|
|
8
8
|
"OPTIONS",
|
|
9
|
-
]
|
|
9
|
+
];
|
|
10
10
|
|
|
11
11
|
export const jsenvServiceCORS = ({
|
|
12
12
|
accessControlAllowedOrigins = [],
|
|
@@ -24,10 +24,10 @@ export const jsenvServiceCORS = ({
|
|
|
24
24
|
// TODO: we should check access control params to throw or warn if we find strange values
|
|
25
25
|
|
|
26
26
|
const corsEnabled =
|
|
27
|
-
accessControlAllowRequestOrigin || accessControlAllowedOrigins.length
|
|
27
|
+
accessControlAllowRequestOrigin || accessControlAllowedOrigins.length;
|
|
28
28
|
|
|
29
29
|
if (!corsEnabled) {
|
|
30
|
-
return []
|
|
30
|
+
return [];
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
return {
|
|
@@ -42,9 +42,9 @@ export const jsenvServiceCORS = ({
|
|
|
42
42
|
headers: {
|
|
43
43
|
"content-length": 0,
|
|
44
44
|
},
|
|
45
|
-
}
|
|
45
|
+
};
|
|
46
46
|
}
|
|
47
|
-
return null
|
|
47
|
+
return null;
|
|
48
48
|
},
|
|
49
49
|
|
|
50
50
|
injectResponseHeaders: (response, { request }) => {
|
|
@@ -59,11 +59,11 @@ export const jsenvServiceCORS = ({
|
|
|
59
59
|
accessControlAllowCredentials,
|
|
60
60
|
accessControlMaxAge,
|
|
61
61
|
timingAllowOrigin,
|
|
62
|
-
})
|
|
63
|
-
return accessControlHeaders
|
|
62
|
+
});
|
|
63
|
+
return accessControlHeaders;
|
|
64
64
|
},
|
|
65
|
-
}
|
|
66
|
-
}
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
67
|
|
|
68
68
|
// https://www.w3.org/TR/cors/
|
|
69
69
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
|
@@ -81,49 +81,49 @@ const generateAccessControlHeaders = ({
|
|
|
81
81
|
accessControlMaxAge = 600,
|
|
82
82
|
timingAllowOrigin,
|
|
83
83
|
} = {}) => {
|
|
84
|
-
const vary = []
|
|
84
|
+
const vary = [];
|
|
85
85
|
|
|
86
|
-
const allowedOriginArray = [...accessControlAllowedOrigins]
|
|
86
|
+
const allowedOriginArray = [...accessControlAllowedOrigins];
|
|
87
87
|
if (accessControlAllowRequestOrigin) {
|
|
88
88
|
if ("origin" in headers && headers.origin !== "null") {
|
|
89
|
-
allowedOriginArray.push(headers.origin)
|
|
90
|
-
vary.push("origin")
|
|
89
|
+
allowedOriginArray.push(headers.origin);
|
|
90
|
+
vary.push("origin");
|
|
91
91
|
} else if ("referer" in headers) {
|
|
92
|
-
allowedOriginArray.push(new URL(headers.referer).origin)
|
|
93
|
-
vary.push("referer")
|
|
92
|
+
allowedOriginArray.push(new URL(headers.referer).origin);
|
|
93
|
+
vary.push("referer");
|
|
94
94
|
} else {
|
|
95
|
-
allowedOriginArray.push("*")
|
|
95
|
+
allowedOriginArray.push("*");
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
const allowedMethodArray = [...accessControlAllowedMethods]
|
|
99
|
+
const allowedMethodArray = [...accessControlAllowedMethods];
|
|
100
100
|
if (
|
|
101
101
|
accessControlAllowRequestMethod &&
|
|
102
102
|
"access-control-request-method" in headers
|
|
103
103
|
) {
|
|
104
|
-
const requestMethodName = headers["access-control-request-method"]
|
|
104
|
+
const requestMethodName = headers["access-control-request-method"];
|
|
105
105
|
if (!allowedMethodArray.includes(requestMethodName)) {
|
|
106
|
-
allowedMethodArray.push(requestMethodName)
|
|
107
|
-
vary.push("access-control-request-method")
|
|
106
|
+
allowedMethodArray.push(requestMethodName);
|
|
107
|
+
vary.push("access-control-request-method");
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
const allowedHeaderArray = [...accessControlAllowedHeaders]
|
|
111
|
+
const allowedHeaderArray = [...accessControlAllowedHeaders];
|
|
112
112
|
if (
|
|
113
113
|
accessControlAllowRequestHeaders &&
|
|
114
114
|
"access-control-request-headers" in headers
|
|
115
115
|
) {
|
|
116
116
|
const requestHeaderNameArray =
|
|
117
|
-
headers["access-control-request-headers"].split(", ")
|
|
117
|
+
headers["access-control-request-headers"].split(", ");
|
|
118
118
|
requestHeaderNameArray.forEach((headerName) => {
|
|
119
|
-
const headerNameLowerCase = headerName.toLowerCase()
|
|
119
|
+
const headerNameLowerCase = headerName.toLowerCase();
|
|
120
120
|
if (!allowedHeaderArray.includes(headerNameLowerCase)) {
|
|
121
|
-
allowedHeaderArray.push(headerNameLowerCase)
|
|
121
|
+
allowedHeaderArray.push(headerNameLowerCase);
|
|
122
122
|
if (!vary.includes("access-control-request-headers")) {
|
|
123
|
-
vary.push("access-control-request-headers")
|
|
123
|
+
vary.push("access-control-request-headers");
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
-
})
|
|
126
|
+
});
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
return {
|
|
@@ -138,5 +138,5 @@ const generateAccessControlHeaders = ({
|
|
|
138
138
|
? { "timing-allow-origin": allowedOriginArray.join(", ") }
|
|
139
139
|
: {}),
|
|
140
140
|
...(vary.length ? { vary: vary.join(", ") } : {}),
|
|
141
|
-
}
|
|
142
|
-
}
|
|
141
|
+
};
|
|
142
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { pickContentType } from "../../content_negotiation/pick_content_type.js"
|
|
1
|
+
import { pickContentType } from "../../content_negotiation/pick_content_type.js";
|
|
2
2
|
|
|
3
3
|
export const jsenvServiceErrorHandler = ({ sendErrorDetails = false } = {}) => {
|
|
4
4
|
return {
|
|
@@ -7,9 +7,9 @@ export const jsenvServiceErrorHandler = ({ sendErrorDetails = false } = {}) => {
|
|
|
7
7
|
const serverInternalErrorIsAPrimitive =
|
|
8
8
|
serverInternalError === null ||
|
|
9
9
|
(typeof serverInternalError !== "object" &&
|
|
10
|
-
typeof serverInternalError !== "function")
|
|
10
|
+
typeof serverInternalError !== "function");
|
|
11
11
|
if (!serverInternalErrorIsAPrimitive && serverInternalError.asResponse) {
|
|
12
|
-
return serverInternalError.asResponse()
|
|
12
|
+
return serverInternalError.asResponse();
|
|
13
13
|
}
|
|
14
14
|
const dataToSend = serverInternalErrorIsAPrimitive
|
|
15
15
|
? {
|
|
@@ -24,13 +24,13 @@ export const jsenvServiceErrorHandler = ({ sendErrorDetails = false } = {}) => {
|
|
|
24
24
|
...serverInternalError,
|
|
25
25
|
}
|
|
26
26
|
: {}),
|
|
27
|
-
}
|
|
27
|
+
};
|
|
28
28
|
|
|
29
29
|
const availableContentTypes = {
|
|
30
30
|
"text/html": () => {
|
|
31
31
|
const renderHtmlForErrorWithoutDetails = () => {
|
|
32
|
-
return `<p>Details not available: to enable them use jsenvServiceErrorHandler({ sendErrorDetails: true }).</p
|
|
33
|
-
}
|
|
32
|
+
return `<p>Details not available: to enable them use jsenvServiceErrorHandler({ sendErrorDetails: true }).</p>`;
|
|
33
|
+
};
|
|
34
34
|
|
|
35
35
|
const renderHtmlForErrorWithDetails = () => {
|
|
36
36
|
if (serverInternalErrorIsAPrimitive) {
|
|
@@ -38,10 +38,10 @@ export const jsenvServiceErrorHandler = ({ sendErrorDetails = false } = {}) => {
|
|
|
38
38
|
serverInternalError,
|
|
39
39
|
null,
|
|
40
40
|
" ",
|
|
41
|
-
)}</pre
|
|
41
|
+
)}</pre>`;
|
|
42
42
|
}
|
|
43
|
-
return `<pre>${serverInternalError.stack}</pre
|
|
44
|
-
}
|
|
43
|
+
return `<pre>${serverInternalError.stack}</pre>`;
|
|
44
|
+
};
|
|
45
45
|
|
|
46
46
|
const body = `<!DOCTYPE html>
|
|
47
47
|
<html>
|
|
@@ -67,7 +67,7 @@ export const jsenvServiceErrorHandler = ({ sendErrorDetails = false } = {}) => {
|
|
|
67
67
|
}
|
|
68
68
|
</details>
|
|
69
69
|
</body>
|
|
70
|
-
</html
|
|
70
|
+
</html>`;
|
|
71
71
|
|
|
72
72
|
return {
|
|
73
73
|
headers: {
|
|
@@ -75,24 +75,24 @@ export const jsenvServiceErrorHandler = ({ sendErrorDetails = false } = {}) => {
|
|
|
75
75
|
"content-length": Buffer.byteLength(body),
|
|
76
76
|
},
|
|
77
77
|
body,
|
|
78
|
-
}
|
|
78
|
+
};
|
|
79
79
|
},
|
|
80
80
|
"application/json": () => {
|
|
81
|
-
const body = JSON.stringify(dataToSend)
|
|
81
|
+
const body = JSON.stringify(dataToSend);
|
|
82
82
|
return {
|
|
83
83
|
headers: {
|
|
84
84
|
"content-type": "application/json",
|
|
85
85
|
"content-length": Buffer.byteLength(body),
|
|
86
86
|
},
|
|
87
87
|
body,
|
|
88
|
-
}
|
|
88
|
+
};
|
|
89
89
|
},
|
|
90
|
-
}
|
|
90
|
+
};
|
|
91
91
|
const bestContentType = pickContentType(
|
|
92
92
|
request,
|
|
93
93
|
Object.keys(availableContentTypes),
|
|
94
|
-
)
|
|
95
|
-
return availableContentTypes[bestContentType || "application/json"]()
|
|
94
|
+
);
|
|
95
|
+
return availableContentTypes[bestContentType || "application/json"]();
|
|
96
96
|
},
|
|
97
|
-
}
|
|
98
|
-
}
|
|
97
|
+
};
|
|
98
|
+
};
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
import { URL_META } from "@jsenv/url-meta"
|
|
1
|
+
import { URL_META } from "@jsenv/url-meta";
|
|
2
2
|
|
|
3
3
|
export const jsenvServiceRequestAliases = (resourceAliases) => {
|
|
4
|
-
const aliases = {}
|
|
4
|
+
const aliases = {};
|
|
5
5
|
Object.keys(resourceAliases).forEach((key) => {
|
|
6
|
-
aliases[asFileUrl(key)] = asFileUrl(resourceAliases[key])
|
|
7
|
-
})
|
|
6
|
+
aliases[asFileUrl(key)] = asFileUrl(resourceAliases[key]);
|
|
7
|
+
});
|
|
8
8
|
return {
|
|
9
9
|
name: "jsenv:request_aliases",
|
|
10
10
|
redirectRequest: (request) => {
|
|
11
|
-
const resourceBeforeAlias = request.resource
|
|
11
|
+
const resourceBeforeAlias = request.resource;
|
|
12
12
|
const urlAfterAliasing = URL_META.applyAliases({
|
|
13
13
|
url: asFileUrl(request.pathname),
|
|
14
14
|
aliases,
|
|
15
|
-
})
|
|
16
|
-
const resourceAfterAlias = urlAfterAliasing.slice("file://".length)
|
|
15
|
+
});
|
|
16
|
+
const resourceAfterAlias = urlAfterAliasing.slice("file://".length);
|
|
17
17
|
if (resourceBeforeAlias === resourceAfterAlias) {
|
|
18
|
-
return null
|
|
18
|
+
return null;
|
|
19
19
|
}
|
|
20
|
-
const resource = replaceResource(resourceBeforeAlias, resourceAfterAlias)
|
|
21
|
-
return { resource }
|
|
20
|
+
const resource = replaceResource(resourceBeforeAlias, resourceAfterAlias);
|
|
21
|
+
return { resource };
|
|
22
22
|
},
|
|
23
|
-
}
|
|
24
|
-
}
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
25
|
|
|
26
|
-
const asFileUrl = (specifier) => new URL(specifier, "file:///").href
|
|
26
|
+
const asFileUrl = (specifier) => new URL(specifier, "file:///").href;
|
|
27
27
|
|
|
28
28
|
const replaceResource = (resourceBeforeAlias, newValue) => {
|
|
29
|
-
const urlObject = new URL(resourceBeforeAlias, "file://")
|
|
30
|
-
const searchSeparatorIndex = newValue.indexOf("?")
|
|
29
|
+
const urlObject = new URL(resourceBeforeAlias, "file://");
|
|
30
|
+
const searchSeparatorIndex = newValue.indexOf("?");
|
|
31
31
|
if (searchSeparatorIndex > -1) {
|
|
32
|
-
return newValue // let new value override search params
|
|
32
|
+
return newValue; // let new value override search params
|
|
33
33
|
}
|
|
34
|
-
urlObject.pathname = newValue
|
|
35
|
-
const resource = `${urlObject.pathname}${urlObject.search}
|
|
36
|
-
return resource
|
|
37
|
-
}
|
|
34
|
+
urlObject.pathname = newValue;
|
|
35
|
+
const resource = `${urlObject.pathname}${urlObject.search}`;
|
|
36
|
+
return resource;
|
|
37
|
+
};
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { pickContentType } from "@jsenv/server/src/content_negotiation/pick_content_type.js"
|
|
2
|
-
import { pickContentLanguage } from "@jsenv/server/src/content_negotiation/pick_content_language.js"
|
|
3
|
-
import { pickContentEncoding } from "@jsenv/server/src/content_negotiation/pick_content_encoding.js"
|
|
1
|
+
import { pickContentType } from "@jsenv/server/src/content_negotiation/pick_content_type.js";
|
|
2
|
+
import { pickContentLanguage } from "@jsenv/server/src/content_negotiation/pick_content_language.js";
|
|
3
|
+
import { pickContentEncoding } from "@jsenv/server/src/content_negotiation/pick_content_encoding.js";
|
|
4
4
|
|
|
5
5
|
export const jsenvServiceResponseAcceptanceCheck = () => {
|
|
6
6
|
return {
|
|
7
7
|
name: "jsenv:response_acceptance_check",
|
|
8
8
|
inspectResponse: (request, { response, warn }) => {
|
|
9
|
-
checkResponseAcceptance(request, response, { warn })
|
|
9
|
+
checkResponseAcceptance(request, response, { warn });
|
|
10
10
|
},
|
|
11
|
-
}
|
|
12
|
-
}
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
13
|
|
|
14
14
|
const checkResponseAcceptance = (request, response, { warn }) => {
|
|
15
|
-
const requestAcceptHeader = request.headers.accept
|
|
16
|
-
const responseContentTypeHeader = response.headers["content-type"]
|
|
15
|
+
const requestAcceptHeader = request.headers.accept;
|
|
16
|
+
const responseContentTypeHeader = response.headers["content-type"];
|
|
17
17
|
if (
|
|
18
18
|
requestAcceptHeader &&
|
|
19
19
|
responseContentTypeHeader &&
|
|
@@ -23,11 +23,11 @@ const checkResponseAcceptance = (request, response, { warn }) => {
|
|
|
23
23
|
--- response content-type header ---
|
|
24
24
|
${responseContentTypeHeader}
|
|
25
25
|
--- request accept header ---
|
|
26
|
-
${requestAcceptHeader}`)
|
|
26
|
+
${requestAcceptHeader}`);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const requestAcceptLanguageHeader = request.headers["accept-language"]
|
|
30
|
-
const responseContentLanguageHeader = response.headers["content-language"]
|
|
29
|
+
const requestAcceptLanguageHeader = request.headers["accept-language"];
|
|
30
|
+
const responseContentLanguageHeader = response.headers["content-language"];
|
|
31
31
|
if (
|
|
32
32
|
requestAcceptLanguageHeader &&
|
|
33
33
|
responseContentLanguageHeader &&
|
|
@@ -37,11 +37,11 @@ ${requestAcceptHeader}`)
|
|
|
37
37
|
--- response content-language header ---
|
|
38
38
|
${responseContentLanguageHeader}
|
|
39
39
|
--- request accept-language header ---
|
|
40
|
-
${requestAcceptLanguageHeader}`)
|
|
40
|
+
${requestAcceptLanguageHeader}`);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const requestAcceptEncodingHeader = request.headers["accept-encoding"]
|
|
44
|
-
const responseContentEncodingHeader = response.headers["content-encoding"]
|
|
43
|
+
const requestAcceptEncodingHeader = request.headers["accept-encoding"];
|
|
44
|
+
const responseContentEncodingHeader = response.headers["content-encoding"];
|
|
45
45
|
if (
|
|
46
46
|
requestAcceptLanguageHeader &&
|
|
47
47
|
responseContentLanguageHeader &&
|
|
@@ -51,6 +51,6 @@ ${requestAcceptLanguageHeader}`)
|
|
|
51
51
|
--- response content-encoding header ---
|
|
52
52
|
${responseContentEncodingHeader}
|
|
53
53
|
--- request accept-encoding header ---
|
|
54
|
-
${requestAcceptEncodingHeader}`)
|
|
54
|
+
${requestAcceptEncodingHeader}`);
|
|
55
55
|
}
|
|
56
|
-
}
|
|
56
|
+
};
|