@ms-cloudpack/app-server 0.18.5 → 0.18.6
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/handleKnownServerErrors.d.ts +9 -0
- package/lib/handleKnownServerErrors.d.ts.map +1 -0
- package/lib/handleKnownServerErrors.js +35 -0
- package/lib/handleKnownServerErrors.js.map +1 -0
- package/lib/renderRoute/renderCustomScript.d.ts +9 -0
- package/lib/renderRoute/renderCustomScript.d.ts.map +1 -0
- package/lib/renderRoute/renderCustomScript.js +54 -0
- package/lib/renderRoute/renderCustomScript.js.map +1 -0
- package/lib/renderRoute/renderRoute.d.ts.map +1 -1
- package/lib/renderRoute/renderRoute.js +1 -51
- package/lib/renderRoute/renderRoute.js.map +1 -1
- package/lib/startServers.d.ts.map +1 -1
- package/lib/startServers.js +3 -3
- package/lib/startServers.js.map +1 -1
- package/package.json +9 -9
- package/lib/handleErrorPortUnavailable.d.ts +0 -5
- package/lib/handleErrorPortUnavailable.d.ts.map +0 -1
- package/lib/handleErrorPortUnavailable.js +0 -17
- package/lib/handleErrorPortUnavailable.js.map +0 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PartialContext } from '@ms-cloudpack/api-server';
|
|
2
|
+
/**
|
|
3
|
+
* Handle known app server startup errors.
|
|
4
|
+
*/
|
|
5
|
+
export declare function handleKnownServerErrors(params: {
|
|
6
|
+
err: unknown;
|
|
7
|
+
ports: number | number[];
|
|
8
|
+
}, context: PartialContext<'session', 'projectName'>): void;
|
|
9
|
+
//# sourceMappingURL=handleKnownServerErrors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handleKnownServerErrors.d.ts","sourceRoot":"","sources":["../src/handleKnownServerErrors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/D;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAAE,EAClD,OAAO,EAAE,cAAc,CAAC,SAAS,EAAE,aAAa,CAAC,GAChD,IAAI,CA8BN"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handle known app server startup errors.
|
|
3
|
+
*/
|
|
4
|
+
export function handleKnownServerErrors(params, context) {
|
|
5
|
+
const { err, ports } = params;
|
|
6
|
+
const { projectName } = context.session;
|
|
7
|
+
if (!err || !(err instanceof Error)) {
|
|
8
|
+
// If the error is not an instance of Error, we can't handle it.
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const portRange = typeof ports === 'number' ? [ports] : ports;
|
|
12
|
+
// If a known error occurs, we can provide a more user-friendly message.
|
|
13
|
+
// and we can also suppress the stack trace for these errors because it's not useful to the user.
|
|
14
|
+
if (err.message === 'Must provide `cwd` when `sslOptions` are provided') {
|
|
15
|
+
err.message = `Cloudpack could not start "${projectName}" because the SSL options were provided without a current working directory. \nThis is a Cloudpack bug, please report it!`;
|
|
16
|
+
}
|
|
17
|
+
else if (err.message === 'Must specify a portRange when requireSpecifiedPort is true') {
|
|
18
|
+
if (portRange.length === 0) {
|
|
19
|
+
err.message = `Cloudpack could probably not start "${projectName}" because a port range was specified, but it was empty "[]".`;
|
|
20
|
+
err.stack = undefined;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
err.message = `Cloudpack could not start "${projectName}" because a port range was specified, but it was empty. \nThis is a Cloudpack bug, please report it!`;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else if (err.message.startsWith('No ports from portRange')) {
|
|
27
|
+
const portNeedsPrivileges = portRange.some((port) => port < 1024)
|
|
28
|
+
? '\nNote: for Mac/Linux/Codespaces/WSL users, you may need to run Cloudpack with elevated privileges to use port numbers less than 1024.'
|
|
29
|
+
: '';
|
|
30
|
+
err.message =
|
|
31
|
+
`Cloudpack could not start "${projectName}" because no port from the allowed list was available: ${portRange.join(', ')}. Close other programs and try again.` + portNeedsPrivileges;
|
|
32
|
+
err.stack = undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=handleKnownServerErrors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handleKnownServerErrors.js","sourceRoot":"","sources":["../src/handleKnownServerErrors.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,uBAAuB,CACrC,MAAkD,EAClD,OAAiD;IAEjD,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IAC9B,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IACxC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC,EAAE,CAAC;QACpC,gEAAgE;QAChE,OAAO;IACT,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAE9D,wEAAwE;IACxE,iGAAiG;IACjG,IAAI,GAAG,CAAC,OAAO,KAAK,mDAAmD,EAAE,CAAC;QACxE,GAAG,CAAC,OAAO,GAAG,8BAA8B,WAAW,2HAA2H,CAAC;IACrL,CAAC;SAAM,IAAI,GAAG,CAAC,OAAO,KAAK,4DAA4D,EAAE,CAAC;QACxF,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,GAAG,CAAC,OAAO,GAAG,uCAAuC,WAAW,8DAA8D,CAAC;YAC/H,GAAG,CAAC,KAAK,GAAG,SAAS,CAAC;QACxB,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,OAAO,GAAG,8BAA8B,WAAW,sGAAsG,CAAC;QAChK,CAAC;IACH,CAAC;SAAM,IAAI,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,yBAAyB,CAAC,EAAE,CAAC;QAC7D,MAAM,mBAAmB,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,IAAI,CAAC;YAC/D,CAAC,CAAC,wIAAwI;YAC1I,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,OAAO;YACT,8BAA8B,WAAW,0DAA0D,SAAS,CAAC,IAAI,CAC/G,IAAI,CACL,uCAAuC,GAAG,mBAAmB,CAAC;QACjE,GAAG,CAAC,KAAK,GAAG,SAAS,CAAC;IACxB,CAAC;AACH,CAAC","sourcesContent":["import type { PartialContext } from '@ms-cloudpack/api-server';\n\n/**\n * Handle known app server startup errors.\n */\nexport function handleKnownServerErrors(\n params: { err: unknown; ports: number | number[] },\n context: PartialContext<'session', 'projectName'>,\n): void {\n const { err, ports } = params;\n const { projectName } = context.session;\n if (!err || !(err instanceof Error)) {\n // If the error is not an instance of Error, we can't handle it.\n return;\n }\n const portRange = typeof ports === 'number' ? [ports] : ports;\n\n // If a known error occurs, we can provide a more user-friendly message.\n // and we can also suppress the stack trace for these errors because it's not useful to the user.\n if (err.message === 'Must provide `cwd` when `sslOptions` are provided') {\n err.message = `Cloudpack could not start \"${projectName}\" because the SSL options were provided without a current working directory. \\nThis is a Cloudpack bug, please report it!`;\n } else if (err.message === 'Must specify a portRange when requireSpecifiedPort is true') {\n if (portRange.length === 0) {\n err.message = `Cloudpack could probably not start \"${projectName}\" because a port range was specified, but it was empty \"[]\".`;\n err.stack = undefined;\n } else {\n err.message = `Cloudpack could not start \"${projectName}\" because a port range was specified, but it was empty. \\nThis is a Cloudpack bug, please report it!`;\n }\n } else if (err.message.startsWith('No ports from portRange')) {\n const portNeedsPrivileges = portRange.some((port) => port < 1024)\n ? '\\nNote: for Mac/Linux/Codespaces/WSL users, you may need to run Cloudpack with elevated privileges to use port numbers less than 1024.'\n : '';\n err.message =\n `Cloudpack could not start \"${projectName}\" because no port from the allowed list was available: ${portRange.join(\n ', ',\n )}. Close other programs and try again.` + portNeedsPrivileges;\n err.stack = undefined;\n }\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RenderFunctionOptions, RenderFunctionResult } from '@ms-cloudpack/common-types';
|
|
2
|
+
/**
|
|
3
|
+
* Load the default export from a JS file passed as the `renderScript` in a route,
|
|
4
|
+
* and return either the result of running the function, or an error page if something fails.
|
|
5
|
+
*/
|
|
6
|
+
export declare function renderCustomScript(params: RenderFunctionOptions & {
|
|
7
|
+
renderScriptPath: string;
|
|
8
|
+
}): Promise<RenderFunctionResult>;
|
|
9
|
+
//# sourceMappingURL=renderCustomScript.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderCustomScript.d.ts","sourceRoot":"","sources":["../../src/renderRoute/renderCustomScript.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,qBAAqB,EACrB,oBAAoB,EAErB,MAAM,4BAA4B,CAAC;AAKpC;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,qBAAqB,GAAG;IAAE,gBAAgB,EAAE,MAAM,CAAA;CAAE,GAC3D,OAAO,CAAC,oBAAoB,CAAC,CA6C/B"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { pathToFileURL } from 'url';
|
|
2
|
+
import { getHtmlErrorResponse } from './getHtmlErrorResponse.js';
|
|
3
|
+
import fsPromises from 'fs/promises';
|
|
4
|
+
/**
|
|
5
|
+
* Load the default export from a JS file passed as the `renderScript` in a route,
|
|
6
|
+
* and return either the result of running the function, or an error page if something fails.
|
|
7
|
+
*/
|
|
8
|
+
export async function renderCustomScript(params) {
|
|
9
|
+
const { renderScriptPath, ...options } = params;
|
|
10
|
+
// Get the html factory function from a script default export.
|
|
11
|
+
const renderScriptUrl = pathToFileURL(renderScriptPath).toString();
|
|
12
|
+
// Note: because there isn't a way to purge the require cache, we need to add a cache breaker query param to the
|
|
13
|
+
// script path to ensure we get the latest version of the script. This could be improved by using the git hash of
|
|
14
|
+
// the file if it exists, or a hash of the content, or even the timestamp of the file.
|
|
15
|
+
// TODO: this won't work to update anything the file imports, and doesn't seem to work for serverEntry at all...
|
|
16
|
+
let cacheBreakerQueryParam = `?t=${Date.now()}`;
|
|
17
|
+
try {
|
|
18
|
+
const { mtime } = await fsPromises.stat(renderScriptPath);
|
|
19
|
+
cacheBreakerQueryParam = `?t=${mtime.getTime()}`;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
/* no-op */
|
|
23
|
+
}
|
|
24
|
+
// Try importing the script
|
|
25
|
+
let createHtml;
|
|
26
|
+
let errorMessage;
|
|
27
|
+
try {
|
|
28
|
+
const importResult = (await import(renderScriptUrl + cacheBreakerQueryParam)).default;
|
|
29
|
+
if (typeof importResult === 'function') {
|
|
30
|
+
createHtml = importResult;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
errorMessage = `The render script at "${renderScriptPath}" does not export a default function.`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
errorMessage = `Error importing render script at "${renderScriptUrl}":\n${e.stack || e}`;
|
|
38
|
+
}
|
|
39
|
+
if (createHtml) {
|
|
40
|
+
// Try running the script
|
|
41
|
+
try {
|
|
42
|
+
return await createHtml(options);
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
errorMessage = `Error running render script at "${renderScriptPath}":\n${e.stack || e}`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Return an error page. Doing this instead of returning a default or empty response ensures that
|
|
49
|
+
// the user is aware of any configuration issues.
|
|
50
|
+
errorMessage ??= `Unknown error loading render script at "${renderScriptPath}".`;
|
|
51
|
+
console.error(errorMessage);
|
|
52
|
+
return getHtmlErrorResponse(errorMessage);
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=renderCustomScript.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderCustomScript.js","sourceRoot":"","sources":["../../src/renderRoute/renderCustomScript.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,UAAU,MAAM,aAAa,CAAC;AAErC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAA4D;IAE5D,MAAM,EAAE,gBAAgB,EAAE,GAAG,OAAO,EAAE,GAAG,MAAM,CAAC;IAChD,8DAA8D;IAC9D,MAAM,eAAe,GAAG,aAAa,CAAC,gBAAgB,CAAC,CAAC,QAAQ,EAAE,CAAC;IAEnE,gHAAgH;IAChH,iHAAiH;IACjH,sFAAsF;IACtF,gHAAgH;IAChH,IAAI,sBAAsB,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IAChD,IAAI,CAAC;QACH,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC1D,sBAAsB,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,WAAW;IACb,CAAC;IAED,2BAA2B;IAC3B,IAAI,UAAsC,CAAC;IAC3C,IAAI,YAAgC,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,YAAY,GAAI,CAAC,MAAM,MAAM,CAAC,eAAe,GAAG,sBAAsB,CAAC,CAA0B,CAAC,OAAO,CAAC;QAChH,IAAI,OAAO,YAAY,KAAK,UAAU,EAAE,CAAC;YACvC,UAAU,GAAG,YAAY,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,YAAY,GAAG,yBAAyB,gBAAgB,uCAAuC,CAAC;QAClG,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,YAAY,GAAG,qCAAqC,eAAe,OAAQ,CAAW,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;IACtG,CAAC;IAED,IAAI,UAAU,EAAE,CAAC;QACf,yBAAyB;QACzB,IAAI,CAAC;YACH,OAAO,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,YAAY,GAAG,mCAAmC,gBAAgB,OAAQ,CAAW,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;QACrG,CAAC;IACH,CAAC;IAED,iGAAiG;IACjG,iDAAiD;IACjD,YAAY,KAAK,2CAA2C,gBAAgB,IAAI,CAAC;IACjF,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC5B,OAAO,oBAAoB,CAAC,YAAY,CAAC,CAAC;AAC5C,CAAC","sourcesContent":["import type {\n RenderFunction,\n RenderFunctionOptions,\n RenderFunctionResult,\n RenderFunctionScript,\n} from '@ms-cloudpack/common-types';\nimport { pathToFileURL } from 'url';\nimport { getHtmlErrorResponse } from './getHtmlErrorResponse.js';\nimport fsPromises from 'fs/promises';\n\n/**\n * Load the default export from a JS file passed as the `renderScript` in a route,\n * and return either the result of running the function, or an error page if something fails.\n */\nexport async function renderCustomScript(\n params: RenderFunctionOptions & { renderScriptPath: string },\n): Promise<RenderFunctionResult> {\n const { renderScriptPath, ...options } = params;\n // Get the html factory function from a script default export.\n const renderScriptUrl = pathToFileURL(renderScriptPath).toString();\n\n // Note: because there isn't a way to purge the require cache, we need to add a cache breaker query param to the\n // script path to ensure we get the latest version of the script. This could be improved by using the git hash of\n // the file if it exists, or a hash of the content, or even the timestamp of the file.\n // TODO: this won't work to update anything the file imports, and doesn't seem to work for serverEntry at all...\n let cacheBreakerQueryParam = `?t=${Date.now()}`;\n try {\n const { mtime } = await fsPromises.stat(renderScriptPath);\n cacheBreakerQueryParam = `?t=${mtime.getTime()}`;\n } catch {\n /* no-op */\n }\n\n // Try importing the script\n let createHtml: RenderFunction | undefined;\n let errorMessage: string | undefined;\n try {\n const importResult = ((await import(renderScriptUrl + cacheBreakerQueryParam)) as RenderFunctionScript).default;\n if (typeof importResult === 'function') {\n createHtml = importResult;\n } else {\n errorMessage = `The render script at \"${renderScriptPath}\" does not export a default function.`;\n }\n } catch (e) {\n errorMessage = `Error importing render script at \"${renderScriptUrl}\":\\n${(e as Error).stack || e}`;\n }\n\n if (createHtml) {\n // Try running the script\n try {\n return await createHtml(options);\n } catch (e) {\n errorMessage = `Error running render script at \"${renderScriptPath}\":\\n${(e as Error).stack || e}`;\n }\n }\n\n // Return an error page. Doing this instead of returning a default or empty response ensures that\n // the user is aware of any configuration issues.\n errorMessage ??= `Unknown error loading render script at \"${renderScriptPath}\".`;\n console.error(errorMessage);\n return getHtmlErrorResponse(errorMessage);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderRoute.d.ts","sourceRoot":"","sources":["../../src/renderRoute/renderRoute.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"renderRoute.d.ts","sourceRoot":"","sources":["../../src/renderRoute/renderRoute.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EAErB,4BAA4B,EAG7B,MAAM,4BAA4B,CAAC;AAcpC,OAAO,EAAwB,KAAK,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAKlG;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,4BAA4B,GAAG,IAAI,CAAC,CAwG9C"}
|
|
@@ -2,7 +2,6 @@ import { makeUrl } from '@ms-cloudpack/path-string-parsing';
|
|
|
2
2
|
import fsPromises from 'fs/promises';
|
|
3
3
|
import { Window } from 'happy-dom';
|
|
4
4
|
import path from 'path';
|
|
5
|
-
import { pathToFileURL } from 'url';
|
|
6
5
|
import { getDefaultHtmlResponse } from './getDefaultHtmlResponse.js';
|
|
7
6
|
import { getHtmlErrorResponse } from './getHtmlErrorResponse.js';
|
|
8
7
|
import HTMLSerializer from 'happy-dom/lib/html-serializer/HTMLSerializer.js';
|
|
@@ -10,6 +9,7 @@ import { javascriptExtensions, sourceExtensions, sourceToIntermediatePath, types
|
|
|
10
9
|
import { ensurePackageBundled } from '@ms-cloudpack/api-server';
|
|
11
10
|
import { bulletedList } from '@ms-cloudpack/task-reporter';
|
|
12
11
|
import { environmentInfo } from '@ms-cloudpack/environment';
|
|
12
|
+
import { renderCustomScript } from './renderCustomScript.js';
|
|
13
13
|
/**
|
|
14
14
|
* Get the response for the given route. If the route has a custom render script, use that.
|
|
15
15
|
* Returns an error response if the file referenced by the script doesn't exist or throws an error.
|
|
@@ -192,56 +192,6 @@ function isHtmlType(acceptOrContentType) {
|
|
|
192
192
|
.map((s) => s.split(';')[0].trim())
|
|
193
193
|
.some((mime) => mime === '*/*' || /xml|x?html/i.test(mime) || /^(text|application)\/\*$/.test(mime));
|
|
194
194
|
}
|
|
195
|
-
/**
|
|
196
|
-
* Load the default export from a JS file passed as the `renderScript` in a route,
|
|
197
|
-
* and return either the result of running the function, or an error page if something fails.
|
|
198
|
-
*/
|
|
199
|
-
async function renderCustomScript(params) {
|
|
200
|
-
const { renderScriptPath, ...options } = params;
|
|
201
|
-
// Get the html factory function from a script default export.
|
|
202
|
-
const renderScriptUrl = pathToFileURL(renderScriptPath).toString();
|
|
203
|
-
// Note: because there isn't a way to purge the require cache, we need to add a cache breaker query param to the
|
|
204
|
-
// script path to ensure we get the latest version of the script. This could be improved by using the git hash of
|
|
205
|
-
// the file if it exists, or a hash of the content, or even the timestamp of the file.
|
|
206
|
-
// TODO: this won't work to update anything the file imports, and doesn't seem to work for serverEntry at all...
|
|
207
|
-
let cacheBreakerQueryParam = `?t=${Date.now()}`;
|
|
208
|
-
try {
|
|
209
|
-
const { mtime } = await fsPromises.stat(renderScriptPath);
|
|
210
|
-
cacheBreakerQueryParam = `?t=${mtime.getTime()}`;
|
|
211
|
-
}
|
|
212
|
-
catch {
|
|
213
|
-
/* no-op */
|
|
214
|
-
}
|
|
215
|
-
// Try importing the script
|
|
216
|
-
let createHtml;
|
|
217
|
-
let errorMessage;
|
|
218
|
-
try {
|
|
219
|
-
const importResult = (await import(renderScriptUrl + cacheBreakerQueryParam)).default;
|
|
220
|
-
if (typeof importResult === 'function') {
|
|
221
|
-
createHtml = importResult;
|
|
222
|
-
}
|
|
223
|
-
else {
|
|
224
|
-
errorMessage = `The render script at "${renderScriptPath}" does not export a default function.`;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
catch (e) {
|
|
228
|
-
errorMessage = `Error importing render script at "${renderScriptUrl}":\n${e.stack || e}`;
|
|
229
|
-
}
|
|
230
|
-
if (createHtml) {
|
|
231
|
-
// Try running the script
|
|
232
|
-
try {
|
|
233
|
-
return await createHtml(options);
|
|
234
|
-
}
|
|
235
|
-
catch (e) {
|
|
236
|
-
errorMessage = `Error running render script at "${renderScriptPath}":\n${e.stack || e}`;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
// Return an error page. Doing this instead of returning a default or empty response ensures that
|
|
240
|
-
// the user is aware of any configuration issues.
|
|
241
|
-
errorMessage ??= `Unknown error loading render script at "${renderScriptPath}".`;
|
|
242
|
-
console.error(errorMessage);
|
|
243
|
-
return getHtmlErrorResponse(errorMessage);
|
|
244
|
-
}
|
|
245
195
|
/**
|
|
246
196
|
* Modify the HTML response by injecting the import map, inline scripts, and entry/overlay scripts.
|
|
247
197
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderRoute.js","sourceRoot":"","sources":["../../src/renderRoute/renderRoute.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n RenderFunction,\n RenderFunctionOptions,\n RenderFunctionResult,\n ExpandedRenderFunctionResult,\n RenderedRoute,\n Request,\n RenderFunctionScript,\n} from '@ms-cloudpack/common-types';\nimport { makeUrl } from '@ms-cloudpack/path-string-parsing';\nimport fsPromises from 'fs/promises';\nimport { Window, type Document } from 'happy-dom';\nimport path from 'path';\nimport { pathToFileURL } from 'url';\nimport { getDefaultHtmlResponse } from './getDefaultHtmlResponse.js';\nimport { getHtmlErrorResponse } from './getHtmlErrorResponse.js';\nimport HTMLSerializer from 'happy-dom/lib/html-serializer/HTMLSerializer.js';\nimport {\n javascriptExtensions,\n sourceExtensions,\n sourceToIntermediatePath,\n typescriptExtensions,\n} from '@ms-cloudpack/path-utilities';\nimport { ensurePackageBundled, type EnsurePackageBundledContext } from '@ms-cloudpack/api-server';\nimport { bulletedList } from '@ms-cloudpack/task-reporter';\nimport { environmentInfo } from '@ms-cloudpack/environment';\n\n/**\n * Get the response for the given route. If the route has a custom render script, use that.\n * Returns an error response if the file referenced by the script doesn't exist or throws an error.\n */\nexport async function renderRoute(\n options: RenderFunctionOptions,\n context: EnsurePackageBundledContext,\n): Promise<ExpandedRenderFunctionResult | null> {\n const { route, session } = options;\n const { appPath } = session.config;\n\n // Get the script to render (if any). Prefer serverEntry if provided.\n // eslint-disable-next-line etc/no-deprecated\n let renderScriptPath = route.renderScript && path.resolve(appPath, route.renderScript);\n\n if (route.serverEntry) {\n const serverEntryExt = path.extname(route.serverEntry.sourcePath).toLowerCase();\n if (sourceExtensions.includes(serverEntryExt)) {\n // Server entry is a source file, so bundle it\n const serverEntryResult = await bundleServerEntry(\n { ...options, serverEntryPath: route.serverEntry.sourcePath },\n context,\n );\n if (typeof serverEntryResult === 'string') {\n renderScriptPath = serverEntryResult;\n } else {\n // This means there was an error\n return serverEntryResult;\n }\n } else {\n // HTML or something, so use it as-is\n renderScriptPath = path.resolve(appPath, route.serverEntry.sourcePath);\n }\n }\n\n // Get an initial result from either the renderScript/serverEntry or default handler.\n let rawResult: RenderFunctionResult;\n if (renderScriptPath) {\n const scriptExt = path.extname(renderScriptPath).toLowerCase();\n if (scriptExt === '.html' || scriptExt === '.htm') {\n // HTML file: just read it\n rawResult = await readStaticHtml(renderScriptPath);\n } else if (javascriptExtensions.includes(scriptExt)) {\n // JS file: run its function\n rawResult = await renderCustomScript({ ...options, renderScriptPath });\n } else {\n // Other file type: error. Technically we could just serve it as-is following the non-HTML\n // content type logic, but this probably indicates there's been a misunderstanding and we\n // should direct them to use a more appropriate type of route instead.\n let message = 'is not a supported file type. ';\n if (typescriptExtensions.includes(scriptExt)) {\n message += 'Use `serverEntry` for TS files.';\n } else {\n message +=\n `Supported file types are ${route.serverEntry ? 'TS, JS, or HTML' : 'JS or HTML (use `serverEntry` for TS)'}. ` +\n 'If you want to serve a static non-HTML file, use a route with `staticPath`, or a `serverEntry` ' +\n 'which returns the file content for advanced scenarios.';\n }\n return makeErrorResponse({\n message,\n route,\n req: options.req,\n scriptPath: renderScriptPath,\n });\n }\n } else {\n rawResult = await getDefaultHtmlResponse(options);\n }\n\n if (rawResult === null) {\n // This means the custom renderScript fully handled the request.\n return null;\n }\n\n if (rawResult === undefined) {\n return makeErrorResponse({\n message:\n `returned undefined. If the script fully handled the request (sent a response), ` +\n `return null instead. Otherwise ensure that the function returns the content to be rendered.`,\n route,\n req: options.req,\n scriptPath: renderScriptPath,\n });\n }\n\n const result: ExpandedRenderFunctionResult = {\n contentType: 'text/html',\n statusCode: 200,\n ...(typeof rawResult === 'string' ? { content: rawResult } : rawResult),\n };\n\n // If it's HTML and a success statusCode, inject the import map and appropriate scripts.\n if (result.statusCode === 200 && isHtmlType(result.contentType)) {\n if (isHtmlAcceptable(options)) {\n // Inject the import map and scripts.\n await injectScripts(options, result);\n } else {\n // It appears we're accidentally returning HTML when a different file type was requested.\n // This could happen for overly broad route matches such as '*', or if no routes are configured.\n return makeErrorResponse({\n message:\n 'returned HTML, but this appears to be a non-HTML request. This is likely due to an ' +\n 'overly broad match or other misconfiguration of server.routes in the cloudpack config.',\n route,\n scriptPath: renderScriptPath,\n req: options.req,\n });\n }\n }\n\n return result;\n}\n\nasync function bundleServerEntry(\n options: Pick<RenderFunctionOptions, 'definition' | 'route' | 'req'> & {\n /** Relative serverEntry source path from the route */\n serverEntryPath: string;\n },\n context: EnsurePackageBundledContext,\n): Promise<string | ExpandedRenderFunctionResult> {\n const { definition, route, req, serverEntryPath } = options;\n const { name, version } = definition;\n\n // targetEnvironment: node currently bundles all node or agnostic entries, and implicitly does\n // production bundling. This seems like it could get expensive, but it's probably okay because\n // createPackageSettingsTransform overwrites the package's actual exports with the ones generated\n // from routes, and those will always have environment conditions.\n const response = await ensurePackageBundled(\n {\n name,\n version,\n targetEnvironment: 'node',\n // TODO: This will only watch for changes in the given package, not dependencies.\n // Since we implicitly use production mode for node bundles, this means changes to\n // dependencies will be missed.\n shouldWatch: !environmentInfo.isJest,\n // Irrelevant with implicit production bundling\n enqueueDependencies: false,\n },\n context,\n );\n const { errors, outputPath = '', outputFiles = [] } = response.result;\n\n if (errors?.length) {\n return makeErrorResponse({\n message: `failed to bundle: ${JSON.stringify(response.result.errors, null, 2)}`,\n route,\n req,\n scriptPath: serverEntryPath,\n });\n }\n\n let serverEntryOutput = outputFiles.find((f) => f.entryPoint === serverEntryPath)?.outputPath;\n if (!serverEntryOutput) {\n // No literal match, so try the intermediate path without extension.\n const intermediateServerEntryPath = sourceToIntermediatePath(serverEntryPath).replace(/\\.\\w+$/, '');\n serverEntryOutput =\n outputFiles.find((f) => f.entryPoint === serverEntryPath)?.outputPath ||\n outputFiles.find((f) => f.outputPath.replace(/\\.\\w+$/, '') === intermediateServerEntryPath)?.outputPath;\n }\n\n if (!serverEntryOutput) {\n return makeErrorResponse({\n message: `did not produce a bundle output file corresponding to the serverEntry script (${serverEntryPath}). Produced files:\\n${\n outputFiles.length\n ? bulletedList(outputFiles.map((f) => `${f.outputPath} (entry: ${f.entryPoint || 'unknown'})`))\n : 'none'\n }`,\n route,\n req: options.req,\n scriptPath: serverEntryPath,\n });\n }\n\n // Translate relative-to-output path into an absolute path.\n return path.resolve(outputPath, serverEntryOutput);\n}\n\n/**\n * Fill in details of the message, log to console.error, and return an error response.\n * `Route ${route.match} with (renderScript|serverEntry) \"${scriptPath}\" ${message}`\n */\nfunction makeErrorResponse(params: {\n message: string;\n route: RenderedRoute;\n scriptPath: string | undefined;\n req: Request;\n}): ExpandedRenderFunctionResult {\n const { message, route, scriptPath, req } = params;\n const scriptType = route.serverEntry ? 'serverEntry' : 'renderScript';\n const renderer = scriptPath ? `${scriptType} \"${scriptPath}\"` : 'the default renderer';\n const content = `Route ${JSON.stringify(route.match)} with ${renderer} ${message}`;\n console.error(`App server: request ${req.url} - ${content}`);\n\n return { statusCode: 500, contentType: 'text/plain', content };\n}\n\nfunction isHtmlAcceptable(options: Pick<RenderFunctionOptions, 'req' | 'baseUrl'>): boolean {\n const { req, baseUrl } = options;\n // Get the request path and extension (the URL.pathname conversion gets rid of any query or hash)\n const requestPath = makeUrl(req.url, baseUrl).pathname;\n const requestExt = path.extname(requestPath);\n // Determine whether HTML is (probably) an acceptable response type (can be refined later if needed).\n // In theory we should be able to use req.accepts() but not all requests set the accept header.\n return (!requestExt || requestExt.startsWith('.htm')) && !req.xhr && isHtmlType(req.headers.accept || '*/*');\n}\n\nfunction isHtmlType(acceptOrContentType: string): boolean {\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation/List_of_default_Accept_values\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types\n return acceptOrContentType\n .split(',')\n .map((s) => s.split(';')[0].trim())\n .some((mime) => mime === '*/*' || /xml|x?html/i.test(mime) || /^(text|application)\\/\\*$/.test(mime));\n}\n\n/**\n * Load the default export from a JS file passed as the `renderScript` in a route,\n * and return either the result of running the function, or an error page if something fails.\n */\nasync function renderCustomScript(\n params: RenderFunctionOptions & { renderScriptPath: string },\n): Promise<RenderFunctionResult> {\n const { renderScriptPath, ...options } = params;\n // Get the html factory function from a script default export.\n const renderScriptUrl = pathToFileURL(renderScriptPath).toString();\n\n // Note: because there isn't a way to purge the require cache, we need to add a cache breaker query param to the\n // script path to ensure we get the latest version of the script. This could be improved by using the git hash of\n // the file if it exists, or a hash of the content, or even the timestamp of the file.\n // TODO: this won't work to update anything the file imports, and doesn't seem to work for serverEntry at all...\n let cacheBreakerQueryParam = `?t=${Date.now()}`;\n try {\n const { mtime } = await fsPromises.stat(renderScriptPath);\n cacheBreakerQueryParam = `?t=${mtime.getTime()}`;\n } catch {\n /* no-op */\n }\n\n // Try importing the script\n let createHtml: RenderFunction | undefined;\n let errorMessage: string | undefined;\n try {\n const importResult = ((await import(renderScriptUrl + cacheBreakerQueryParam)) as RenderFunctionScript).default;\n if (typeof importResult === 'function') {\n createHtml = importResult;\n } else {\n errorMessage = `The render script at \"${renderScriptPath}\" does not export a default function.`;\n }\n } catch (e) {\n errorMessage = `Error importing render script at \"${renderScriptUrl}\":\\n${(e as Error).stack || e}`;\n }\n\n if (createHtml) {\n // Try running the script\n try {\n return await createHtml(options);\n } catch (e) {\n errorMessage = `Error running render script at \"${renderScriptPath}\":\\n${(e as Error).stack || e}`;\n }\n }\n\n // Return an error page. Doing this instead of returning a default or empty response ensures that\n // the user is aware of any configuration issues.\n errorMessage ??= `Unknown error loading render script at \"${renderScriptPath}\".`;\n console.error(errorMessage);\n return getHtmlErrorResponse(errorMessage);\n}\n\n/**\n * Modify the HTML response by injecting the import map, inline scripts, and entry/overlay scripts.\n *\n * Note that this just logs an error rather than returning an error response if something fails, for now.\n */\nasync function injectScripts(options: RenderFunctionOptions, result: ExpandedRenderFunctionResult): Promise<void> {\n const { route, overlayScript, entryScripts, inlineScripts, importMap, baseUrl } = options;\n const window = new Window({\n url: baseUrl,\n settings: {\n disableJavaScriptFileLoading: true,\n disableJavaScriptEvaluation: true,\n disableCSSFileLoading: true,\n disableIframePageLoading: true,\n },\n });\n try {\n // TODO: happy-dom is overkill for what we're doing here--a full browser-like environment isn't\n // needed to just parse and modify the HTML. If it becomes a perf concern (less likely since\n // HTML page rendering is probably just done once per page load), we could switch to\n // a pure parser like parse5 or htmlparser2.\n window.document.write(result.content);\n } catch (e) {\n // Trying to write a test for the above, it seemed very permissive, but catch just in case\n console.error(\n `Error parsing html response for rendering route \"${JSON.stringify(route.match)}\":\\n${(e as Error).stack || e}`,\n );\n return;\n }\n\n try {\n const document = window.document;\n\n if (entryScripts || overlayScript) {\n // Inject the import map at the top of the head, in case other scripts use it\n addScript({ document, type: 'importmap', prepend: true, content: JSON.stringify(importMap, null, 2) });\n }\n\n if (inlineScripts) {\n for (const inlineScript of inlineScripts) {\n addScript({ document, content: inlineScript });\n }\n }\n\n // inject the overlay and entry scripts\n addScript({ document, url: overlayScript });\n\n if (entryScripts) {\n for (const entryScript of entryScripts) {\n addScript({ document, url: entryScript });\n }\n }\n\n const serializer = new HTMLSerializer();\n result.content = serializer.serializeToString(document);\n await window.happyDOM.close();\n } catch (e) {\n console.error(`Error injecting scripts for route \"${JSON.stringify(route.match)}\":\\n${(e as Error).stack || e}`);\n }\n}\n\n/**\n * Helper function to add a script to the document.\n * No-op if neither `url` nor `content` is provided.\n */\nfunction addScript(params: {\n document: Document;\n type?: string;\n prepend?: boolean;\n url?: string;\n content?: string;\n}): void {\n const { document, type = 'module', prepend, url, content } = params;\n if (!url && !content) {\n return;\n }\n\n const script = document.createElement('script');\n script.type = type;\n if (url) {\n script.src = url;\n } else if (content) {\n script.innerHTML = content;\n }\n\n if (prepend) {\n document.head.prepend(script);\n } else {\n document.head.appendChild(script);\n }\n}\n\n/**\n * Read a static HTML file, or return an error response if the file can't be read.\n */\nasync function readStaticHtml(renderScriptPath: string): Promise<RenderFunctionResult> {\n try {\n return (await fsPromises.readFile(renderScriptPath, 'utf8')) || '';\n } catch (e) {\n const message = `Error reading HTML file at \"${renderScriptPath}\":\\n${(e as Error).stack || e}`;\n console.error(message);\n return getHtmlErrorResponse(message);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"renderRoute.js","sourceRoot":"","sources":["../../src/renderRoute/renderRoute.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n RenderFunctionOptions,\n RenderFunctionResult,\n ExpandedRenderFunctionResult,\n RenderedRoute,\n Request,\n} from '@ms-cloudpack/common-types';\nimport { makeUrl } from '@ms-cloudpack/path-string-parsing';\nimport fsPromises from 'fs/promises';\nimport { Window, type Document } from 'happy-dom';\nimport path from 'path';\nimport { getDefaultHtmlResponse } from './getDefaultHtmlResponse.js';\nimport { getHtmlErrorResponse } from './getHtmlErrorResponse.js';\nimport HTMLSerializer from 'happy-dom/lib/html-serializer/HTMLSerializer.js';\nimport {\n javascriptExtensions,\n sourceExtensions,\n sourceToIntermediatePath,\n typescriptExtensions,\n} from '@ms-cloudpack/path-utilities';\nimport { ensurePackageBundled, type EnsurePackageBundledContext } from '@ms-cloudpack/api-server';\nimport { bulletedList } from '@ms-cloudpack/task-reporter';\nimport { environmentInfo } from '@ms-cloudpack/environment';\nimport { renderCustomScript } from './renderCustomScript.js';\n\n/**\n * Get the response for the given route. If the route has a custom render script, use that.\n * Returns an error response if the file referenced by the script doesn't exist or throws an error.\n */\nexport async function renderRoute(\n options: RenderFunctionOptions,\n context: EnsurePackageBundledContext,\n): Promise<ExpandedRenderFunctionResult | null> {\n const { route, session } = options;\n const { appPath } = session.config;\n\n // Get the script to render (if any). Prefer serverEntry if provided.\n // eslint-disable-next-line etc/no-deprecated\n let renderScriptPath = route.renderScript && path.resolve(appPath, route.renderScript);\n\n if (route.serverEntry) {\n const serverEntryExt = path.extname(route.serverEntry.sourcePath).toLowerCase();\n if (sourceExtensions.includes(serverEntryExt)) {\n // Server entry is a source file, so bundle it\n const serverEntryResult = await bundleServerEntry(\n { ...options, serverEntryPath: route.serverEntry.sourcePath },\n context,\n );\n if (typeof serverEntryResult === 'string') {\n renderScriptPath = serverEntryResult;\n } else {\n // This means there was an error\n return serverEntryResult;\n }\n } else {\n // HTML or something, so use it as-is\n renderScriptPath = path.resolve(appPath, route.serverEntry.sourcePath);\n }\n }\n\n // Get an initial result from either the renderScript/serverEntry or default handler.\n let rawResult: RenderFunctionResult;\n if (renderScriptPath) {\n const scriptExt = path.extname(renderScriptPath).toLowerCase();\n if (scriptExt === '.html' || scriptExt === '.htm') {\n // HTML file: just read it\n rawResult = await readStaticHtml(renderScriptPath);\n } else if (javascriptExtensions.includes(scriptExt)) {\n // JS file: run its function\n rawResult = await renderCustomScript({ ...options, renderScriptPath });\n } else {\n // Other file type: error. Technically we could just serve it as-is following the non-HTML\n // content type logic, but this probably indicates there's been a misunderstanding and we\n // should direct them to use a more appropriate type of route instead.\n let message = 'is not a supported file type. ';\n if (typescriptExtensions.includes(scriptExt)) {\n message += 'Use `serverEntry` for TS files.';\n } else {\n message +=\n `Supported file types are ${route.serverEntry ? 'TS, JS, or HTML' : 'JS or HTML (use `serverEntry` for TS)'}. ` +\n 'If you want to serve a static non-HTML file, use a route with `staticPath`, or a `serverEntry` ' +\n 'which returns the file content for advanced scenarios.';\n }\n return makeErrorResponse({\n message,\n route,\n req: options.req,\n scriptPath: renderScriptPath,\n });\n }\n } else {\n rawResult = await getDefaultHtmlResponse(options);\n }\n\n if (rawResult === null) {\n // This means the custom renderScript fully handled the request.\n return null;\n }\n\n if (rawResult === undefined) {\n return makeErrorResponse({\n message:\n `returned undefined. If the script fully handled the request (sent a response), ` +\n `return null instead. Otherwise ensure that the function returns the content to be rendered.`,\n route,\n req: options.req,\n scriptPath: renderScriptPath,\n });\n }\n\n const result: ExpandedRenderFunctionResult = {\n contentType: 'text/html',\n statusCode: 200,\n ...(typeof rawResult === 'string' ? { content: rawResult } : rawResult),\n };\n\n // If it's HTML and a success statusCode, inject the import map and appropriate scripts.\n if (result.statusCode === 200 && isHtmlType(result.contentType)) {\n if (isHtmlAcceptable(options)) {\n // Inject the import map and scripts.\n await injectScripts(options, result);\n } else {\n // It appears we're accidentally returning HTML when a different file type was requested.\n // This could happen for overly broad route matches such as '*', or if no routes are configured.\n return makeErrorResponse({\n message:\n 'returned HTML, but this appears to be a non-HTML request. This is likely due to an ' +\n 'overly broad match or other misconfiguration of server.routes in the cloudpack config.',\n route,\n scriptPath: renderScriptPath,\n req: options.req,\n });\n }\n }\n\n return result;\n}\n\nasync function bundleServerEntry(\n options: Pick<RenderFunctionOptions, 'definition' | 'route' | 'req'> & {\n /** Relative serverEntry source path from the route */\n serverEntryPath: string;\n },\n context: EnsurePackageBundledContext,\n): Promise<string | ExpandedRenderFunctionResult> {\n const { definition, route, req, serverEntryPath } = options;\n const { name, version } = definition;\n\n // targetEnvironment: node currently bundles all node or agnostic entries, and implicitly does\n // production bundling. This seems like it could get expensive, but it's probably okay because\n // createPackageSettingsTransform overwrites the package's actual exports with the ones generated\n // from routes, and those will always have environment conditions.\n const response = await ensurePackageBundled(\n {\n name,\n version,\n targetEnvironment: 'node',\n // TODO: This will only watch for changes in the given package, not dependencies.\n // Since we implicitly use production mode for node bundles, this means changes to\n // dependencies will be missed.\n shouldWatch: !environmentInfo.isJest,\n // Irrelevant with implicit production bundling\n enqueueDependencies: false,\n },\n context,\n );\n const { errors, outputPath = '', outputFiles = [] } = response.result;\n\n if (errors?.length) {\n return makeErrorResponse({\n message: `failed to bundle: ${JSON.stringify(response.result.errors, null, 2)}`,\n route,\n req,\n scriptPath: serverEntryPath,\n });\n }\n\n let serverEntryOutput = outputFiles.find((f) => f.entryPoint === serverEntryPath)?.outputPath;\n if (!serverEntryOutput) {\n // No literal match, so try the intermediate path without extension.\n const intermediateServerEntryPath = sourceToIntermediatePath(serverEntryPath).replace(/\\.\\w+$/, '');\n serverEntryOutput =\n outputFiles.find((f) => f.entryPoint === serverEntryPath)?.outputPath ||\n outputFiles.find((f) => f.outputPath.replace(/\\.\\w+$/, '') === intermediateServerEntryPath)?.outputPath;\n }\n\n if (!serverEntryOutput) {\n return makeErrorResponse({\n message: `did not produce a bundle output file corresponding to the serverEntry script (${serverEntryPath}). Produced files:\\n${\n outputFiles.length\n ? bulletedList(outputFiles.map((f) => `${f.outputPath} (entry: ${f.entryPoint || 'unknown'})`))\n : 'none'\n }`,\n route,\n req: options.req,\n scriptPath: serverEntryPath,\n });\n }\n\n // Translate relative-to-output path into an absolute path.\n return path.resolve(outputPath, serverEntryOutput);\n}\n\n/**\n * Fill in details of the message, log to console.error, and return an error response.\n * `Route ${route.match} with (renderScript|serverEntry) \"${scriptPath}\" ${message}`\n */\nfunction makeErrorResponse(params: {\n message: string;\n route: RenderedRoute;\n scriptPath: string | undefined;\n req: Request;\n}): ExpandedRenderFunctionResult {\n const { message, route, scriptPath, req } = params;\n const scriptType = route.serverEntry ? 'serverEntry' : 'renderScript';\n const renderer = scriptPath ? `${scriptType} \"${scriptPath}\"` : 'the default renderer';\n const content = `Route ${JSON.stringify(route.match)} with ${renderer} ${message}`;\n console.error(`App server: request ${req.url} - ${content}`);\n\n return { statusCode: 500, contentType: 'text/plain', content };\n}\n\nfunction isHtmlAcceptable(options: Pick<RenderFunctionOptions, 'req' | 'baseUrl'>): boolean {\n const { req, baseUrl } = options;\n // Get the request path and extension (the URL.pathname conversion gets rid of any query or hash)\n const requestPath = makeUrl(req.url, baseUrl).pathname;\n const requestExt = path.extname(requestPath);\n // Determine whether HTML is (probably) an acceptable response type (can be refined later if needed).\n // In theory we should be able to use req.accepts() but not all requests set the accept header.\n return (!requestExt || requestExt.startsWith('.htm')) && !req.xhr && isHtmlType(req.headers.accept || '*/*');\n}\n\nfunction isHtmlType(acceptOrContentType: string): boolean {\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation/List_of_default_Accept_values\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type\n // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types\n return acceptOrContentType\n .split(',')\n .map((s) => s.split(';')[0].trim())\n .some((mime) => mime === '*/*' || /xml|x?html/i.test(mime) || /^(text|application)\\/\\*$/.test(mime));\n}\n\n/**\n * Modify the HTML response by injecting the import map, inline scripts, and entry/overlay scripts.\n *\n * Note that this just logs an error rather than returning an error response if something fails, for now.\n */\nasync function injectScripts(options: RenderFunctionOptions, result: ExpandedRenderFunctionResult): Promise<void> {\n const { route, overlayScript, entryScripts, inlineScripts, importMap, baseUrl } = options;\n const window = new Window({\n url: baseUrl,\n settings: {\n disableJavaScriptFileLoading: true,\n disableJavaScriptEvaluation: true,\n disableCSSFileLoading: true,\n disableIframePageLoading: true,\n },\n });\n try {\n // TODO: happy-dom is overkill for what we're doing here--a full browser-like environment isn't\n // needed to just parse and modify the HTML. If it becomes a perf concern (less likely since\n // HTML page rendering is probably just done once per page load), we could switch to\n // a pure parser like parse5 or htmlparser2.\n window.document.write(result.content);\n } catch (e) {\n // Trying to write a test for the above, it seemed very permissive, but catch just in case\n console.error(\n `Error parsing html response for rendering route \"${JSON.stringify(route.match)}\":\\n${(e as Error).stack || e}`,\n );\n return;\n }\n\n try {\n const document = window.document;\n\n if (entryScripts || overlayScript) {\n // Inject the import map at the top of the head, in case other scripts use it\n addScript({ document, type: 'importmap', prepend: true, content: JSON.stringify(importMap, null, 2) });\n }\n\n if (inlineScripts) {\n for (const inlineScript of inlineScripts) {\n addScript({ document, content: inlineScript });\n }\n }\n\n // inject the overlay and entry scripts\n addScript({ document, url: overlayScript });\n\n if (entryScripts) {\n for (const entryScript of entryScripts) {\n addScript({ document, url: entryScript });\n }\n }\n\n const serializer = new HTMLSerializer();\n result.content = serializer.serializeToString(document);\n await window.happyDOM.close();\n } catch (e) {\n console.error(`Error injecting scripts for route \"${JSON.stringify(route.match)}\":\\n${(e as Error).stack || e}`);\n }\n}\n\n/**\n * Helper function to add a script to the document.\n * No-op if neither `url` nor `content` is provided.\n */\nfunction addScript(params: {\n document: Document;\n type?: string;\n prepend?: boolean;\n url?: string;\n content?: string;\n}): void {\n const { document, type = 'module', prepend, url, content } = params;\n if (!url && !content) {\n return;\n }\n\n const script = document.createElement('script');\n script.type = type;\n if (url) {\n script.src = url;\n } else if (content) {\n script.innerHTML = content;\n }\n\n if (prepend) {\n document.head.prepend(script);\n } else {\n document.head.appendChild(script);\n }\n}\n\n/**\n * Read a static HTML file, or return an error response if the file can't be read.\n */\nasync function readStaticHtml(renderScriptPath: string): Promise<RenderFunctionResult> {\n try {\n return (await fsPromises.readFile(renderScriptPath, 'utf8')) || '';\n } catch (e) {\n const message = `Error reading HTML file at \"${renderScriptPath}\":\\n${(e as Error).stack || e}`;\n console.error(message);\n return getHtmlErrorResponse(message);\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"startServers.d.ts","sourceRoot":"","sources":["../src/startServers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACzB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAI9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAGnC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE;IACP,UAAU,EAAE,WAAW,CAAC;IACxB,0FAA0F;IAC1F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB,mBAAmB,EAAE,mBAAmB,CAAC;CAC1C,EACD,OAAO,EAAE,OAAO,GACf,OAAO,CAAC;IACT,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,CAAC,
|
|
1
|
+
{"version":3,"file":"startServers.d.ts","sourceRoot":"","sources":["../src/startServers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACzB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAI9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAGnC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE;IACP,UAAU,EAAE,WAAW,CAAC;IACxB,0FAA0F;IAC1F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB,mBAAmB,EAAE,mBAAmB,CAAC;CAC1C,EACD,OAAO,EAAE,OAAO,GACf,OAAO,CAAC;IACT,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,CAAC,CAoDD"}
|
package/lib/startServers.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createExpressApp, getDomain } from '@ms-cloudpack/create-express-app';
|
|
|
3
3
|
import { combinedServerBundleRequestPrefix } from '@ms-cloudpack/import-map';
|
|
4
4
|
import { cyan } from '@ms-cloudpack/task-reporter';
|
|
5
5
|
import { createRoutes } from './createRoutes.js';
|
|
6
|
-
import {
|
|
6
|
+
import { handleKnownServerErrors } from './handleKnownServerErrors.js';
|
|
7
7
|
/**
|
|
8
8
|
* Start the app server, and (usually) the bundle server (but see below).
|
|
9
9
|
*
|
|
@@ -34,7 +34,7 @@ export async function startServers(options, context) {
|
|
|
34
34
|
requireSpecifiedPort: customPorts !== undefined,
|
|
35
35
|
hostname,
|
|
36
36
|
requestHeaders: server?.requestHeaders,
|
|
37
|
-
sslOptions: server?.https,
|
|
37
|
+
sslOptions: context.session.config?.server?.parsedHttps ?? context.session.config?.server?.https,
|
|
38
38
|
server: expressServer,
|
|
39
39
|
cwd: config.appPath,
|
|
40
40
|
});
|
|
@@ -63,7 +63,7 @@ export async function startServers(options, context) {
|
|
|
63
63
|
}
|
|
64
64
|
catch (err) {
|
|
65
65
|
task.complete({ status: 'fail', message: 'Failed to start app server', forceShow: true });
|
|
66
|
-
|
|
66
|
+
handleKnownServerErrors({ err, ports }, context);
|
|
67
67
|
throw err;
|
|
68
68
|
}
|
|
69
69
|
}
|
package/lib/startServers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"startServers.js","sourceRoot":"","sources":["../src/startServers.ts"],"names":[],"mappings":"AACA,OAAO,EACL,mBAAmB,EACnB,iBAAiB,GAGlB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,iCAAiC,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAC;AAEnD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"startServers.js","sourceRoot":"","sources":["../src/startServers.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from '@ms-cloudpack/api-server';\nimport {\n handleBundleRequest,\n startBundleServer,\n type BundleServer,\n type BundleServerOptions,\n} from '@ms-cloudpack/bundle-server';\nimport type { PackageJson } from '@ms-cloudpack/common-types';\nimport { createExpressApp, getDomain } from '@ms-cloudpack/create-express-app';\nimport { combinedServerBundleRequestPrefix } from '@ms-cloudpack/import-map';\nimport { cyan } from '@ms-cloudpack/task-reporter';\nimport type { Server } from 'http';\nimport { createRoutes } from './createRoutes.js';\nimport { handleKnownServerErrors } from './handleKnownServerErrors.js';\nimport type { AppServer } from './types/AppServer.js';\n\n/**\n * Start the app server, and (usually) the bundle server (but see below).\n *\n * The app server hosts the appropriate routes for the web app, primarily returning html content\n * which loads resources from the bundle server.\n *\n * Separating the app server from the bundle service keeps the routes separate - the app server\n * can support whichever routes the app needs, while the bundle server can provide package\n * assets in various forms using its own routing.\n *\n * Exception: some projects need the bundle server and app server on the same origin for local\n * development due to security policies. If the `useSingleWebServer` feature is enabled, the app\n * server will also serve bundles rather than creating a separate bundle server.\n */\nexport async function startServers(\n options: {\n definition: PackageJson;\n /** If provided, act as a middleware on this server, rather than starting a new server. */\n expressServer?: Server;\n /** Custom port for the app server (probably from CLI options) */\n port?: number | number[];\n bundleServerOptions: BundleServerOptions;\n },\n context: Context,\n): Promise<{\n appServer: AppServer;\n bundleServer?: BundleServer;\n}> {\n const { definition, expressServer, bundleServerOptions } = options;\n const { session, reporter } = context;\n const { config } = session;\n const { server = {} } = config;\n\n // Read the port from the config file or default to array of ports.\n const customPorts = server.port ?? options.port;\n const ports = customPorts ?? [5000, 5001, 5002, 5003];\n const task = reporter.addTask(`Starting app server for \"${definition.name}\"`);\n const hostname = getDomain(server.domain);\n\n try {\n const { app, close, port, url } = await createExpressApp({\n portRange: ports,\n requireSpecifiedPort: customPorts !== undefined,\n hostname,\n requestHeaders: server?.requestHeaders,\n sslOptions: context.session.config?.server?.parsedHttps ?? context.session.config?.server?.https,\n server: expressServer,\n cwd: config.appPath,\n });\n session.urls.appServer = url;\n\n let bundleServer: BundleServer | undefined;\n if (config.features?.useSingleWebServer) {\n // Add a route on this server for bundles.\n session.urls.bundleServer = `${url}/${combinedServerBundleRequestPrefix}`;\n app.get(`/${combinedServerBundleRequestPrefix}/*`, (req, res) => {\n handleBundleRequest({ req, res, ...bundleServerOptions }, context).catch((err) => {\n console.error((err as Error)?.stack || err);\n res.status(500).send(`Error bundling: ${err}`);\n });\n });\n } else {\n // Start the bundle server.\n bundleServer = await startBundleServer(bundleServerOptions, context);\n }\n\n createRoutes({ ...options, app, url }, context);\n\n task.complete({ message: `Available @ ${cyan(url)}`, forceShow: true });\n\n return {\n appServer: { app, close, port, url },\n bundleServer,\n };\n } catch (err: unknown) {\n task.complete({ status: 'fail', message: 'Failed to start app server', forceShow: true });\n handleKnownServerErrors({ err, ports }, context);\n throw err;\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ms-cloudpack/app-server",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.6",
|
|
4
4
|
"description": "An implementation of the App server for Cloudpack.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -14,22 +14,22 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@ms-cloudpack/api-server": "^0.63.
|
|
18
|
-
"@ms-cloudpack/bundle-server": "^0.7.
|
|
19
|
-
"@ms-cloudpack/common-types": "^0.24.
|
|
20
|
-
"@ms-cloudpack/create-express-app": "^1.10.
|
|
17
|
+
"@ms-cloudpack/api-server": "^0.63.3",
|
|
18
|
+
"@ms-cloudpack/bundle-server": "^0.7.6",
|
|
19
|
+
"@ms-cloudpack/common-types": "^0.24.19",
|
|
20
|
+
"@ms-cloudpack/create-express-app": "^1.10.33",
|
|
21
21
|
"@ms-cloudpack/environment": "^0.1.1",
|
|
22
|
-
"@ms-cloudpack/import-map": "^0.10.
|
|
23
|
-
"@ms-cloudpack/inline-scripts": "^0.2.
|
|
22
|
+
"@ms-cloudpack/import-map": "^0.10.24",
|
|
23
|
+
"@ms-cloudpack/inline-scripts": "^0.2.5",
|
|
24
24
|
"@ms-cloudpack/path-string-parsing": "^1.2.7",
|
|
25
|
-
"@ms-cloudpack/path-utilities": "^3.1.
|
|
25
|
+
"@ms-cloudpack/path-utilities": "^3.1.4",
|
|
26
26
|
"@ms-cloudpack/task-reporter": "^0.16.3",
|
|
27
27
|
"happy-dom": "^17.1.1",
|
|
28
28
|
"http-proxy-middleware": "^2.0.6"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@ms-cloudpack/common-types-browser": "^0.6.0",
|
|
32
|
-
"@ms-cloudpack/config": "^0.35.
|
|
32
|
+
"@ms-cloudpack/config": "^0.35.6",
|
|
33
33
|
"@ms-cloudpack/eslint-plugin-internal": "^0.0.1",
|
|
34
34
|
"@ms-cloudpack/scripts": "^0.0.1",
|
|
35
35
|
"@ms-cloudpack/test-utilities": "^0.5.0"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"handleErrorPortUnavailable.d.ts","sourceRoot":"","sources":["../src/handleErrorPortUnavailable.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAgB5G"}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Handle the error if the port is already in use.
|
|
3
|
-
*/
|
|
4
|
-
export function handleErrorPortUnavailable(err, projectName, ports) {
|
|
5
|
-
const portRange = typeof ports === 'number' ? [ports] : ports;
|
|
6
|
-
if (err instanceof Error && err.message === 'Specified port not available') {
|
|
7
|
-
const portNeedsPrivileges = portRange.some((port) => port < 1024)
|
|
8
|
-
? '\nNote: for Mac/Linux/WSL users, you may need to run Cloudpack with elevated privileges to use port numbers less than 1024. Try running `sudo cloudpack start`.'
|
|
9
|
-
: '';
|
|
10
|
-
const errorMessage = `Cloudpack could not start "${projectName}" because no port from the allowed list was available: ${portRange.join(', ')}. Close other programs and try again.` + portNeedsPrivileges;
|
|
11
|
-
// Edit the error message to be more user-friendly.
|
|
12
|
-
err.message = errorMessage;
|
|
13
|
-
// Suppress the stack trace for this error because it's not useful to the user.
|
|
14
|
-
err.stack = undefined;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
//# sourceMappingURL=handleErrorPortUnavailable.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"handleErrorPortUnavailable.js","sourceRoot":"","sources":["../src/handleErrorPortUnavailable.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,0BAA0B,CAAC,GAAY,EAAE,WAAmB,EAAE,KAAwB;IACpG,MAAM,SAAS,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC9D,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,KAAK,8BAA8B,EAAE,CAAC;QAC3E,MAAM,mBAAmB,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,IAAI,CAAC;YAC/D,CAAC,CAAC,iKAAiK;YACnK,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,YAAY,GAChB,8BAA8B,WAAW,0DAA0D,SAAS,CAAC,IAAI,CAC/G,IAAI,CACL,uCAAuC,GAAG,mBAAmB,CAAC;QAEjE,mDAAmD;QACnD,GAAG,CAAC,OAAO,GAAG,YAAY,CAAC;QAC3B,+EAA+E;QAC/E,GAAG,CAAC,KAAK,GAAG,SAAS,CAAC;IACxB,CAAC;AACH,CAAC","sourcesContent":["/**\n * Handle the error if the port is already in use.\n */\nexport function handleErrorPortUnavailable(err: unknown, projectName: string, ports: number | number[]): void {\n const portRange = typeof ports === 'number' ? [ports] : ports;\n if (err instanceof Error && err.message === 'Specified port not available') {\n const portNeedsPrivileges = portRange.some((port) => port < 1024)\n ? '\\nNote: for Mac/Linux/WSL users, you may need to run Cloudpack with elevated privileges to use port numbers less than 1024. Try running `sudo cloudpack start`.'\n : '';\n const errorMessage =\n `Cloudpack could not start \"${projectName}\" because no port from the allowed list was available: ${portRange.join(\n ', ',\n )}. Close other programs and try again.` + portNeedsPrivileges;\n\n // Edit the error message to be more user-friendly.\n err.message = errorMessage;\n // Suppress the stack trace for this error because it's not useful to the user.\n err.stack = undefined;\n }\n}\n"]}
|