@bleedingdev/modern-js-server-core 3.9.0-ultramodern.4 → 3.9.0-ultramodern.5

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.
Files changed (47) hide show
  1. package/dist/cjs/adapters/node/index.js +4 -0
  2. package/dist/cjs/adapters/node/plugins/static.js +107 -29
  3. package/dist/cjs/index.js +10 -34
  4. package/dist/cjs/plugins/compat/index.js +1 -0
  5. package/dist/cjs/serverBase.js +58 -17
  6. package/dist/cjs/utils/error.js +0 -10
  7. package/dist/esm/adapters/node/index.mjs +1 -0
  8. package/dist/esm/adapters/node/plugins/static.mjs +107 -32
  9. package/dist/esm/index.mjs +1 -1
  10. package/dist/esm/plugins/compat/index.mjs +1 -0
  11. package/dist/esm/serverBase.mjs +58 -17
  12. package/dist/esm/utils/error.mjs +0 -1
  13. package/dist/esm-node/adapters/node/index.mjs +1 -0
  14. package/dist/esm-node/adapters/node/plugins/static.mjs +107 -32
  15. package/dist/esm-node/index.mjs +1 -1
  16. package/dist/esm-node/plugins/compat/index.mjs +1 -0
  17. package/dist/esm-node/serverBase.mjs +58 -17
  18. package/dist/esm-node/utils/error.mjs +0 -1
  19. package/dist/types/adapters/node/index.d.ts +2 -0
  20. package/dist/types/adapters/node/plugins/static.d.ts +42 -4
  21. package/dist/types/index.d.ts +1 -2
  22. package/dist/types/serverBase.d.ts +6 -0
  23. package/dist/types/types/config/bff.d.ts +4 -15
  24. package/dist/types/types/config/server.d.ts +1 -3
  25. package/dist/types/types/plugins/plugin.d.ts +15 -1
  26. package/dist/types/utils/error.d.ts +0 -2
  27. package/package.json +5 -6
  28. package/dist/cjs/adapters/node/plugins/staticModuleFederation.js +0 -173
  29. package/dist/cjs/adapters/node/plugins/staticPrecompressed.js +0 -152
  30. package/dist/cjs/adapters/node/plugins/staticServing.js +0 -206
  31. package/dist/cjs/types/config/bffRuntime.js +0 -18
  32. package/dist/cjs/types/config/serverTelemetry.js +0 -18
  33. package/dist/esm/adapters/node/plugins/staticModuleFederation.mjs +0 -104
  34. package/dist/esm/adapters/node/plugins/staticPrecompressed.mjs +0 -111
  35. package/dist/esm/adapters/node/plugins/staticServing.mjs +0 -152
  36. package/dist/esm/types/config/bffRuntime.mjs +0 -0
  37. package/dist/esm/types/config/serverTelemetry.mjs +0 -0
  38. package/dist/esm-node/adapters/node/plugins/staticModuleFederation.mjs +0 -105
  39. package/dist/esm-node/adapters/node/plugins/staticPrecompressed.mjs +0 -112
  40. package/dist/esm-node/adapters/node/plugins/staticServing.mjs +0 -153
  41. package/dist/esm-node/types/config/bffRuntime.mjs +0 -1
  42. package/dist/esm-node/types/config/serverTelemetry.mjs +0 -1
  43. package/dist/types/adapters/node/plugins/staticModuleFederation.d.ts +0 -13
  44. package/dist/types/adapters/node/plugins/staticPrecompressed.d.ts +0 -13
  45. package/dist/types/adapters/node/plugins/staticServing.d.ts +0 -25
  46. package/dist/types/types/config/bffRuntime.d.ts +0 -116
  47. package/dist/types/types/config/serverTelemetry.d.ts +0 -319
@@ -1,8 +1,55 @@
1
- import path_0 from "path";
1
+ import { fileReader } from "@modern-js/runtime-utils/fileReader";
2
+ import { fs } from "@modern-js/utils";
3
+ import { getMimeType } from "hono/utils/mime";
4
+ import path from "path";
2
5
  import { sortRoutes } from "../../../utils/index.mjs";
3
6
  import { getPublicDirPatterns } from "../../../utils/publicDir.mjs";
