@jsenv/server 16.4.0 → 17.0.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/dist/jsenv_server.js +3384 -3214
- package/index.js +12 -9
- package/package.json +1 -1
- package/src/{services/autoreload_on_server_restart/jsenv_service_autoreload_on_server_restart.js → plugins/autoreload_on_server_restart/server_plugin_autoreload_on_server_restart.js} +1 -1
- package/src/{services/cors/jsenv_service_cors.js → plugins/cors/server_plugin_cors.js} +1 -1
- package/src/{services → plugins}/default_body_4xx_5xx/client/4xx.html +1 -1
- package/src/{services/default_body_4xx_5xx/jsenv_service_default_body_4xx_5xx.js → plugins/default_body_4xx_5xx/server_plugin_default_body_4xx_5xx.js} +2 -1
- package/src/{services/error_handler/jsenv_service_error_handler.js → plugins/error_handler/server_plugin_error_handler.js} +1 -1
- package/src/{services/filesystem/jsenv_service_static_files.js → plugins/filesystem/server_plugin_static_files.js} +2 -2
- package/src/{services/internal_client_files/jsenv_service_internal_client_files.js → plugins/internal_client_files/server_plugin_internal_client_files.js} +1 -1
- package/src/{services/open_file/jsenv_service_open_file.js → plugins/open_file/server_plugin_open_file.js} +1 -1
- package/src/{services/request_aliases/jsenv_service_request_aliases.js → plugins/request_aliases/server_plugin_request_aliases.js} +1 -1
- package/src/{services/response_acceptance_check/jsenv_service_response_acceptance_check.js → plugins/response_acceptance_check/server_plugin_response_acceptance_check.js} +1 -1
- package/src/{services → plugins}/route_inspector/client/route_inspector.html +2 -2
- package/src/{services/route_inspector/jsenv_service_route_inspector.js → plugins/route_inspector/server_plugin_route_inspector.js} +1 -1
- package/src/plugins/server_plugins_controller.js +21 -0
- package/src/plugins_controller.js +341 -0
- package/src/router/router.js +8 -5
- package/src/start_server.js +34 -38
- package/src/service_controller.js +0 -193
- /package/src/{services → plugins}/autoreload_on_server_restart/client/autoreload_on_server_restart.js +0 -0
- /package/src/{services → plugins}/error_handler/client/500.html +0 -0
- /package/src/{services → plugins}/filesystem/etag.js +0 -0
- /package/src/{services → plugins}/filesystem/fetch_filesystem.js +0 -0
- /package/src/{services → plugins}/filesystem/filesystem_error_to_response.js +0 -0
- /package/src/{services → plugins}/filesystem/filesystem_path_and_url.js +0 -0
- /package/src/{services → plugins}/filesystem/serve_directory.js +0 -0
- /package/src/{services → plugins}/route_inspector/client/code_block.js +0 -0
- /package/src/{stopReasons.js → stop_reasons.js} +0 -0
package/index.js
CHANGED
|
@@ -16,8 +16,8 @@ export {
|
|
|
16
16
|
STOP_REASON_PROCESS_BEFORE_EXIT,
|
|
17
17
|
STOP_REASON_PROCESS_EXIT,
|
|
18
18
|
STOP_REASON_NOT_SPECIFIED,
|
|
19
|
-
} from "./src/
|
|
20
|
-
export {
|
|
19
|
+
} from "./src/stop_reasons.js";
|
|
20
|
+
export { serverPluginErrorHandler } from "./src/plugins/error_handler/server_plugin_error_handler.js";
|
|
21
21
|
|
|
22
22
|
// webSocket
|
|
23
23
|
export { WebSocketResponse } from "./src/web_socket_response.js";
|
|
@@ -28,26 +28,29 @@ export { ServerEvents, LazyServerEvents } from "./src/server_events.js";
|
|
|
28
28
|
|
|
29
29
|
// CORS
|
|
30
30
|
export {
|
|
31
|
-
|
|
31
|
+
serverPluginCORS,
|
|
32
32
|
jsenvAccessControlAllowedHeaders,
|
|
33
33
|
jsenvAccessControlAllowedMethods,
|
|
34
|
-
} from "./src/
|
|
34
|
+
} from "./src/plugins/cors/server_plugin_cors.js";
|
|
35
35
|
|
|
36
36
|
// filesystem
|
|
37
37
|
export {
|
|
38
38
|
createFileSystemFetch,
|
|
39
39
|
fetchFileSystem,
|
|
40
|
-
} from "./src/
|
|
41
|
-
export { serveDirectory } from "./src/
|
|
42
|
-
export {
|
|
40
|
+
} from "./src/plugins/filesystem/fetch_filesystem.js";
|
|
41
|
+
export { serveDirectory } from "./src/plugins/filesystem/serve_directory.js";
|
|
42
|
+
export { serverPluginStaticFiles } from "./src/plugins/filesystem/server_plugin_static_files.js";
|
|
43
43
|
|
|
44
44
|
// content-negotiation
|
|
45
45
|
export { pickContentType } from "./src/content_negotiation/pick_content_type.js";
|
|
46
46
|
export { pickContentEncoding } from "./src/content_negotiation/pick_content_encoding.js";
|
|
47
47
|
export { pickContentLanguage } from "./src/content_negotiation/pick_content_language.js";
|
|
48
|
-
export {
|
|
48
|
+
export { serverPluginResponseAcceptanceCheck } from "./src/plugins/response_acceptance_check/server_plugin_response_acceptance_check.js";
|
|
49
49
|
|
|
50
50
|
// others
|
|
51
51
|
export { composeTwoResponses } from "./src/internal/response_composition.js";
|
|
52
|
-
export {
|
|
52
|
+
export { serverPluginRequestAliases } from "./src/plugins/request_aliases/server_plugin_request_aliases.js";
|
|
53
53
|
export { findFreePort } from "./src/internal/listen.js";
|
|
54
|
+
|
|
55
|
+
// internal (used by @jsenv/core)
|
|
56
|
+
export { createPluginsController } from "./src/plugins_controller.js";
|
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@ export const jsenvAccessControlAllowedMethods = [
|
|
|
13
13
|
"OPTIONS",
|
|
14
14
|
];
|
|
15
15
|
|
|
16
|
-
export const
|
|
16
|
+
export const serverPluginCORS = ({
|
|
17
17
|
accessControlAllowedOrigins = [],
|
|
18
18
|
accessControlAllowedMethods = jsenvAccessControlAllowedMethods,
|
|
19
19
|
accessControlAllowedHeaders = jsenvAccessControlAllowedHeaders,
|
|
@@ -60,6 +60,6 @@
|
|
|
60
60
|
<p class="error_message">${statusMessage}</p>
|
|
61
61
|
<a href="/" jsenv-ignore class="home_link">Return to Home</a>
|
|
62
62
|
</div>
|
|
63
|
-
<script src="/@jsenv/server/src/
|
|
63
|
+
<script src="/@jsenv/server/src/plugins/autoreload_on_server_restart/client/autoreload_on_server_restart.js?inline"></script>
|
|
64
64
|
</body>
|
|
65
65
|
</html>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
+
|
|
2
3
|
import { pickContentType } from "../../content_negotiation/pick_content_type.js";
|
|
3
4
|
import { replacePlaceholdersInHtml } from "../../replace_placeholder_in_html.js";
|
|
4
5
|
|
|
5
6
|
const clientErrorHtmlTemplateFileUrl = import.meta.resolve("./client/4xx.html");
|
|
6
7
|
|
|
7
|
-
export const
|
|
8
|
+
export const serverPluginDefaultBody4xx5xx = () => {
|
|
8
9
|
return {
|
|
9
10
|
name: "jsenv:default_body_4xx_5xx",
|
|
10
11
|
|
|
@@ -5,7 +5,7 @@ import { replacePlaceholdersInHtml } from "../../replace_placeholder_in_html.js"
|
|
|
5
5
|
|
|
6
6
|
const internalErrorHtmlFileUrl = import.meta.resolve("./client/500.html");
|
|
7
7
|
|
|
8
|
-
export const
|
|
8
|
+
export const serverPluginErrorHandler = ({ sendErrorDetails = false } = {}) => {
|
|
9
9
|
return {
|
|
10
10
|
name: "jsenv:error_handler",
|
|
11
11
|
handleError: (serverInternalError, { request }) => {
|
|
@@ -2,9 +2,9 @@ import { urlToExtension, urlToPathname } from "@jsenv/urls";
|
|
|
2
2
|
|
|
3
3
|
import { createFileSystemFetch } from "./fetch_filesystem.js";
|
|
4
4
|
|
|
5
|
-
export const
|
|
5
|
+
export const serverPluginStaticFiles = ({ directoryUrl, mainFilePath }) => {
|
|
6
6
|
return {
|
|
7
|
-
name: "jsenv:
|
|
7
|
+
name: "jsenv:static_files",
|
|
8
8
|
routes: [
|
|
9
9
|
{
|
|
10
10
|
endpoint: "GET *",
|
|
@@ -3,7 +3,7 @@ import { readFileSync } from "node:fs";
|
|
|
3
3
|
|
|
4
4
|
const jsenvServerRootDirectoryUrl = import.meta.resolve("../../../");
|
|
5
5
|
|
|
6
|
-
export const
|
|
6
|
+
export const serverPluginInternalClientFiles = () => {
|
|
7
7
|
return {
|
|
8
8
|
name: "jsenv:internal_client_files",
|
|
9
9
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { URL_META } from "@jsenv/url-meta";
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const serverPluginRequestAliases = (resourceAliases) => {
|
|
4
4
|
const aliases = {};
|
|
5
5
|
Object.keys(resourceAliases).forEach((key) => {
|
|
6
6
|
aliases[asFileUrl(key)] = asFileUrl(resourceAliases[key]);
|
|
@@ -2,7 +2,7 @@ import { pickContentEncoding } from "@jsenv/server/src/content_negotiation/pick_
|
|
|
2
2
|
import { pickContentLanguage } from "@jsenv/server/src/content_negotiation/pick_content_language.js";
|
|
3
3
|
import { pickContentType } from "@jsenv/server/src/content_negotiation/pick_content_type.js";
|
|
4
4
|
|
|
5
|
-
export const
|
|
5
|
+
export const serverPluginResponseAcceptanceCheck = () => {
|
|
6
6
|
return {
|
|
7
7
|
name: "jsenv:response_acceptance_check",
|
|
8
8
|
inspectResponse: (request, { response, warn }) => {
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
</script>
|
|
17
|
-
<script src="/@jsenv/server/src/
|
|
18
|
-
<script src="/@jsenv/server/src/
|
|
17
|
+
<script src="/@jsenv/server/src/plugins/route_inspector/client/code_block.js?inline"></script>
|
|
18
|
+
<script src="/@jsenv/server/src/plugins/autoreload_on_server_restart/client/autoreload_on_server_restart.js?inline"></script>
|
|
19
19
|
</head>
|
|
20
20
|
<body>
|
|
21
21
|
<style>
|
|
@@ -3,7 +3,7 @@ import { readFileSync } from "node:fs";
|
|
|
3
3
|
const routeInspectorHtmlFileUrl = import.meta
|
|
4
4
|
.resolve("./client/route_inspector.html");
|
|
5
5
|
|
|
6
|
-
export const
|
|
6
|
+
export const serverPluginRouteInspector = () => {
|
|
7
7
|
return {
|
|
8
8
|
name: "jsenv:route_inspector",
|
|
9
9
|
routes: [
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createPluginsController } from "../plugins_controller.js";
|
|
2
|
+
|
|
3
|
+
export const createServerPluginsController = async (serverPlugins) => {
|
|
4
|
+
const jsenvServerPluginsController = await createPluginsController({
|
|
5
|
+
plugins: serverPlugins,
|
|
6
|
+
pluginDescription: {
|
|
7
|
+
name: "server plugin",
|
|
8
|
+
properties: {
|
|
9
|
+
serverListening: { type: "hook" },
|
|
10
|
+
redirectRequest: { type: "hook" },
|
|
11
|
+
augmentRouteFetchSecondArg: { type: "hook" },
|
|
12
|
+
handleError: { type: "hook" },
|
|
13
|
+
onResponsePush: { type: "hook" },
|
|
14
|
+
injectResponseProperties: { type: "hook" },
|
|
15
|
+
serverStopped: { type: "hook" },
|
|
16
|
+
routes: {}, // routes are handled separately (used to populate the router)
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
return jsenvServerPluginsController;
|
|
21
|
+
};
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { performance } from "node:perf_hooks";
|
|
2
|
+
|
|
3
|
+
// createPluginsController manages an ordered list of plugins.
|
|
4
|
+
// "plugins" is a generic term: it can refer to jsenv core plugins,
|
|
5
|
+
// server plugins, or any extensibility mechanism using the same pattern.
|
|
6
|
+
export const createPluginsController = async ({
|
|
7
|
+
plugins,
|
|
8
|
+
pluginDescription = {},
|
|
9
|
+
filterPlugin,
|
|
10
|
+
getInitPluginArgs,
|
|
11
|
+
getHookFunction = defaultGetHookFunction,
|
|
12
|
+
getTimingKey = defaultGetTimingKey,
|
|
13
|
+
getEffectArgs = defaultGetEffectArgs,
|
|
14
|
+
meta = {},
|
|
15
|
+
}) => {
|
|
16
|
+
const { name: pluginName = "plugin", properties = {} } = pluginDescription;
|
|
17
|
+
|
|
18
|
+
const hookSetMap = new Map();
|
|
19
|
+
|
|
20
|
+
const addHook = (hook) => {
|
|
21
|
+
let hookSet = hookSetMap.get(hook.name);
|
|
22
|
+
if (!hookSet) {
|
|
23
|
+
hookSet = new Set();
|
|
24
|
+
hookSetMap.set(hook.name, hookSet);
|
|
25
|
+
}
|
|
26
|
+
hookSet.add(hook);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const addPluginToHookSetMap = (plugin) => {
|
|
30
|
+
for (const key of Object.keys(plugin)) {
|
|
31
|
+
if (
|
|
32
|
+
key === "name" ||
|
|
33
|
+
key === "init" ||
|
|
34
|
+
key === "destroy" ||
|
|
35
|
+
key === "meta" ||
|
|
36
|
+
key === "effect"
|
|
37
|
+
) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const propDef = properties[key];
|
|
41
|
+
if (propDef === undefined) {
|
|
42
|
+
console.warn(
|
|
43
|
+
`Unexpected "${key}" property on "${plugin.name}" ${pluginName}`,
|
|
44
|
+
);
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (propDef.type !== "hook") {
|
|
48
|
+
// non-hook key, silently skip
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const hookValue = plugin[key];
|
|
52
|
+
if (!hookValue) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
addHook({
|
|
56
|
+
plugin,
|
|
57
|
+
name: key,
|
|
58
|
+
value: hookValue,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const activatePlugin = (plugin) => {
|
|
64
|
+
if (plugin.meta) {
|
|
65
|
+
const pluginMeta = plugin.meta;
|
|
66
|
+
if (typeof pluginMeta !== "object" || pluginMeta === null) {
|
|
67
|
+
console.warn(`${pluginName}.meta must be an object, got ${pluginMeta}`);
|
|
68
|
+
} else {
|
|
69
|
+
Object.assign(meta, pluginMeta);
|
|
70
|
+
Object.freeze(pluginMeta);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
addPluginToHookSetMap(plugin);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const pluginCandidates = plugins;
|
|
77
|
+
const activePlugins = [];
|
|
78
|
+
const pluginsWithEffect = [];
|
|
79
|
+
|
|
80
|
+
for (const pluginCandidate of pluginCandidates) {
|
|
81
|
+
if (filterPlugin && !filterPlugin(pluginCandidate)) {
|
|
82
|
+
pluginCandidate.destroy?.();
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const active = await callInitOnPlugin(pluginCandidate, getInitPluginArgs);
|
|
86
|
+
if (!active) {
|
|
87
|
+
pluginCandidate.destroy?.();
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (pluginCandidate.effect) {
|
|
91
|
+
pluginsWithEffect.push(pluginCandidate);
|
|
92
|
+
} else {
|
|
93
|
+
activePlugins.push(pluginCandidate);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
for (const pluginWithEffect of pluginsWithEffect) {
|
|
98
|
+
const effectArgs = getEffectArgs({ otherPlugins: activePlugins });
|
|
99
|
+
const returnValue = pluginWithEffect.effect(...effectArgs);
|
|
100
|
+
if (!returnValue) {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
activePlugins.push(pluginWithEffect);
|
|
104
|
+
if (typeof returnValue === "function" && !pluginWithEffect.destroy) {
|
|
105
|
+
pluginWithEffect.destroy = returnValue;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Preserve original declaration order
|
|
110
|
+
activePlugins.sort(
|
|
111
|
+
(a, b) => pluginCandidates.indexOf(a) - pluginCandidates.indexOf(b),
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
for (const activePlugin of activePlugins) {
|
|
115
|
+
activatePlugin(activePlugin);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const assertAndNormalizeReturnValue = (valueReturned, info, { hook }) => {
|
|
119
|
+
if (valueReturned === null || valueReturned === undefined) {
|
|
120
|
+
// all hooks are allowed to return null/undefined as a signal of "I don't do anything"
|
|
121
|
+
return valueReturned;
|
|
122
|
+
}
|
|
123
|
+
const propDef = properties[hook.name];
|
|
124
|
+
if (!propDef || !propDef.assertAndNormalize) {
|
|
125
|
+
return valueReturned;
|
|
126
|
+
}
|
|
127
|
+
const assertAndNormalizeResult = propDef.assertAndNormalize(
|
|
128
|
+
valueReturned,
|
|
129
|
+
info,
|
|
130
|
+
{ hook },
|
|
131
|
+
);
|
|
132
|
+
return assertAndNormalizeResult;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
let lastPluginUsed = null;
|
|
136
|
+
let currentPlugin = null;
|
|
137
|
+
let currentHookName = null;
|
|
138
|
+
|
|
139
|
+
const callHook = (hook, info, secondArg) => {
|
|
140
|
+
const hookFn = getHookFunction(hook, info);
|
|
141
|
+
if (!hookFn) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
lastPluginUsed = hook.plugin;
|
|
145
|
+
currentPlugin = hook.plugin;
|
|
146
|
+
currentHookName = hook.name;
|
|
147
|
+
let startTimestamp;
|
|
148
|
+
if (info && info.timing) {
|
|
149
|
+
startTimestamp = performance.now();
|
|
150
|
+
}
|
|
151
|
+
let valueReturned = hookFn(info, secondArg);
|
|
152
|
+
if (startTimestamp !== undefined) {
|
|
153
|
+
info.timing[getTimingKey(hook)] = performance.now() - startTimestamp;
|
|
154
|
+
}
|
|
155
|
+
valueReturned = assertAndNormalizeReturnValue(valueReturned, info, {
|
|
156
|
+
hook,
|
|
157
|
+
});
|
|
158
|
+
currentPlugin = null;
|
|
159
|
+
currentHookName = null;
|
|
160
|
+
return valueReturned;
|
|
161
|
+
};
|
|
162
|
+
const callAsyncHook = async (hook, info, secondArg) => {
|
|
163
|
+
const hookFn = getHookFunction(hook, info);
|
|
164
|
+
if (!hookFn) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
lastPluginUsed = hook.plugin;
|
|
168
|
+
currentPlugin = hook.plugin;
|
|
169
|
+
currentHookName = hook.name;
|
|
170
|
+
let startTimestamp;
|
|
171
|
+
if (info && info.timing) {
|
|
172
|
+
startTimestamp = performance.now();
|
|
173
|
+
}
|
|
174
|
+
let valueReturned = await hookFn(info, secondArg);
|
|
175
|
+
if (startTimestamp !== undefined) {
|
|
176
|
+
info.timing[getTimingKey(hook)] = performance.now() - startTimestamp;
|
|
177
|
+
}
|
|
178
|
+
valueReturned = assertAndNormalizeReturnValue(valueReturned, info, {
|
|
179
|
+
hook,
|
|
180
|
+
});
|
|
181
|
+
currentPlugin = null;
|
|
182
|
+
currentHookName = null;
|
|
183
|
+
return valueReturned;
|
|
184
|
+
};
|
|
185
|
+
// callHooks(hookName, info, callback)
|
|
186
|
+
// callHooks(hookName, info, secondArg, callback)
|
|
187
|
+
const callHooks = (hookName, info, secondArgOrCallback, callback) => {
|
|
188
|
+
const hookSet = hookSetMap.get(hookName);
|
|
189
|
+
if (!hookSet) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
let secondArg;
|
|
193
|
+
if (typeof secondArgOrCallback === "function") {
|
|
194
|
+
callback = secondArgOrCallback;
|
|
195
|
+
secondArg = undefined;
|
|
196
|
+
} else {
|
|
197
|
+
secondArg = secondArgOrCallback;
|
|
198
|
+
}
|
|
199
|
+
const setHookParams = (firstArg = info) => {
|
|
200
|
+
info = firstArg;
|
|
201
|
+
};
|
|
202
|
+
for (const hook of hookSet) {
|
|
203
|
+
const returnValue = callHook(hook, info, secondArg);
|
|
204
|
+
if (returnValue && callback) {
|
|
205
|
+
callback(returnValue, hook.plugin, setHookParams);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
// callAsyncHooks(hookName, info, callback)
|
|
210
|
+
// callAsyncHooks(hookName, info, secondArg, callback)
|
|
211
|
+
const callAsyncHooks = async (
|
|
212
|
+
hookName,
|
|
213
|
+
info,
|
|
214
|
+
secondArgOrCallback,
|
|
215
|
+
callback,
|
|
216
|
+
) => {
|
|
217
|
+
const hookSet = hookSetMap.get(hookName);
|
|
218
|
+
if (!hookSet) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
let secondArg;
|
|
222
|
+
if (typeof secondArgOrCallback === "function") {
|
|
223
|
+
callback = secondArgOrCallback;
|
|
224
|
+
secondArg = undefined;
|
|
225
|
+
} else {
|
|
226
|
+
secondArg = secondArgOrCallback;
|
|
227
|
+
}
|
|
228
|
+
for (const hook of hookSet) {
|
|
229
|
+
const returnValue = await callAsyncHook(hook, info, secondArg);
|
|
230
|
+
if (returnValue && callback) {
|
|
231
|
+
await callback(returnValue, hook.plugin);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
// callHooksUntil(hookName, info)
|
|
236
|
+
// callHooksUntil(hookName, info, secondArg)
|
|
237
|
+
// callHooksUntil(hookName, info, secondArg, until)
|
|
238
|
+
const callHooksUntil = (hookName, info, secondArgOrUntil, until) => {
|
|
239
|
+
const hookSet = hookSetMap.get(hookName);
|
|
240
|
+
if (!hookSet) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
let secondArg;
|
|
244
|
+
if (typeof secondArgOrUntil === "function") {
|
|
245
|
+
until = secondArgOrUntil;
|
|
246
|
+
secondArg = undefined;
|
|
247
|
+
} else {
|
|
248
|
+
secondArg = secondArgOrUntil;
|
|
249
|
+
}
|
|
250
|
+
for (const hook of hookSet) {
|
|
251
|
+
const returnValue = callHook(hook, info, secondArg);
|
|
252
|
+
if (until) {
|
|
253
|
+
const untilValue = until(returnValue);
|
|
254
|
+
if (untilValue) {
|
|
255
|
+
return untilValue;
|
|
256
|
+
}
|
|
257
|
+
} else if (returnValue) {
|
|
258
|
+
return returnValue;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return null;
|
|
262
|
+
};
|
|
263
|
+
// callAsyncHooksUntil(hookName, info)
|
|
264
|
+
// callAsyncHooksUntil(hookName, info, secondArg)
|
|
265
|
+
const callAsyncHooksUntil = async (hookName, info, secondArg) => {
|
|
266
|
+
const hookSet = hookSetMap.get(hookName);
|
|
267
|
+
if (!hookSet) {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
if (hookSet.size === 0) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
const iterator = hookSet.values()[Symbol.iterator]();
|
|
274
|
+
let result;
|
|
275
|
+
const visit = async () => {
|
|
276
|
+
const { done, value: hook } = iterator.next();
|
|
277
|
+
if (done) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const returnValue = await callAsyncHook(hook, info, secondArg);
|
|
281
|
+
if (returnValue) {
|
|
282
|
+
result = returnValue;
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
await visit();
|
|
286
|
+
};
|
|
287
|
+
await visit();
|
|
288
|
+
return result;
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
activePlugins,
|
|
293
|
+
|
|
294
|
+
callHook,
|
|
295
|
+
callAsyncHook,
|
|
296
|
+
callHooks,
|
|
297
|
+
callHooksUntil,
|
|
298
|
+
callAsyncHooks,
|
|
299
|
+
callAsyncHooksUntil,
|
|
300
|
+
|
|
301
|
+
getPluginMeta: (pluginName) => meta[pluginName],
|
|
302
|
+
getMeta: () => meta,
|
|
303
|
+
getLastPluginUsed: () => lastPluginUsed,
|
|
304
|
+
getCurrentPlugin: () => currentPlugin,
|
|
305
|
+
getCurrentHookName: () => currentHookName,
|
|
306
|
+
};
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const callInitOnPlugin = async (plugin, getInitPluginArgs) => {
|
|
310
|
+
const { init } = plugin;
|
|
311
|
+
if (!init) {
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
const initArgs = getInitPluginArgs ? getInitPluginArgs(plugin) : [];
|
|
315
|
+
const initReturnValue = await init(...initArgs);
|
|
316
|
+
if (initReturnValue === false) {
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
if (typeof initReturnValue === "function" && !plugin.destroy) {
|
|
320
|
+
plugin.destroy = initReturnValue;
|
|
321
|
+
}
|
|
322
|
+
return true;
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const defaultGetTimingKey = (hook) =>
|
|
326
|
+
`${hook.name}-${hook.plugin.name.replace("jsenv:", "")}`;
|
|
327
|
+
const defaultGetEffectArgs = ({ otherPlugins }) => [{ otherPlugins }];
|
|
328
|
+
|
|
329
|
+
// By default, hook values can be a function or an object keyed by info.type.
|
|
330
|
+
// When an object, the hook for the matching type (or "*") is used.
|
|
331
|
+
const defaultGetHookFunction = (hook, info = {}) => {
|
|
332
|
+
const hookValue = hook.value;
|
|
333
|
+
if (typeof hookValue === "object") {
|
|
334
|
+
const hookForType = hookValue[info.type] || hookValue["*"];
|
|
335
|
+
if (!hookForType) {
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
return hookForType;
|
|
339
|
+
}
|
|
340
|
+
return hookValue;
|
|
341
|
+
};
|
package/src/router/router.js
CHANGED
|
@@ -188,19 +188,20 @@ export const createRouter = (
|
|
|
188
188
|
upgrade: false,
|
|
189
189
|
};
|
|
190
190
|
|
|
191
|
-
let
|
|
191
|
+
let currentServerPlugin;
|
|
192
192
|
let currentRoutingTiming;
|
|
193
193
|
const onRouteMatchStart = (route) => {
|
|
194
|
-
|
|
194
|
+
const { serverPlugin } = route;
|
|
195
|
+
if (serverPlugin === currentServerPlugin) {
|
|
195
196
|
return;
|
|
196
197
|
}
|
|
197
198
|
onRouteGroupEnd(route);
|
|
198
199
|
currentRoutingTiming = fetchSecondArg.timing(
|
|
199
|
-
|
|
200
|
-
? `${
|
|
200
|
+
serverPlugin && serverPlugin.name
|
|
201
|
+
? `${serverPlugin.name.replace("jsenv:", "")}.routing`
|
|
201
202
|
: "routing",
|
|
202
203
|
);
|
|
203
|
-
|
|
204
|
+
currentServerPlugin = serverPlugin;
|
|
204
205
|
};
|
|
205
206
|
const onRouteGroupEnd = () => {
|
|
206
207
|
if (currentRoutingTiming) {
|
|
@@ -540,6 +541,7 @@ const createRoute = ({
|
|
|
540
541
|
description,
|
|
541
542
|
headers,
|
|
542
543
|
service,
|
|
544
|
+
serverPlugin,
|
|
543
545
|
availableMediaTypes = [],
|
|
544
546
|
availableLanguages = [],
|
|
545
547
|
availableVersions = [],
|
|
@@ -589,6 +591,7 @@ const createRoute = ({
|
|
|
589
591
|
resource,
|
|
590
592
|
description,
|
|
591
593
|
service,
|
|
594
|
+
serverPlugin,
|
|
592
595
|
availableMediaTypes,
|
|
593
596
|
availableLanguages,
|
|
594
597
|
availableVersions,
|