@bleedingdev/modern-js-server-core 3.8.3-ultramodern.1 → 3.8.3-ultramodern.3

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.
@@ -69,7 +69,9 @@ const createWebRequest = (req, res, body)=>{
69
69
  signal: controller.signal
70
70
  };
71
71
  res.on('close', ()=>controller.abort('res closed'));
72
- const url = `http://${req.headers.host}${req.url}`;
72
+ const protocol = 'encrypted' in req.socket && req.socket.encrypted ? 'https' : 'http';
73
+ const authority = req.headers[':authority'] ?? req.headers.host;
74
+ const url = `${protocol}://${authority}${req.url}`;
73
75
  const needsRequestBody = body || !('GET' === method || 'HEAD' === method);
74
76
  const cloneableReq = needsRequestBody ? external_cloneable_readable_default()(req) : null;
75
77
  if (needsRequestBody) {
@@ -60,7 +60,11 @@ const MODULE_FEDERATION_OPTIONAL_FILES = [
60
60
  'mf-stats.json'
61
61
  ];
62
62
  const trimLeadingSlash = (value)=>value.replace(/^\/+/, '');
63
- const getModuleFederationRequestPath = (pathname, pathPrefix)=>trimLeadingSlash(pathname.replace(pathPrefix, ()=>''));
63
+ const getModuleFederationRequestPath = (pathname, pathPrefix)=>{
64
+ const normalizedPrefix = `/${trimLeadingSlash(pathPrefix)}`.replace(/\/+$/u, '');
65
+ const requestPath = normalizedPrefix && (pathname === normalizedPrefix || pathname.startsWith(`${normalizedPrefix}/`)) ? pathname.slice(normalizedPrefix.length) : pathname;
66
+ return trimLeadingSlash(requestPath);
67
+ };
64
68
  const isModuleFederationManifestRequest = (requestPath)=>MODULE_FEDERATION_MANIFEST_FILES.includes(requestPath);
65
69
  const isBackendModuleFederationManifestRequest = (requestPath)=>requestPath === BACKEND_MODULE_FEDERATION_MANIFEST_FILE;
66
70
  const applyModuleFederationAssetHeaders = (c)=>{
@@ -40,23 +40,32 @@ const PRE_COMPRESSED_SUPPORTED_ENCODINGS = [
40
40
  'br',
41
41
  'gzip'
42
42
  ];
43
+ const QUALITY_VALUE_PATTERN = /^(?:0(?:\.\d{0,3})?|1(?:\.0{0,3})?)$/u;
43
44
  const parseAcceptEncoding = (value)=>value.split(',').map((item)=>item.trim()).filter(Boolean).map((item)=>{
44
45
  const [rawName, ...params] = item.split(';');
45
46
  const name = rawName.trim().toLowerCase();
46
47
  let q = 1;
48
+ let qualitySeen = false;
47
49
  for (const param of params){
48
50
  const [key, rawValue] = param.split('=').map((v)=>v.trim());
49
- if ('q' !== key.toLowerCase() || null == rawValue) continue;
50
- const parsedQ = Number(rawValue);
51
- if (!Number.isNaN(parsedQ)) q = Math.max(0, Math.min(parsedQ, 1));
51
+ if ('q' === key.toLowerCase()) {
52
+ if (qualitySeen || null == rawValue || !QUALITY_VALUE_PATTERN.test(rawValue)) {
53
+ q = 0;
54
+ break;
55
+ }
56
+ qualitySeen = true;
57
+ q = Number(rawValue);
58
+ }
52
59
  }
53
60
  return {
54
61
  name,
55
62
  q
56
63
  };
57
64
  });
58
- const getAcceptedEncodings = (value)=>{
59
- if (!value) return [];
65
+ const getAcceptedRepresentations = (value)=>{
66
+ if (!value) return [
67
+ 'identity'
68
+ ];
60
69
  const parsed = parseAcceptEncoding(value);
61
70
  const qualityByEncoding = new Map();
62
71
  let wildcardQuality;
@@ -72,10 +81,17 @@ const getAcceptedEncodings = (value)=>{
72
81
  if (void 0 !== explicit) return explicit;
73
82
  return wildcardQuality ?? 0;
74
83
  };
75
- return PRE_COMPRESSED_SUPPORTED_ENCODINGS.map((encoding)=>({
76
- encoding,
77
- quality: getQuality(encoding)
78
- })).filter((item)=>item.quality > 0).sort((a, b)=>b.quality - a.quality).map((item)=>item.encoding);
84
+ const identityQuality = qualityByEncoding.get('identity') ?? (0 === wildcardQuality ? 0 : 1);
85
+ return [
86
+ ...PRE_COMPRESSED_SUPPORTED_ENCODINGS.map((encoding)=>({
87
+ encoding,
88
+ quality: getQuality(encoding)
89
+ })),
90
+ {
91
+ encoding: 'identity',
92
+ quality: identityQuality
93
+ }
94
+ ].filter((item)=>item.quality > 0).sort((a, b)=>b.quality - a.quality).map((item)=>item.encoding);
79
95
  };
80
96
  const appendVaryHeader = (c, value)=>{
81
97
  const current = c.res.headers.get('Vary');
@@ -91,34 +107,38 @@ const resolvePreCompressedAsset = async (c, filepath)=>{
91
107
  utils_namespaceObject.fs.pathExists(gzipPath)
92
108
  ]);
93
109
  const hasVariant = hasBr || hasGzip;
94
- if (!hasVariant) return {
95
- selected: null,
96
- hasVariant: false
97
- };
98
- const acceptedEncodings = getAcceptedEncodings(c.req.header('accept-encoding'));
99
- for (const encoding of acceptedEncodings){
110
+ const acceptedRepresentations = getAcceptedRepresentations(c.req.header('accept-encoding'));
111
+ for (const encoding of acceptedRepresentations){
112
+ if ('identity' === encoding) return {
113
+ selected: null,
114
+ hasVariant,
115
+ acceptable: true
116
+ };
100
117
  if ('br' === encoding && hasBr) return {
101
118
  selected: {
102
119
  filepath: brPath,
103
120
  encoding
104
121
  },
105
- hasVariant: true
122
+ hasVariant: true,
123
+ acceptable: true
106
124
  };
107
125
  if ('gzip' === encoding && hasGzip) return {
108
126
  selected: {
109
127
  filepath: gzipPath,
110
128
  encoding
111
129
  },
112
- hasVariant: true
130
+ hasVariant: true,
131
+ acceptable: true
113
132
  };
114
133
  }
115
134
  return {
116
135
  selected: null,
117
- hasVariant: true
136
+ hasVariant,
137
+ acceptable: false
118
138
  };
119
139
  };
120
140
  const applyPreCompressedAssetHeaders = (c, preCompressedAsset)=>{
121
- if (preCompressedAsset.hasVariant) appendVaryHeader(c, 'Accept-Encoding');
141
+ if (preCompressedAsset.hasVariant || !preCompressedAsset.acceptable) appendVaryHeader(c, 'Accept-Encoding');
122
142
  if (preCompressedAsset.selected) c.header('Content-Encoding', preCompressedAsset.selected.encoding);
123
143
  };
124
144
  exports.applyPreCompressedAssetHeaders = __webpack_exports__.applyPreCompressedAssetHeaders;
@@ -48,11 +48,16 @@ const external_path_namespaceObject = require("path");
48
48
  var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
49
49
  const external_staticModuleFederation_js_namespaceObject = require("./staticModuleFederation.js");
50
50
  const external_staticPrecompressed_js_namespaceObject = require("./staticPrecompressed.js");
51
+ const MODULE_FEDERATION_ASSET_REFRESH_INTERVAL_MS = 1000;
51
52
  const getStaticMimeType = (filename)=>(0, mime_namespaceObject.getMimeType)(filename) ?? ('.cjs' === external_path_default().extname(filename).toLowerCase() ? "text/javascript; charset=UTF-8" : void 0);
52
53
  const servePreCompressedPublicRouteAsset = async (c, pwd, route)=>{
53
54
  const { entryPath } = route;
54
55
  const originFilename = external_path_default().join(pwd, entryPath);
55
56
  const preCompressedAsset = await (0, external_staticPrecompressed_js_namespaceObject.resolvePreCompressedAsset)(c, originFilename);
57
+ if (!preCompressedAsset.acceptable) {
58
+ (0, external_staticPrecompressed_js_namespaceObject.applyPreCompressedAssetHeaders)(c, preCompressedAsset);
59
+ return c.body(null, 406);
60
+ }
56
61
  const filename = preCompressedAsset.selected?.filepath ?? originFilename;
57
62
  const data = await fileReader_namespaceObject.fileReader.readFile(filename, 'buffer');
58
63
  const mimeType = getStaticMimeType(originFilename);
@@ -99,6 +104,10 @@ const servePublicDirectoryAsset = async (c, pwd)=>{
99
104
  const originFilename = await resolvePublicDirectoryAsset(pwd, c.req.path);
100
105
  if (null === originFilename) return null;
101
106
  const preCompressedAsset = await (0, external_staticPrecompressed_js_namespaceObject.resolvePreCompressedAsset)(c, originFilename);
107
+ if (!preCompressedAsset.acceptable) {
108
+ (0, external_staticPrecompressed_js_namespaceObject.applyPreCompressedAssetHeaders)(c, preCompressedAsset);
109
+ return c.body(null, 406);
110
+ }
102
111
  const selectedFilename = preCompressedAsset.selected?.filepath ?? originFilename;
103
112
  const publicDirectory = await utils_namespaceObject.fs.realpath(external_path_default().join(pwd, 'public'));
104
113
  let realSelectedFilename;
@@ -119,10 +128,19 @@ const servePublicDirectoryAsset = async (c, pwd)=>{
119
128
  return c.body(body, 200);
120
129
  };
121
130
  const createModuleFederationStaticServing = ({ pwd, pathPrefix })=>{
122
- let moduleFederationAssetsPromise = null;
131
+ let moduleFederationAssets = null;
132
+ let moduleFederationAssetsExpiresAt = 0;
133
+ let moduleFederationAssetsRefresh = null;
123
134
  const getModuleFederationAssets = async ()=>{
124
- if (!moduleFederationAssetsPromise) moduleFederationAssetsPromise = (0, external_staticModuleFederation_js_namespaceObject.getModuleFederationAssetList)(pwd);
125
- return moduleFederationAssetsPromise;
135
+ if (moduleFederationAssets && Date.now() < moduleFederationAssetsExpiresAt) return moduleFederationAssets;
136
+ if (!moduleFederationAssetsRefresh) moduleFederationAssetsRefresh = (0, external_staticModuleFederation_js_namespaceObject.getModuleFederationAssetList)(pwd).then((assets)=>{
137
+ moduleFederationAssets = assets;
138
+ moduleFederationAssetsExpiresAt = Date.now() + MODULE_FEDERATION_ASSET_REFRESH_INTERVAL_MS;
139
+ return assets;
140
+ }).finally(()=>{
141
+ moduleFederationAssetsRefresh = null;
142
+ });
143
+ return moduleFederationAssetsRefresh;
126
144
  };
127
145
  const resolveRequest = async (pathname)=>{
128
146
  const requestPath = (0, external_staticModuleFederation_js_namespaceObject.getModuleFederationRequestPath)(pathname, pathPrefix);
@@ -143,8 +161,13 @@ const createModuleFederationStaticServing = ({ pwd, pathPrefix })=>{
143
161
  const canUsePreCompressed = !shouldPatchManifest && !shouldPatchRemoteEntry;
144
162
  const preCompressedAsset = canUsePreCompressed ? await (0, external_staticPrecompressed_js_namespaceObject.resolvePreCompressedAsset)(c, filepath) : {
145
163
  selected: null,
146
- hasVariant: false
164
+ hasVariant: false,
165
+ acceptable: true
147
166
  };
167
+ if (!preCompressedAsset.acceptable) {
168
+ (0, external_staticPrecompressed_js_namespaceObject.applyPreCompressedAssetHeaders)(c, preCompressedAsset);
169
+ return c.body(null, 406);
170
+ }
148
171
  const targetFilepath = preCompressedAsset.selected?.filepath ?? filepath;
149
172
  const chunk = await fileReader_namespaceObject.fileReader.readFileFromSystem(targetFilepath, 'buffer');
150
173
  if (null === chunk) return null;
@@ -32,6 +32,12 @@ __webpack_require__.d(__webpack_exports__, {
32
32
  });
33
33
  const constants_namespaceObject = require("@modern-js/utils/universal/constants");
34
34
  const serverActionHandler = async (req, { serverManifest, routeInfo, rscClientManifest })=>{
35
+ if ('POST' !== req.method) return new Response('Method not allowed', {
36
+ status: 405,
37
+ headers: {
38
+ Allow: 'POST'
39
+ }
40
+ });
35
41
  const serverBundle = serverManifest?.renderBundles?.[routeInfo.entryName || constants_namespaceObject.MAIN_ENTRY_NAME];
36
42
  if (!serverBundle) return new Response('Cannot find server bundle for server action', {
37
43
  status: 500
@@ -30,69 +30,17 @@ __webpack_require__.r(__webpack_exports__);
30
30
  __webpack_require__.d(__webpack_exports__, {
31
31
  ErrorDigest: ()=>error_ErrorDigest,
32
32
  createErrorHtml: ()=>createErrorHtml,
33
- createSafeFailureHttpResult: ()=>createSafeFailureHttpResult,
34
- createSafeJsonFailureResponse: ()=>createSafeJsonFailureResponse,
35
- getSafeFailureStatus: ()=>getSafeFailureStatus,
33
+ createSafeFailureHttpResult: ()=>safe_failure_namespaceObject.createSafeFailureHttpResult,
34
+ createSafeJsonFailureResponse: ()=>safe_failure_namespaceObject.createSafeJsonFailureResponse,
35
+ getSafeFailureStatus: ()=>safe_failure_namespaceObject.getSafeFailureStatus,
36
36
  onError: ()=>onError
37
37
  });
38
+ const safe_failure_namespaceObject = require("@modern-js/runtime-extensions/safe-failure");
38
39
  const external_request_js_namespaceObject = require("./request.js");
39
40
  const ERROR_PAGE_TEXT = {
40
41
  404: 'This page could not be found.',
41
42
  500: 'Internal Server Error.'
42
43
  };
43
- const SAFE_FAILURE_MESSAGES = {
44
- 500: 'Internal Server Error',
45
- 503: 'Service Unavailable'
46
- };
47
- const SAFE_FAILURE_CODES = {
48
- 500: 'INTERNAL_SERVER_ERROR',
49
- 503: 'SERVICE_UNAVAILABLE'
50
- };
51
- const readErrorProperty = (error, key)=>{
52
- if ('object' != typeof error || null === error || !(key in error)) return;
53
- return error[key];
54
- };
55
- const normalizeFailureStatus = (value)=>{
56
- if ('number' != typeof value || !Number.isInteger(value)) return;
57
- return value >= 400 && value <= 599 ? value : void 0;
58
- };
59
- const normalizeRetryAfter = (value)=>{
60
- if ('number' == typeof value && Number.isFinite(value) && value >= 0) return String(Math.ceil(value));
61
- if ('string' == typeof value) {
62
- const trimmed = value.trim();
63
- return trimmed.length > 0 ? trimmed : void 0;
64
- }
65
- if (value instanceof Date) return value.toUTCString();
66
- };
67
- const getSafeFailureStatus = (error)=>normalizeFailureStatus(readErrorProperty(error, 'status')) ?? normalizeFailureStatus(readErrorProperty(error, 'statusCode')) ?? 500;
68
- const createSafeFailureHttpResult = (error)=>{
69
- const status = getSafeFailureStatus(error);
70
- const retryAfter = 503 === status ? normalizeRetryAfter(readErrorProperty(error, 'retryAfter')) ?? normalizeRetryAfter(readErrorProperty(error, 'retryAfterSeconds')) ?? normalizeRetryAfter('number' == typeof readErrorProperty(error, 'retryAfterMs') ? readErrorProperty(error, 'retryAfterMs') / 1000 : void 0) : void 0;
71
- return {
72
- status,
73
- body: {
74
- success: false,
75
- error: {
76
- code: SAFE_FAILURE_CODES[status] ?? 'REQUEST_FAILED',
77
- message: SAFE_FAILURE_MESSAGES[status] ?? 'Request failed',
78
- status
79
- }
80
- },
81
- headers: {
82
- 'content-type': 'application/json; charset=utf-8',
83
- ...retryAfter ? {
84
- 'Retry-After': retryAfter
85
- } : {}
86
- }
87
- };
88
- };
89
- const createSafeJsonFailureResponse = (error)=>{
90
- const result = createSafeFailureHttpResult(error);
91
- return new Response(JSON.stringify(result.body), {
92
- status: result.status,
93
- headers: result.headers
94
- });
95
- };
96
44
  const createErrorHtml = (status)=>{
97
45
  const text = ERROR_PAGE_TEXT[status] || '';
98
46
  const title = `${status}: ${text}`;
@@ -25,7 +25,9 @@ const createWebRequest = (req, res, body)=>{
25
25
  signal: controller.signal
26
26
  };
27
27
  res.on('close', ()=>controller.abort('res closed'));
28
- const url = `http://${req.headers.host}${req.url}`;
28
+ const protocol = 'encrypted' in req.socket && req.socket.encrypted ? 'https' : 'http';
29
+ const authority = req.headers[':authority'] ?? req.headers.host;
30
+ const url = `${protocol}://${authority}${req.url}`;
29
31
  const needsRequestBody = body || !('GET' === method || 'HEAD' === method);
30
32
  const cloneableReq = needsRequestBody ? cloneable_readable(req) : null;
31
33
  if (needsRequestBody) {
@@ -11,7 +11,11 @@ const MODULE_FEDERATION_OPTIONAL_FILES = [
11
11
  'mf-stats.json'
12
12
  ];
13
13
  const trimLeadingSlash = (value)=>value.replace(/^\/+/, '');
14
- const getModuleFederationRequestPath = (pathname, pathPrefix)=>trimLeadingSlash(pathname.replace(pathPrefix, ()=>''));
14
+ const getModuleFederationRequestPath = (pathname, pathPrefix)=>{
15
+ const normalizedPrefix = `/${trimLeadingSlash(pathPrefix)}`.replace(/\/+$/u, '');
16
+ const requestPath = normalizedPrefix && (pathname === normalizedPrefix || pathname.startsWith(`${normalizedPrefix}/`)) ? pathname.slice(normalizedPrefix.length) : pathname;
17
+ return trimLeadingSlash(requestPath);
18
+ };
15
19
  const isModuleFederationManifestRequest = (requestPath)=>MODULE_FEDERATION_MANIFEST_FILES.includes(requestPath);
16
20
  const isBackendModuleFederationManifestRequest = (requestPath)=>requestPath === BACKEND_MODULE_FEDERATION_MANIFEST_FILE;
17
21
  const applyModuleFederationAssetHeaders = (c)=>{
@@ -7,23 +7,32 @@ const PRE_COMPRESSED_SUPPORTED_ENCODINGS = [
7
7
  'br',
8
8
  'gzip'
9
9
  ];
10
+ const QUALITY_VALUE_PATTERN = /^(?:0(?:\.\d{0,3})?|1(?:\.0{0,3})?)$/u;
10
11
  const parseAcceptEncoding = (value)=>value.split(',').map((item)=>item.trim()).filter(Boolean).map((item)=>{
11
12
  const [rawName, ...params] = item.split(';');
12
13
  const name = rawName.trim().toLowerCase();
13
14
  let q = 1;
15
+ let qualitySeen = false;
14
16
  for (const param of params){
15
17
  const [key, rawValue] = param.split('=').map((v)=>v.trim());
16
- if ('q' !== key.toLowerCase() || null == rawValue) continue;
17
- const parsedQ = Number(rawValue);
18
- if (!Number.isNaN(parsedQ)) q = Math.max(0, Math.min(parsedQ, 1));
18
+ if ('q' === key.toLowerCase()) {
19
+ if (qualitySeen || null == rawValue || !QUALITY_VALUE_PATTERN.test(rawValue)) {
20
+ q = 0;
21
+ break;
22
+ }
23
+ qualitySeen = true;
24
+ q = Number(rawValue);
25
+ }
19
26
  }
20
27
  return {
21
28
  name,
22
29
  q
23
30
  };
24
31
  });
25
- const getAcceptedEncodings = (value)=>{
26
- if (!value) return [];
32
+ const getAcceptedRepresentations = (value)=>{
33
+ if (!value) return [
34
+ 'identity'
35
+ ];
27
36
  const parsed = parseAcceptEncoding(value);
28
37
  const qualityByEncoding = new Map();
29
38
  let wildcardQuality;
@@ -39,10 +48,17 @@ const getAcceptedEncodings = (value)=>{
39
48
  if (void 0 !== explicit) return explicit;
40
49
  return wildcardQuality ?? 0;
41
50
  };
42
- return PRE_COMPRESSED_SUPPORTED_ENCODINGS.map((encoding)=>({
43
- encoding,
44
- quality: getQuality(encoding)
45
- })).filter((item)=>item.quality > 0).sort((a, b)=>b.quality - a.quality).map((item)=>item.encoding);
51
+ const identityQuality = qualityByEncoding.get('identity') ?? (0 === wildcardQuality ? 0 : 1);
52
+ return [
53
+ ...PRE_COMPRESSED_SUPPORTED_ENCODINGS.map((encoding)=>({
54
+ encoding,
55
+ quality: getQuality(encoding)
56
+ })),
57
+ {
58
+ encoding: 'identity',
59
+ quality: identityQuality
60
+ }
61
+ ].filter((item)=>item.quality > 0).sort((a, b)=>b.quality - a.quality).map((item)=>item.encoding);
46
62
  };
47
63
  const appendVaryHeader = (c, value)=>{
48
64
  const current = c.res.headers.get('Vary');
@@ -58,34 +74,38 @@ const resolvePreCompressedAsset = async (c, filepath)=>{
58
74
  fs.pathExists(gzipPath)
59
75
  ]);
60
76
  const hasVariant = hasBr || hasGzip;
61
- if (!hasVariant) return {
62
- selected: null,
63
- hasVariant: false
64
- };
65
- const acceptedEncodings = getAcceptedEncodings(c.req.header('accept-encoding'));
66
- for (const encoding of acceptedEncodings){
77
+ const acceptedRepresentations = getAcceptedRepresentations(c.req.header('accept-encoding'));
78
+ for (const encoding of acceptedRepresentations){
79
+ if ('identity' === encoding) return {
80
+ selected: null,
81
+ hasVariant,
82
+ acceptable: true
83
+ };
67
84
  if ('br' === encoding && hasBr) return {
68
85
  selected: {
69
86
  filepath: brPath,
70
87
  encoding
71
88
  },
72
- hasVariant: true
89
+ hasVariant: true,
90
+ acceptable: true
73
91
  };
74
92
  if ('gzip' === encoding && hasGzip) return {
75
93
  selected: {
76
94
  filepath: gzipPath,
77
95
  encoding
78
96
  },
79
- hasVariant: true
97
+ hasVariant: true,
98
+ acceptable: true
80
99
  };
81
100
  }
82
101
  return {
83
102
  selected: null,
84
- hasVariant: true
103
+ hasVariant,
104
+ acceptable: false
85
105
  };
86
106
  };
87
107
  const applyPreCompressedAssetHeaders = (c, preCompressedAsset)=>{
88
- if (preCompressedAsset.hasVariant) appendVaryHeader(c, 'Accept-Encoding');
108
+ if (preCompressedAsset.hasVariant || !preCompressedAsset.acceptable) appendVaryHeader(c, 'Accept-Encoding');
89
109
  if (preCompressedAsset.selected) c.header('Content-Encoding', preCompressedAsset.selected.encoding);
90
110
  };
91
111
  export { applyPreCompressedAssetHeaders, resolvePreCompressedAsset };
@@ -4,11 +4,16 @@ import { getMimeType } from "hono/utils/mime";
4
4
  import path from "path";
5
5
  import { applyModuleFederationAssetHeaders, getModuleFederationAssetList, getModuleFederationRequestPath, isBackendModuleFederationManifestRequest, isModuleFederationManifestRequest, patchModuleFederationManifestPublicPath, patchModuleFederationRemoteEntryPublicPath } from "./staticModuleFederation.mjs";
6
6
  import { applyPreCompressedAssetHeaders, resolvePreCompressedAsset } from "./staticPrecompressed.mjs";
7
+ const MODULE_FEDERATION_ASSET_REFRESH_INTERVAL_MS = 1000;
7
8
  const getStaticMimeType = (filename)=>getMimeType(filename) ?? ('.cjs' === path.extname(filename).toLowerCase() ? "text/javascript; charset=UTF-8" : void 0);
8
9
  const servePreCompressedPublicRouteAsset = async (c, pwd, route)=>{
9
10
  const { entryPath } = route;
10
11
  const originFilename = path.join(pwd, entryPath);
11
12
  const preCompressedAsset = await resolvePreCompressedAsset(c, originFilename);
13
+ if (!preCompressedAsset.acceptable) {
14
+ applyPreCompressedAssetHeaders(c, preCompressedAsset);
15
+ return c.body(null, 406);
16
+ }
12
17
  const filename = preCompressedAsset.selected?.filepath ?? originFilename;
13
18
  const data = await fileReader.readFile(filename, 'buffer');
14
19
  const mimeType = getStaticMimeType(originFilename);
@@ -55,6 +60,10 @@ const servePublicDirectoryAsset = async (c, pwd)=>{
55
60
  const originFilename = await resolvePublicDirectoryAsset(pwd, c.req.path);
56
61
  if (null === originFilename) return null;
57
62
  const preCompressedAsset = await resolvePreCompressedAsset(c, originFilename);
63
+ if (!preCompressedAsset.acceptable) {
64
+ applyPreCompressedAssetHeaders(c, preCompressedAsset);
65
+ return c.body(null, 406);
66
+ }
58
67
  const selectedFilename = preCompressedAsset.selected?.filepath ?? originFilename;
59
68
  const publicDirectory = await fs.realpath(path.join(pwd, 'public'));
60
69
  let realSelectedFilename;
@@ -75,10 +84,19 @@ const servePublicDirectoryAsset = async (c, pwd)=>{
75
84
  return c.body(body, 200);
76
85
  };
77
86
  const createModuleFederationStaticServing = ({ pwd, pathPrefix })=>{
78
- let moduleFederationAssetsPromise = null;
87
+ let moduleFederationAssets = null;
88
+ let moduleFederationAssetsExpiresAt = 0;
89
+ let moduleFederationAssetsRefresh = null;
79
90
  const getModuleFederationAssets = async ()=>{
80
- if (!moduleFederationAssetsPromise) moduleFederationAssetsPromise = getModuleFederationAssetList(pwd);
81
- return moduleFederationAssetsPromise;
91
+ if (moduleFederationAssets && Date.now() < moduleFederationAssetsExpiresAt) return moduleFederationAssets;
92
+ if (!moduleFederationAssetsRefresh) moduleFederationAssetsRefresh = getModuleFederationAssetList(pwd).then((assets)=>{
93
+ moduleFederationAssets = assets;
94
+ moduleFederationAssetsExpiresAt = Date.now() + MODULE_FEDERATION_ASSET_REFRESH_INTERVAL_MS;
95
+ return assets;
96
+ }).finally(()=>{
97
+ moduleFederationAssetsRefresh = null;
98
+ });
99
+ return moduleFederationAssetsRefresh;
82
100
  };
83
101
  const resolveRequest = async (pathname)=>{
84
102
  const requestPath = getModuleFederationRequestPath(pathname, pathPrefix);
@@ -99,8 +117,13 @@ const createModuleFederationStaticServing = ({ pwd, pathPrefix })=>{
99
117
  const canUsePreCompressed = !shouldPatchManifest && !shouldPatchRemoteEntry;
100
118
  const preCompressedAsset = canUsePreCompressed ? await resolvePreCompressedAsset(c, filepath) : {
101
119
  selected: null,
102
- hasVariant: false
120
+ hasVariant: false,
121
+ acceptable: true
103
122
  };
123
+ if (!preCompressedAsset.acceptable) {
124
+ applyPreCompressedAssetHeaders(c, preCompressedAsset);
125
+ return c.body(null, 406);
126
+ }
104
127
  const targetFilepath = preCompressedAsset.selected?.filepath ?? filepath;
105
128
  const chunk = await fileReader.readFileFromSystem(targetFilepath, 'buffer');
106
129
  if (null === chunk) return null;
@@ -1,5 +1,11 @@
1
1
  import { MAIN_ENTRY_NAME } from "@modern-js/utils/universal/constants";
2
2
  const serverActionHandler = async (req, { serverManifest, routeInfo, rscClientManifest })=>{
3
+ if ('POST' !== req.method) return new Response('Method not allowed', {
4
+ status: 405,
5
+ headers: {
6
+ Allow: 'POST'
7
+ }
8
+ });
3
9
  const serverBundle = serverManifest?.renderBundles?.[routeInfo.entryName || MAIN_ENTRY_NAME];
4
10
  if (!serverBundle) return new Response('Cannot find server bundle for server action', {
5
11
  status: 500
@@ -3,59 +3,6 @@ const ERROR_PAGE_TEXT = {
3
3
  404: 'This page could not be found.',
4
4
  500: 'Internal Server Error.'
5
5
  };
6
- const SAFE_FAILURE_MESSAGES = {
7
- 500: 'Internal Server Error',
8
- 503: 'Service Unavailable'
9
- };
10
- const SAFE_FAILURE_CODES = {
11
- 500: 'INTERNAL_SERVER_ERROR',
12
- 503: 'SERVICE_UNAVAILABLE'
13
- };
14
- const readErrorProperty = (error, key)=>{
15
- if ('object' != typeof error || null === error || !(key in error)) return;
16
- return error[key];
17
- };
18
- const normalizeFailureStatus = (value)=>{
19
- if ('number' != typeof value || !Number.isInteger(value)) return;
20
- return value >= 400 && value <= 599 ? value : void 0;
21
- };
22
- const normalizeRetryAfter = (value)=>{
23
- if ('number' == typeof value && Number.isFinite(value) && value >= 0) return String(Math.ceil(value));
24
- if ('string' == typeof value) {
25
- const trimmed = value.trim();
26
- return trimmed.length > 0 ? trimmed : void 0;
27
- }
28
- if (value instanceof Date) return value.toUTCString();
29
- };
30
- const getSafeFailureStatus = (error)=>normalizeFailureStatus(readErrorProperty(error, 'status')) ?? normalizeFailureStatus(readErrorProperty(error, 'statusCode')) ?? 500;
31
- const createSafeFailureHttpResult = (error)=>{
32
- const status = getSafeFailureStatus(error);
33
- const retryAfter = 503 === status ? normalizeRetryAfter(readErrorProperty(error, 'retryAfter')) ?? normalizeRetryAfter(readErrorProperty(error, 'retryAfterSeconds')) ?? normalizeRetryAfter('number' == typeof readErrorProperty(error, 'retryAfterMs') ? readErrorProperty(error, 'retryAfterMs') / 1000 : void 0) : void 0;
34
- return {
35
- status,
36
- body: {
37
- success: false,
38
- error: {
39
- code: SAFE_FAILURE_CODES[status] ?? 'REQUEST_FAILED',
40
- message: SAFE_FAILURE_MESSAGES[status] ?? 'Request failed',
41
- status
42
- }
43
- },
44
- headers: {
45
- 'content-type': 'application/json; charset=utf-8',
46
- ...retryAfter ? {
47
- 'Retry-After': retryAfter
48
- } : {}
49
- }
50
- };
51
- };
52
- const createSafeJsonFailureResponse = (error)=>{
53
- const result = createSafeFailureHttpResult(error);
54
- return new Response(JSON.stringify(result.body), {
55
- status: result.status,
56
- headers: result.headers
57
- });
58
- };
59
6
  const createErrorHtml = (status)=>{
60
7
  const text = ERROR_PAGE_TEXT[status] || '';
61
8
  const title = `${status}: ${text}`;
@@ -103,4 +50,5 @@ function onError(digest, error, monitors, req) {
103
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)}`);
104
51
  else console.error(`Server Error - ${digest}, error = ${error instanceof Error ? error.stack || error.message : error} `);
105
52
  }
106
- export { createErrorHtml, createSafeFailureHttpResult, createSafeJsonFailureResponse, error_ErrorDigest as ErrorDigest, getSafeFailureStatus, onError };
53
+ export { createSafeFailureHttpResult, createSafeJsonFailureResponse, getSafeFailureStatus } from "@modern-js/runtime-extensions/safe-failure";
54
+ export { createErrorHtml, error_ErrorDigest as ErrorDigest, onError };
@@ -26,7 +26,9 @@ const createWebRequest = (req, res, body)=>{
26
26
  signal: controller.signal
27
27
  };
28
28
  res.on('close', ()=>controller.abort('res closed'));
29
- const url = `http://${req.headers.host}${req.url}`;
29
+ const protocol = 'encrypted' in req.socket && req.socket.encrypted ? 'https' : 'http';
30
+ const authority = req.headers[':authority'] ?? req.headers.host;
31
+ const url = `${protocol}://${authority}${req.url}`;
30
32
  const needsRequestBody = body || !('GET' === method || 'HEAD' === method);
31
33
  const cloneableReq = needsRequestBody ? cloneable_readable(req) : null;
32
34
  if (needsRequestBody) {
@@ -12,7 +12,11 @@ const MODULE_FEDERATION_OPTIONAL_FILES = [
12
12
  'mf-stats.json'
13
13
  ];
14
14
  const trimLeadingSlash = (value)=>value.replace(/^\/+/, '');
15
- const getModuleFederationRequestPath = (pathname, pathPrefix)=>trimLeadingSlash(pathname.replace(pathPrefix, ()=>''));
15
+ const getModuleFederationRequestPath = (pathname, pathPrefix)=>{
16
+ const normalizedPrefix = `/${trimLeadingSlash(pathPrefix)}`.replace(/\/+$/u, '');
17
+ const requestPath = normalizedPrefix && (pathname === normalizedPrefix || pathname.startsWith(`${normalizedPrefix}/`)) ? pathname.slice(normalizedPrefix.length) : pathname;
18
+ return trimLeadingSlash(requestPath);
19
+ };
16
20
  const isModuleFederationManifestRequest = (requestPath)=>MODULE_FEDERATION_MANIFEST_FILES.includes(requestPath);
17
21
  const isBackendModuleFederationManifestRequest = (requestPath)=>requestPath === BACKEND_MODULE_FEDERATION_MANIFEST_FILE;
18
22
  const applyModuleFederationAssetHeaders = (c)=>{
@@ -8,23 +8,32 @@ const PRE_COMPRESSED_SUPPORTED_ENCODINGS = [
8
8
  'br',
9
9
  'gzip'
10
10
  ];
11
+ const QUALITY_VALUE_PATTERN = /^(?:0(?:\.\d{0,3})?|1(?:\.0{0,3})?)$/u;
11
12
  const parseAcceptEncoding = (value)=>value.split(',').map((item)=>item.trim()).filter(Boolean).map((item)=>{
12
13
  const [rawName, ...params] = item.split(';');
13
14
  const name = rawName.trim().toLowerCase();
14
15
  let q = 1;
16
+ let qualitySeen = false;
15
17
  for (const param of params){
16
18
  const [key, rawValue] = param.split('=').map((v)=>v.trim());
17
- if ('q' !== key.toLowerCase() || null == rawValue) continue;
18
- const parsedQ = Number(rawValue);
19
- if (!Number.isNaN(parsedQ)) q = Math.max(0, Math.min(parsedQ, 1));
19
+ if ('q' === key.toLowerCase()) {
20
+ if (qualitySeen || null == rawValue || !QUALITY_VALUE_PATTERN.test(rawValue)) {
21
+ q = 0;
22
+ break;
23
+ }
24
+ qualitySeen = true;
25
+ q = Number(rawValue);
26
+ }
20
27
  }
21
28
  return {
22
29
  name,
23
30
  q
24
31
  };
25
32
  });
26
- const getAcceptedEncodings = (value)=>{
27
- if (!value) return [];
33
+ const getAcceptedRepresentations = (value)=>{
34
+ if (!value) return [
35
+ 'identity'
36
+ ];
28
37
  const parsed = parseAcceptEncoding(value);
29
38
  const qualityByEncoding = new Map();
30
39
  let wildcardQuality;
@@ -40,10 +49,17 @@ const getAcceptedEncodings = (value)=>{
40
49
  if (void 0 !== explicit) return explicit;
41
50
  return wildcardQuality ?? 0;
42
51
  };
43
- return PRE_COMPRESSED_SUPPORTED_ENCODINGS.map((encoding)=>({
44
- encoding,
45
- quality: getQuality(encoding)
46
- })).filter((item)=>item.quality > 0).sort((a, b)=>b.quality - a.quality).map((item)=>item.encoding);
52
+ const identityQuality = qualityByEncoding.get('identity') ?? (0 === wildcardQuality ? 0 : 1);
53
+ return [
54
+ ...PRE_COMPRESSED_SUPPORTED_ENCODINGS.map((encoding)=>({
55
+ encoding,
56
+ quality: getQuality(encoding)
57
+ })),
58
+ {
59
+ encoding: 'identity',
60
+ quality: identityQuality
61
+ }
62
+ ].filter((item)=>item.quality > 0).sort((a, b)=>b.quality - a.quality).map((item)=>item.encoding);
47
63
  };
48
64
  const appendVaryHeader = (c, value)=>{
49
65
  const current = c.res.headers.get('Vary');
@@ -59,34 +75,38 @@ const resolvePreCompressedAsset = async (c, filepath)=>{
59
75
  fs.pathExists(gzipPath)
60
76
  ]);
61
77
  const hasVariant = hasBr || hasGzip;
62
- if (!hasVariant) return {
63
- selected: null,
64
- hasVariant: false
65
- };
66
- const acceptedEncodings = getAcceptedEncodings(c.req.header('accept-encoding'));
67
- for (const encoding of acceptedEncodings){
78
+ const acceptedRepresentations = getAcceptedRepresentations(c.req.header('accept-encoding'));
79
+ for (const encoding of acceptedRepresentations){
80
+ if ('identity' === encoding) return {
81
+ selected: null,
82
+ hasVariant,
83
+ acceptable: true
84
+ };
68
85
  if ('br' === encoding && hasBr) return {
69
86
  selected: {
70
87
  filepath: brPath,
71
88
  encoding
72
89
  },
73
- hasVariant: true
90
+ hasVariant: true,
91
+ acceptable: true
74
92
  };
75
93
  if ('gzip' === encoding && hasGzip) return {
76
94
  selected: {
77
95
  filepath: gzipPath,
78
96
  encoding
79
97
  },
80
- hasVariant: true
98
+ hasVariant: true,
99
+ acceptable: true
81
100
  };
82
101
  }
83
102
  return {
84
103
  selected: null,
85
- hasVariant: true
104
+ hasVariant,
105
+ acceptable: false
86
106
  };
87
107
  };
88
108
  const applyPreCompressedAssetHeaders = (c, preCompressedAsset)=>{
89
- if (preCompressedAsset.hasVariant) appendVaryHeader(c, 'Accept-Encoding');
109
+ if (preCompressedAsset.hasVariant || !preCompressedAsset.acceptable) appendVaryHeader(c, 'Accept-Encoding');
90
110
  if (preCompressedAsset.selected) c.header('Content-Encoding', preCompressedAsset.selected.encoding);
91
111
  };
92
112
  export { applyPreCompressedAssetHeaders, resolvePreCompressedAsset };
@@ -5,11 +5,16 @@ import { getMimeType } from "hono/utils/mime";
5
5
  import path from "path";
6
6
  import { applyModuleFederationAssetHeaders, getModuleFederationAssetList, getModuleFederationRequestPath, isBackendModuleFederationManifestRequest, isModuleFederationManifestRequest, patchModuleFederationManifestPublicPath, patchModuleFederationRemoteEntryPublicPath } from "./staticModuleFederation.mjs";
7
7
  import { applyPreCompressedAssetHeaders, resolvePreCompressedAsset } from "./staticPrecompressed.mjs";
8
+ const MODULE_FEDERATION_ASSET_REFRESH_INTERVAL_MS = 1000;
8
9
  const getStaticMimeType = (filename)=>getMimeType(filename) ?? ('.cjs' === path.extname(filename).toLowerCase() ? "text/javascript; charset=UTF-8" : void 0);
9
10
  const servePreCompressedPublicRouteAsset = async (c, pwd, route)=>{
10
11
  const { entryPath } = route;
11
12
  const originFilename = path.join(pwd, entryPath);
12
13
  const preCompressedAsset = await resolvePreCompressedAsset(c, originFilename);
14
+ if (!preCompressedAsset.acceptable) {
15
+ applyPreCompressedAssetHeaders(c, preCompressedAsset);
16
+ return c.body(null, 406);
17
+ }
13
18
  const filename = preCompressedAsset.selected?.filepath ?? originFilename;
14
19
  const data = await fileReader.readFile(filename, 'buffer');
15
20
  const mimeType = getStaticMimeType(originFilename);
@@ -56,6 +61,10 @@ const servePublicDirectoryAsset = async (c, pwd)=>{
56
61
  const originFilename = await resolvePublicDirectoryAsset(pwd, c.req.path);
57
62
  if (null === originFilename) return null;
58
63
  const preCompressedAsset = await resolvePreCompressedAsset(c, originFilename);
64
+ if (!preCompressedAsset.acceptable) {
65
+ applyPreCompressedAssetHeaders(c, preCompressedAsset);
66
+ return c.body(null, 406);
67
+ }
59
68
  const selectedFilename = preCompressedAsset.selected?.filepath ?? originFilename;
60
69
  const publicDirectory = await fs.realpath(path.join(pwd, 'public'));
61
70
  let realSelectedFilename;
@@ -76,10 +85,19 @@ const servePublicDirectoryAsset = async (c, pwd)=>{
76
85
  return c.body(body, 200);
77
86
  };
78
87
  const createModuleFederationStaticServing = ({ pwd, pathPrefix })=>{
79
- let moduleFederationAssetsPromise = null;
88
+ let moduleFederationAssets = null;
89
+ let moduleFederationAssetsExpiresAt = 0;
90
+ let moduleFederationAssetsRefresh = null;
80
91
  const getModuleFederationAssets = async ()=>{
81
- if (!moduleFederationAssetsPromise) moduleFederationAssetsPromise = getModuleFederationAssetList(pwd);
82
- return moduleFederationAssetsPromise;
92
+ if (moduleFederationAssets && Date.now() < moduleFederationAssetsExpiresAt) return moduleFederationAssets;
93
+ if (!moduleFederationAssetsRefresh) moduleFederationAssetsRefresh = getModuleFederationAssetList(pwd).then((assets)=>{
94
+ moduleFederationAssets = assets;
95
+ moduleFederationAssetsExpiresAt = Date.now() + MODULE_FEDERATION_ASSET_REFRESH_INTERVAL_MS;
96
+ return assets;
97
+ }).finally(()=>{
98
+ moduleFederationAssetsRefresh = null;
99
+ });
100
+ return moduleFederationAssetsRefresh;
83
101
  };
84
102
  const resolveRequest = async (pathname)=>{
85
103
  const requestPath = getModuleFederationRequestPath(pathname, pathPrefix);
@@ -100,8 +118,13 @@ const createModuleFederationStaticServing = ({ pwd, pathPrefix })=>{
100
118
  const canUsePreCompressed = !shouldPatchManifest && !shouldPatchRemoteEntry;
101
119
  const preCompressedAsset = canUsePreCompressed ? await resolvePreCompressedAsset(c, filepath) : {
102
120
  selected: null,
103
- hasVariant: false
121
+ hasVariant: false,
122
+ acceptable: true
104
123
  };
124
+ if (!preCompressedAsset.acceptable) {
125
+ applyPreCompressedAssetHeaders(c, preCompressedAsset);
126
+ return c.body(null, 406);
127
+ }
105
128
  const targetFilepath = preCompressedAsset.selected?.filepath ?? filepath;
106
129
  const chunk = await fileReader.readFileFromSystem(targetFilepath, 'buffer');
107
130
  if (null === chunk) return null;
@@ -1,6 +1,12 @@
1
1
  import "node:module";
2
2
  import { MAIN_ENTRY_NAME } from "@modern-js/utils/universal/constants";
3
3
  const serverActionHandler = async (req, { serverManifest, routeInfo, rscClientManifest })=>{
4
+ if ('POST' !== req.method) return new Response('Method not allowed', {
5
+ status: 405,
6
+ headers: {
7
+ Allow: 'POST'
8
+ }
9
+ });
4
10
  const serverBundle = serverManifest?.renderBundles?.[routeInfo.entryName || MAIN_ENTRY_NAME];
5
11
  if (!serverBundle) return new Response('Cannot find server bundle for server action', {
6
12
  status: 500
@@ -4,59 +4,6 @@ const ERROR_PAGE_TEXT = {
4
4
  404: 'This page could not be found.',
5
5
  500: 'Internal Server Error.'
6
6
  };
7
- const SAFE_FAILURE_MESSAGES = {
8
- 500: 'Internal Server Error',
9
- 503: 'Service Unavailable'
10
- };
11
- const SAFE_FAILURE_CODES = {
12
- 500: 'INTERNAL_SERVER_ERROR',
13
- 503: 'SERVICE_UNAVAILABLE'
14
- };
15
- const readErrorProperty = (error, key)=>{
16
- if ('object' != typeof error || null === error || !(key in error)) return;
17
- return error[key];
18
- };
19
- const normalizeFailureStatus = (value)=>{
20
- if ('number' != typeof value || !Number.isInteger(value)) return;
21
- return value >= 400 && value <= 599 ? value : void 0;
22
- };
23
- const normalizeRetryAfter = (value)=>{
24
- if ('number' == typeof value && Number.isFinite(value) && value >= 0) return String(Math.ceil(value));
25
- if ('string' == typeof value) {
26
- const trimmed = value.trim();
27
- return trimmed.length > 0 ? trimmed : void 0;
28
- }
29
- if (value instanceof Date) return value.toUTCString();
30
- };
31
- const getSafeFailureStatus = (error)=>normalizeFailureStatus(readErrorProperty(error, 'status')) ?? normalizeFailureStatus(readErrorProperty(error, 'statusCode')) ?? 500;
32
- const createSafeFailureHttpResult = (error)=>{
33
- const status = getSafeFailureStatus(error);
34
- const retryAfter = 503 === status ? normalizeRetryAfter(readErrorProperty(error, 'retryAfter')) ?? normalizeRetryAfter(readErrorProperty(error, 'retryAfterSeconds')) ?? normalizeRetryAfter('number' == typeof readErrorProperty(error, 'retryAfterMs') ? readErrorProperty(error, 'retryAfterMs') / 1000 : void 0) : void 0;
35
- return {
36
- status,
37
- body: {
38
- success: false,
39
- error: {
40
- code: SAFE_FAILURE_CODES[status] ?? 'REQUEST_FAILED',
41
- message: SAFE_FAILURE_MESSAGES[status] ?? 'Request failed',
42
- status
43
- }
44
- },
45
- headers: {
46
- 'content-type': 'application/json; charset=utf-8',
47
- ...retryAfter ? {
48
- 'Retry-After': retryAfter
49
- } : {}
50
- }
51
- };
52
- };
53
- const createSafeJsonFailureResponse = (error)=>{
54
- const result = createSafeFailureHttpResult(error);
55
- return new Response(JSON.stringify(result.body), {
56
- status: result.status,
57
- headers: result.headers
58
- });
59
- };
60
7
  const createErrorHtml = (status)=>{
61
8
  const text = ERROR_PAGE_TEXT[status] || '';
62
9
  const title = `${status}: ${text}`;
@@ -104,4 +51,5 @@ function onError(digest, error, monitors, req) {
104
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)}`);
105
52
  else console.error(`Server Error - ${digest}, error = ${error instanceof Error ? error.stack || error.message : error} `);
106
53
  }
107
- export { createErrorHtml, createSafeFailureHttpResult, createSafeJsonFailureResponse, error_ErrorDigest as ErrorDigest, getSafeFailureStatus, onError };
54
+ export { createSafeFailureHttpResult, createSafeJsonFailureResponse, getSafeFailureStatus } from "@modern-js/runtime-extensions/safe-failure";
55
+ export { createErrorHtml, error_ErrorDigest as ErrorDigest, onError };
@@ -6,6 +6,7 @@ type ResolvePreCompressedAssetResult = {
6
6
  encoding: SupportedEncoding;
7
7
  } | null;
8
8
  hasVariant: boolean;
9
+ acceptable: boolean;
9
10
  };
10
11
  export declare const resolvePreCompressedAsset: (c: Parameters<Middleware>[0], filepath: string) => Promise<ResolvePreCompressedAssetResult>;
11
12
  export declare const applyPreCompressedAssetHeaders: (c: Parameters<Middleware>[0], preCompressedAsset: ResolvePreCompressedAssetResult) => void;
@@ -10,16 +10,16 @@ type StaticServingOptions = {
10
10
  pwd: string;
11
11
  pathPrefix: string;
12
12
  };
13
- export declare const servePreCompressedPublicRouteAsset: (c: MiddlewareContext, pwd: string, route: ServerRoute) => Promise<(Response & import("hono").TypedResponse<Uint8Array<ArrayBuffer>, 200, "body">) | null>;
13
+ export declare const servePreCompressedPublicRouteAsset: (c: MiddlewareContext, pwd: string, route: ServerRoute) => Promise<(Response & import("hono").TypedResponse<null, 406, "body">) | (Response & import("hono").TypedResponse<Uint8Array<ArrayBuffer>, 200, "body">) | null>;
14
14
  /**
15
15
  * Serves post-build convention assets generated under dist/public at their
16
16
  * root URL. This is intentionally independent of config/public and route.json:
17
17
  * the generator runs after the route manifest is built.
18
18
  */
19
- export declare const servePublicDirectoryAsset: (c: MiddlewareContext, pwd: string) => Promise<(Response & import("hono").TypedResponse<null, 200, "body">) | (Response & import("hono").TypedResponse<Uint8Array<ArrayBuffer>, 200, "body">) | null>;
19
+ export declare const servePublicDirectoryAsset: (c: MiddlewareContext, pwd: string) => Promise<(Response & import("hono").TypedResponse<null, 200, "body">) | (Response & import("hono").TypedResponse<null, 406, "body">) | (Response & import("hono").TypedResponse<Uint8Array<ArrayBuffer>, 200, "body">) | null>;
20
20
  export declare const createModuleFederationStaticServing: ({ pwd, pathPrefix, }: StaticServingOptions) => {
21
21
  resolveRequest: (pathname: string) => Promise<StaticServingRequest | null>;
22
- serveStaticHit: (c: MiddlewareContext, request: StaticServingRequest) => Promise<(Response & import("hono").TypedResponse<Uint8Array<ArrayBuffer>, 200, "body">) | null>;
23
- serveModuleFederationAsset: (c: MiddlewareContext, request: StaticServingRequest) => Promise<(Response & import("hono").TypedResponse<Uint8Array<ArrayBuffer>, 200, "body">) | null> | null;
22
+ serveStaticHit: (c: MiddlewareContext, request: StaticServingRequest) => Promise<(Response & import("hono").TypedResponse<null, 406, "body">) | (Response & import("hono").TypedResponse<Uint8Array<ArrayBuffer>, 200, "body">) | null>;
23
+ serveModuleFederationAsset: (c: MiddlewareContext, request: StaticServingRequest) => Promise<(Response & import("hono").TypedResponse<null, 406, "body">) | (Response & import("hono").TypedResponse<Uint8Array<ArrayBuffer>, 200, "body">) | null> | null;
24
24
  };
25
25
  export {};
@@ -1,20 +1,6 @@
1
1
  import type { Monitors } from '@modern-js/types';
2
- export type SafeFailureEnvelope = {
3
- success: false;
4
- error: {
5
- code: string;
6
- message: string;
7
- status: number;
8
- };
9
- };
10
- export type SafeFailureHttpResult = {
11
- status: number;
12
- body: SafeFailureEnvelope;
13
- headers: Record<string, string>;
14
- };
15
- export declare const getSafeFailureStatus: (error: unknown) => number;
16
- export declare const createSafeFailureHttpResult: (error: unknown) => SafeFailureHttpResult;
17
- export declare const createSafeJsonFailureResponse: (error: unknown) => Response;
2
+ export type { SafeFailureEnvelope, SafeFailureHttpResult, } from '@modern-js/runtime-extensions/safe-failure';
3
+ export { createSafeFailureHttpResult, createSafeJsonFailureResponse, getSafeFailureStatus, } from '@modern-js/runtime-extensions/safe-failure';
18
4
  export declare const createErrorHtml: (status: number) => string;
19
5
  export declare enum ErrorDigest {
20
6
  ENOTF = "Page could not be found",
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "modern",
18
18
  "modern.js"
19
19
  ],
20
- "version": "3.8.3-ultramodern.1",
20
+ "version": "3.8.3-ultramodern.3",
21
21
  "types": "./dist/types/index.d.ts",
22
22
  "main": "./dist/cjs/index.js",
23
23
  "exports": {
@@ -66,9 +66,10 @@
66
66
  "node": ">=20"
67
67
  },
68
68
  "dependencies": {
69
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.8.3-ultramodern.1",
70
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.8.3-ultramodern.1",
71
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.8.3-ultramodern.1",
69
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.8.3-ultramodern.3",
70
+ "@modern-js/runtime-extensions": "npm:@bleedingdev/modern-js-runtime-extensions@3.8.3-ultramodern.3",
71
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.8.3-ultramodern.3",
72
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.8.3-ultramodern.3",
72
73
  "@swc/helpers": "^0.5.23",
73
74
  "@web-std/fetch": "^4.2.1",
74
75
  "@web-std/file": "^3.0.3",
@@ -79,7 +80,7 @@
79
80
  "ts-deepmerge": "8.0.0"
80
81
  },
81
82
  "devDependencies": {
82
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.8.3-ultramodern.1",
83
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.8.3-ultramodern.3",
83
84
  "@rslib/core": "0.23.2",
84
85
  "@scripts/rstest-config": "2.66.0",
85
86
  "@types/cloneable-readable": "^2.0.3",
@@ -95,7 +96,7 @@
95
96
  },
96
97
  "scripts": {
97
98
  "dev": "rslib build --watch",
98
- "build": "rslib build && pnpm -w tsgo:dts \"$PWD\"",
99
+ "build": "rslib build",
99
100
  "test": "rstest --passWithNoTests"
100
101
  }
101
102
  }