4
- import { createModuleFederationStaticServing, servePreCompressedPublicRouteAsset, servePublicDirectoryAsset } from "./staticServing.mjs";
5
- const serverStaticPlugin = ()=>({
7
+ const isPathInside = (target, root)=>{
8
+ const relative = path.relative(path.resolve(root), path.resolve(target));
9
+ return '' === relative || !relative.startsWith(`..${path.sep}`) && '..' !== relative && !path.isAbsolute(relative);
10
+ };
11
+ async function serveStaticAsset(context, asset, respond) {
12
+ let { filename } = asset;
13
+ const { kind } = asset;
14
+ if (asset.root && !isPathInside(filename, asset.root)) return null;
15
+ if (asset.realpath) try {
16
+ const [realFilename, realRoot, stat] = await Promise.all([
17
+ fs.realpath(filename),
18
+ asset.root ? fs.realpath(asset.root) : Promise.resolve(void 0),
19
+ fs.stat(filename)
20
+ ]);
21
+ if (!stat.isFile() || realRoot && !isPathInside(realFilename, realRoot)) return null;
22
+ filename = realFilename;
23
+ asset = {
24
+ ...asset,
25
+ filename,
26
+ root: realRoot
27
+ };
28
+ } catch {
29
+ return null;
30
+ }
31
+ if ('static' === kind && !await fs.pathExists(filename)) return null;
32
+ if (respond) return respond(asset, (representation)=>serveStaticAsset(context, {
33
+ ...asset,
34
+ ...representation,
35
+ filename: representation?.filename ?? asset.filename,
36
+ mimeFilename: asset.mimeFilename ?? filename
37
+ }));
38
+ const mimeType = getMimeType(asset.mimeFilename ?? filename);
39
+ if ('static' === kind && mimeType) context.header('Content-Type', mimeType);
40
+ const size = 'static' === kind ? (await fs.lstat(filename)).size : void 0;
41
+ const data = 'static' === kind ? await fileReader.readFileFromSystem(filename, 'buffer') : await fileReader.readFile(filename, 'buffer');
42
+ if ('static' === kind && (asset.contentLength ?? true)) context.header('Content-Length', String(true === asset.contentLength && data ? data.byteLength : size));
43
+ if (null === data) return null;
44
+ if ('public' === kind && mimeType) context.header('Content-Type', mimeType);
45
+ Object.entries(asset.responseHeaders || {}).forEach(([key, value])=>{
46
+ context.header(key, value);
47
+ });
48
+ if ('public' === kind && asset.contentLength) context.header('Content-Length', String(data.byteLength));
49
+ const body = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
50
+ return context.body(body, 200);
51
+ }
52
+ const serverStaticPlugin = (options = {})=>({
6
53
  name: '@modern-js/plugin-server-static',
7
54
  setup (api) {
8
55
  api.onPrepare(()=>{
@@ -13,7 +60,9 @@ const serverStaticPlugin = ()=>({
13
60
  routes,
14
61
  output: config.output || {},
15
62
  html: config.html || {},
16
- server: config.server || {}
63
+ server: config.server || {},
64
+ ...api.getServerContext().staticAssetResponders,
65
+ ...options
17
66
  });
18
67
  middlewares.push({
19
68
  name: 'server-static',
@@ -22,16 +71,33 @@ const serverStaticPlugin = ()=>({
22
71
  });
23
72
  }
24
73
  });
25
- function createPublicMiddleware({ pwd, routes }) {
74
+ function createPublicMiddleware({ pwd, routes, pathPrefix = '/', respondAsset, respondPublicFallback }) {
26
75
  return async (c, next)=>{
27
- const route = matchPublicRoute(c.req, routes);
28
- if (route) {
29
- const response = await servePreCompressedPublicRouteAsset(c, pwd, route);
30
- if (null !== response) return response;
31
- }
32
- const generatedPublicAsset = await servePublicDirectoryAsset(c, pwd);
33
- if (null !== generatedPublicAsset) return generatedPublicAsset;
34
- return await next();
76
+ const respondPublic = async ()=>{
77
+ const route = matchPublicRoute(c.req, routes);
78
+ if (!route) return null;
79
+ const asset = {
80
+ filename: path.join(pwd, route.entryPath),
81
+ kind: 'public',
82
+ responseHeaders: route.responseHeaders
83
+ };
84
+ const serve = (representation)=>serveStaticAsset(c, {
85
+ ...asset,
86
+ ...representation,
87
+ filename: representation?.filename ?? asset.filename,
88
+ mimeFilename: asset.filename
89
+ });
90
+ const response = respondAsset ? await respondAsset(c, asset, serve, {
91
+ root: pwd,
92
+ pathPrefix
93
+ }) : void 0;
94
+ return void 0 === response ? serve() : response;
95
+ };
96
+ const response = respondPublicFallback ? await respondPublicFallback(c, respondPublic, {
97
+ root: pwd,
98
+ pathPrefix
99
+ }) : await respondPublic();
100
+ return response ?? next();
35
101
  };
36
102
  }
37
103
  function matchPublicRoute(req, routes) {
@@ -76,29 +142,38 @@ function createStaticMiddleware(options) {
76
142
  ...staticReg,
77
143
  ...iconReg
78
144
  ].join('|')})`);
79
- const publicMiddleware = createPublicMiddleware({
80
- pwd,
81
- routes: routes || []
82
- });
83
- const moduleFederationStaticServing = createModuleFederationStaticServing({
84
- pwd,
85
- pathPrefix
86
- });
87
145
  return async (c, next)=>{
88
146
  const pageRoute = c.get('route');
89
147
  const pathname = c.req.path;
90
- if (pageRoute && '' === path_0.extname(pathname)) return next();
148
+ if (pageRoute && '' === path.extname(pathname)) return next();
91
149
  const hit = staticPathRegExp.test(pathname);
92
- const staticServingRequest = await moduleFederationStaticServing.resolveRequest(pathname);
93
- if (null === staticServingRequest) return next();
94
- if (hit) {
95
- const response = await moduleFederationStaticServing.serveStaticHit(c, staticServingRequest);
96
- if (null !== response) return response;
97
- return next();
150
+ if (!hit) return createPublicMiddleware({
151
+ pwd,
152
+ routes: routes || [],
153
+ pathPrefix,
154
+ respondAsset: options.respondAsset,
155
+ respondPublicFallback: options.respondPublicFallback
156
+ })(c, next);
157
+ {
158
+ const filepath = path.join(pwd, pathname.replace(pathPrefix, ()=>''));
159
+ if (!isPathInside(filepath, pwd)) return next();
160
+ if (!await fs.pathExists(filepath)) return next();
161
+ const asset = {
162
+ filename: filepath,
163
+ kind: 'static'
164
+ };
165
+ const serve = (representation)=>serveStaticAsset(c, {
166
+ ...asset,
167
+ ...representation,
168
+ filename: representation?.filename ?? asset.filename,
169
+ mimeFilename: filepath
170
+ });
171
+ const response = options.respondAsset ? await options.respondAsset(c, asset, serve, {
172
+ root: pwd,
173
+ pathPrefix
174
+ }) : void 0;
175
+ return (void 0 === response ? await serve() : response) ?? next();
98
176
  }
99
- const moduleFederationResponse = await moduleFederationStaticServing.serveModuleFederationAsset(c, staticServingRequest);
100
- if (null !== moduleFederationResponse) return moduleFederationResponse;
101
- return publicMiddleware(c, next);
102
177
  };
103
178
  }
104
179
  const prepareFavicons = (favicon)=>{
@@ -106,4 +181,4 @@ const prepareFavicons = (favicon)=>{
106
181
  if (favicon && 'string' == typeof favicon) faviconNames.push(favicon.substring(favicon.lastIndexOf('/') + 1));
107
182
  return faviconNames;
108
183
  };
109
- export { createPublicMiddleware, createStaticMiddleware, serverStaticPlugin };
184
+ export { createPublicMiddleware, createStaticMiddleware, serveStaticAsset, serverStaticPlugin };
@@ -8,5 +8,5 @@ export { AGGRED_DIR } from "./constants.mjs";
8
8
  export { run, useHonoContext } from "./context.mjs";
9
9
  export { getLoaderCtx } from "./helper.mjs";
10
10
  export { createServerBase } from "./serverBase.mjs";
11
- export { ErrorDigest, createErrorHtml, createSafeFailureHttpResult, createSafeJsonFailureResponse, getSafeFailureStatus, onError } from "./utils/index.mjs";
11
+ export { ErrorDigest, createErrorHtml, onError } from "./utils/index.mjs";
12
12
  export { getPublicDirConfig, getPublicDirPatterns, getPublicDirRoutePrefixes, normalizePublicDir, normalizePublicDirPath, resolvePublicDirPaths } from "./utils/publicDir.mjs";
@@ -3,6 +3,7 @@ import { getHookRunners, handleSetupResult } from "./hooks.mjs";
3
3
  const compatPlugin = ()=>({
4
4
  name: '@modern-js/server-compat',
5
5
  registryHooks: {
6
+ handleError: createAsyncPipelineHook(),
6
7
  prepareWebServer: createAsyncPipelineHook(),
7
8
  prepareApiServer: createAsyncPipelineHook(),
8
9
  afterMatch: createAsyncPipelineHook(),
@@ -1,4 +1,5 @@
1
1
  import { server as server_server } from "@modern-js/plugin/server";
2
+ import { logger } from "@modern-js/utils";
2
3
  import { Hono } from "hono";
3
4
  import { run } from "./context.mjs";
4
5
  import { handleSetupResult } from "./plugins/compat/hooks.mjs";
@@ -17,22 +18,54 @@ function _class_private_method_init(obj, privateSet) {
17
18
  var _applyMiddlewares = /*#__PURE__*/ new WeakSet();
18
19
  class ServerBase {
19
20
  async init() {
20
- const { serverConfig, config: cliConfig } = this.serverOptions;
21
- const mergedConfig = loadConfig({
22
- cliConfig,
23
- serverConfig: serverConfig || {}
24
- });
25
- const { serverContext } = await server_server.run({
26
- plugins: this.plugins,
27
- options: this.serverOptions,
28
- config: mergedConfig,
29
- handleSetupResult: handleSetupResult
30
- });
31
- serverContext.serverBase = this;
32
- this.serverContext = serverContext;
33
- await serverContext.hooks.onPrepare.call();
34
- _class_private_method_get(this, _applyMiddlewares, applyMiddlewares).call(this);
35
- return this;
21
+ try {
22
+ const { serverConfig, config: cliConfig } = this.serverOptions;
23
+ const mergedConfig = loadConfig({
24
+ cliConfig,
25
+ serverConfig: serverConfig || {}
26
+ });
27
+ const { serverContext } = await server_server.run({
28
+ plugins: this.plugins,
29
+ options: this.serverOptions,
30
+ config: mergedConfig,
31
+ handleSetupResult: handleSetupResult
32
+ });
33
+ serverContext.serverBase = this;
34
+ this.serverContext = serverContext;
35
+ await serverContext.hooks.onPrepare.call();
36
+ _class_private_method_get(this, _applyMiddlewares, applyMiddlewares).call(this);
37
+ return this;
38
+ } catch (error) {
39
+ await this.dispose().catch((disposeError)=>{
40
+ logger.error(disposeError);
41
+ });
42
+ throw error;
43
+ }
44
+ }
45
+ onDispose(disposer) {
46
+ if (this.disposePromise) throw new Error('Cannot register a disposer on a retired server.');
47
+ this.disposers.add(disposer);
48
+ return ()=>{
49
+ this.disposers.delete(disposer);
50
+ };
51
+ }
52
+ dispose() {
53
+ if (!this.disposePromise) {
54
+ const disposers = [
55
+ ...this.disposers
56
+ ].reverse();
57
+ this.disposers.clear();
58
+ this.disposePromise = Promise.resolve().then(async ()=>{
59
+ const errors = [];
60
+ for (const disposer of disposers)try {
61
+ await disposer();
62
+ } catch (error) {
63
+ errors.push(error);
64
+ }
65
+ if (errors.length > 0) throw new AggregateError(errors, 'Failed to dispose server.');
66
+ });
67
+ }
68
+ return this.disposePromise;
36
69
  }
37
70
  addPlugins(plugins) {
38
71
  this.plugins.push(...plugins);
@@ -78,7 +111,15 @@ class ServerBase {
78
111
  }
79
112
  constructor(options){
80
113
  _class_private_method_init(this, _applyMiddlewares);
81
- this.plugins = [];
114
+ this.plugins = [
115
+ {
116
+ name: '@modern-js/server-lifecycle',
117
+ _registryApi: ()=>({
118
+ onDispose: (disposer)=>this.onDispose(disposer)
119
+ })
120
+ }
121
+ ];
122
+ this.disposers = new Set();
82
123
  this.serverContext = null;
83
124
  this.serverOptions = options;
84
125
  this.app = new Hono();
@@ -50,5 +50,4 @@ function onError(digest, error, monitors, req) {
50
50
  else if (req) console.error(`Server Error - ${digest}, error = ${error instanceof Error ? error.stack || error.message : error}, req.url = ${req.url}, req.headers = ${JSON.stringify(headerData)}`);
51
51
  else console.error(`Server Error - ${digest}, error = ${error instanceof Error ? error.stack || error.message : error} `);
52
52
  }
53
- export { createSafeFailureHttpResult, createSafeJsonFailureResponse, getSafeFailureStatus } from "@modern-js/runtime-extensions/safe-failure";
54
53
  export { createErrorHtml, error_ErrorDigest as ErrorDigest, onError };
@@ -3,3 +3,4 @@ export { loadCacheConfig, loadServerCliConfig, loadServerEnv, loadServerPlugins,
3
3
  export { connectMid2HonoMid, connectMockMid2HonoMid, httpCallBack2HonoMid } from "./hono.mjs";
4
4
  export { createNodeServer, createWebRequest, sendResponse } from "./node.mjs";
5
5
  export { getHtmlTemplates, getServerManifest, injectNodeSeverPlugin, injectResourcePlugin, injectRscManifestPlugin, serverStaticPlugin } from "./plugins/index.mjs";
6
+ export { serveStaticAsset } from "./plugins/static.mjs";
@@ -1,9 +1,56 @@
1
1
  import "node:module";
2
- import path_0 from "path";
2
+ import { fileReader } from "@modern-js/runtime-utils/fileReader";
3
+ import { fs } from "@modern-js/utils";
4
+ import { getMimeType } from "hono/utils/mime";
5
+ import path from "path";
3
6
  import { sortRoutes } from "../../../utils/index.mjs";
4
7
  import { getPublicDirPatterns } from "../../../utils/publicDir.mjs";
5
- import { createModuleFederationStaticServing, servePreCompressedPublicRouteAsset, servePublicDirectoryAsset } from "./staticServing.mjs";
6
- const serverStaticPlugin = ()=>({
8
+ const isPathInside = (target, root)=>{
9
+ const relative = path.relative(path.resolve(root), path.resolve(target));
10
+ return '' === relative || !relative.startsWith(`..${path.sep}`) && '..' !== relative && !path.isAbsolute(relative);
11
+ };
12
+ async function serveStaticAsset(context, asset, respond) {
13
+ let { filename } = asset;
14
+ const { kind } = asset;
15
+ if (asset.root && !isPathInside(filename, asset.root)) return null;
16
+ if (asset.realpath) try {
17
+ const [realFilename, realRoot, stat] = await Promise.all([
18
+ fs.realpath(filename),
19
+ asset.root ? fs.realpath(asset.root) : Promise.resolve(void 0),
20
+ fs.stat(filename)
21
+ ]);
22
+ if (!stat.isFile() || realRoot && !isPathInside(realFilename, realRoot)) return null;
23
+ filename = realFilename;
24
+ asset = {
25
+ ...asset,
26
+ filename,
27
+ root: realRoot
28
+ };
29
+ } catch {
30
+ return null;
31
+ }
32
+ if ('static' === kind && !await fs.pathExists(filename)) return null;
33
+ if (respond) return respond(asset, (representation)=>serveStaticAsset(context, {
34
+ ...asset,
35
+ ...representation,
36
+ filename: representation?.filename ?? asset.filename,
37
+ mimeFilename: asset.mimeFilename ?? filename
38
+ }));
39
+ const mimeType = getMimeType(asset.mimeFilename ?? filename);
40
+ if ('static' === kind && mimeType) context.header('Content-Type', mimeType);
41
+ const size = 'static' === kind ? (await fs.lstat(filename)).size : void 0;
42
+ const data = 'static' === kind ? await fileReader.readFileFromSystem(filename, 'buffer') : await fileReader.readFile(filename, 'buffer');
43
+ if ('static' === kind && (asset.contentLength ?? true)) context.header('Content-Length', String(true === asset.contentLength && data ? data.byteLength : size));
44
+ if (null === data) return null;
45
+ if ('public' === kind && mimeType) context.header('Content-Type', mimeType);
46
+ Object.entries(asset.responseHeaders || {}).forEach(([key, value])=>{
47
+ context.header(key, value);
48
+ });
49
+ if ('public' === kind && asset.contentLength) context.header('Content-Length', String(data.byteLength));
50
+ const body = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
51
+ return context.body(body, 200);
52
+ }
53
+ const serverStaticPlugin = (options = {})=>({
7
54
  name: '@modern-js/plugin-server-static',
8
55
  setup (api) {
9
56
  api.onPrepare(()=>{
@@ -14,7 +61,9 @@ const serverStaticPlugin = ()=>({
14
61
  routes,
15
62
  output: config.output || {},
16
63
  html: config.html || {},
17
- server: config.server || {}
64
+ server: config.server || {},
65
+ ...api.getServerContext().staticAssetResponders,
66
+ ...options
18
67
  });
19
68
  middlewares.push({
20
69
  name: 'server-static',
@@ -23,16 +72,33 @@ const serverStaticPlugin = ()=>({
23
72
  });
24
73
  }
25
74
  });
26
- function createPublicMiddleware({ pwd, routes }) {
75
+ function createPublicMiddleware({ pwd, routes, pathPrefix = '/', respondAsset, respondPublicFallback }) {
27
76
  return async (c, next)=>{
28
- const route = matchPublicRoute(c.req, routes);
29
- if (route) {
30
- const response = await servePreCompressedPublicRouteAsset(c, pwd, route);
31
- if (null !== response) return response;
32
- }
33
- const generatedPublicAsset = await servePublicDirectoryAsset(c, pwd);
34
- if (null !== generatedPublicAsset) return generatedPublicAsset;
35
- return await next();
77
+ const respondPublic = async ()=>{
78
+ const route = matchPublicRoute(c.req, routes);
79
+ if (!route) return null;
80
+ const asset = {
81
+ filename: path.join(pwd, route.entryPath),
82
+ kind: 'public',
83
+ responseHeaders: route.responseHeaders
84
+ };
85
+ const serve = (representation)=>serveStaticAsset(c, {
86
+ ...asset,
87
+ ...representation,
88
+ filename: representation?.filename ?? asset.filename,
89
+ mimeFilename: asset.filename
90
+ });
91
+ const response = respondAsset ? await respondAsset(c, asset, serve, {
92
+ root: pwd,
93
+ pathPrefix
94
+ }) : void 0;
95
+ return void 0 === response ? serve() : response;
96
+ };
97
+ const response = respondPublicFallback ? await respondPublicFallback(c, respondPublic, {
98
+ root: pwd,
99
+ pathPrefix
100
+ }) : await respondPublic();
101
+ return response ?? next();
36
102
  };
37
103
  }
38
104
  function matchPublicRoute(req, routes) {
@@ -77,29 +143,38 @@ function createStaticMiddleware(options) {
77
143
  ...staticReg,
78
144
  ...iconReg
79
145
  ].join('|')})`);
80
- const publicMiddleware = createPublicMiddleware({
81
- pwd,
82
- routes: routes || []
83
- });
84
- const moduleFederationStaticServing = createModuleFederationStaticServing({
85
- pwd,
86
- pathPrefix
87
- });
88
146
  return async (c, next)=>{
89
147
  const pageRoute = c.get('route');
90
148
  const pathname = c.req.path;
91
- if (pageRoute && '' === path_0.extname(pathname)) return next();
149
+ if (pageRoute && '' === path.extname(pathname)) return next();
92
150
  const hit = staticPathRegExp.test(pathname);
93
- const staticServingRequest = await moduleFederationStaticServing.resolveRequest(pathname);
94
- if (null === staticServingRequest) return next();
95
- if (hit) {
96
- const response = await moduleFederationStaticServing.serveStaticHit(c, staticServingRequest);
97
- if (null !== response) return response;
98
- return next();
151
+ if (!hit) return createPublicMiddleware({
152
+ pwd,
153
+ routes: routes || [],
154
+ pathPrefix,
155
+ respondAsset: options.respondAsset,
156
+ respondPublicFallback: options.respondPublicFallback
157
+ })(c, next);
158
+ {
159
+ const filepath = path.join(pwd, pathname.replace(pathPrefix, ()=>''));
160
+ if (!isPathInside(filepath, pwd)) return next();
161
+ if (!await fs.pathExists(filepath)) return next();
162
+ const asset = {
163
+ filename: filepath,
164
+ kind: 'static'
165
+ };
166
+ const serve = (representation)=>serveStaticAsset(c, {
167
+ ...asset,
168
+ ...representation,
169
+ filename: representation?.filename ?? asset.filename,
170
+ mimeFilename: filepath
171
+ });
172
+ const response = options.respondAsset ? await options.respondAsset(c, asset, serve, {
173
+ root: pwd,
174
+ pathPrefix
175
+ }) : void 0;
176
+ return (void 0 === response ? await serve() : response) ?? next();
99
177
  }
100
- const moduleFederationResponse = await moduleFederationStaticServing.serveModuleFederationAsset(c, staticServingRequest);
101
- if (null !== moduleFederationResponse) return moduleFederationResponse;
102
- return publicMiddleware(c, next);
103
178
  };
104
179
  }
105
180
  const prepareFavicons = (favicon)=>{
@@ -107,4 +182,4 @@ const prepareFavicons = (favicon)=>{
107
182
  if (favicon && 'string' == typeof favicon) faviconNames.push(favicon.substring(favicon.lastIndexOf('/') + 1));
108
183
  return faviconNames;
109
184
  };
110
- export { createPublicMiddleware, createStaticMiddleware, serverStaticPlugin };
185
+ export { createPublicMiddleware, createStaticMiddleware, serveStaticAsset, serverStaticPlugin };
@@ -9,5 +9,5 @@ export { AGGRED_DIR } from "./constants.mjs";
9
9
  export { run, useHonoContext } from "./context.mjs";
10
10
  export { getLoaderCtx } from "./helper.mjs";
11
11
  export { createServerBase } from "./serverBase.mjs";
12
- export { ErrorDigest, createErrorHtml, createSafeFailureHttpResult, createSafeJsonFailureResponse, getSafeFailureStatus, onError } from "./utils/index.mjs";
12
+ export { ErrorDigest, createErrorHtml, onError } from "./utils/index.mjs";
13
13
  export { getPublicDirConfig, getPublicDirPatterns, getPublicDirRoutePrefixes, normalizePublicDir, normalizePublicDirPath, resolvePublicDirPaths } from "./utils/publicDir.mjs";
@@ -4,6 +4,7 @@ import { getHookRunners, handleSetupResult } from "./hooks.mjs";
4
4
  const compatPlugin = ()=>({
5
5
  name: '@modern-js/server-compat',
6
6
  registryHooks: {
7
+ handleError: createAsyncPipelineHook(),
7
8
  prepareWebServer: createAsyncPipelineHook(),
8
9
  prepareApiServer: createAsyncPipelineHook(),
9
10
  afterMatch: createAsyncPipelineHook(),
@@ -1,5 +1,6 @@
1
1
  import "node:module";
2
2
  import { server as server_server } from "@modern-js/plugin/server";
3
+ import { logger } from "@modern-js/utils";
3
4
  import { Hono } from "hono";
4
5
  import { run } from "./context.mjs";
5
6
  import { handleSetupResult } from "./plugins/compat/hooks.mjs";
@@ -18,22 +19,54 @@ function _class_private_method_init(obj, privateSet) {
18
19
  var _applyMiddlewares = /*#__PURE__*/ new WeakSet();
19
20
  class ServerBase {
20
21
  async init() {
21
- const { serverConfig, config: cliConfig } = this.serverOptions;
22
- const mergedConfig = loadConfig({
23
- cliConfig,
24
- serverConfig: serverConfig || {}
25
- });
26
- const { serverContext } = await server_server.run({
27
- plugins: this.plugins,
28
- options: this.serverOptions,
29
- config: mergedConfig,
30
- handleSetupResult: handleSetupResult
31
- });
32
- serverContext.serverBase = this;
33
- this.serverContext = serverContext;
34
- await serverContext.hooks.onPrepare.call();
35
- _class_private_method_get(this, _applyMiddlewares, applyMiddlewares).call(this);
36
- return this;
22
+ try {
23
+ const { serverConfig, config: cliConfig } = this.serverOptions;
24
+ const mergedConfig = loadConfig({
25
+ cliConfig,
26
+ serverConfig: serverConfig || {}
27
+ });
28
+ const { serverContext } = await server_server.run({
29
+ plugins: this.plugins,
30
+ options: this.serverOptions,
31
+ config: mergedConfig,
32
+ handleSetupResult: handleSetupResult
33
+ });
34
+ serverContext.serverBase = this;
35
+ this.serverContext = serverContext;
36
+ await serverContext.hooks.onPrepare.call();
37
+ _class_private_method_get(this, _applyMiddlewares, applyMiddlewares).call(this);
38
+ return this;
39
+ } catch (error) {
40
+ await this.dispose().catch((disposeError)=>{
41
+ logger.error(disposeError);
42
+ });
43
+ throw error;
44
+ }
45
+ }
46
+ onDispose(disposer) {
47
+ if (this.disposePromise) throw new Error('Cannot register a disposer on a retired server.');
48
+ this.disposers.add(disposer);
49
+ return ()=>{
50
+ this.disposers.delete(disposer);
51
+ };
52
+ }
53
+ dispose() {
54
+ if (!this.disposePromise) {
55
+ const disposers = [
56
+ ...this.disposers
57
+ ].reverse();
58
+ this.disposers.clear();
59
+ this.disposePromise = Promise.resolve().then(async ()=>{
60
+ const errors = [];
61
+ for (const disposer of disposers)try {
62
+ await disposer();
63
+ } catch (error) {
64
+ errors.push(error);
65
+ }
66
+ if (errors.length > 0) throw new AggregateError(errors, 'Failed to dispose server.');
67
+ });
68
+ }
69
+ return this.disposePromise;
37
70
  }
38
71
  addPlugins(plugins) {
39
72
  this.plugins.push(...plugins);
@@ -79,7 +112,15 @@ class ServerBase {
79
112
  }
80
113
  constructor(options){
81
114
  _class_private_method_init(this, _applyMiddlewares);
82
- this.plugins = [];
115
+ this.plugins = [
116
+ {
117
+ name: '@modern-js/server-lifecycle',
118
+ _registryApi: ()=>({
119
+ onDispose: (disposer)=>this.onDispose(disposer)
120
+ })
121
+ }
122
+ ];
123
+ this.disposers = new Set();
83
124
  this.serverContext = null;
84
125
  this.serverOptions = options;
85
126
  this.app = new Hono();
@@ -51,5 +51,4 @@ function onError(digest, error, monitors, req) {
51
51
  else if (req) console.error(`Server Error - ${digest}, error = ${error instanceof Error ? error.stack || error.message : error}, req.url = ${req.url}, req.headers = ${JSON.stringify(headerData)}`);
52
52
  else console.error(`Server Error - ${digest}, error = ${error instanceof Error ? error.stack || error.message : error} `);
53
53
  }
54
- export { createSafeFailureHttpResult, createSafeJsonFailureResponse, getSafeFailureStatus } from "@modern-js/runtime-extensions/safe-failure";
55
54
  export { createErrorHtml, error_ErrorDigest as ErrorDigest, onError };
@@ -3,3 +3,5 @@ export type { ServerNodeContext, ServerNodeMiddleware } from './hono.js';
3
3
  export { connectMid2HonoMid, connectMockMid2HonoMid, httpCallBack2HonoMid, } from './hono.js';
4
4
  export { createNodeServer, createWebRequest, sendResponse, } from './node.js';
5
5
  export { getHtmlTemplates, getServerManifest, injectNodeSeverPlugin, injectResourcePlugin, injectRscManifestPlugin, serverStaticPlugin, } from './plugins/index.js';
6
+ export type { ServerStaticPluginOptions, ServeStaticAsset, StaticAsset, StaticAssetRequest, StaticAssetResponder, StaticPublicFallbackResponder, } from './plugins/static.js';
7
+ export { serveStaticAsset } from './plugins/static.js';