@jsenv/server 10.2.0 → 12.1.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.
- package/README.md +4 -1
- package/main.js +3 -5
- package/package.json +13 -14
- package/src/internal/request_factory.js +78 -106
- package/src/plugin_request_waiting_check.js +0 -2
- package/src/ressource_aliases/plugin_ressource_aliases.js +55 -0
- package/src/service_composition/routing.js +35 -0
- package/src/service_composition/service_composition.js +7 -41
- package/src/sse/createSSERoom.js +16 -0
- package/src/startServer.js +13 -12
package/README.md
CHANGED
|
@@ -70,7 +70,10 @@ const notFoundService = (request) => {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
const server = await startServer({
|
|
73
|
-
requestToResponse: composeServices(
|
|
73
|
+
requestToResponse: composeServices({
|
|
74
|
+
index: indexService,
|
|
75
|
+
otherwise: notFoundService,
|
|
76
|
+
}),
|
|
74
77
|
})
|
|
75
78
|
|
|
76
79
|
const fetch = await import("node-fetch")
|
package/main.js
CHANGED
|
@@ -5,11 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
export { startServer } from "./src/startServer.js"
|
|
8
|
-
export {
|
|
9
|
-
|
|
10
|
-
composeServicesWithTiming,
|
|
11
|
-
redirectRequest,
|
|
12
|
-
} from "./src/service_composition/service_composition.js"
|
|
8
|
+
export { composeServices } from "./src/service_composition/service_composition.js"
|
|
9
|
+
export { setupRoutes } from "./src/service_composition/routing.js"
|
|
13
10
|
export { jsenvServerInternalErrorToResponse } from "./src/jsenvServerInternalErrorToResponse.js"
|
|
14
11
|
export { readRequestBody } from "./src/readRequestBody.js"
|
|
15
12
|
export { fetchFileSystem } from "./src/fetch_filesystem.js"
|
|
@@ -46,6 +43,7 @@ export { pluginContentNegotiationCheck } from "./src/content_negotiation/plugin_
|
|
|
46
43
|
// others
|
|
47
44
|
export { fetchUrl } from "./src/fetch_url.js"
|
|
48
45
|
export { composeTwoResponses } from "./src/internal/response_composition.js"
|
|
46
|
+
export { pluginRessourceAliases } from "./src/ressource_aliases/plugin_ressource_aliases.js"
|
|
49
47
|
export { pluginRequestWaitingCheck } from "./src/plugin_request_waiting_check.js"
|
|
50
48
|
export { findFreePort } from "./src/internal/listen.js"
|
|
51
49
|
export { urlToContentType } from "./src/urlToContentType.js"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "12.1.0",
|
|
4
4
|
"description": "Write your Node.js server using pure functions",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"url": "https://github.com/jsenv/server"
|
|
14
14
|
},
|
|
15
15
|
"engines": {
|
|
16
|
-
"node": ">=
|
|
16
|
+
"node": ">=16.12.0"
|
|
17
17
|
},
|
|
18
18
|
"publishConfig": {
|
|
19
19
|
"access": "public"
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"./*": "./*"
|
|
27
27
|
},
|
|
28
|
+
"main": "./main.js",
|
|
28
29
|
"files": [
|
|
29
30
|
"/src/",
|
|
30
31
|
"/main.js"
|
|
@@ -48,28 +49,26 @@
|
|
|
48
49
|
"@jsenv/abort": "4.1.1",
|
|
49
50
|
"@jsenv/logger": "4.0.1",
|
|
50
51
|
"@jsenv/url-meta": "6.0.1",
|
|
51
|
-
"node-fetch": "2.6.2"
|
|
52
|
+
"node-fetch": "2.6.2",
|
|
53
|
+
"path-to-regexp": "6.2.0"
|
|
52
54
|
},
|
|
53
55
|
"devDependencies": {
|
|
54
|
-
"@babel/core": "7.16.0",
|
|
55
|
-
"@babel/eslint-parser": "7.16.0",
|
|
56
56
|
"@jsenv/assert": "2.3.2",
|
|
57
|
-
"@jsenv/
|
|
58
|
-
"@jsenv/
|
|
59
|
-
"@jsenv/eslint-config": "16.0.8",
|
|
57
|
+
"@jsenv/core": "23.10.0",
|
|
58
|
+
"@jsenv/eslint-config": "16.0.9",
|
|
60
59
|
"@jsenv/filesystem": "2.5.1",
|
|
61
60
|
"@jsenv/github-release-package": "1.2.3",
|
|
62
|
-
"@jsenv/https-local": "1.0.
|
|
61
|
+
"@jsenv/https-local": "1.0.3",
|
|
63
62
|
"@jsenv/importmap-eslint-resolver": "5.1.2",
|
|
64
|
-
"@jsenv/importmap-node-module": "2.
|
|
63
|
+
"@jsenv/importmap-node-module": "2.7.1",
|
|
65
64
|
"@jsenv/package-publish": "1.6.2",
|
|
66
65
|
"@jsenv/performance-impact": "2.2.1",
|
|
67
66
|
"@jsenv/prettier-check-project": "5.6.1",
|
|
68
|
-
"eslint": "
|
|
69
|
-
"eslint-plugin-import": "2.25.
|
|
67
|
+
"eslint": "8.2.0",
|
|
68
|
+
"eslint-plugin-import": "2.25.3",
|
|
70
69
|
"eventsource": "1.1.0",
|
|
71
|
-
"fastify": "3.
|
|
72
|
-
"fastify-static": "4.
|
|
70
|
+
"fastify": "3.24.0",
|
|
71
|
+
"fastify-static": "4.5.0",
|
|
73
72
|
"playwright": "1.16.3",
|
|
74
73
|
"prettier": "2.4.1"
|
|
75
74
|
}
|
|
@@ -1,116 +1,93 @@
|
|
|
1
|
-
import { urlToMeta, normalizeStructuredMetaMap } from "@jsenv/url-meta"
|
|
2
|
-
|
|
3
1
|
import { nodeStreamToObservable } from "./nodeStreamToObservable.js"
|
|
4
2
|
import { headersFromObject } from "./headersFromObject.js"
|
|
5
3
|
|
|
6
|
-
export const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
4
|
+
export const fromNodeRequest = (nodeRequest, { serverOrigin, signal }) => {
|
|
5
|
+
const headers = headersFromObject(nodeRequest.headers)
|
|
6
|
+
const body = nodeStreamToObservable(nodeRequest)
|
|
7
|
+
|
|
8
|
+
let requestOrigin
|
|
9
|
+
if (nodeRequest.authority) {
|
|
10
|
+
requestOrigin = nodeRequest.connection.encrypted
|
|
11
|
+
? `https://${nodeRequest.authority}`
|
|
12
|
+
: `http://${nodeRequest.authority}`
|
|
13
|
+
} else if (nodeRequest.headers.host) {
|
|
14
|
+
requestOrigin = nodeRequest.connection.encrypted
|
|
15
|
+
? `https://${nodeRequest.headers.host}`
|
|
16
|
+
: `http://${nodeRequest.headers.host}`
|
|
17
|
+
} else {
|
|
18
|
+
requestOrigin = serverOrigin
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
21
|
+
return Object.freeze({
|
|
22
|
+
signal,
|
|
23
|
+
http2: Boolean(nodeRequest.stream),
|
|
24
|
+
origin: requestOrigin,
|
|
25
|
+
...getPropertiesFromRessource({
|
|
26
|
+
ressource: nodeRequest.url,
|
|
27
|
+
baseUrl: requestOrigin,
|
|
28
|
+
}),
|
|
29
|
+
method: nodeRequest.method,
|
|
30
|
+
headers,
|
|
31
|
+
body,
|
|
32
|
+
})
|
|
33
|
+
}
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
export const applyRedirectionToRequest = (
|
|
36
|
+
request,
|
|
37
|
+
{ ressource, pathname, ...rest },
|
|
38
|
+
) => {
|
|
39
|
+
return {
|
|
40
|
+
...request,
|
|
41
|
+
...(ressource
|
|
42
|
+
? getPropertiesFromRessource({
|
|
43
|
+
ressource,
|
|
44
|
+
baseUrl: request.url,
|
|
45
|
+
})
|
|
46
|
+
: pathname
|
|
47
|
+
? getPropertiesFromPathname({
|
|
48
|
+
pathname,
|
|
49
|
+
baseUrl: request.url,
|
|
50
|
+
})
|
|
51
|
+
: {}),
|
|
52
|
+
...rest,
|
|
35
53
|
}
|
|
54
|
+
}
|
|
36
55
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
baseUrl,
|
|
41
|
-
})
|
|
42
|
-
}
|
|
56
|
+
const getPropertiesFromRessource = ({ ressource, baseUrl }) => {
|
|
57
|
+
const urlObject = new URL(ressource, baseUrl)
|
|
58
|
+
let pathname = urlObject.pathname
|
|
43
59
|
|
|
44
60
|
return {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (nodeRequest.authority) {
|
|
51
|
-
requestOrigin = nodeRequest.connection.encrypted
|
|
52
|
-
? `https://${nodeRequest.authority}`
|
|
53
|
-
: `http://${nodeRequest.authority}`
|
|
54
|
-
} else if (nodeRequest.headers.host) {
|
|
55
|
-
requestOrigin = nodeRequest.connection.encrypted
|
|
56
|
-
? `https://${nodeRequest.headers.host}`
|
|
57
|
-
: `http://${nodeRequest.headers.host}`
|
|
58
|
-
} else {
|
|
59
|
-
requestOrigin = serverOrigin
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return Object.freeze({
|
|
63
|
-
signal,
|
|
64
|
-
http2: Boolean(nodeRequest.stream),
|
|
65
|
-
origin: requestOrigin,
|
|
66
|
-
...getPropertiesFromRessource({
|
|
67
|
-
ressource: nodeRequest.url,
|
|
68
|
-
baseUrl: requestOrigin,
|
|
69
|
-
}),
|
|
70
|
-
method: nodeRequest.method,
|
|
71
|
-
headers,
|
|
72
|
-
body,
|
|
73
|
-
})
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
createPushRequest: (request, { signal, pathname, method }) => {
|
|
77
|
-
const pushRequest = Object.freeze({
|
|
78
|
-
...request,
|
|
79
|
-
parent: request,
|
|
80
|
-
signal,
|
|
81
|
-
http2: true,
|
|
82
|
-
...(pathname
|
|
83
|
-
? getPropertiesFromPathname({
|
|
84
|
-
pathname,
|
|
85
|
-
baseUrl: request.url,
|
|
86
|
-
})
|
|
87
|
-
: {}),
|
|
88
|
-
method: method || request.method,
|
|
89
|
-
headers: getHeadersInheritedByPushRequest(request),
|
|
90
|
-
body: undefined,
|
|
91
|
-
})
|
|
61
|
+
url: String(urlObject),
|
|
62
|
+
pathname,
|
|
63
|
+
ressource,
|
|
64
|
+
}
|
|
65
|
+
}
|
|
92
66
|
|
|
93
|
-
|
|
94
|
-
|
|
67
|
+
const getPropertiesFromPathname = ({ pathname, baseUrl }) => {
|
|
68
|
+
return getPropertiesFromRessource({
|
|
69
|
+
ressource: `${pathname}${new URL(baseUrl).search}`,
|
|
70
|
+
baseUrl,
|
|
71
|
+
})
|
|
72
|
+
}
|
|
95
73
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
74
|
+
export const createPushRequest = (request, { signal, pathname, method }) => {
|
|
75
|
+
const pushRequest = Object.freeze({
|
|
76
|
+
...request,
|
|
77
|
+
parent: request,
|
|
78
|
+
signal,
|
|
79
|
+
http2: true,
|
|
80
|
+
...(pathname
|
|
81
|
+
? getPropertiesFromPathname({
|
|
82
|
+
pathname,
|
|
83
|
+
baseUrl: request.url,
|
|
84
|
+
})
|
|
85
|
+
: {}),
|
|
86
|
+
method: method || request.method,
|
|
87
|
+
headers: getHeadersInheritedByPushRequest(request),
|
|
88
|
+
body: undefined,
|
|
89
|
+
})
|
|
90
|
+
return pushRequest
|
|
114
91
|
}
|
|
115
92
|
|
|
116
93
|
const getHeadersInheritedByPushRequest = (request) => {
|
|
@@ -126,8 +103,3 @@ const getHeadersInheritedByPushRequest = (request) => {
|
|
|
126
103
|
|
|
127
104
|
return headersInherited
|
|
128
105
|
}
|
|
129
|
-
|
|
130
|
-
const replaceFilename = (pathname, filename) => {
|
|
131
|
-
const lastSlashIndex = pathname.lastIndexOf("/")
|
|
132
|
-
return `${pathname.slice(0, lastSlashIndex + 1)}${filename}`
|
|
133
|
-
}
|
|
@@ -16,8 +16,6 @@ export const pluginRequestWaitingCheck = ({
|
|
|
16
16
|
} = {}) => {
|
|
17
17
|
return {
|
|
18
18
|
plugin_request_waiting_check: {
|
|
19
|
-
init: () => {},
|
|
20
|
-
|
|
21
19
|
onRequest: (request, { logger }) => {
|
|
22
20
|
const timeout = setTimeout(
|
|
23
21
|
() => requestWaitingCallback({ request, logger, requestWaitingMs }),
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { urlToMeta, normalizeStructuredMetaMap } from "@jsenv/url-meta"
|
|
2
|
+
|
|
3
|
+
export const pluginRessourceAliases = (ressourceAliases) => {
|
|
4
|
+
const ressourceAliasMap = {}
|
|
5
|
+
Object.keys(ressourceAliases).forEach((key) => {
|
|
6
|
+
ressourceAliasMap[ressourceAliases[key]] = key
|
|
7
|
+
})
|
|
8
|
+
const structuredMetaMap = normalizeStructuredMetaMap(
|
|
9
|
+
{ alias: ressourceAliasMap },
|
|
10
|
+
"file:///",
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
const getRessourceAlias = (pathname) => {
|
|
14
|
+
const { alias } = urlToMeta({
|
|
15
|
+
url: `file://${pathname}`,
|
|
16
|
+
structuredMetaMap,
|
|
17
|
+
})
|
|
18
|
+
return alias
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
ressource_aliases: {
|
|
23
|
+
onRequest: (request, { redirectRequest }) => {
|
|
24
|
+
const ressourceAlias = getRessourceAlias(request.pathname)
|
|
25
|
+
if (!ressourceAlias) {
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const urlObjectAfterAlias = replaceUrlRessource(
|
|
30
|
+
request.url,
|
|
31
|
+
ressourceAlias,
|
|
32
|
+
)
|
|
33
|
+
const ressource = `${urlObjectAfterAlias.pathname}${urlObjectAfterAlias.search}`
|
|
34
|
+
redirectRequest({
|
|
35
|
+
ressourceBeforeAlias: request.ressource,
|
|
36
|
+
ressource,
|
|
37
|
+
})
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const replaceUrlRessource = (url, ressource) => {
|
|
44
|
+
const urlObject = new URL(url)
|
|
45
|
+
|
|
46
|
+
const searchSeparatorIndex = ressource.indexOf("?")
|
|
47
|
+
if (searchSeparatorIndex === -1) {
|
|
48
|
+
urlObject.pathname = ressource
|
|
49
|
+
return urlObject
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const pathname = ressource.slice(0, searchSeparatorIndex)
|
|
53
|
+
urlObject.pathname = pathname
|
|
54
|
+
return urlObject
|
|
55
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createRequire } from "node:module"
|
|
2
|
+
|
|
3
|
+
const require = createRequire(import.meta.url)
|
|
4
|
+
|
|
5
|
+
export const setupRoutes = (routes) => {
|
|
6
|
+
const { match } = require("path-to-regexp")
|
|
7
|
+
|
|
8
|
+
const candidates = Object.keys(routes).map((pathPattern) => {
|
|
9
|
+
const applyPatternMatching = match(pathPattern, {
|
|
10
|
+
decode: decodeURIComponent,
|
|
11
|
+
})
|
|
12
|
+
return {
|
|
13
|
+
applyPatternMatching,
|
|
14
|
+
requestHandler: routes[pathPattern],
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
return (request, { pushResponse, redirectRequest }) => {
|
|
19
|
+
let result
|
|
20
|
+
const found = candidates.find((candidate) => {
|
|
21
|
+
result = candidate.applyPatternMatching(request.pathname)
|
|
22
|
+
return Boolean(result)
|
|
23
|
+
})
|
|
24
|
+
if (found) {
|
|
25
|
+
return found.requestHandler(
|
|
26
|
+
{
|
|
27
|
+
...request,
|
|
28
|
+
routeParams: result.params,
|
|
29
|
+
},
|
|
30
|
+
{ pushResponse, redirectRequest },
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -1,39 +1,13 @@
|
|
|
1
1
|
import { timeFunction } from "@jsenv/server/src/server_timing/timing_measure.js"
|
|
2
2
|
import { composeTwoResponses } from "@jsenv/server/src/internal/response_composition.js"
|
|
3
|
+
import { applyRedirectionToRequest } from "@jsenv/server/src/internal/request_factory.js"
|
|
3
4
|
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const composeServices = (...callbacks) => {
|
|
13
|
-
return (request, { pushResponse, requestFactory }) => {
|
|
14
|
-
return firstOperationMatching({
|
|
15
|
-
array: callbacks,
|
|
16
|
-
start: (callback) => callback(request, { pushResponse }),
|
|
17
|
-
predicate: (returnValue) => {
|
|
18
|
-
if (returnValue === null || typeof returnValue !== "object") {
|
|
19
|
-
return false
|
|
20
|
-
}
|
|
21
|
-
const requestRedirection = returnValue[requestRedirectionSymbol]
|
|
22
|
-
if (requestRedirection) {
|
|
23
|
-
request = requestFactory.applyRedirectionToRequest(
|
|
24
|
-
request,
|
|
25
|
-
requestRedirection,
|
|
26
|
-
)
|
|
27
|
-
return false
|
|
28
|
-
}
|
|
29
|
-
return true
|
|
30
|
-
},
|
|
31
|
-
})
|
|
32
|
-
}
|
|
33
|
-
}
|
|
5
|
+
export const composeServices = (namedServices) => {
|
|
6
|
+
return async (request, { pushResponse }) => {
|
|
7
|
+
const redirectRequest = (requestRedirection) => {
|
|
8
|
+
request = applyRedirectionToRequest(request, requestRedirection)
|
|
9
|
+
}
|
|
34
10
|
|
|
35
|
-
export const composeServicesWithTiming = (namedServices) => {
|
|
36
|
-
return async (request, { pushResponse, requestFactory }) => {
|
|
37
11
|
const servicesTiming = {}
|
|
38
12
|
const response = await firstOperationMatching({
|
|
39
13
|
array: Object.keys(namedServices).map((serviceName) => {
|
|
@@ -41,7 +15,7 @@ export const composeServicesWithTiming = (namedServices) => {
|
|
|
41
15
|
}),
|
|
42
16
|
start: async ({ serviceName, serviceFn }) => {
|
|
43
17
|
const [serviceTiming, value] = await timeFunction(serviceName, () =>
|
|
44
|
-
serviceFn(request, { pushResponse }),
|
|
18
|
+
serviceFn(request, { pushResponse, redirectRequest }),
|
|
45
19
|
)
|
|
46
20
|
Object.assign(servicesTiming, serviceTiming)
|
|
47
21
|
return value
|
|
@@ -50,14 +24,6 @@ export const composeServicesWithTiming = (namedServices) => {
|
|
|
50
24
|
if (returnValue === null || typeof returnValue !== "object") {
|
|
51
25
|
return false
|
|
52
26
|
}
|
|
53
|
-
const requestRedirection = returnValue[requestRedirectionSymbol]
|
|
54
|
-
if (requestRedirection) {
|
|
55
|
-
request = requestFactory.applyRedirectionToRequest(
|
|
56
|
-
request,
|
|
57
|
-
requestRedirection,
|
|
58
|
-
)
|
|
59
|
-
return false
|
|
60
|
-
}
|
|
61
27
|
return true
|
|
62
28
|
},
|
|
63
29
|
})
|
package/src/sse/createSSERoom.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
// https://www.html5rocks.com/en/tutorials/eventsource/basics/
|
|
9
9
|
export const createSSERoom = ({
|
|
10
10
|
logLevel,
|
|
11
|
+
effect = () => {},
|
|
11
12
|
// do not keep process alive because of rooms, something else must keep it alive
|
|
12
13
|
keepProcessAlive = false,
|
|
13
14
|
keepaliveDuration = 30 * 1000,
|
|
@@ -29,6 +30,7 @@ export const createSSERoom = ({
|
|
|
29
30
|
let previousEventId = 0
|
|
30
31
|
let opened = false
|
|
31
32
|
let interval
|
|
33
|
+
let cleanupEffect = CLEANUP_NOOP
|
|
32
34
|
|
|
33
35
|
const join = (request) => {
|
|
34
36
|
// should we ensure a given request can join a room only once?
|
|
@@ -56,6 +58,14 @@ export const createSSERoom = ({
|
|
|
56
58
|
next,
|
|
57
59
|
complete,
|
|
58
60
|
}
|
|
61
|
+
if (clients.size === 0) {
|
|
62
|
+
const effectReturnValue = effect()
|
|
63
|
+
if (typeof effectReturnValue === "function") {
|
|
64
|
+
cleanupEffect = effectReturnValue
|
|
65
|
+
} else {
|
|
66
|
+
cleanupEffect = CLEANUP_NOOP
|
|
67
|
+
}
|
|
68
|
+
}
|
|
59
69
|
clients.add(client)
|
|
60
70
|
logger.debug(
|
|
61
71
|
`A client has joined. Number of client in room: ${clients.size}`,
|
|
@@ -101,6 +111,10 @@ export const createSSERoom = ({
|
|
|
101
111
|
|
|
102
112
|
return () => {
|
|
103
113
|
clients.delete(client)
|
|
114
|
+
if (clients.size === 0) {
|
|
115
|
+
cleanupEffect()
|
|
116
|
+
cleanupEffect = CLEANUP_NOOP
|
|
117
|
+
}
|
|
104
118
|
logger.debug(`A client left. Number of client in room: ${clients.size}`)
|
|
105
119
|
}
|
|
106
120
|
})
|
|
@@ -210,6 +224,8 @@ export const createSSERoom = ({
|
|
|
210
224
|
return room
|
|
211
225
|
}
|
|
212
226
|
|
|
227
|
+
const CLEANUP_NOOP = () => {}
|
|
228
|
+
|
|
213
229
|
const requestMap = new Map()
|
|
214
230
|
|
|
215
231
|
const connectRequestAndRoom = (request, room, roomObservable) => {
|
package/src/startServer.js
CHANGED
|
@@ -11,7 +11,11 @@ import {
|
|
|
11
11
|
import { createPolyglotServer } from "./internal/server-polyglot.js"
|
|
12
12
|
import { trackServerPendingConnections } from "./internal/trackServerPendingConnections.js"
|
|
13
13
|
import { trackServerPendingRequests } from "./internal/trackServerPendingRequests.js"
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
fromNodeRequest,
|
|
16
|
+
createPushRequest,
|
|
17
|
+
applyRedirectionToRequest,
|
|
18
|
+
} from "./internal/request_factory.js"
|
|
15
19
|
import { populateNodeResponse } from "./internal/populateNodeResponse.js"
|
|
16
20
|
import { colorizeResponseStatus } from "./internal/colorizeResponseStatus.js"
|
|
17
21
|
import { getServerOrigins } from "./internal/getServerOrigins.js"
|
|
@@ -56,7 +60,6 @@ export const startServer = async ({
|
|
|
56
60
|
stopOnInternalError = false,
|
|
57
61
|
keepProcessAlive = true,
|
|
58
62
|
requestToResponse = () => null,
|
|
59
|
-
filenameAliases = {},
|
|
60
63
|
plugins = {},
|
|
61
64
|
|
|
62
65
|
sendServerInternalErrorDetails = false,
|
|
@@ -226,11 +229,6 @@ export const startServer = async ({
|
|
|
226
229
|
})
|
|
227
230
|
})
|
|
228
231
|
|
|
229
|
-
const requestFactory = createRequestFactory({
|
|
230
|
-
serverOrigin,
|
|
231
|
-
filenameAliases,
|
|
232
|
-
})
|
|
233
|
-
|
|
234
232
|
const requestCallback = async (nodeRequest, nodeResponse) => {
|
|
235
233
|
if (!nagle) {
|
|
236
234
|
nodeRequest.connection.setNoDelay(true)
|
|
@@ -268,7 +266,8 @@ export const startServer = async ({
|
|
|
268
266
|
return stopCallbackList.add(abort)
|
|
269
267
|
})
|
|
270
268
|
|
|
271
|
-
const request =
|
|
269
|
+
const request = fromNodeRequest(nodeRequest, {
|
|
270
|
+
serverOrigin,
|
|
272
271
|
signal: receiveRequestOperation.signal,
|
|
273
272
|
})
|
|
274
273
|
|
|
@@ -414,7 +413,7 @@ export const startServer = async ({
|
|
|
414
413
|
pushResponseOperation.addAbortSignal(signal)
|
|
415
414
|
pushResponseOperation.addAbortSignal(abortController.signal)
|
|
416
415
|
|
|
417
|
-
const pushRequest =
|
|
416
|
+
const pushRequest = createPushRequest(request, {
|
|
418
417
|
signal: pushResponseOperation.signal,
|
|
419
418
|
pathname: path,
|
|
420
419
|
method,
|
|
@@ -476,12 +475,17 @@ export const startServer = async ({
|
|
|
476
475
|
const shortcircuitResponse = (value) => {
|
|
477
476
|
responseFromShortcircuit = value
|
|
478
477
|
}
|
|
478
|
+
const redirectRequest = (requestRedirection) => {
|
|
479
|
+
request = applyRedirectionToRequest(request, requestRedirection)
|
|
480
|
+
}
|
|
479
481
|
Object.keys(plugins).forEach((pluginName) => {
|
|
480
482
|
const { onRequest } = plugins[pluginName]
|
|
481
483
|
if (onRequest) {
|
|
482
484
|
const returnValue = onRequest(request, {
|
|
483
485
|
warn,
|
|
486
|
+
logger,
|
|
484
487
|
shortcircuitResponse,
|
|
488
|
+
redirectRequest,
|
|
485
489
|
})
|
|
486
490
|
if (returnValue) {
|
|
487
491
|
requestPlugins.push(returnValue)
|
|
@@ -504,7 +508,6 @@ export const startServer = async ({
|
|
|
504
508
|
},
|
|
505
509
|
)
|
|
506
510
|
},
|
|
507
|
-
requestFactory,
|
|
508
511
|
})
|
|
509
512
|
|
|
510
513
|
if (aborted) {
|
|
@@ -727,12 +730,10 @@ const generateResponseDescription = async ({
|
|
|
727
730
|
requestToResponse,
|
|
728
731
|
request,
|
|
729
732
|
pushResponse,
|
|
730
|
-
requestFactory,
|
|
731
733
|
}) => {
|
|
732
734
|
try {
|
|
733
735
|
const returnValue = await requestToResponse(request, {
|
|
734
736
|
pushResponse,
|
|
735
|
-
requestFactory,
|
|
736
737
|
})
|
|
737
738
|
return {
|
|
738
739
|
responseProperties: returnValue,
|