@jsenv/server 14.0.0 → 14.1.2
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 +2 -2
- package/package.json +3 -3
- package/src/fetch_filesystem.js +2 -2
- package/src/internal/nodeStreamToObservable.js +1 -1
- package/src/internal/request_factory.js +13 -13
- package/src/main.js +1 -1
- package/src/services/request_aliases/jsenv_service_request_aliases.js +37 -0
- package/src/sse/sse_room.js +1 -3
- package/src/start_server.js +4 -4
- package/src/services/ressource_aliases/jsenv_service_ressource_aliases.js +0 -40
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ const server = await startServer({
|
|
|
43
43
|
{
|
|
44
44
|
name: "index",
|
|
45
45
|
handleRequest: (request) => {
|
|
46
|
-
if (request.
|
|
46
|
+
if (request.resource === "/") {
|
|
47
47
|
return { status: 200 }
|
|
48
48
|
}
|
|
49
49
|
return null
|
|
@@ -104,7 +104,7 @@ await startServer({
|
|
|
104
104
|
services: [
|
|
105
105
|
{
|
|
106
106
|
handleRequest: async (request) => {
|
|
107
|
-
const fileUrl = new URL(request.
|
|
107
|
+
const fileUrl = new URL(request.resource.slice(1), import.meta.url)
|
|
108
108
|
const response = await fetchFileSystem(fileUrl, request)
|
|
109
109
|
return response
|
|
110
110
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/server",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.1.2",
|
|
4
4
|
"description": "Write your Node.js server using pure functions",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"test": "node --conditions=development ./scripts/test.mjs"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@jsenv/abort": "4.2.
|
|
38
|
-
"@jsenv/log": "3.
|
|
37
|
+
"@jsenv/abort": "4.2.4",
|
|
38
|
+
"@jsenv/log": "3.2.0",
|
|
39
39
|
"@jsenv/url-meta": "7.0.0",
|
|
40
40
|
"@jsenv/utils": "2.0.1",
|
|
41
41
|
"ws": "8.8.1"
|
package/src/fetch_filesystem.js
CHANGED
|
@@ -64,7 +64,7 @@ export const fetchFileSystem = async (
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
// here you might be tempted to add || cacheControl === 'no-cache'
|
|
67
|
-
// but no-cache means
|
|
67
|
+
// but no-cache means resource can be cache but must be revalidated (yeah naming is strange)
|
|
68
68
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Cacheability
|
|
69
69
|
if (cacheControl === "no-store") {
|
|
70
70
|
if (etagEnabled) {
|
|
@@ -206,7 +206,7 @@ const getClientCacheResponse = async ({
|
|
|
206
206
|
sourceUrl,
|
|
207
207
|
}) => {
|
|
208
208
|
// here you might be tempted to add || headers["cache-control"] === "no-cache"
|
|
209
|
-
// but no-cache means
|
|
209
|
+
// but no-cache means resource can be cache but must be revalidated (yeah naming is strange)
|
|
210
210
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Cacheability
|
|
211
211
|
|
|
212
212
|
if (
|
|
@@ -38,7 +38,7 @@ export const nodeStreamToObservable = (nodeStream) => {
|
|
|
38
38
|
// used in the next ${readableStreamLifetimeInSeconds} otherwise destroys it
|
|
39
39
|
const timeout = setTimeout(() => {
|
|
40
40
|
process.emitWarning(
|
|
41
|
-
`Readable stream not used after ${readableStreamLifetimeInSeconds} seconds. It will be destroyed to release
|
|
41
|
+
`Readable stream not used after ${readableStreamLifetimeInSeconds} seconds. It will be destroyed to release resources`,
|
|
42
42
|
{
|
|
43
43
|
CODE: "READABLE_STREAM_TIMEOUT",
|
|
44
44
|
// url is for http client request
|
|
@@ -24,8 +24,8 @@ export const fromNodeRequest = (nodeRequest, { serverOrigin, signal }) => {
|
|
|
24
24
|
signal,
|
|
25
25
|
http2: Boolean(nodeRequest.stream),
|
|
26
26
|
origin: requestOrigin,
|
|
27
|
-
...
|
|
28
|
-
|
|
27
|
+
...getPropertiesFromResource({
|
|
28
|
+
resource: nodeRequest.url,
|
|
29
29
|
baseUrl: requestOrigin,
|
|
30
30
|
}),
|
|
31
31
|
method: nodeRequest.method,
|
|
@@ -36,13 +36,13 @@ export const fromNodeRequest = (nodeRequest, { serverOrigin, signal }) => {
|
|
|
36
36
|
|
|
37
37
|
export const applyRedirectionToRequest = (
|
|
38
38
|
request,
|
|
39
|
-
{
|
|
39
|
+
{ resource, pathname, ...rest },
|
|
40
40
|
) => {
|
|
41
41
|
return {
|
|
42
42
|
...request,
|
|
43
|
-
...(
|
|
44
|
-
?
|
|
45
|
-
|
|
43
|
+
...(resource
|
|
44
|
+
? getPropertiesFromResource({
|
|
45
|
+
resource,
|
|
46
46
|
baseUrl: request.url,
|
|
47
47
|
})
|
|
48
48
|
: pathname
|
|
@@ -55,20 +55,20 @@ export const applyRedirectionToRequest = (
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const
|
|
59
|
-
const urlObject = new URL(
|
|
58
|
+
const getPropertiesFromResource = ({ resource, baseUrl }) => {
|
|
59
|
+
const urlObject = new URL(resource, baseUrl)
|
|
60
60
|
let pathname = urlObject.pathname
|
|
61
61
|
|
|
62
62
|
return {
|
|
63
63
|
url: String(urlObject),
|
|
64
64
|
pathname,
|
|
65
|
-
|
|
65
|
+
resource,
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
const getPropertiesFromPathname = ({ pathname, baseUrl }) => {
|
|
70
|
-
return
|
|
71
|
-
|
|
70
|
+
return getPropertiesFromResource({
|
|
71
|
+
resource: `${pathname}${new URL(baseUrl).search}`,
|
|
72
72
|
baseUrl,
|
|
73
73
|
})
|
|
74
74
|
}
|
|
@@ -95,12 +95,12 @@ export const createPushRequest = (request, { signal, pathname, method }) => {
|
|
|
95
95
|
const getHeadersInheritedByPushRequest = (request) => {
|
|
96
96
|
const headersInherited = { ...request.headers }
|
|
97
97
|
// mtime sent by the client in request headers concerns the main request
|
|
98
|
-
// Time remains valid for request to other
|
|
98
|
+
// Time remains valid for request to other resources so we keep it
|
|
99
99
|
// in child requests
|
|
100
100
|
// delete childHeaders["if-modified-since"]
|
|
101
101
|
|
|
102
102
|
// eTag sent by the client in request headers concerns the main request
|
|
103
|
-
// A request made to an other
|
|
103
|
+
// A request made to an other resource must not inherit the eTag
|
|
104
104
|
delete headersInherited["if-none-match"]
|
|
105
105
|
|
|
106
106
|
return headersInherited
|
package/src/main.js
CHANGED
|
@@ -42,5 +42,5 @@ export { jsenvServiceResponseAcceptanceCheck } from "./services/response_accepta
|
|
|
42
42
|
export { serveDirectory } from "./serve_directory.js"
|
|
43
43
|
export { fromFetchResponse } from "./from_fetch_response.js"
|
|
44
44
|
export { composeTwoResponses } from "./internal/response_composition.js"
|
|
45
|
-
export {
|
|
45
|
+
export { jsenvServiceRequestAliases } from "./services/request_aliases/jsenv_service_request_aliases.js"
|
|
46
46
|
export { findFreePort } from "./internal/listen.js"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { URL_META } from "@jsenv/url-meta"
|
|
2
|
+
|
|
3
|
+
export const jsenvServiceRequestAliases = (resourceAliases) => {
|
|
4
|
+
const aliases = {}
|
|
5
|
+
Object.keys(resourceAliases).forEach((key) => {
|
|
6
|
+
aliases[asFileUrl(key)] = asFileUrl(resourceAliases[key])
|
|
7
|
+
})
|
|
8
|
+
return {
|
|
9
|
+
name: "jsenv:request_aliases",
|
|
10
|
+
redirectRequest: (request) => {
|
|
11
|
+
const resourceBeforeAlias = request.resource
|
|
12
|
+
const urlAfterAliasing = URL_META.applyAliases({
|
|
13
|
+
url: asFileUrl(request.pathname),
|
|
14
|
+
aliases,
|
|
15
|
+
})
|
|
16
|
+
const resourceAfterAlias = urlAfterAliasing.slice("file://".length)
|
|
17
|
+
if (resourceBeforeAlias === resourceAfterAlias) {
|
|
18
|
+
return null
|
|
19
|
+
}
|
|
20
|
+
const resource = replaceResource(resourceBeforeAlias, resourceAfterAlias)
|
|
21
|
+
return { resource }
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const asFileUrl = (specifier) => new URL(specifier, "file:///").href
|
|
27
|
+
|
|
28
|
+
const replaceResource = (resourceBeforeAlias, newValue) => {
|
|
29
|
+
const urlObject = new URL(resourceBeforeAlias, "file://")
|
|
30
|
+
const searchSeparatorIndex = newValue.indexOf("?")
|
|
31
|
+
if (searchSeparatorIndex > -1) {
|
|
32
|
+
return newValue // let new value override search params
|
|
33
|
+
}
|
|
34
|
+
urlObject.pathname = newValue
|
|
35
|
+
const resource = `${urlObject.pathname}${urlObject.search}`
|
|
36
|
+
return resource
|
|
37
|
+
}
|
package/src/sse/sse_room.js
CHANGED
|
@@ -37,9 +37,7 @@ export const createSSERoom = ({
|
|
|
37
37
|
|
|
38
38
|
const lastKnownId =
|
|
39
39
|
request.headers["last-event-id"] ||
|
|
40
|
-
new URL(request.
|
|
41
|
-
"last-event-id",
|
|
42
|
-
)
|
|
40
|
+
new URL(request.url).searchParams.get("last-event-id")
|
|
43
41
|
|
|
44
42
|
if (clients.size >= maxClientAllowed) {
|
|
45
43
|
return {
|
package/src/start_server.js
CHANGED
|
@@ -81,7 +81,7 @@ export const startServer = async ({
|
|
|
81
81
|
createDetailedMessage(
|
|
82
82
|
`still no response found for request after ${requestWaitingMs} ms`,
|
|
83
83
|
{
|
|
84
|
-
"request url":
|
|
84
|
+
"request url": request.url,
|
|
85
85
|
"request headers": JSON.stringify(request.headers, null, " "),
|
|
86
86
|
},
|
|
87
87
|
),
|
|
@@ -467,7 +467,7 @@ export const startServer = async ({
|
|
|
467
467
|
addRequestLog(requestNode, {
|
|
468
468
|
type: "error",
|
|
469
469
|
value: createDetailedMessage(
|
|
470
|
-
`An error occured while pushing a stream to the response for ${request.
|
|
470
|
+
`An error occured while pushing a stream to the response for ${request.resource}`,
|
|
471
471
|
{
|
|
472
472
|
"error stack": e.stack,
|
|
473
473
|
},
|
|
@@ -572,8 +572,8 @@ export const startServer = async ({
|
|
|
572
572
|
addRequestLog(requestNode, {
|
|
573
573
|
type: "info",
|
|
574
574
|
value: request.parent
|
|
575
|
-
? `Push ${request.
|
|
576
|
-
: `${request.method} ${request.
|
|
575
|
+
? `Push ${request.resource}`
|
|
576
|
+
: `${request.method} ${request.url}`,
|
|
577
577
|
})
|
|
578
578
|
const warn = (value) => {
|
|
579
579
|
addRequestLog(requestNode, {
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { URL_META } from "@jsenv/url-meta"
|
|
2
|
-
|
|
3
|
-
export const jsenvServiceRessourceAliases = (ressourceAliases) => {
|
|
4
|
-
const aliases = {}
|
|
5
|
-
Object.keys(ressourceAliases).forEach((key) => {
|
|
6
|
-
aliases[asFileUrl(key)] = asFileUrl(ressourceAliases[key])
|
|
7
|
-
})
|
|
8
|
-
return {
|
|
9
|
-
name: "jsenv:ressource_aliases",
|
|
10
|
-
redirectRequest: (request) => {
|
|
11
|
-
const ressourceBeforeAlias = request.ressource
|
|
12
|
-
const urlAfterAliasing = URL_META.applyAliases({
|
|
13
|
-
url: asFileUrl(request.pathname),
|
|
14
|
-
aliases,
|
|
15
|
-
})
|
|
16
|
-
const ressourceAfterAlias = urlAfterAliasing.slice("file://".length)
|
|
17
|
-
if (ressourceBeforeAlias === ressourceAfterAlias) {
|
|
18
|
-
return null
|
|
19
|
-
}
|
|
20
|
-
const ressource = replaceRessource(
|
|
21
|
-
ressourceBeforeAlias,
|
|
22
|
-
ressourceAfterAlias,
|
|
23
|
-
)
|
|
24
|
-
return { ressource }
|
|
25
|
-
},
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const asFileUrl = (specifier) => new URL(specifier, "file:///").href
|
|
30
|
-
|
|
31
|
-
const replaceRessource = (ressourceBeforeAlias, newValue) => {
|
|
32
|
-
const urlObject = new URL(ressourceBeforeAlias, "file://")
|
|
33
|
-
const searchSeparatorIndex = newValue.indexOf("?")
|
|
34
|
-
if (searchSeparatorIndex > -1) {
|
|
35
|
-
return newValue // let new value override search params
|
|
36
|
-
}
|
|
37
|
-
urlObject.pathname = newValue
|
|
38
|
-
const ressource = `${urlObject.pathname}${urlObject.search}`
|
|
39
|
-
return ressource
|
|
40
|
-
}
|