@ms-cloudpack/app-server 0.4.4 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/createRoutes.d.ts +10 -17
- package/lib/createRoutes.d.ts.map +1 -1
- package/lib/createRoutes.js +18 -22
- package/lib/createRoutes.js.map +1 -1
- package/lib/inlineScripts/getInlineScripts.d.ts.map +1 -1
- package/lib/inlineScripts/getInlineScripts.js +2 -0
- package/lib/inlineScripts/getInlineScripts.js.map +1 -1
- package/lib/renderRoute/renderRoute.d.ts +5 -1
- package/lib/renderRoute/renderRoute.d.ts.map +1 -1
- package/lib/renderRoute/renderRoute.js +39 -11
- package/lib/renderRoute/renderRoute.js.map +1 -1
- package/lib/setHeaders.d.ts +2 -5
- package/lib/setHeaders.d.ts.map +1 -1
- package/lib/setHeaders.js +4 -3
- package/lib/setHeaders.js.map +1 -1
- package/lib/startAppServer.d.ts +5 -16
- package/lib/startAppServer.d.ts.map +1 -1
- package/lib/startAppServer.js +8 -6
- package/lib/startAppServer.js.map +1 -1
- package/lib/types/RenderRouteResult.d.ts +3 -0
- package/lib/types/RenderRouteResult.d.ts.map +1 -1
- package/lib/types/RenderRouteResult.js.map +1 -1
- package/package.json +8 -10
package/lib/createRoutes.d.ts
CHANGED
|
@@ -1,22 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Context } from '@ms-cloudpack/api-server';
|
|
2
|
+
import type { PackageJson } from '@ms-cloudpack/common-types';
|
|
2
3
|
import type { Express } from '@ms-cloudpack/create-express-app';
|
|
3
|
-
|
|
4
|
-
import type { ApiServer, Session } from '@ms-cloudpack/api-server';
|
|
5
|
-
import type { PackageImportPaths } from '@ms-cloudpack/import-map';
|
|
6
|
-
import type { PackageHashes } from '@ms-cloudpack/package-hashes';
|
|
7
|
-
/**
|
|
8
|
-
* Creates the routes for the express app based on the config.
|
|
9
|
-
*/
|
|
10
|
-
export declare function createRoutes(params: {
|
|
4
|
+
interface CreateRoutesOptions {
|
|
11
5
|
app: Express;
|
|
12
6
|
url: string;
|
|
13
|
-
session: Session;
|
|
14
7
|
definition: PackageJson;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
8
|
+
}
|
|
9
|
+
export type CreateRoutesContext = Pick<Context, 'packages' | 'packageImportPaths' | 'packageHashes' | 'session'>;
|
|
10
|
+
/**
|
|
11
|
+
* Creates the routes for the express app based on the config.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createRoutes(options: CreateRoutesOptions, context: CreateRoutesContext): void;
|
|
14
|
+
export {};
|
|
22
15
|
//# sourceMappingURL=createRoutes.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createRoutes.d.ts","sourceRoot":"","sources":["../src/createRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"createRoutes.d.ts","sourceRoot":"","sources":["../src/createRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAS,MAAM,4BAA4B,CAAC;AACrE,OAAO,KAAK,EAAE,OAAO,EAAqB,MAAM,kCAAkC,CAAC;AASnF,UAAU,mBAAmB;IAC3B,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,WAAW,CAAC;CACzB;AAED,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,OAAO,EAAE,UAAU,GAAG,oBAAoB,GAAG,eAAe,GAAG,SAAS,CAAC,CAAC;AAEjH;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,mBAAmB,QA6BtF"}
|
package/lib/createRoutes.js
CHANGED
|
@@ -2,35 +2,34 @@ import { express } from '@ms-cloudpack/create-express-app';
|
|
|
2
2
|
import { createImportMap } from '@ms-cloudpack/import-map';
|
|
3
3
|
import { slash } from '@ms-cloudpack/path-string-parsing';
|
|
4
4
|
import path from 'path';
|
|
5
|
-
import { renderRoute } from './renderRoute/renderRoute.js';
|
|
6
5
|
import { getInlineScripts } from './inlineScripts/getInlineScripts.js';
|
|
6
|
+
import { renderRoute } from './renderRoute/renderRoute.js';
|
|
7
7
|
import { setHeaders } from './setHeaders.js';
|
|
8
8
|
/**
|
|
9
9
|
* Creates the routes for the express app based on the config.
|
|
10
10
|
*/
|
|
11
|
-
export function createRoutes(
|
|
12
|
-
const { app,
|
|
13
|
-
const
|
|
11
|
+
export function createRoutes(options, context) {
|
|
12
|
+
const { app, ...otherOptions } = options;
|
|
13
|
+
const { session } = context;
|
|
14
|
+
const routes = [...(session.config.devServer?.routes || [])];
|
|
14
15
|
// Only as a basic fallback if no routes are specified, add a default catch-all route.
|
|
15
16
|
// Adding this automatically has the risk of hiding errors, like if an unexpected path
|
|
16
17
|
// being requested, or a JS file somehow being loaded from the app server unexpectedly.
|
|
17
|
-
if (!
|
|
18
|
-
|
|
18
|
+
if (!routes.length) {
|
|
19
|
+
const fallbackRoute = {
|
|
19
20
|
match: '*',
|
|
20
21
|
exportEntry: '.',
|
|
21
|
-
|
|
22
|
+
isDefaultFallback: true, // this internal property is read by renderRoute
|
|
23
|
+
};
|
|
24
|
+
routes.push(fallbackRoute);
|
|
22
25
|
}
|
|
23
|
-
// Hashes from external packages to be used in the import map.
|
|
24
|
-
const getPackageHash = (packagePath) => {
|
|
25
|
-
return packageHashes.get({ packagePath, isSourceHashingEnabled: false });
|
|
26
|
-
};
|
|
27
26
|
for (const route of routes) {
|
|
28
27
|
if (route.staticPath) {
|
|
29
28
|
app.use(route.match, express.static(path.resolve(session.appPath, route.staticPath)));
|
|
30
29
|
}
|
|
31
30
|
else {
|
|
32
31
|
app.get(route.match, (req, res) => {
|
|
33
|
-
handleRequest({ ...
|
|
32
|
+
handleRequest({ ...otherOptions, route, req, res }, context).catch((err) => {
|
|
34
33
|
console.error(err?.stack || err);
|
|
35
34
|
res.status(500).send(`Error loading app: ${err}`);
|
|
36
35
|
});
|
|
@@ -38,15 +37,11 @@ export function createRoutes(params) {
|
|
|
38
37
|
}
|
|
39
38
|
}
|
|
40
39
|
}
|
|
41
|
-
async function handleRequest(
|
|
42
|
-
const { req, res,
|
|
40
|
+
async function handleRequest(options, context) {
|
|
41
|
+
const { req, res, definition, route, url } = options;
|
|
42
|
+
const { session } = context;
|
|
43
43
|
// Build the import map if it hasn't been built yet for this session version.
|
|
44
|
-
const importMap = (session.importMap ??= await createImportMap({
|
|
45
|
-
resolveMap: session.resolveMap,
|
|
46
|
-
bundleServerUrl: bundleServer.url,
|
|
47
|
-
sessionVersion: session.sessionVersion,
|
|
48
|
-
targetVersions: session.targetVersions,
|
|
49
|
-
}, { packages, packageImportPaths, getPackageHash, config: session.config }));
|
|
44
|
+
const importMap = (session.importMap ??= await createImportMap({ ...context, ...context.session }));
|
|
50
45
|
// Parse the request path, extension, grab the overlay script path.
|
|
51
46
|
const requestPath = slash(req.path.substring(1));
|
|
52
47
|
const requestExt = path.extname(requestPath);
|
|
@@ -58,10 +53,11 @@ async function handleRequest(params) {
|
|
|
58
53
|
// TODO: setting the headers here prohibits cases where the page rendering is owned by existing server code
|
|
59
54
|
// that can only accept changing the scripts. We should consider moving to a model where an initial script
|
|
60
55
|
// fetch loads the import map and sets Cloudpack settings in local storage rather than headers/cookies.
|
|
61
|
-
setHeaders({ res, session
|
|
56
|
+
setHeaders({ res, session });
|
|
62
57
|
const inlineScripts = await getInlineScripts();
|
|
63
58
|
const { content, statusCode, contentType } = await renderRoute({
|
|
64
|
-
...
|
|
59
|
+
...options,
|
|
60
|
+
session,
|
|
65
61
|
baseUrl: url,
|
|
66
62
|
importMap,
|
|
67
63
|
overlayScript,
|
package/lib/createRoutes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createRoutes.js","sourceRoot":"","sources":["../src/createRoutes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"createRoutes.js","sourceRoot":"","sources":["../src/createRoutes.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,KAAK,EAAE,MAAM,mCAAmC,CAAC;AAC1D,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AACvE,OAAO,EAAE,WAAW,EAAsB,MAAM,8BAA8B,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAU7C;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,OAA4B,EAAE,OAA4B;IACrF,MAAM,EAAE,GAAG,EAAE,GAAG,YAAY,EAAE,GAAG,OAAO,CAAC;IACzC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAC5B,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC;IAE7D,sFAAsF;IACtF,sFAAsF;IACtF,uFAAuF;IACvF,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,aAAa,GAAkB;YACnC,KAAK,EAAE,GAAG;YACV,WAAW,EAAE,GAAG;YAChB,iBAAiB,EAAE,IAAI,EAAE,gDAAgD;SAC1E,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC7B,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YACrB,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACxF,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;gBACnD,aAAa,CAAC,EAAE,GAAG,YAAY,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;oBACzE,OAAO,CAAC,KAAK,CAAE,GAAa,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC;oBAC5C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,sBAAsB,GAAG,EAAE,CAAC,CAAC;gBACpD,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,OAIC,EACD,OAA4B;IAE5B,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IACrD,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAE5B,6EAA6E;IAC7E,MAAM,SAAS,GAAG,CAAC,OAAO,CAAC,SAAS,KAAK,MAAM,eAAe,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAEpG,mEAAmE;IACnE,MAAM,WAAW,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC7C,MAAM,aAAa,GAAG,SAAS,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACjE,MAAM,WAAW;IACf,oEAAoE;IACpE,KAAK,CAAC,WAAW,IAAI,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAK,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAEhG,iEAAiE;IACjE,2GAA2G;IAC3G,0GAA0G;IAC1G,uGAAuG;IACvG,UAAU,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;IAE7B,MAAM,aAAa,GAAG,MAAM,gBAAgB,EAAE,CAAC;IAC/C,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,MAAM,WAAW,CAAC;QAC7D,GAAG,OAAO;QACV,OAAO;QACP,OAAO,EAAE,GAAG;QACZ,SAAS;QACT,aAAa;QACb,WAAW;QACX,aAAa;KACd,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC;IAE7D,OAAO,CAAC,KAAK,CAAC,wBAAwB,WAAW,UAAU,UAAU,EAAE,CAAC,CAAC;AAC3E,CAAC","sourcesContent":["import type { Context } from '@ms-cloudpack/api-server';\nimport type { PackageJson, Route } from '@ms-cloudpack/common-types';\nimport type { Express, Request, Response } from '@ms-cloudpack/create-express-app';\nimport { express } from '@ms-cloudpack/create-express-app';\nimport { createImportMap } from '@ms-cloudpack/import-map';\nimport { slash } from '@ms-cloudpack/path-string-parsing';\nimport path from 'path';\nimport { getInlineScripts } from './inlineScripts/getInlineScripts.js';\nimport { renderRoute, type RouteInternal } from './renderRoute/renderRoute.js';\nimport { setHeaders } from './setHeaders.js';\n\ninterface CreateRoutesOptions {\n app: Express;\n url: string;\n definition: PackageJson;\n}\n\nexport type CreateRoutesContext = Pick<Context, 'packages' | 'packageImportPaths' | 'packageHashes' | 'session'>;\n\n/**\n * Creates the routes for the express app based on the config.\n */\nexport function createRoutes(options: CreateRoutesOptions, context: CreateRoutesContext) {\n const { app, ...otherOptions } = options;\n const { session } = context;\n const routes = [...(session.config.devServer?.routes || [])];\n\n // Only as a basic fallback if no routes are specified, add a default catch-all route.\n // Adding this automatically has the risk of hiding errors, like if an unexpected path\n // being requested, or a JS file somehow being loaded from the app server unexpectedly.\n if (!routes.length) {\n const fallbackRoute: RouteInternal = {\n match: '*',\n exportEntry: '.',\n isDefaultFallback: true, // this internal property is read by renderRoute\n };\n routes.push(fallbackRoute);\n }\n\n for (const route of routes) {\n if (route.staticPath) {\n app.use(route.match, express.static(path.resolve(session.appPath, route.staticPath)));\n } else {\n app.get(route.match, (req: Request, res: Response) => {\n handleRequest({ ...otherOptions, route, req, res }, context).catch((err) => {\n console.error((err as Error)?.stack || err);\n res.status(500).send(`Error loading app: ${err}`);\n });\n });\n }\n }\n}\n\nasync function handleRequest(\n options: Omit<CreateRoutesOptions, 'app'> & {\n req: Request;\n res: Response;\n route: Route;\n },\n context: CreateRoutesContext,\n) {\n const { req, res, definition, route, url } = options;\n const { session } = context;\n\n // Build the import map if it hasn't been built yet for this session version.\n const importMap = (session.importMap ??= await createImportMap({ ...context, ...context.session }));\n\n // Parse the request path, extension, grab the overlay script path.\n const requestPath = slash(req.path.substring(1));\n const requestExt = path.extname(requestPath);\n const overlayScript = importMap.imports['@ms-cloudpack/overlay'];\n const entryScript =\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n route.exportEntry && importMap.imports[slash(path.join(definition.name!, route.exportEntry))];\n\n // Set the appropriate Cloudpack headers/cookies in the response.\n // TODO: setting the headers here prohibits cases where the page rendering is owned by existing server code\n // that can only accept changing the scripts. We should consider moving to a model where an initial script\n // fetch loads the import map and sets Cloudpack settings in local storage rather than headers/cookies.\n setHeaders({ res, session });\n\n const inlineScripts = await getInlineScripts();\n const { content, statusCode, contentType } = await renderRoute({\n ...options,\n session,\n baseUrl: url,\n importMap,\n overlayScript,\n entryScript,\n inlineScripts,\n });\n\n res.type(contentType).status(statusCode).send(content).end();\n\n console.debug(`App server: Request: ${requestPath}, ext: ${requestExt}`);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getInlineScripts.d.ts","sourceRoot":"","sources":["../../src/inlineScripts/getInlineScripts.ts"],"names":[],"mappings":"AAKA,wBAAsB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"getInlineScripts.d.ts","sourceRoot":"","sources":["../../src/inlineScripts/getInlineScripts.ts"],"names":[],"mappings":"AAKA,wBAAsB,gBAAgB,sBASrC"}
|
|
@@ -5,6 +5,8 @@ export async function getInlineScripts() {
|
|
|
5
5
|
if (!inlineScripts) {
|
|
6
6
|
const inlineScriptFiles = await globSourceFiles(import.meta.url, '*.inline');
|
|
7
7
|
inlineScripts = await Promise.all(inlineScriptFiles.map((filename) => fsPromises.readFile(filename, 'utf-8')));
|
|
8
|
+
// Remove source maps from inline scripts to avoid warnings in the browser.
|
|
9
|
+
inlineScripts = inlineScripts.map((script) => script.replace('# sourceMappingURL=', ' '));
|
|
8
10
|
}
|
|
9
11
|
return inlineScripts;
|
|
10
12
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getInlineScripts.js","sourceRoot":"","sources":["../../src/inlineScripts/getInlineScripts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,UAAU,MAAM,aAAa,CAAC;AAErC,IAAI,aAAmC,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,iBAAiB,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAC7E,aAAa,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"getInlineScripts.js","sourceRoot":"","sources":["../../src/inlineScripts/getInlineScripts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,UAAU,MAAM,aAAa,CAAC;AAErC,IAAI,aAAmC,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,iBAAiB,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAC7E,aAAa,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;QAC/G,2EAA2E;QAC3E,aAAa,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC,CAAC;IAC5F,CAAC;IAED,OAAO,aAAa,CAAC;AACvB,CAAC","sourcesContent":["import { globSourceFiles } from '@ms-cloudpack/path-utilities';\nimport fsPromises from 'fs/promises';\n\nlet inlineScripts: string[] | undefined;\n\nexport async function getInlineScripts() {\n if (!inlineScripts) {\n const inlineScriptFiles = await globSourceFiles(import.meta.url, '*.inline');\n inlineScripts = await Promise.all(inlineScriptFiles.map((filename) => fsPromises.readFile(filename, 'utf-8')));\n // Remove source maps from inline scripts to avoid warnings in the browser.\n inlineScripts = inlineScripts.map((script) => script.replace('# sourceMappingURL=', ' '));\n }\n\n return inlineScripts;\n}\n"]}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Route } from '@ms-cloudpack/common-types';
|
|
2
2
|
import type { RenderRouteOptions } from '../types/RenderRouteOptions.js';
|
|
3
|
+
import type { CompleteRenderRouteResult } from '../types/RenderRouteResult.js';
|
|
4
|
+
export type RouteInternal = Route & {
|
|
5
|
+
isDefaultFallback?: boolean;
|
|
6
|
+
};
|
|
3
7
|
/**
|
|
4
8
|
* Get the response for the given route. If the route has a custom render script, use that.
|
|
5
9
|
* Returns an error response if the file referenced by the script doesn't exist or throws an error.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderRoute.d.ts","sourceRoot":"","sources":["../../src/renderRoute/renderRoute.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"renderRoute.d.ts","sourceRoot":"","sources":["../../src/renderRoute/renderRoute.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AAMxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,KAAK,EAAE,yBAAyB,EAAqB,MAAM,+BAA+B,CAAC;AAMlG,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG;IAAE,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEpE;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAoDjG"}
|
|
@@ -1,19 +1,26 @@
|
|
|
1
|
+
import fsPromises from 'fs/promises';
|
|
2
|
+
import { JSDOM } from 'jsdom';
|
|
1
3
|
import path from 'path';
|
|
2
4
|
import { pathToFileURL } from 'url';
|
|
3
5
|
import { getDefaultHtmlResponse } from './getDefaultHtmlResponse.js';
|
|
4
|
-
import { JSDOM } from 'jsdom';
|
|
5
|
-
import fsPromises from 'fs/promises';
|
|
6
6
|
import { getHtmlErrorResponse } from './getHtmlErrorResponse.js';
|
|
7
|
+
import { makeUrl } from '@ms-cloudpack/path-string-parsing';
|
|
7
8
|
/**
|
|
8
9
|
* Get the response for the given route. If the route has a custom render script, use that.
|
|
9
10
|
* Returns an error response if the file referenced by the script doesn't exist or throws an error.
|
|
10
11
|
*/
|
|
11
12
|
export async function renderRoute(options) {
|
|
12
|
-
const { route, session } = options;
|
|
13
|
+
const { route, session, req } = options;
|
|
14
|
+
// Get the request path and extension (the URL.pathname conversion gets rid of any query or hash)
|
|
15
|
+
const requestPath = makeUrl(req.url, options.baseUrl).pathname;
|
|
16
|
+
const requestExt = path.extname(requestPath);
|
|
17
|
+
// Determine whether HTML is (probably) an acceptable response type (can be refined later if needed).
|
|
18
|
+
// In theory we should be able to use req.accepts() but not all requests set the accept header.
|
|
19
|
+
const isHtmlAcceptable = (!requestExt || requestExt.startsWith('.htm')) && !req.xhr && isHtmlType(req.headers.accept || '*/*');
|
|
13
20
|
// Get an initial result from either the render script or default handler.
|
|
14
21
|
let rawResult;
|
|
15
|
-
|
|
16
|
-
|
|
22
|
+
const renderScriptPath = route.renderScript && path.resolve(session.appPath, route.renderScript);
|
|
23
|
+
if (renderScriptPath) {
|
|
17
24
|
const scriptExt = path.extname(renderScriptPath).toLowerCase();
|
|
18
25
|
if (scriptExt === '.html' || scriptExt === '.htm') {
|
|
19
26
|
rawResult = await readStaticHtml(renderScriptPath);
|
|
@@ -30,15 +37,36 @@ export async function renderRoute(options) {
|
|
|
30
37
|
statusCode: 200,
|
|
31
38
|
...(typeof rawResult === 'string' ? { content: rawResult } : rawResult),
|
|
32
39
|
};
|
|
33
|
-
// If it's HTML and
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
// If it's HTML and a success statusCode, inject the import map and appropriate scripts.
|
|
41
|
+
if (result.statusCode === 200 && isHtmlType(result.contentType)) {
|
|
42
|
+
// First verify that we're not accidentally returning HTML if a different file type is requested.
|
|
43
|
+
// This could happen for overly broad route matches such as '*', or if no routes are configured.
|
|
44
|
+
if (!isHtmlAcceptable) {
|
|
45
|
+
const renderer = route.renderScript ? `renderScript "${renderScriptPath}"` : 'the default renderer';
|
|
46
|
+
const content = route.isDefaultFallback
|
|
47
|
+
? // no routes configured
|
|
48
|
+
`Cloudpack's default route can only return HTML, but this appears to be a non-HTML request. ` +
|
|
49
|
+
`Please add a route under devServer.routes in the cloudpack config.`
|
|
50
|
+
: // route with or without custom renderer
|
|
51
|
+
`Route "${route.match}" with ${renderer} returned HTML, but this appears to be a non-HTML request. ` +
|
|
52
|
+
`This is likely a misconfiguration of devServer.routes in the cloudpack config.`;
|
|
53
|
+
return { statusCode: 500, contentType: 'text/plain', content };
|
|
54
|
+
}
|
|
55
|
+
// Inject the import map and scripts.
|
|
38
56
|
injectScripts(options, result);
|
|
39
57
|
}
|
|
40
58
|
return result;
|
|
41
59
|
}
|
|
60
|
+
function isHtmlType(acceptOrContentType) {
|
|
61
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept
|
|
62
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation/List_of_default_Accept_values
|
|
63
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type
|
|
64
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
|
65
|
+
return acceptOrContentType
|
|
66
|
+
.split(',')
|
|
67
|
+
.map((s) => s.split(';')[0].trim())
|
|
68
|
+
.some((mime) => mime === '*/*' || /xml|x?html/i.test(mime) || /^(text|application)\/\*$/.test(mime));
|
|
69
|
+
}
|
|
42
70
|
/**
|
|
43
71
|
* Load the default export from a JS file passed as the `renderScript` in a route,
|
|
44
72
|
* and return either the result of running the function, or an error page if something fails.
|
|
@@ -97,7 +125,7 @@ function injectScripts(options, result) {
|
|
|
97
125
|
const { route, overlayScript, entryScript, inlineScripts, importMap } = options;
|
|
98
126
|
let jsdom;
|
|
99
127
|
try {
|
|
100
|
-
jsdom = new JSDOM(result.content);
|
|
128
|
+
jsdom = new JSDOM(result.content, { contentType: result.contentType });
|
|
101
129
|
}
|
|
102
130
|
catch (e) {
|
|
103
131
|
// Trying to write a test for the above, it seemed very permissive, but catch just in case
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderRoute.js","sourceRoot":"","sources":["../../src/renderRoute/renderRoute.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'path';\nimport { pathToFileURL } from 'url';\nimport { getDefaultHtmlResponse } from './getDefaultHtmlResponse.js';\nimport { JSDOM } from 'jsdom';\nimport fsPromises from 'fs/promises';\nimport { getHtmlErrorResponse } from './getHtmlErrorResponse.js';\nimport type { CompleteRenderRouteResult, RenderRouteResult } from '../types/RenderRouteResult.js';\nimport type { RenderRouteOptions } from '../types/RenderRouteOptions.js';\nimport type { RenderRouteFunction } from '../types/RenderRouteFunction.js';\nimport type { RenderRouteScript } from '../types/RenderRouteScript.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(options: RenderRouteOptions): Promise<CompleteRenderRouteResult> {\n const { route, session } = options;\n\n // Get an initial result from either the render script or default handler.\n let rawResult: RenderRouteResult;\n if (route.renderScript) {\n const renderScriptPath = path.resolve(session.appPath, route.renderScript);\n const scriptExt = path.extname(renderScriptPath).toLowerCase();\n if (scriptExt === '.html' || scriptExt === '.htm') {\n rawResult = await readStaticHtml(renderScriptPath);\n } else {\n rawResult = await renderCustomScript({ ...options, renderScriptPath });\n }\n } else {\n rawResult = await getDefaultHtmlResponse(options);\n }\n\n const result: CompleteRenderRouteResult = {\n contentType: 'text/html',\n statusCode: 200,\n ...(typeof rawResult === 'string' ? { content: rawResult } : rawResult),\n };\n\n // If it's HTML and not an error, inject the import map and appropriate scripts.\n // endsWith('html') covers: 'html', '.html', and 'text/html'\n // (Might need to modify the status check in the future if there are other codes where injecting\n // scripts is appropriate, but for now, only do this for 200.)\n if (result.contentType.endsWith('html') && result.statusCode === 200) {\n injectScripts(options, result);\n }\n\n return result;\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: RenderRouteOptions & { renderScriptPath: string },\n): Promise<RenderRouteResult> {\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 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: RenderRouteFunction | undefined;\n let errorMessage: string | undefined;\n try {\n const importResult = ((await import(renderScriptUrl + cacheBreakerQueryParam)) as RenderRouteScript).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 */\nfunction injectScripts(options: RenderRouteOptions, result: CompleteRenderRouteResult): void {\n const { route, overlayScript, entryScript, inlineScripts, importMap } = options;\n let jsdom: JSDOM;\n try {\n jsdom = new JSDOM(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(`Error parsing html response for rendering route \"${route.match}\":\\n${(e as Error).stack || e}`);\n return;\n }\n\n try {\n const { document } = jsdom.window;\n\n // inject the import map.\n if (entryScript || overlayScript) {\n addScript({ document, type: 'importmap', content: JSON.stringify(importMap, null, 2) });\n }\n\n for (const inlineScript of inlineScripts) {\n addScript({ document, content: inlineScript });\n }\n\n // inject the overlay and entry scripts\n addScript({ document, url: overlayScript });\n addScript({ document, url: entryScript });\n\n result.content = jsdom.serialize();\n } catch (e) {\n console.error(`Error injecting scripts for route \"${route.match}\":\\n${(e as Error).stack || e}`);\n }\n}\n\n/**\n * Helper function to add a script to the document.\n */\nfunction addScript(params: { document: Document; type?: string; prepend?: boolean; url?: string; content?: string }) {\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<RenderRouteResult> {\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 { Route } from '@ms-cloudpack/common-types';\nimport fsPromises from 'fs/promises';\nimport { JSDOM } from 'jsdom';\nimport path from 'path';\nimport { pathToFileURL } from 'url';\nimport type { RenderRouteFunction } from '../types/RenderRouteFunction.js';\nimport type { RenderRouteOptions } from '../types/RenderRouteOptions.js';\nimport type { CompleteRenderRouteResult, RenderRouteResult } from '../types/RenderRouteResult.js';\nimport type { RenderRouteScript } from '../types/RenderRouteScript.js';\nimport { getDefaultHtmlResponse } from './getDefaultHtmlResponse.js';\nimport { getHtmlErrorResponse } from './getHtmlErrorResponse.js';\nimport { makeUrl } from '@ms-cloudpack/path-string-parsing';\n\nexport type RouteInternal = Route & { isDefaultFallback?: boolean };\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(options: RenderRouteOptions): Promise<CompleteRenderRouteResult> {\n const { route, session, req } = options;\n\n // Get the request path and extension (the URL.pathname conversion gets rid of any query or hash)\n const requestPath = makeUrl(req.url, options.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 const isHtmlAcceptable =\n (!requestExt || requestExt.startsWith('.htm')) && !req.xhr && isHtmlType(req.headers.accept || '*/*');\n\n // Get an initial result from either the render script or default handler.\n let rawResult: RenderRouteResult;\n const renderScriptPath = route.renderScript && path.resolve(session.appPath, route.renderScript);\n if (renderScriptPath) {\n const scriptExt = path.extname(renderScriptPath).toLowerCase();\n if (scriptExt === '.html' || scriptExt === '.htm') {\n rawResult = await readStaticHtml(renderScriptPath);\n } else {\n rawResult = await renderCustomScript({ ...options, renderScriptPath });\n }\n } else {\n rawResult = await getDefaultHtmlResponse(options);\n }\n\n const result: CompleteRenderRouteResult = {\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 // First verify that we're not accidentally returning HTML if a different file type is requested.\n // This could happen for overly broad route matches such as '*', or if no routes are configured.\n if (!isHtmlAcceptable) {\n const renderer = route.renderScript ? `renderScript \"${renderScriptPath}\"` : 'the default renderer';\n const content = (route as RouteInternal).isDefaultFallback\n ? // no routes configured\n `Cloudpack's default route can only return HTML, but this appears to be a non-HTML request. ` +\n `Please add a route under devServer.routes in the cloudpack config.`\n : // route with or without custom renderer\n `Route \"${route.match}\" with ${renderer} returned HTML, but this appears to be a non-HTML request. ` +\n `This is likely a misconfiguration of devServer.routes in the cloudpack config.`;\n return { statusCode: 500, contentType: 'text/plain', content };\n }\n\n // Inject the import map and scripts.\n injectScripts(options, result);\n }\n\n return result;\n}\n\nfunction isHtmlType(acceptOrContentType: string) {\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: RenderRouteOptions & { renderScriptPath: string },\n): Promise<RenderRouteResult> {\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 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: RenderRouteFunction | undefined;\n let errorMessage: string | undefined;\n try {\n const importResult = ((await import(renderScriptUrl + cacheBreakerQueryParam)) as RenderRouteScript).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 */\nfunction injectScripts(options: RenderRouteOptions, result: CompleteRenderRouteResult): void {\n const { route, overlayScript, entryScript, inlineScripts, importMap } = options;\n let jsdom: JSDOM;\n try {\n jsdom = new JSDOM(result.content, { contentType: result.contentType } as ConstructorParameters<typeof JSDOM>[1]);\n } catch (e) {\n // Trying to write a test for the above, it seemed very permissive, but catch just in case\n console.error(`Error parsing html response for rendering route \"${route.match}\":\\n${(e as Error).stack || e}`);\n return;\n }\n\n try {\n const { document } = jsdom.window;\n\n // inject the import map.\n if (entryScript || overlayScript) {\n addScript({ document, type: 'importmap', content: JSON.stringify(importMap, null, 2) });\n }\n\n for (const inlineScript of inlineScripts) {\n addScript({ document, content: inlineScript });\n }\n\n // inject the overlay and entry scripts\n addScript({ document, url: overlayScript });\n addScript({ document, url: entryScript });\n\n result.content = jsdom.serialize();\n } catch (e) {\n console.error(`Error injecting scripts for route \"${route.match}\":\\n${(e as Error).stack || e}`);\n }\n}\n\n/**\n * Helper function to add a script to the document.\n */\nfunction addScript(params: { document: Document; type?: string; prepend?: boolean; url?: string; content?: string }) {\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<RenderRouteResult> {\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"]}
|
package/lib/setHeaders.d.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import type { Response } from '@ms-cloudpack/create-express-app';
|
|
2
|
-
import type {
|
|
3
|
-
import type { BundleServer } from '@ms-cloudpack/bundle-server';
|
|
2
|
+
import type { Session } from '@ms-cloudpack/api-server';
|
|
4
3
|
/**
|
|
5
4
|
* Set the Cloudpack headers and cookies in the response.
|
|
6
5
|
*/
|
|
7
6
|
export declare function setHeaders(params: {
|
|
8
7
|
res: Response;
|
|
9
|
-
session: Session
|
|
10
|
-
apiServer: ApiServer;
|
|
11
|
-
bundleServer: BundleServer;
|
|
8
|
+
session: Pick<Session, 'id' | 'sequence' | 'projectName' | 'sessionVersion' | 'urls'>;
|
|
12
9
|
}): void;
|
|
13
10
|
//# sourceMappingURL=setHeaders.d.ts.map
|
package/lib/setHeaders.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setHeaders.d.ts","sourceRoot":"","sources":["../src/setHeaders.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"setHeaders.d.ts","sourceRoot":"","sources":["../src/setHeaders.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAGxD;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE;IACjC,GAAG,EAAE,QAAQ,CAAC;IACd,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,GAAG,UAAU,GAAG,aAAa,GAAG,gBAAgB,GAAG,MAAM,CAAC,CAAC;CACvF,QAYA"}
|
package/lib/setHeaders.js
CHANGED
|
@@ -3,13 +3,14 @@ import { cookieNames, getCloudpackSessionVersionCookie } from './cookieNames.js'
|
|
|
3
3
|
* Set the Cloudpack headers and cookies in the response.
|
|
4
4
|
*/
|
|
5
5
|
export function setHeaders(params) {
|
|
6
|
-
const { res, session
|
|
6
|
+
const { res, session } = params;
|
|
7
|
+
const { apiServer, bundleServer } = session.urls;
|
|
7
8
|
res.header('Cache-Control', 'no-cache');
|
|
8
9
|
res.header('Access-Control-Allow-Origin', '*');
|
|
9
10
|
res.cookie(cookieNames.sessionId, session.id);
|
|
10
11
|
res.cookie(cookieNames.sessionSequence, session.sequence);
|
|
11
12
|
res.cookie(getCloudpackSessionVersionCookie(session.projectName), session.sessionVersion);
|
|
12
|
-
res.cookie(cookieNames.apiUrl, apiServer
|
|
13
|
-
res.cookie(cookieNames.bundleServerUrl, bundleServer
|
|
13
|
+
res.cookie(cookieNames.apiUrl, apiServer);
|
|
14
|
+
res.cookie(cookieNames.bundleServerUrl, bundleServer);
|
|
14
15
|
}
|
|
15
16
|
//# sourceMappingURL=setHeaders.js.map
|
package/lib/setHeaders.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setHeaders.js","sourceRoot":"","sources":["../src/setHeaders.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"setHeaders.js","sourceRoot":"","sources":["../src/setHeaders.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,gCAAgC,EAAE,MAAM,kBAAkB,CAAC;AAEjF;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,MAG1B;IACC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IAChC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAEjD,GAAG,CAAC,MAAM,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;IACxC,GAAG,CAAC,MAAM,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;IAE/C,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9C,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,eAAe,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1D,GAAG,CAAC,MAAM,CAAC,gCAAgC,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IAC1F,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC1C,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC;AACxD,CAAC","sourcesContent":["import type { Response } from '@ms-cloudpack/create-express-app';\nimport type { Session } from '@ms-cloudpack/api-server';\nimport { cookieNames, getCloudpackSessionVersionCookie } from './cookieNames.js';\n\n/**\n * Set the Cloudpack headers and cookies in the response.\n */\nexport function setHeaders(params: {\n res: Response;\n session: Pick<Session, 'id' | 'sequence' | 'projectName' | 'sessionVersion' | 'urls'>;\n}) {\n const { res, session } = params;\n const { apiServer, bundleServer } = session.urls;\n\n res.header('Cache-Control', 'no-cache');\n res.header('Access-Control-Allow-Origin', '*');\n\n res.cookie(cookieNames.sessionId, session.id);\n res.cookie(cookieNames.sessionSequence, session.sequence);\n res.cookie(getCloudpackSessionVersionCookie(session.projectName), session.sessionVersion);\n res.cookie(cookieNames.apiUrl, apiServer);\n res.cookie(cookieNames.bundleServerUrl, bundleServer);\n}\n"]}
|
package/lib/startAppServer.d.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
import { type TaskReporter } from '@ms-cloudpack/task-reporter';
|
|
5
|
-
import type { ApiServer, Session } from '@ms-cloudpack/api-server';
|
|
6
|
-
import type { PackageImportPaths } from '@ms-cloudpack/import-map';
|
|
7
|
-
import type { PackageHashes } from '@ms-cloudpack/package-hashes';
|
|
2
|
+
import type { PartialContext } from '@ms-cloudpack/api-server';
|
|
3
|
+
import type { PackageJson } from '@ms-cloudpack/common-types';
|
|
8
4
|
import type { Server } from 'http';
|
|
5
|
+
import { type CreateRoutesContext } from './createRoutes.js';
|
|
9
6
|
import type { AppServer } from './types/AppServer.js';
|
|
10
7
|
/**
|
|
11
8
|
* The app server hosts the appropriate routes for the web app, primarily returning html content
|
|
@@ -15,17 +12,9 @@ import type { AppServer } from './types/AppServer.js';
|
|
|
15
12
|
* can support whichever routes the app needs, while the bundle server can provide package
|
|
16
13
|
* assets in various forms using its own routing.
|
|
17
14
|
*/
|
|
18
|
-
export declare function startAppServer(
|
|
19
|
-
session: Session;
|
|
15
|
+
export declare function startAppServer(options: {
|
|
20
16
|
definition: PackageJson;
|
|
21
|
-
bundleServer: BundleServer;
|
|
22
|
-
apiServer: ApiServer;
|
|
23
|
-
config: CloudpackConfig;
|
|
24
|
-
packages: PackageDefinitionsCache;
|
|
25
|
-
packageImportPaths: PackageImportPaths;
|
|
26
|
-
reporter: TaskReporter;
|
|
27
|
-
packageHashes: PackageHashes;
|
|
28
17
|
middlewareMode?: boolean;
|
|
29
18
|
server?: Server;
|
|
30
|
-
}): Promise<AppServer>;
|
|
19
|
+
}, context: PartialContext<'reporter', 'config' | 'projectName'> & CreateRoutesContext): Promise<AppServer>;
|
|
31
20
|
//# sourceMappingURL=startAppServer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"startAppServer.d.ts","sourceRoot":"","sources":["../src/startAppServer.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"startAppServer.d.ts","sourceRoot":"","sources":["../src/startAppServer.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAG9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE3E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE;IACP,UAAU,EAAE,WAAW,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,EACD,OAAO,EAAE,cAAc,CAAC,UAAU,EAAE,QAAQ,GAAG,aAAa,CAAC,GAAG,mBAAmB,GAClF,OAAO,CAAC,SAAS,CAAC,CAkCpB"}
|
package/lib/startAppServer.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createExpressApp } from '@ms-cloudpack/create-express-app';
|
|
2
|
+
import { cyan } from '@ms-cloudpack/task-reporter';
|
|
2
3
|
import { createRoutes } from './createRoutes.js';
|
|
3
4
|
import { handleErrorPortUnavailable } from './handleErrorPortUnavailable.js';
|
|
4
|
-
import { cyan } from '@ms-cloudpack/task-reporter';
|
|
5
5
|
/**
|
|
6
6
|
* The app server hosts the appropriate routes for the web app, primarily returning html content
|
|
7
7
|
* which loads resources from the bundle server.
|
|
@@ -10,12 +10,14 @@ import { cyan } from '@ms-cloudpack/task-reporter';
|
|
|
10
10
|
* can support whichever routes the app needs, while the bundle server can provide package
|
|
11
11
|
* assets in various forms using its own routing.
|
|
12
12
|
*/
|
|
13
|
-
export async function startAppServer(
|
|
14
|
-
const {
|
|
13
|
+
export async function startAppServer(options, context) {
|
|
14
|
+
const { definition, middlewareMode, server } = options;
|
|
15
|
+
const { session, reporter } = context;
|
|
16
|
+
const { config } = session;
|
|
15
17
|
const { devServer = {} } = config;
|
|
16
18
|
// Read the port from the config file or default to array of ports.
|
|
17
|
-
const requireSpecifiedPort = devServer
|
|
18
|
-
const ports = devServer
|
|
19
|
+
const requireSpecifiedPort = devServer.port !== undefined;
|
|
20
|
+
const ports = devServer.port ?? [5000, 5001, 5002, 5003];
|
|
19
21
|
const task = reporter.addTask(`Starting app server for "${definition.name}"`);
|
|
20
22
|
try {
|
|
21
23
|
const { app, close, port, url } = await createExpressApp({
|
|
@@ -27,7 +29,7 @@ export async function startAppServer(params) {
|
|
|
27
29
|
middlewareMode,
|
|
28
30
|
server,
|
|
29
31
|
});
|
|
30
|
-
createRoutes({ ...
|
|
32
|
+
createRoutes({ ...options, app, url }, context);
|
|
31
33
|
task.complete({ message: `Available @ ${cyan(url)}`, forceShow: true });
|
|
32
34
|
return { app, close, port, url };
|
|
33
35
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"startAppServer.js","sourceRoot":"","sources":["../src/startAppServer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"startAppServer.js","sourceRoot":"","sources":["../src/startAppServer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAC;AAEnD,OAAO,EAAE,YAAY,EAA4B,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAG7E;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAIC,EACD,OAAmF;IAEnF,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACvD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IACtC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC3B,MAAM,EAAE,SAAS,GAAG,EAAE,EAAE,GAAG,MAAM,CAAC;IAElC,mEAAmE;IACnE,MAAM,oBAAoB,GAAG,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC;IAC1D,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAEzD,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,4BAA4B,UAAU,CAAC,IAAI,GAAG,CAAC,CAAC;IAE9E,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,gBAAgB,CAAC;YACvD,SAAS,EAAE,KAAK;YAChB,oBAAoB;YACpB,QAAQ,EAAE,SAAS,EAAE,MAAM;YAC3B,cAAc,EAAE,SAAS,EAAE,cAAc;YACzC,UAAU,EAAE,SAAS,EAAE,KAAK;YAC5B,cAAc;YACd,MAAM;SACP,CAAC,CAAC;QAEH,YAAY,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;QAEhD,IAAI,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,eAAe,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAExE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;IACnC,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,4BAA4B,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1F,0BAA0B,CAAC,GAAG,EAAE,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAE5D,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC","sourcesContent":["import type { PartialContext } from '@ms-cloudpack/api-server';\nimport type { PackageJson } from '@ms-cloudpack/common-types';\nimport { createExpressApp } from '@ms-cloudpack/create-express-app';\nimport { cyan } from '@ms-cloudpack/task-reporter';\nimport type { Server } from 'http';\nimport { createRoutes, type CreateRoutesContext } from './createRoutes.js';\nimport { handleErrorPortUnavailable } from './handleErrorPortUnavailable.js';\nimport type { AppServer } from './types/AppServer.js';\n\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 */\nexport async function startAppServer(\n options: {\n definition: PackageJson;\n middlewareMode?: boolean;\n server?: Server;\n },\n context: PartialContext<'reporter', 'config' | 'projectName'> & CreateRoutesContext,\n): Promise<AppServer> {\n const { definition, middlewareMode, server } = options;\n const { session, reporter } = context;\n const { config } = session;\n const { devServer = {} } = config;\n\n // Read the port from the config file or default to array of ports.\n const requireSpecifiedPort = devServer.port !== undefined;\n const ports = devServer.port ?? [5000, 5001, 5002, 5003];\n\n const task = reporter.addTask(`Starting app server for \"${definition.name}\"`);\n\n try {\n const { app, close, port, url } = await createExpressApp({\n portRange: ports,\n requireSpecifiedPort,\n hostname: devServer?.domain,\n requestHeaders: devServer?.requestHeaders,\n sslOptions: devServer?.https,\n middlewareMode,\n server,\n });\n\n createRoutes({ ...options, app, url }, context);\n\n task.complete({ message: `Available @ ${cyan(url)}`, forceShow: true });\n\n return { app, close, port, url };\n } catch (err: unknown) {\n task.complete({ status: 'fail', message: 'Failed to start app server', forceShow: true });\n handleErrorPortUnavailable(err, session.projectName, ports);\n\n throw err;\n }\n}\n"]}
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
* The complete expected result of the `renderScript` custom server render function.
|
|
3
3
|
*/
|
|
4
4
|
export interface CompleteRenderRouteResult {
|
|
5
|
+
/** Response body */
|
|
5
6
|
content: string;
|
|
7
|
+
/** Standard HTTP status code - https://developer.mozilla.org/en-US/docs/Web/HTTP/Status */
|
|
6
8
|
statusCode: number;
|
|
9
|
+
/** MIME type for the response - https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types */
|
|
7
10
|
contentType: string;
|
|
8
11
|
}
|
|
9
12
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RenderRouteResult.d.ts","sourceRoot":"","sources":["../../src/types/RenderRouteResult.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"RenderRouteResult.d.ts","sourceRoot":"","sources":["../../src/types/RenderRouteResult.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,2FAA2F;IAC3F,UAAU,EAAE,MAAM,CAAC;IACnB,4HAA4H;IAC5H,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RenderRouteResult.js","sourceRoot":"","sources":["../../src/types/RenderRouteResult.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * The complete expected result of the `renderScript` custom server render function.\n */\nexport interface CompleteRenderRouteResult {\n content: string;\n statusCode: number;\n contentType: string;\n}\n\n/**\n * The expected result of the `renderScript` custom server render function.\n *\n * If the result is a string, it's assumed to be the HTML content.\n *\n * If an object and the `content` is non-HTML, `contentType` must be set.\n */\nexport type RenderRouteResult = string | ({ content: string } & Partial<CompleteRenderRouteResult>);\n"]}
|
|
1
|
+
{"version":3,"file":"RenderRouteResult.js","sourceRoot":"","sources":["../../src/types/RenderRouteResult.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * The complete expected result of the `renderScript` custom server render function.\n */\nexport interface CompleteRenderRouteResult {\n /** Response body */\n content: string;\n /** Standard HTTP status code - https://developer.mozilla.org/en-US/docs/Web/HTTP/Status */\n statusCode: number;\n /** MIME type for the response - https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types */\n contentType: string;\n}\n\n/**\n * The expected result of the `renderScript` custom server render function.\n *\n * If the result is a string, it's assumed to be the HTML content.\n *\n * If an object and the `content` is non-HTML, `contentType` must be set.\n */\nexport type RenderRouteResult = string | ({ content: string } & Partial<CompleteRenderRouteResult>);\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ms-cloudpack/app-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "An implementation of the App server for Cloudpack.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -14,15 +14,13 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@ms-cloudpack/api-server": "^0.
|
|
18
|
-
"@ms-cloudpack/
|
|
19
|
-
"@ms-cloudpack/
|
|
20
|
-
"@ms-cloudpack/
|
|
21
|
-
"@ms-cloudpack/
|
|
22
|
-
"@ms-cloudpack/
|
|
23
|
-
"@ms-cloudpack/
|
|
24
|
-
"@ms-cloudpack/path-string-parsing": "^1.2.1",
|
|
25
|
-
"@ms-cloudpack/path-utilities": "^2.7.5",
|
|
17
|
+
"@ms-cloudpack/api-server": "^0.42.1",
|
|
18
|
+
"@ms-cloudpack/common-types": "^0.5.0",
|
|
19
|
+
"@ms-cloudpack/create-express-app": "^1.6.10",
|
|
20
|
+
"@ms-cloudpack/import-map": "^0.4.0",
|
|
21
|
+
"@ms-cloudpack/overlay": "^0.16.112",
|
|
22
|
+
"@ms-cloudpack/path-string-parsing": "^1.2.2",
|
|
23
|
+
"@ms-cloudpack/path-utilities": "^2.7.7",
|
|
26
24
|
"@ms-cloudpack/task-reporter": "^0.14.0",
|
|
27
25
|
"jsdom": "^22.0.0"
|
|
28
26
|
},
|