@ms-cloudpack/bundle-server 0.7.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/handleBundleRequest.d.ts.map +1 -1
- package/lib/handleBundleRequest.js +127 -67
- package/lib/handleBundleRequest.js.map +1 -1
- package/lib/handleWorker.d.ts.map +1 -1
- package/lib/handleWorker.js +8 -3
- package/lib/handleWorker.js.map +1 -1
- package/lib/responseHelpers.d.ts +22 -0
- package/lib/responseHelpers.d.ts.map +1 -0
- package/lib/responseHelpers.js +17 -0
- package/lib/responseHelpers.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handleBundleRequest.d.ts","sourceRoot":"","sources":["../src/handleBundleRequest.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8D,KAAK,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACpH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"handleBundleRequest.d.ts","sourceRoot":"","sources":["../src/handleBundleRequest.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8D,KAAK,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACpH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAK1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAU1E,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,mBAAmB,GAAG;IAC7B,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,QAAQ,CAAC;CACf,EACD,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,IAAI,CAAC,CA4Lf"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { ensurePackageBundled, addPackageOverride, errorEntrySource } from '@ms-cloudpack/api-server';
|
|
2
2
|
import { parseRequestInfo } from '@ms-cloudpack/import-map';
|
|
3
3
|
import { findResolveMapEntry } from '@ms-cloudpack/package-utilities';
|
|
4
|
-
import { isExternalPackage } from '@ms-cloudpack/path-utilities';
|
|
5
4
|
import { normalizeRelativePath, slash } from '@ms-cloudpack/path-string-parsing';
|
|
6
5
|
import path from 'path';
|
|
7
6
|
import { handleWorker } from './handleWorker.js';
|
|
8
7
|
import { getLinkedPackageRedirectUrl } from './getLinkedPackageRedirectUrl.js';
|
|
8
|
+
import { logResponse, sendErrorResponse } from './responseHelpers.js';
|
|
9
|
+
const maxAge = 31536000; // 1 year
|
|
9
10
|
export async function handleBundleRequest(options, context) {
|
|
10
11
|
const { req, res, disableCache } = options;
|
|
11
12
|
const { session, packageHashes, bus, telemetryClient } = context;
|
|
@@ -15,15 +16,26 @@ export async function handleBundleRequest(options, context) {
|
|
|
15
16
|
const force = req.query.force !== undefined;
|
|
16
17
|
const worker = req.query.worker !== undefined;
|
|
17
18
|
const { packageName, version, hash, bundled, filePath, missing } = parseRequestInfo(requestPath);
|
|
19
|
+
console.debug(`Bundle request: ${requestPath}`);
|
|
20
|
+
// If the package is missing, invalid arguments.
|
|
21
|
+
if (!requestPath || !packageName) {
|
|
22
|
+
return sendErrorResponse({
|
|
23
|
+
res,
|
|
24
|
+
status: 400,
|
|
25
|
+
requestPath,
|
|
26
|
+
message: `Requests must be in the format "/{packageName}@{version}/{type}/path/file.ext"`,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
18
29
|
const packageEntry = findResolveMapEntry({ packageName, version, resolveMap });
|
|
19
|
-
const packageRequestPath = `${packageName}@${version || '<unknown>'}${filePath}`;
|
|
20
30
|
if (!packageEntry) {
|
|
21
31
|
// If the package is missing, invalid arguments.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
return sendErrorResponse({
|
|
33
|
+
res,
|
|
34
|
+
status: 404,
|
|
35
|
+
requestPath,
|
|
36
|
+
message: `Unrecognized package "${packageName}@${version || '<unknown>'}"`,
|
|
37
|
+
});
|
|
25
38
|
}
|
|
26
|
-
console.debug(`Request: "${packageRequestPath}"`);
|
|
27
39
|
// This is a hot path, so doing a quick check first.
|
|
28
40
|
if (linkedPaths.length) {
|
|
29
41
|
// Try to redirect to remote host if necessary.
|
|
@@ -35,29 +47,27 @@ export async function handleBundleRequest(options, context) {
|
|
|
35
47
|
originalUrl: req.originalUrl,
|
|
36
48
|
});
|
|
37
49
|
if (redirectUrl) {
|
|
38
|
-
|
|
39
|
-
res.redirect(redirectUrl);
|
|
40
|
-
return;
|
|
50
|
+
return sendRedirect({ res, requestPath, message: `Redirecting to linked package ${redirectUrl}`, redirectUrl });
|
|
41
51
|
}
|
|
42
52
|
}
|
|
43
|
-
// If the package is missing, invalid arguments.
|
|
44
|
-
if (!requestPath || !packageName) {
|
|
45
|
-
console.debug(`Response (400): "${packageRequestPath}"`);
|
|
46
|
-
res.status(400).send(`Requests must be in the format "/{packageName}@{version}/{type}/path/file.ext".`);
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
53
|
// If the version is missing, redirect to the primary version.
|
|
50
54
|
if (!version) {
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
return sendRedirect({
|
|
56
|
+
res,
|
|
57
|
+
requestPath,
|
|
58
|
+
message: `Redirecting versionless request to primary version ${packageEntry.version}`,
|
|
59
|
+
redirectUrl: `/${packageName}@${packageEntry.version}${filePath}`,
|
|
60
|
+
});
|
|
53
61
|
}
|
|
62
|
+
const packageRequestPath = `${packageName}@${version}${filePath}`;
|
|
54
63
|
if (missing && features.autoUpdateEntries) {
|
|
64
|
+
// Exports map for this package is missing, so try adding an override for the requested file.
|
|
55
65
|
const addedPackageOverride = await addPackageOverride({
|
|
56
66
|
input: { packageName, version, importPath: filePath, initiatedBy: 'autoUpdateEntries' },
|
|
57
67
|
ctx: context,
|
|
58
68
|
});
|
|
59
69
|
if (addedPackageOverride) {
|
|
60
|
-
// Trigger a re-bundle to ensure there is no stale cache.
|
|
70
|
+
// The requested file exists. Trigger a re-bundle to ensure there is no stale cache.
|
|
61
71
|
await ensurePackageBundled({
|
|
62
72
|
name: packageName,
|
|
63
73
|
version,
|
|
@@ -74,29 +84,20 @@ export async function handleBundleRequest(options, context) {
|
|
|
74
84
|
// We need to pass the error to the bus so that it can be displayed in the UI,
|
|
75
85
|
// and notify the user when autoUpdateEntries is enabled.
|
|
76
86
|
bus.publish(errorEntrySource, message);
|
|
77
|
-
res
|
|
78
|
-
return;
|
|
87
|
+
return sendErrorResponse({ res, status: 404, requestPath, message });
|
|
79
88
|
}
|
|
80
89
|
// If we are accessing a non-bundled asset, return the file directly.
|
|
81
90
|
if (!bundled) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
console.debug(`Unbundled response returning: "${packageRequestPath}"`);
|
|
91
|
-
// If the package is external, set cache headers.
|
|
92
|
-
if (isExternalPackage(packageEntry.path)) {
|
|
93
|
-
res.setHeader('Cache-Control', 'public, max-age=31536000');
|
|
94
|
-
}
|
|
95
|
-
res.sendFile(fullPath);
|
|
96
|
-
}
|
|
97
|
-
return;
|
|
91
|
+
return sendFile({
|
|
92
|
+
res,
|
|
93
|
+
fileFullPath: path.join(packageEntry.path, filePath),
|
|
94
|
+
requestPath: packageRequestPath,
|
|
95
|
+
parentPath: packageEntry.path,
|
|
96
|
+
isBundled: false,
|
|
97
|
+
shouldCache: !!packageEntry.isExternal,
|
|
98
|
+
});
|
|
98
99
|
}
|
|
99
|
-
console.debug(`
|
|
100
|
+
console.debug(`Bundle request: bundling "${packageName}@${version}"`);
|
|
100
101
|
const { result } = await ensurePackageBundled({
|
|
101
102
|
name: packageName,
|
|
102
103
|
version,
|
|
@@ -106,10 +107,21 @@ export async function handleBundleRequest(options, context) {
|
|
|
106
107
|
disableCache,
|
|
107
108
|
}, context);
|
|
108
109
|
if (result.errors?.length || !result.outputPath) {
|
|
110
|
+
// Bundling errors will be logged by ensurePackageBundled on bundle task completion.
|
|
111
|
+
// The only case where they wouldn't be logged is if the user manually refreshes the page and
|
|
112
|
+
// the same failed bundle task result is reused. There's not an easy way to detect that condition
|
|
113
|
+
// (and the overlay and response details will show the error regardless), so only do a debug log
|
|
114
|
+
// to avoid extra noise in the more common case.
|
|
115
|
+
logResponse({
|
|
116
|
+
status: 500,
|
|
117
|
+
requestPath: packageRequestPath,
|
|
118
|
+
message: 'Bundling failed (see error overlay or response data)',
|
|
119
|
+
level: 'debug',
|
|
120
|
+
});
|
|
109
121
|
res.status(500).type('json').send(result);
|
|
110
122
|
return;
|
|
111
123
|
}
|
|
112
|
-
// If worker is not set but the file is a worker, handle it.
|
|
124
|
+
// If the worker query param is not set but the file is a worker, handle it.
|
|
113
125
|
if (features.enableModuleWorkers &&
|
|
114
126
|
!worker &&
|
|
115
127
|
// Currently, only webpack and rspack setup isWorker in the result.
|
|
@@ -118,41 +130,30 @@ export async function handleBundleRequest(options, context) {
|
|
|
118
130
|
await handleWorker({ req, res }, context);
|
|
119
131
|
return;
|
|
120
132
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
console.debug(`Response (400): "${packageRequestPath}"`);
|
|
138
|
-
res.status(400).send(`Invalid file path.`);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
res.sendFile(fullPath, (err) => {
|
|
142
|
-
if (err) {
|
|
143
|
-
console.error(`Error serving file ${packageRequestPath}. Please refresh your browser to try again.`);
|
|
133
|
+
sendFile({
|
|
134
|
+
res,
|
|
135
|
+
fileFullPath: path.join(result.outputPath, filePath),
|
|
136
|
+
requestPath: packageRequestPath,
|
|
137
|
+
parentPath: result.outputPath,
|
|
138
|
+
isBundled: true,
|
|
139
|
+
shouldCache: result.isExternal ||
|
|
140
|
+
(hash !== 'pending' &&
|
|
141
|
+
// For internal packages, cache if the request hash matches the current source hash
|
|
142
|
+
hash ===
|
|
143
|
+
(await packageHashes.get({
|
|
144
|
+
packagePath: packageEntry.path,
|
|
145
|
+
isSourceHashingEnabled: true,
|
|
146
|
+
targetEnvironment: 'browser',
|
|
147
|
+
}))),
|
|
148
|
+
onError: (err) => {
|
|
144
149
|
// This is a workaround for the browser cache issue.
|
|
145
150
|
// It seems to happen when a file is cached in the browser but the imported files are not cached and not on disk.
|
|
146
151
|
// Increase the target version to clear browser cache for that specific package.
|
|
147
152
|
session.incrementTargetVersion(packageEntry.path);
|
|
148
|
-
// Only send a response if headers haven't been sent yet
|
|
149
|
-
if (!res.headersSent) {
|
|
150
|
-
res.status(404).send(`File not found: ${packageRequestPath}`);
|
|
151
|
-
}
|
|
152
153
|
// If the file is not found, we need to report it to telemetry.
|
|
153
154
|
void telemetryClient.tracer.startActiveSpan('FILE_SERVE_ERROR', (span) => {
|
|
154
155
|
span.setAttributes({
|
|
155
|
-
isCacheHit: !!result.bundleSource,
|
|
156
|
+
isCacheHit: !!result.bundleSource && result.bundleSource !== 'bundler',
|
|
156
157
|
isExternal: result.isExternal,
|
|
157
158
|
packageName,
|
|
158
159
|
packageVersion: version,
|
|
@@ -162,9 +163,68 @@ export async function handleBundleRequest(options, context) {
|
|
|
162
163
|
filePath,
|
|
163
164
|
request: requestPath,
|
|
164
165
|
hash: result.hash,
|
|
165
|
-
|
|
166
|
+
// Actual FS errors will have a `code`, which is more informative than `statusCode`.
|
|
167
|
+
// If `sendFile` manually returns an HTTP error, only `statusCode` is set.
|
|
168
|
+
errorCode: String(err.code || err.statusCode || ''),
|
|
166
169
|
});
|
|
167
170
|
});
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/** Debug log about the redirect and then send it */
|
|
175
|
+
function sendRedirect(params) {
|
|
176
|
+
const { res, requestPath, message, redirectUrl } = params;
|
|
177
|
+
logResponse({ status: 302, requestPath, message });
|
|
178
|
+
res.redirect(redirectUrl);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Do some error checks, then send the file.
|
|
182
|
+
*/
|
|
183
|
+
function sendFile(params) {
|
|
184
|
+
const { res, fileFullPath, requestPath, parentPath, isBundled, shouldCache, onError } = params;
|
|
185
|
+
// If the file path is outside of the package, return a 403 error.
|
|
186
|
+
// This is a security measure to prevent access to files outside of the package.
|
|
187
|
+
// (On Windows, path.relative() returns an absolute path for different drives.)
|
|
188
|
+
const relativePath = path.relative(parentPath, fileFullPath);
|
|
189
|
+
if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
|
|
190
|
+
sendErrorResponse({
|
|
191
|
+
res,
|
|
192
|
+
status: 403,
|
|
193
|
+
requestPath,
|
|
194
|
+
message: `Resolved file path is outside of the package`,
|
|
195
|
+
consoleMessage: `File resolved to "${fileFullPath}" which is outside of "${parentPath}"`,
|
|
196
|
+
});
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
console.debug(`Bundle response: ${isBundled ? 'bundled' : 'unbundled'} file "${requestPath}"`);
|
|
200
|
+
// The below header is need to be able to track the browser cache hit ratio.
|
|
201
|
+
res.setHeader('Timing-Allow-Origin', '*');
|
|
202
|
+
// Express v5 default behavior is to ignore (404) anything with a leading dot in any path segment,
|
|
203
|
+
// such as .cloudpack, so we have to explicitly allow it.
|
|
204
|
+
res.sendFile(fileFullPath, { dotfiles: 'allow', maxAge: shouldCache ? maxAge : 0 }, (err) => {
|
|
205
|
+
const errWithCode = err;
|
|
206
|
+
// It appears this callback is called on success (even though the types don't indicate that),
|
|
207
|
+
// so only continue if there's an error. Also ignore ECONNABORTED errors (don't log to console
|
|
208
|
+
// or telemetry) since this probably means the user refreshed or closed the tab.
|
|
209
|
+
if (!errWithCode || errWithCode.code === 'ECONNABORTED') {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
// Send to telemetry if applicable
|
|
213
|
+
onError?.(errWithCode);
|
|
214
|
+
const status = errWithCode.statusCode || 500;
|
|
215
|
+
let message = `Error serving file: ${errWithCode.message}`;
|
|
216
|
+
if (errWithCode.code === 'ENOENT') {
|
|
217
|
+
// If it's an actual file not found error (not something else where sendFile manually returns 404),
|
|
218
|
+
// use a custom message since often it's due to a stale cache.
|
|
219
|
+
message =
|
|
220
|
+
`Request resolved to "${fileFullPath}" which does not exist. ` +
|
|
221
|
+
'This might be due to an outdated browser cache, so please try reloading the page (or report the issue if it persists).';
|
|
222
|
+
}
|
|
223
|
+
logResponse({ status, requestPath, message });
|
|
224
|
+
err.stack && console.info('Bundle request error details:', err.stack);
|
|
225
|
+
if (!res.headersSent) {
|
|
226
|
+
// Only send a response if headers haven't been sent yet
|
|
227
|
+
res.status(status).send(message);
|
|
168
228
|
}
|
|
169
229
|
});
|
|
170
230
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handleBundleRequest.js","sourceRoot":"","sources":["../src/handleBundleRequest.ts"],"names":[],"mappings":"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","sourcesContent":["import { ensurePackageBundled, addPackageOverride, errorEntrySource, type Context } from '@ms-cloudpack/api-server';\nimport type { Request, Response } from '@ms-cloudpack/create-express-app';\nimport { parseRequestInfo } from '@ms-cloudpack/import-map';\nimport { findResolveMapEntry } from '@ms-cloudpack/package-utilities';\nimport { isExternalPackage } from '@ms-cloudpack/path-utilities';\nimport { normalizeRelativePath, slash } from '@ms-cloudpack/path-string-parsing';\nimport path from 'path';\nimport type { BundleServerOptions } from './types/BundleServerOptions.js';\nimport { handleWorker } from './handleWorker.js';\nimport { getLinkedPackageRedirectUrl } from './getLinkedPackageRedirectUrl.js';\n\nexport async function handleBundleRequest(\n options: BundleServerOptions & {\n req: Request;\n res: Response;\n },\n context: Context,\n): Promise<void> {\n const { req, res, disableCache } = options;\n const { session, packageHashes, bus, telemetryClient } = context;\n const { resolveMap, config, linkedPaths } = session;\n const { features = {} } = config;\n const requestPath = slash(req.path.substring(1));\n const force = req.query.force !== undefined;\n const worker = req.query.worker !== undefined;\n const { packageName, version, hash, bundled, filePath, missing } = parseRequestInfo(requestPath);\n const packageEntry = findResolveMapEntry({ packageName, version, resolveMap });\n\n const packageRequestPath = `${packageName}@${version || '<unknown>'}${filePath}`;\n\n if (!packageEntry) {\n // If the package is missing, invalid arguments.\n console.debug(`Response (404): Unrecognized package \"${packageRequestPath}\"`);\n res.status(404).send(`Unrecognized package \"${packageRequestPath}\".`);\n return;\n }\n\n console.debug(`Request: \"${packageRequestPath}\"`);\n\n // This is a hot path, so doing a quick check first.\n if (linkedPaths.length) {\n // Try to redirect to remote host if necessary.\n const redirectUrl = getLinkedPackageRedirectUrl({\n linkedPaths,\n packageName,\n version,\n packageEntry,\n originalUrl: req.originalUrl,\n });\n\n if (redirectUrl) {\n console.debug(`Redirecting to linked package: ${redirectUrl}`);\n res.redirect(redirectUrl);\n return;\n }\n }\n\n // If the package is missing, invalid arguments.\n if (!requestPath || !packageName) {\n console.debug(`Response (400): \"${packageRequestPath}\"`);\n\n res.status(400).send(`Requests must be in the format \"/{packageName}@{version}/{type}/path/file.ext\".`);\n return;\n }\n\n // If the version is missing, redirect to the primary version.\n if (!version) {\n res.redirect(`/${packageName}@${packageEntry.version}${filePath}`);\n return;\n }\n\n if (missing && features.autoUpdateEntries) {\n const addedPackageOverride = await addPackageOverride({\n input: { packageName, version, importPath: filePath, initiatedBy: 'autoUpdateEntries' },\n ctx: context,\n });\n\n if (addedPackageOverride) {\n // Trigger a re-bundle to ensure there is no stale cache.\n await ensurePackageBundled(\n {\n name: packageName,\n version,\n shouldForce: true,\n shouldRerun: true,\n disableCache,\n },\n context,\n );\n // This will never be seen by the user, but it is needed to satisfy the response type.\n res.status(202).type('application/javascript').send('');\n return;\n }\n // If no entry was added, the import path could not resolve to a file in the package.\n const message = `Failed to resolve module specifier \"${packageRequestPath}\".`;\n // We need to pass the error to the bus so that it can be displayed in the UI,\n // and notify the user when autoUpdateEntries is enabled.\n bus.publish(errorEntrySource, message);\n res.status(500).type('text/plain').send(message);\n return;\n }\n\n // If we are accessing a non-bundled asset, return the file directly.\n if (!bundled) {\n const fullPath = path.join(packageEntry.path, filePath);\n\n // If the file path is outside of the package, return a 400 error.\n // This is a security measure to prevent access to files outside of the package.\n if (path.relative(packageEntry.path, fullPath).startsWith('..')) {\n console.debug(`Response (400): \"${packageRequestPath}\"`);\n\n res.status(400).send(`Invalid file path.`);\n } else {\n console.debug(`Unbundled response returning: \"${packageRequestPath}\"`);\n // If the package is external, set cache headers.\n if (isExternalPackage(packageEntry.path)) {\n res.setHeader('Cache-Control', 'public, max-age=31536000');\n }\n\n res.sendFile(fullPath);\n }\n return;\n }\n\n console.debug(`Trying to bundle \"${packageName}@${version}\"`);\n const { result } = await ensurePackageBundled(\n {\n name: packageName,\n version,\n shouldForce: force,\n shouldRerun: force,\n shouldWatch: true, // This will only watch the package if it is internal.\n disableCache,\n },\n context,\n );\n\n if (result.errors?.length || !result.outputPath) {\n res.status(500).type('json').send(result);\n return;\n }\n\n // If worker is not set but the file is a worker, handle it.\n if (\n features.enableModuleWorkers &&\n !worker &&\n // Currently, only webpack and rspack setup isWorker in the result.\n // Webpack is fully supported, but support for rspack is still in progress.\n result.outputFiles?.find((file) => file.isWorker && file.outputPath === normalizeRelativePath(filePath))\n ) {\n await handleWorker({ req, res }, context);\n return;\n }\n\n console.debug(`Bundle returning: \"${packageRequestPath}\"`);\n // Try to return the result.\n if (\n result.isExternal ||\n (hash !== 'pending' &&\n hash ===\n (await packageHashes.get({\n packagePath: packageEntry.path,\n isSourceHashingEnabled: true,\n targetEnvironment: 'browser',\n })))\n ) {\n res.setHeader('Cache-Control', 'public, max-age=31536000');\n }\n\n // The below header is need to be able to track the browser cache hit ratio.\n res.setHeader('Timing-Allow-Origin', '*');\n\n const fullPath = path.join(result.outputPath, filePath);\n if (path.relative(result.outputPath, fullPath).startsWith('..')) {\n console.debug(`Response (400): \"${packageRequestPath}\"`);\n\n res.status(400).send(`Invalid file path.`);\n return;\n }\n\n res.sendFile(fullPath, (err) => {\n if (err) {\n console.error(`Error serving file ${packageRequestPath}. Please refresh your browser to try again.`);\n // This is a workaround for the browser cache issue.\n // It seems to happen when a file is cached in the browser but the imported files are not cached and not on disk.\n // Increase the target version to clear browser cache for that specific package.\n session.incrementTargetVersion(packageEntry.path);\n // Only send a response if headers haven't been sent yet\n if (!res.headersSent) {\n res.status(404).send(`File not found: ${packageRequestPath}`);\n }\n // If the file is not found, we need to report it to telemetry.\n void telemetryClient.tracer.startActiveSpan('FILE_SERVE_ERROR', (span) => {\n span.setAttributes({\n isCacheHit: !!result.bundleSource,\n isExternal: result.isExternal,\n packageName,\n packageVersion: version,\n bundleSource: result?.bundleSource,\n bundler: result?.bundler,\n bundlerVersion: result?.bundlerVersion,\n filePath,\n request: requestPath,\n hash: result.hash,\n errorCode: (err as { code?: string })?.code || '',\n });\n });\n }\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"handleBundleRequest.js","sourceRoot":"","sources":["../src/handleBundleRequest.ts"],"names":[],"mappings":"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","sourcesContent":["import { ensurePackageBundled, addPackageOverride, errorEntrySource, type Context } from '@ms-cloudpack/api-server';\nimport type { Request, Response } from '@ms-cloudpack/create-express-app';\nimport { parseRequestInfo } from '@ms-cloudpack/import-map';\nimport { findResolveMapEntry } from '@ms-cloudpack/package-utilities';\nimport { normalizeRelativePath, slash } from '@ms-cloudpack/path-string-parsing';\nimport path from 'path';\nimport type { BundleServerOptions } from './types/BundleServerOptions.js';\nimport { handleWorker } from './handleWorker.js';\nimport { getLinkedPackageRedirectUrl } from './getLinkedPackageRedirectUrl.js';\nimport { logResponse, sendErrorResponse } from './responseHelpers.js';\n\n/** File read errors have a `code` like `ENOENT`. HTTP errors from `sendFile` have a `statusCode`. */\ntype ErrorWithCodes = Error & { statusCode?: number; code?: string };\n\nconst maxAge = 31536000; // 1 year\n\nexport async function handleBundleRequest(\n options: BundleServerOptions & {\n req: Request;\n res: Response;\n },\n context: Context,\n): Promise<void> {\n const { req, res, disableCache } = options;\n const { session, packageHashes, bus, telemetryClient } = context;\n const { resolveMap, config, linkedPaths } = session;\n const { features = {} } = config;\n const requestPath = slash(req.path.substring(1));\n const force = req.query.force !== undefined;\n const worker = req.query.worker !== undefined;\n const { packageName, version, hash, bundled, filePath, missing } = parseRequestInfo(requestPath);\n\n console.debug(`Bundle request: ${requestPath}`);\n\n // If the package is missing, invalid arguments.\n if (!requestPath || !packageName) {\n return sendErrorResponse({\n res,\n status: 400,\n requestPath,\n message: `Requests must be in the format \"/{packageName}@{version}/{type}/path/file.ext\"`,\n });\n }\n\n const packageEntry = findResolveMapEntry({ packageName, version, resolveMap });\n if (!packageEntry) {\n // If the package is missing, invalid arguments.\n return sendErrorResponse({\n res,\n status: 404,\n requestPath,\n message: `Unrecognized package \"${packageName}@${version || '<unknown>'}\"`,\n });\n }\n\n // This is a hot path, so doing a quick check first.\n if (linkedPaths.length) {\n // Try to redirect to remote host if necessary.\n const redirectUrl = getLinkedPackageRedirectUrl({\n linkedPaths,\n packageName,\n version,\n packageEntry,\n originalUrl: req.originalUrl,\n });\n\n if (redirectUrl) {\n return sendRedirect({ res, requestPath, message: `Redirecting to linked package ${redirectUrl}`, redirectUrl });\n }\n }\n\n // If the version is missing, redirect to the primary version.\n if (!version) {\n return sendRedirect({\n res,\n requestPath,\n message: `Redirecting versionless request to primary version ${packageEntry.version}`,\n redirectUrl: `/${packageName}@${packageEntry.version}${filePath}`,\n });\n }\n\n const packageRequestPath = `${packageName}@${version}${filePath}`;\n\n if (missing && features.autoUpdateEntries) {\n // Exports map for this package is missing, so try adding an override for the requested file.\n const addedPackageOverride = await addPackageOverride({\n input: { packageName, version, importPath: filePath, initiatedBy: 'autoUpdateEntries' },\n ctx: context,\n });\n\n if (addedPackageOverride) {\n // The requested file exists. Trigger a re-bundle to ensure there is no stale cache.\n await ensurePackageBundled(\n {\n name: packageName,\n version,\n shouldForce: true,\n shouldRerun: true,\n disableCache,\n },\n context,\n );\n // This will never be seen by the user, but it is needed to satisfy the response type.\n res.status(202).type('application/javascript').send('');\n return;\n }\n\n // If no entry was added, the import path could not resolve to a file in the package.\n const message = `Failed to resolve module specifier \"${packageRequestPath}\".`;\n // We need to pass the error to the bus so that it can be displayed in the UI,\n // and notify the user when autoUpdateEntries is enabled.\n bus.publish(errorEntrySource, message);\n return sendErrorResponse({ res, status: 404, requestPath, message });\n }\n\n // If we are accessing a non-bundled asset, return the file directly.\n if (!bundled) {\n return sendFile({\n res,\n fileFullPath: path.join(packageEntry.path, filePath),\n requestPath: packageRequestPath,\n parentPath: packageEntry.path,\n isBundled: false,\n shouldCache: !!packageEntry.isExternal,\n });\n }\n\n console.debug(`Bundle request: bundling \"${packageName}@${version}\"`);\n const { result } = await ensurePackageBundled(\n {\n name: packageName,\n version,\n shouldForce: force,\n shouldRerun: force,\n shouldWatch: true, // This will only watch the package if it is internal.\n disableCache,\n },\n context,\n );\n\n if (result.errors?.length || !result.outputPath) {\n // Bundling errors will be logged by ensurePackageBundled on bundle task completion.\n // The only case where they wouldn't be logged is if the user manually refreshes the page and\n // the same failed bundle task result is reused. There's not an easy way to detect that condition\n // (and the overlay and response details will show the error regardless), so only do a debug log\n // to avoid extra noise in the more common case.\n logResponse({\n status: 500,\n requestPath: packageRequestPath,\n message: 'Bundling failed (see error overlay or response data)',\n level: 'debug',\n });\n res.status(500).type('json').send(result);\n return;\n }\n\n // If the worker query param is not set but the file is a worker, handle it.\n if (\n features.enableModuleWorkers &&\n !worker &&\n // Currently, only webpack and rspack setup isWorker in the result.\n // Webpack is fully supported, but support for rspack is still in progress.\n result.outputFiles?.find((file) => file.isWorker && file.outputPath === normalizeRelativePath(filePath))\n ) {\n await handleWorker({ req, res }, context);\n return;\n }\n\n sendFile({\n res,\n fileFullPath: path.join(result.outputPath, filePath),\n requestPath: packageRequestPath,\n parentPath: result.outputPath,\n isBundled: true,\n shouldCache:\n result.isExternal ||\n (hash !== 'pending' &&\n // For internal packages, cache if the request hash matches the current source hash\n hash ===\n (await packageHashes.get({\n packagePath: packageEntry.path,\n isSourceHashingEnabled: true,\n targetEnvironment: 'browser',\n }))),\n onError: (err) => {\n // This is a workaround for the browser cache issue.\n // It seems to happen when a file is cached in the browser but the imported files are not cached and not on disk.\n // Increase the target version to clear browser cache for that specific package.\n session.incrementTargetVersion(packageEntry.path);\n\n // If the file is not found, we need to report it to telemetry.\n void telemetryClient.tracer.startActiveSpan('FILE_SERVE_ERROR', (span) => {\n span.setAttributes({\n isCacheHit: !!result.bundleSource && result.bundleSource !== 'bundler',\n isExternal: result.isExternal,\n packageName,\n packageVersion: version,\n bundleSource: result?.bundleSource,\n bundler: result?.bundler,\n bundlerVersion: result?.bundlerVersion,\n filePath,\n request: requestPath,\n hash: result.hash,\n // Actual FS errors will have a `code`, which is more informative than `statusCode`.\n // If `sendFile` manually returns an HTTP error, only `statusCode` is set.\n errorCode: String(err.code || err.statusCode || ''),\n });\n });\n },\n });\n}\n\n/** Debug log about the redirect and then send it */\nfunction sendRedirect(params: { res: Response; requestPath: string; message: string; redirectUrl: string }): void {\n const { res, requestPath, message, redirectUrl } = params;\n logResponse({ status: 302, requestPath, message });\n res.redirect(redirectUrl);\n}\n\n/**\n * Do some error checks, then send the file.\n */\nfunction sendFile(params: {\n res: Response;\n /** Output path for bundled files, package path for unbundled files */\n parentPath: string;\n /** Full path to the file to return */\n fileFullPath: string;\n requestPath: string;\n isBundled: boolean;\n shouldCache: boolean;\n /** Extra error handling such as telemetry */\n onError?: (err: ErrorWithCodes) => void;\n}): void {\n const { res, fileFullPath, requestPath, parentPath, isBundled, shouldCache, onError } = params;\n\n // If the file path is outside of the package, return a 403 error.\n // This is a security measure to prevent access to files outside of the package.\n // (On Windows, path.relative() returns an absolute path for different drives.)\n const relativePath = path.relative(parentPath, fileFullPath);\n if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {\n sendErrorResponse({\n res,\n status: 403,\n requestPath,\n message: `Resolved file path is outside of the package`,\n consoleMessage: `File resolved to \"${fileFullPath}\" which is outside of \"${parentPath}\"`,\n });\n return;\n }\n\n console.debug(`Bundle response: ${isBundled ? 'bundled' : 'unbundled'} file \"${requestPath}\"`);\n\n // The below header is need to be able to track the browser cache hit ratio.\n res.setHeader('Timing-Allow-Origin', '*');\n\n // Express v5 default behavior is to ignore (404) anything with a leading dot in any path segment,\n // such as .cloudpack, so we have to explicitly allow it.\n res.sendFile(fileFullPath, { dotfiles: 'allow', maxAge: shouldCache ? maxAge : 0 }, (err) => {\n const errWithCode = err as ErrorWithCodes | undefined;\n // It appears this callback is called on success (even though the types don't indicate that),\n // so only continue if there's an error. Also ignore ECONNABORTED errors (don't log to console\n // or telemetry) since this probably means the user refreshed or closed the tab.\n if (!errWithCode || errWithCode.code === 'ECONNABORTED') {\n return;\n }\n\n // Send to telemetry if applicable\n onError?.(errWithCode);\n\n const status = errWithCode.statusCode || 500;\n let message = `Error serving file: ${errWithCode.message}`;\n\n if (errWithCode.code === 'ENOENT') {\n // If it's an actual file not found error (not something else where sendFile manually returns 404),\n // use a custom message since often it's due to a stale cache.\n message =\n `Request resolved to \"${fileFullPath}\" which does not exist. ` +\n 'This might be due to an outdated browser cache, so please try reloading the page (or report the issue if it persists).';\n }\n logResponse({ status, requestPath, message });\n err.stack && console.info('Bundle request error details:', err.stack);\n\n if (!res.headersSent) {\n // Only send a response if headers haven't been sent yet\n res.status(status).send(message);\n }\n });\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handleWorker.d.ts","sourceRoot":"","sources":["../src/handleWorker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"handleWorker.d.ts","sourceRoot":"","sources":["../src/handleWorker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAa1E;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE;IACP,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,QAAQ,CAAC;CACf,EACD,OAAO,EAAE,cAAc,CAAC,SAAS,EAAE,WAAW,GAAG,MAAM,GAAG,QAAQ,CAAC,GAClE,OAAO,CAAC,IAAI,CAAC,CA8Ef"}
|
package/lib/handleWorker.js
CHANGED
|
@@ -5,6 +5,7 @@ import path from 'path';
|
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import fsPromises from 'fs/promises';
|
|
7
7
|
import { getInlineScript } from '@ms-cloudpack/inline-scripts';
|
|
8
|
+
import { logResponse, sendErrorResponse } from './responseHelpers.js';
|
|
8
9
|
const filename = fileURLToPath(import.meta.url);
|
|
9
10
|
let esModuleShim;
|
|
10
11
|
/**
|
|
@@ -24,13 +25,17 @@ export async function handleWorker(options, context) {
|
|
|
24
25
|
esModuleShim ??= await getShim();
|
|
25
26
|
if (!esModuleShim) {
|
|
26
27
|
// If we can't get the shim, we can't handle the worker request
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
return sendErrorResponse({
|
|
29
|
+
res,
|
|
30
|
+
status: 500,
|
|
31
|
+
requestPath: req.path,
|
|
32
|
+
message: 'Failed to load import map shim for worker',
|
|
33
|
+
});
|
|
30
34
|
}
|
|
31
35
|
const originalUrl = makeUrl(req.url, session.urls.bundleServer);
|
|
32
36
|
// Add the worker query parameter so the bundle server knows it has ben shimmed
|
|
33
37
|
originalUrl.searchParams.set('worker', 'shim');
|
|
38
|
+
logResponse({ status: 200, requestPath: req.path, message: 'Returning worker shim' });
|
|
34
39
|
// The following code is based on https://github.com/guybedford/es-module-shims?tab=readme-ov-file#module-workers
|
|
35
40
|
// but it uses the bundle server to avoid sending a blob and to preserve url parameters
|
|
36
41
|
// it also shims the global object and process.browser for compatibility with some packages
|
package/lib/handleWorker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handleWorker.js","sourceRoot":"","sources":["../src/handleWorker.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,mCAAmC,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AACvD,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,UAAU,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"handleWorker.js","sourceRoot":"","sources":["../src/handleWorker.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,mCAAmC,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AACvD,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,UAAU,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEtE,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAChD,IAAI,YAAgC,CAAC;AAErC;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAGC,EACD,OAAmE;IAEnE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IAC7B,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAE5B,YAAY,KAAK,MAAM,OAAO,EAAE,CAAC;IAEjC,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,+DAA+D;QAC/D,OAAO,iBAAiB,CAAC;YACvB,GAAG;YACH,MAAM,EAAE,GAAG;YACX,WAAW,EAAE,GAAG,CAAC,IAAI;YACrB,OAAO,EAAE,2CAA2C;SACrD,CAAC,CAAC;IACL,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAChE,+EAA+E;IAC/E,WAAW,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAE/C,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAEtF,iHAAiH;IACjH,uFAAuF;IACvF,2FAA2F;IAC3F,oFAAoF;IACpF,2CAA2C;IAC3C,gFAAgF;IAChF,iEAAiE;IACjE,gDAAgD;IAChD,GAAG,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;MA2BnC,MAAM,eAAe,CAAC,eAAe,CAAC;;MAEtC,MAAM,eAAe,CAAC,qBAAqB,CAAC;;kCAEhB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;;;MAGvE,YAAY;8BACY,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC;kBAC7C,WAAW,CAAC,IAAI;;;;;;;;;;;KAW7B,CAAC,CAAC;AACP,CAAC;AAED,oEAAoE;AACpE,2FAA2F;AAC3F,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;AAC/C,MAAM,iBAAiB,GAAG,8BAA8B,CAAC;AAEzD,yDAAyD;AACzD,KAAK,UAAU,OAAO;IACpB,MAAM,iBAAiB,GAAG,MAAM,OAAO,CAAC,oBAAoB,EAAE,QAAQ,CAAC,CAAC;IACxE,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,OAAO;IACT,CAAC;IACD,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,CAAC;IAC1E,IAAI,CAAC;QACH,OAAO,MAAM,UAAU,CAAC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;AACH,CAAC","sourcesContent":["import type { PartialContext } from '@ms-cloudpack/api-server';\nimport type { Request, Response } from '@ms-cloudpack/create-express-app';\nimport { makeUrl } from '@ms-cloudpack/path-string-parsing';\nimport { dedent } from 'ts-dedent';\nimport { resolve } from '@ms-cloudpack/path-utilities';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nimport fsPromises from 'fs/promises';\nimport { getInlineScript } from '@ms-cloudpack/inline-scripts';\nimport { logResponse, sendErrorResponse } from './responseHelpers.js';\n\nconst filename = fileURLToPath(import.meta.url);\nlet esModuleShim: string | undefined;\n\n/**\n * The function handles a worker request by shimming import maps, a global object and process.browser\n * It also adds the worker query parameter so the bundle server knows it has been shimmed,\n * while preserving other query parameters\n *\n * It is called when the feature `enableModuleWorkers` is enabled, the worker has not been handled yet,\n * and the output file is a worker.\n *\n * This works even for workers that are not in the import map, as long as they are in the bundle. It also works\n * when the worker is requested through a blob as the the request goes through the bundle server.\n */\nexport async function handleWorker(\n options: {\n req: Request;\n res: Response;\n },\n context: PartialContext<'session', 'importMap' | 'urls' | 'config'>,\n): Promise<void> {\n const { req, res } = options;\n const { session } = context;\n\n esModuleShim ??= await getShim();\n\n if (!esModuleShim) {\n // If we can't get the shim, we can't handle the worker request\n return sendErrorResponse({\n res,\n status: 500,\n requestPath: req.path,\n message: 'Failed to load import map shim for worker',\n });\n }\n\n const originalUrl = makeUrl(req.url, session.urls.bundleServer);\n // Add the worker query parameter so the bundle server knows it has ben shimmed\n originalUrl.searchParams.set('worker', 'shim');\n\n logResponse({ status: 200, requestPath: req.path, message: 'Returning worker shim' });\n\n // The following code is based on https://github.com/guybedford/es-module-shims?tab=readme-ov-file#module-workers\n // but it uses the bundle server to avoid sending a blob and to preserve url parameters\n // it also shims the global object and process.browser for compatibility with some packages\n // We also need to set up web worker and shared worker temporary handlers right away\n // to receive messages from the main thread\n // This is necessary as there is a bug in the browser that when the handlers are\n // set up inside an async function the messages are not received.\n // In this case importShim is an async function.\n res.type('text/javascript').send(dedent`\n var pending = true;\n var messageQueue = [];\n self.onmessage = function (event) {\n if (pending) {\n messageQueue.push(event);\n }\n };\n self.onconnect = function (event) {\n if (pending) {\n messageQueue.push(event);\n }\n };\n\n // Shim the global object in workers\n var globalObject =\n typeof globalThis !== 'undefined'\n ? globalThis\n : typeof self !== 'undefined'\n ? self\n : typeof window !== 'undefined'\n ? window\n : typeof global !== 'undefined'\n ? global\n : {};\n var window = globalObject;\n\n ${await getInlineScript('defineProcess')}\n\n ${await getInlineScript('registerDefineFlags')}\n\n registerDefineFlags(window, ${JSON.stringify(session.config.define ?? {})});\n\n globalObject.esmsInitOptions = { shimMode: true };\n ${esModuleShim}\n importShim.addImportMap(${JSON.stringify(session.importMap)});\n importShim('${originalUrl.href}')\n .then(() => {\n pending = false;\n // Process the message queue\n while (messageQueue.length) {\n const message = messageQueue.shift();\n self.onmessage(message);\n self.onconnect(message);\n }\n })\n .catch(e => setTimeout(() => { throw e; }));\n `);\n}\n\n// esModuleShims must point to the non-CSP build of ES Module Shims,\n// namely the `es-module-shim.wasm.js` output: es-module-shims/dist/es-module-shims.wasm.js\nconst esModuleShimsPackage = 'es-module-shims';\nconst esModuleShimsFile = 'dist/es-module-shims.wasm.js';\n\n/** Get the `es-module-shims` WASM build file contents */\nasync function getShim(): Promise<string | undefined> {\n const esModuleShimsPath = await resolve(esModuleShimsPackage, filename);\n if (!esModuleShimsPath) {\n return;\n }\n const esModuleShimsWasm = path.join(esModuleShimsPath, esModuleShimsFile);\n try {\n return await fsPromises.readFile(esModuleShimsWasm, 'utf8');\n } catch {\n // ignore\n }\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Response } from '@ms-cloudpack/create-express-app';
|
|
2
|
+
/**
|
|
3
|
+
* Log response info to the console. Will use console.debug for success/redirect, console.error for error.
|
|
4
|
+
*/
|
|
5
|
+
export declare function logResponse(params: {
|
|
6
|
+
status: number;
|
|
7
|
+
requestPath: string;
|
|
8
|
+
message?: string;
|
|
9
|
+
/** Console method (could add others as needed) */
|
|
10
|
+
level?: 'error' | 'debug';
|
|
11
|
+
}): void;
|
|
12
|
+
/**
|
|
13
|
+
* Log the error to the console and send a plain-text error response.
|
|
14
|
+
*/
|
|
15
|
+
export declare function sendErrorResponse(params: {
|
|
16
|
+
res: Response;
|
|
17
|
+
status: number;
|
|
18
|
+
requestPath: string;
|
|
19
|
+
message: string;
|
|
20
|
+
consoleMessage?: string;
|
|
21
|
+
}): void;
|
|
22
|
+
//# sourceMappingURL=responseHelpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responseHelpers.d.ts","sourceRoot":"","sources":["../src/responseHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAEjE;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;CAC3B,GAAG,IAAI,CAIP;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,GAAG,EAAE,QAAQ,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,IAAI,CAIP"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log response info to the console. Will use console.debug for success/redirect, console.error for error.
|
|
3
|
+
*/
|
|
4
|
+
export function logResponse(params) {
|
|
5
|
+
const { status, requestPath, message, level = status >= 400 ? 'error' : 'debug' } = params;
|
|
6
|
+
const logMessage = `Bundle response (${status}): ${requestPath}${message ? ` - ${message}` : ''}`;
|
|
7
|
+
console[level](logMessage);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Log the error to the console and send a plain-text error response.
|
|
11
|
+
*/
|
|
12
|
+
export function sendErrorResponse(params) {
|
|
13
|
+
const { res, status, requestPath, message, consoleMessage = message } = params;
|
|
14
|
+
logResponse({ status, requestPath, message: consoleMessage });
|
|
15
|
+
res.status(status).type('text/plain').send(message);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=responseHelpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responseHelpers.js","sourceRoot":"","sources":["../src/responseHelpers.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,MAM3B;IACC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,GAAG,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC;IAC3F,MAAM,UAAU,GAAG,oBAAoB,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IAClG,OAAO,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,CAAC;AAC7B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAMjC;IACC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,EAAE,GAAG,MAAM,CAAC;IAC/E,WAAW,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;IAC9D,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACtD,CAAC","sourcesContent":["import type { Response } from '@ms-cloudpack/create-express-app';\n\n/**\n * Log response info to the console. Will use console.debug for success/redirect, console.error for error.\n */\nexport function logResponse(params: {\n status: number;\n requestPath: string;\n message?: string;\n /** Console method (could add others as needed) */\n level?: 'error' | 'debug';\n}): void {\n const { status, requestPath, message, level = status >= 400 ? 'error' : 'debug' } = params;\n const logMessage = `Bundle response (${status}): ${requestPath}${message ? ` - ${message}` : ''}`;\n console[level](logMessage);\n}\n\n/**\n * Log the error to the console and send a plain-text error response.\n */\nexport function sendErrorResponse(params: {\n res: Response;\n status: number;\n requestPath: string;\n message: string;\n consoleMessage?: string;\n}): void {\n const { res, status, requestPath, message, consoleMessage = message } = params;\n logResponse({ status, requestPath, message: consoleMessage });\n res.status(status).type('text/plain').send(message);\n}\n"]}
|