@ms-cloudpack/app-server 0.20.3 → 0.20.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/lib/createRoutes.d.ts.map +1 -1
  2. package/lib/createRoutes.js +2 -10
  3. package/lib/createRoutes.js.map +1 -1
  4. package/lib/renderRoute/getDefaultHtmlResponse.d.ts +2 -2
  5. package/lib/renderRoute/getDefaultHtmlResponse.d.ts.map +1 -1
  6. package/lib/renderRoute/getDefaultHtmlResponse.js +2 -2
  7. package/lib/renderRoute/getDefaultHtmlResponse.js.map +1 -1
  8. package/lib/renderRoute/getErrorResponse.d.ts +4 -3
  9. package/lib/renderRoute/getErrorResponse.d.ts.map +1 -1
  10. package/lib/renderRoute/getErrorResponse.js.map +1 -1
  11. package/lib/renderRoute/injectScripts.d.ts +7 -2
  12. package/lib/renderRoute/injectScripts.d.ts.map +1 -1
  13. package/lib/renderRoute/injectScripts.js +17 -3
  14. package/lib/renderRoute/injectScripts.js.map +1 -1
  15. package/lib/renderRoute/renderRoute.d.ts +3 -2
  16. package/lib/renderRoute/renderRoute.d.ts.map +1 -1
  17. package/lib/renderRoute/renderRoute.js +25 -8
  18. package/lib/renderRoute/renderRoute.js.map +1 -1
  19. package/lib/renderRoute/ssr/bundleServerEntry.d.ts +3 -2
  20. package/lib/renderRoute/ssr/bundleServerEntry.d.ts.map +1 -1
  21. package/lib/renderRoute/ssr/bundleServerEntry.js.map +1 -1
  22. package/lib/renderRoute/ssr/renderCustomScript.d.ts +5 -3
  23. package/lib/renderRoute/ssr/renderCustomScript.d.ts.map +1 -1
  24. package/lib/renderRoute/ssr/renderCustomScript.js +41 -19
  25. package/lib/renderRoute/ssr/renderCustomScript.js.map +1 -1
  26. package/lib/renderRoute/ssr/runServerEntryInWorker.d.ts +7 -3
  27. package/lib/renderRoute/ssr/runServerEntryInWorker.d.ts.map +1 -1
  28. package/lib/renderRoute/ssr/runServerEntryInWorker.js +9 -20
  29. package/lib/renderRoute/ssr/runServerEntryInWorker.js.map +1 -1
  30. package/lib/renderRoute/ssr/types/RunServerEntryOptions.d.ts +2 -2
  31. package/lib/renderRoute/ssr/types/RunServerEntryOptions.d.ts.map +1 -1
  32. package/lib/renderRoute/ssr/types/RunServerEntryOptions.js.map +1 -1
  33. package/lib/renderRoute/ssr/worker/importMapLoader.d.ts +25 -8
  34. package/lib/renderRoute/ssr/worker/importMapLoader.d.ts.map +1 -1
  35. package/lib/renderRoute/ssr/worker/importMapLoader.js +29 -8
  36. package/lib/renderRoute/ssr/worker/importMapLoader.js.map +1 -1
  37. package/lib/renderRoute/ssr/worker/runServerEntry.d.ts +5 -8
  38. package/lib/renderRoute/ssr/worker/runServerEntry.d.ts.map +1 -1
  39. package/lib/renderRoute/ssr/worker/runServerEntry.js +82 -28
  40. package/lib/renderRoute/ssr/worker/runServerEntry.js.map +1 -1
  41. package/lib/startServers.d.ts.map +1 -1
  42. package/lib/startServers.js +10 -1
  43. package/lib/startServers.js.map +1 -1
  44. package/lib/types/RenderRouteOptions.d.ts +10 -0
  45. package/lib/types/RenderRouteOptions.d.ts.map +1 -0
  46. package/lib/types/RenderRouteOptions.js +2 -0
  47. package/lib/types/RenderRouteOptions.js.map +1 -0
  48. package/package.json +9 -9
  49. package/lib/renderRoute/ssr/types/RunServerEntryResult.d.ts +0 -8
  50. package/lib/renderRoute/ssr/types/RunServerEntryResult.d.ts.map +0 -1
  51. package/lib/renderRoute/ssr/types/RunServerEntryResult.js +0 -2
  52. package/lib/renderRoute/ssr/types/RunServerEntryResult.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"createRoutes.d.ts","sourceRoot":"","sources":["../src/createRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAML,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,aAAa,EAGnB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAYnF,UAAU,mBAAmB;IAC3B,GAAG,EAAE,OAAO,CAAC;IACb,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,+BAA+B;IAC/B,UAAU,EAAE,WAAW,CAAC;CACzB;AAcD;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,2BAA2B,GAAG,IAAI,CA0DrG;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,KAAK,CAAC,GAAG;IAC1C,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,QAAQ,CAAC;IACd,KAAK,EAAE,aAAa,GAAG,cAAc,CAAC;CACvC,EACD,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,IAAI,CAAC,CA4Df"}
1
+ {"version":3,"file":"createRoutes.d.ts","sourceRoot":"","sources":["../src/createRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAML,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,aAAa,EAGnB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAYnF,UAAU,mBAAmB;IAC3B,GAAG,EAAE,OAAO,CAAC;IACb,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,+BAA+B;IAC/B,UAAU,EAAE,WAAW,CAAC;CACzB;AAcD;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,2BAA2B,GAAG,IAAI,CA0DrG;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,KAAK,CAAC,GAAG;IAC1C,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,EAAE,QAAQ,CAAC;IACd,KAAK,EAAE,aAAa,GAAG,cAAc,CAAC;CACvC,EACD,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,IAAI,CAAC,CAiDf"}
@@ -74,7 +74,7 @@ export function createRoutes(options, context) {
74
74
  }
75
75
  }
76
76
  export async function _handleRequest(options, context) {
77
- const { req, res, definition, route, url } = options;
77
+ const { req, res, definition, route, url: baseUrl } = options;
78
78
  const { session } = context;
79
79
  // Build the import map if it hasn't been built yet for this session version.
80
80
  // (TS can't infer that urls.bundleServer is set on session from a check above.)
@@ -104,15 +104,7 @@ export async function _handleRequest(options, context) {
104
104
  }
105
105
  else {
106
106
  // HTML or custom script rendering
107
- rendered = await renderRoute({
108
- ...options,
109
- session,
110
- baseUrl: url,
111
- importMap,
112
- overlayScript,
113
- inlineScripts,
114
- entryScripts,
115
- }, context);
107
+ rendered = await renderRoute({ ...options, baseUrl, overlayScript, inlineScripts, entryScripts }, context);
116
108
  }
117
109
  if (rendered) {
118
110
  res.type(rendered.contentType).status(rendered.statusCode).send(rendered.content).end();
@@ -1 +1 @@
1
- {"version":3,"file":"createRoutes.js","sourceRoot":"","sources":["../src/createRoutes.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EnsurePackageBundledContext } from '@ms-cloudpack/api-server';\nimport {\n isBootstrapRoute,\n isProxyRoute,\n isRedirectRoute,\n isRenderedRoute,\n isStaticRoute,\n type BootstrapRoute,\n type PackageJson,\n type RenderedRoute,\n type ExpandedRenderFunctionResult,\n type RequestHandler,\n} 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 '@ms-cloudpack/inline-scripts';\nimport { renderRoute } from './renderRoute/renderRoute.js';\nimport { renderBootstrapRoute } from './renderBootstrapRoute.js';\nimport { createPageSessionContext } from './createPageSessionContext.js';\nimport { parseRouteMatch } from './parseRouteMatch.js';\nimport { createProxyMiddleware } from 'http-proxy-middleware';\n\ninterface CreateRoutesOptions {\n app: Express;\n /** Base app server URL, including the port */\n url: string;\n /** package.json for the app */\n definition: PackageJson;\n}\n\n/** Express middleware that will match the domain to continue the route execution. */\nfunction matchDomain(domain?: RegExp): RequestHandler {\n return (req, _, next) => {\n if (!domain || domain.test(req.hostname)) {\n return next();\n }\n\n // Jump to next route.\n return next('route');\n };\n}\n\n/**\n * Creates the routes for the express app based on the config.\n */\nexport function createRoutes(options: CreateRoutesOptions, context: EnsurePackageBundledContext): void {\n const { app, ...otherOptions } = options;\n const { session } = context;\n const { config } = session;\n const { appPath } = config;\n const routes = [...(session.config.routes || [])];\n\n if (!options.definition.name) {\n throw new Error('Name field is required in the package.json file.');\n }\n\n if (!routes.length) {\n // If no routes have been defined, register a default (no path) route only.\n // This will render default HTML which includes the app's default path ('.') from the import map.\n routes.push({\n match: '/',\n // sourcePath and requestPath are required by types but unused in this case.\n entry: [{ importPath: '.', sourcePath: '' }],\n });\n }\n\n for (const route of routes) {\n const matches = Array.isArray(route.match) ? route.match : [route.match];\n\n for (const match of matches) {\n const { pathMatch, domainMatch } = parseRouteMatch(match);\n\n if (isStaticRoute(route)) {\n const resolvedPath = path.resolve(appPath, route.staticPath);\n app.use(\n pathMatch,\n matchDomain(domainMatch),\n // Express v5 default behavior with dotfiles is to ignore them (404), even if the original\n // path had a directory with a leading dot...\n express.static(resolvedPath, { dotfiles: slash(resolvedPath).includes('/.') ? 'allow' : 'deny' }),\n );\n } else if (isRedirectRoute(route)) {\n app.get(pathMatch, matchDomain(domainMatch), (req, res) => {\n res.redirect(route.redirectTo);\n });\n } else if (isRenderedRoute(route) || isBootstrapRoute(route)) {\n app.get(pathMatch, matchDomain(domainMatch), (req, res) => {\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 } else if (isProxyRoute(route)) {\n app.use(\n pathMatch,\n matchDomain(domainMatch),\n createProxyMiddleware({ target: route.proxyTo, ...route }) as RequestHandler,\n );\n } else {\n throw new Error(`Unknown route: ${JSON.stringify(route)}`);\n }\n }\n }\n}\n\nexport async function _handleRequest(\n options: Omit<CreateRoutesOptions, 'app'> & {\n req: Request;\n res: Response;\n route: RenderedRoute | BootstrapRoute;\n },\n context: EnsurePackageBundledContext,\n): Promise<void> {\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 // (TS can't infer that urls.bundleServer is set on session from a check above.)\n const importMap = (session.importMap ??= await createImportMap({ targetEnvironment: 'browser' }, context));\n\n // For production mode, no overlay script.\n const overlayScript = session.config.mode !== 'production' ? importMap.imports['@ms-cloudpack/overlay'] : undefined;\n\n // Get all entry scripts for the route.\n const entryScripts = route.entry?.map(\n (entry) => importMap.imports[slash(path.join(definition.name, entry.importPath))],\n );\n\n const pageSessionContextScript =\n `window.__cloudpack ??= {};\\n` +\n `window.__cloudpack.pageSessionContext = ${JSON.stringify(createPageSessionContext(session, req.path))};`;\n\n // Add the pageSessionContext to the top of the inlineScripts\n const inlineScripts = [pageSessionContextScript, ...(await getInlineScripts())];\n\n // Set the appropriate Cloudpack headers in the response.\n res.header('Cache-Control', 'no-cache');\n res.header('Access-Control-Allow-Origin', '*');\n\n let rendered: ExpandedRenderFunctionResult | null;\n\n if (isBootstrapRoute(route)) {\n // Render the bootstrap script\n rendered = await renderBootstrapRoute({\n session,\n importMap,\n overlayScript,\n inlineScripts,\n entryScripts,\n requestPath: req.path,\n });\n } else {\n // HTML or custom script rendering\n rendered = await renderRoute(\n {\n ...options,\n session,\n baseUrl: url,\n importMap,\n overlayScript,\n inlineScripts,\n entryScripts,\n },\n context,\n );\n }\n\n if (rendered) {\n res.type(rendered.contentType).status(rendered.statusCode).send(rendered.content).end();\n }\n\n console.debug(`App server: Request: ${req.path}`);\n}\n"]}
1
+ {"version":3,"file":"createRoutes.js","sourceRoot":"","sources":["../src/createRoutes.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EnsurePackageBundledContext } from '@ms-cloudpack/api-server';\nimport {\n isBootstrapRoute,\n isProxyRoute,\n isRedirectRoute,\n isRenderedRoute,\n isStaticRoute,\n type BootstrapRoute,\n type PackageJson,\n type RenderedRoute,\n type ExpandedRenderFunctionResult,\n type RequestHandler,\n} 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 '@ms-cloudpack/inline-scripts';\nimport { renderRoute } from './renderRoute/renderRoute.js';\nimport { renderBootstrapRoute } from './renderBootstrapRoute.js';\nimport { createPageSessionContext } from './createPageSessionContext.js';\nimport { parseRouteMatch } from './parseRouteMatch.js';\nimport { createProxyMiddleware } from 'http-proxy-middleware';\n\ninterface CreateRoutesOptions {\n app: Express;\n /** Base app server URL, including the port */\n url: string;\n /** package.json for the app */\n definition: PackageJson;\n}\n\n/** Express middleware that will match the domain to continue the route execution. */\nfunction matchDomain(domain?: RegExp): RequestHandler {\n return (req, _, next) => {\n if (!domain || domain.test(req.hostname)) {\n return next();\n }\n\n // Jump to next route.\n return next('route');\n };\n}\n\n/**\n * Creates the routes for the express app based on the config.\n */\nexport function createRoutes(options: CreateRoutesOptions, context: EnsurePackageBundledContext): void {\n const { app, ...otherOptions } = options;\n const { session } = context;\n const { config } = session;\n const { appPath } = config;\n const routes = [...(session.config.routes || [])];\n\n if (!options.definition.name) {\n throw new Error('Name field is required in the package.json file.');\n }\n\n if (!routes.length) {\n // If no routes have been defined, register a default (no path) route only.\n // This will render default HTML which includes the app's default path ('.') from the import map.\n routes.push({\n match: '/',\n // sourcePath and requestPath are required by types but unused in this case.\n entry: [{ importPath: '.', sourcePath: '' }],\n });\n }\n\n for (const route of routes) {\n const matches = Array.isArray(route.match) ? route.match : [route.match];\n\n for (const match of matches) {\n const { pathMatch, domainMatch } = parseRouteMatch(match);\n\n if (isStaticRoute(route)) {\n const resolvedPath = path.resolve(appPath, route.staticPath);\n app.use(\n pathMatch,\n matchDomain(domainMatch),\n // Express v5 default behavior with dotfiles is to ignore them (404), even if the original\n // path had a directory with a leading dot...\n express.static(resolvedPath, { dotfiles: slash(resolvedPath).includes('/.') ? 'allow' : 'deny' }),\n );\n } else if (isRedirectRoute(route)) {\n app.get(pathMatch, matchDomain(domainMatch), (req, res) => {\n res.redirect(route.redirectTo);\n });\n } else if (isRenderedRoute(route) || isBootstrapRoute(route)) {\n app.get(pathMatch, matchDomain(domainMatch), (req, res) => {\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 } else if (isProxyRoute(route)) {\n app.use(\n pathMatch,\n matchDomain(domainMatch),\n createProxyMiddleware({ target: route.proxyTo, ...route }) as RequestHandler,\n );\n } else {\n throw new Error(`Unknown route: ${JSON.stringify(route)}`);\n }\n }\n }\n}\n\nexport async function _handleRequest(\n options: Omit<CreateRoutesOptions, 'app'> & {\n req: Request;\n res: Response;\n route: RenderedRoute | BootstrapRoute;\n },\n context: EnsurePackageBundledContext,\n): Promise<void> {\n const { req, res, definition, route, url: baseUrl } = options;\n const { session } = context;\n\n // Build the import map if it hasn't been built yet for this session version.\n // (TS can't infer that urls.bundleServer is set on session from a check above.)\n const importMap = (session.importMap ??= await createImportMap({ targetEnvironment: 'browser' }, context));\n\n // For production mode, no overlay script.\n const overlayScript = session.config.mode !== 'production' ? importMap.imports['@ms-cloudpack/overlay'] : undefined;\n\n // Get all entry scripts for the route.\n const entryScripts = route.entry?.map(\n (entry) => importMap.imports[slash(path.join(definition.name, entry.importPath))],\n );\n\n const pageSessionContextScript =\n `window.__cloudpack ??= {};\\n` +\n `window.__cloudpack.pageSessionContext = ${JSON.stringify(createPageSessionContext(session, req.path))};`;\n\n // Add the pageSessionContext to the top of the inlineScripts\n const inlineScripts = [pageSessionContextScript, ...(await getInlineScripts())];\n\n // Set the appropriate Cloudpack headers in the response.\n res.header('Cache-Control', 'no-cache');\n res.header('Access-Control-Allow-Origin', '*');\n\n let rendered: ExpandedRenderFunctionResult | null;\n\n if (isBootstrapRoute(route)) {\n // Render the bootstrap script\n rendered = await renderBootstrapRoute({\n session,\n importMap,\n overlayScript,\n inlineScripts,\n entryScripts,\n requestPath: req.path,\n });\n } else {\n // HTML or custom script rendering\n rendered = await renderRoute({ ...options, baseUrl, overlayScript, inlineScripts, entryScripts }, context);\n }\n\n if (rendered) {\n res.type(rendered.contentType).status(rendered.statusCode).send(rendered.content).end();\n }\n\n console.debug(`App server: Request: ${req.path}`);\n}\n"]}
@@ -1,6 +1,6 @@
1
- import type { RenderFunction } from '@ms-cloudpack/common-types';
1
+ import type { ServerEntryFunctionOptions, ServerEntryFunctionResult } from '@ms-cloudpack/common-types';
2
2
  /**
3
3
  * Return the default HTML response.
4
4
  */
5
- export declare const getDefaultHtmlResponse: RenderFunction;
5
+ export declare function getDefaultHtmlResponse(options: Pick<ServerEntryFunctionOptions, 'definition'>): ServerEntryFunctionResult;
6
6
  //# sourceMappingURL=getDefaultHtmlResponse.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getDefaultHtmlResponse.d.ts","sourceRoot":"","sources":["../../src/renderRoute/getDefaultHtmlResponse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE;;GAEG;AACH,eAAO,MAAM,sBAAsB,EAAE,cAUpC,CAAC"}
1
+ {"version":3,"file":"getDefaultHtmlResponse.d.ts","sourceRoot":"","sources":["../../src/renderRoute/getDefaultHtmlResponse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAExG;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,IAAI,CAAC,0BAA0B,EAAE,YAAY,CAAC,GACtD,yBAAyB,CAU3B"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Return the default HTML response.
3
3
  */
4
- export const getDefaultHtmlResponse = (options) => {
4
+ export function getDefaultHtmlResponse(options) {
5
5
  return `<!DOCTYPE html>
6
6
  <html lang="en">
7
7
  <head>
@@ -11,5 +11,5 @@ export const getDefaultHtmlResponse = (options) => {
11
11
  <div id="root"></div>
12
12
  </body>
13
13
  </html>`;
14
- };
14
+ }
15
15
  //# sourceMappingURL=getDefaultHtmlResponse.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"getDefaultHtmlResponse.js","sourceRoot":"","sources":["../../src/renderRoute/getDefaultHtmlResponse.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAmB,CAAC,OAAO,EAAE,EAAE;IAChE,OAAO;;;WAGE,OAAO,CAAC,UAAU,CAAC,IAAI;;;;;QAK1B,CAAC;AACT,CAAC,CAAC","sourcesContent":["import type { RenderFunction } from '@ms-cloudpack/common-types';\n\n/**\n * Return the default HTML response.\n */\nexport const getDefaultHtmlResponse: RenderFunction = (options) => {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <title>${options.definition.name}</title>\n</head>\n<body>\n <div id=\"root\"></div>\n</body>\n</html>`;\n};\n"]}
1
+ {"version":3,"file":"getDefaultHtmlResponse.js","sourceRoot":"","sources":["../../src/renderRoute/getDefaultHtmlResponse.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAAuD;IAEvD,OAAO;;;WAGE,OAAO,CAAC,UAAU,CAAC,IAAI;;;;;QAK1B,CAAC;AACT,CAAC","sourcesContent":["import type { ServerEntryFunctionOptions, ServerEntryFunctionResult } from '@ms-cloudpack/common-types';\n\n/**\n * Return the default HTML response.\n */\nexport function getDefaultHtmlResponse(\n options: Pick<ServerEntryFunctionOptions, 'definition'>,\n): ServerEntryFunctionResult {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <title>${options.definition.name}</title>\n</head>\n<body>\n <div id=\"root\"></div>\n</body>\n</html>`;\n}\n"]}
@@ -1,18 +1,19 @@
1
- import type { ExpandedRenderFunctionResult, RenderFunctionOptions } from '@ms-cloudpack/common-types';
1
+ import type { ExpandedRenderFunctionResult } from '@ms-cloudpack/common-types';
2
+ import type { RenderRouteOptions } from '../types/RenderRouteOptions.js';
2
3
  /**
3
4
  * Log the error to the console and return an error response (500 status code).
4
5
  *
5
6
  * If it appears that the given request accepts HTML, it returns an HTML page,
6
7
  * or plain text otherwise.
7
8
  */
8
- export declare function getErrorResponse(params: Pick<RenderFunctionOptions, 'req'> & {
9
+ export declare function getErrorResponse(params: Pick<RenderRouteOptions, 'req'> & {
9
10
  message: string;
10
11
  }): ExpandedRenderFunctionResult;
11
12
  /**
12
13
  * Fill in details of the message, log to console.error, and return an error response.
13
14
  * `Route ${route.match} with (renderScript|serverEntry) "${scriptPath}" ${message}`
14
15
  */
15
- export declare function getCustomRenderErrorResponse(params: Pick<RenderFunctionOptions, 'req' | 'route'> & {
16
+ export declare function getCustomRenderErrorResponse(params: Pick<RenderRouteOptions, 'req' | 'route'> & {
16
17
  /** Error details (NOTE the format in the main function comment) */
17
18
  message: string;
18
19
  scriptPath: string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"getErrorResponse.d.ts","sourceRoot":"","sources":["../../src/renderRoute/getErrorResponse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAKtG;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,IAAI,CAAC,qBAAqB,EAAE,KAAK,CAAC,GAAG;IAC3C,OAAO,EAAE,MAAM,CAAC;CACjB,GACA,4BAA4B,CA0B9B;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,IAAI,CAAC,qBAAqB,EAAE,KAAK,GAAG,OAAO,CAAC,GAAG;IACrD,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,GACA,4BAA4B,CAO9B"}
1
+ {"version":3,"file":"getErrorResponse.d.ts","sourceRoot":"","sources":["../../src/renderRoute/getErrorResponse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAE/E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAIzE;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,IAAI,CAAC,kBAAkB,EAAE,KAAK,CAAC,GAAG;IACxC,OAAO,EAAE,MAAM,CAAC;CACjB,GACA,4BAA4B,CA0B9B;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,IAAI,CAAC,kBAAkB,EAAE,KAAK,GAAG,OAAO,CAAC,GAAG;IAClD,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,GACA,4BAA4B,CAO9B"}
@@ -1 +1 @@
1
- {"version":3,"file":"getErrorResponse.js","sourceRoot":"","sources":["../../src/renderRoute/getErrorResponse.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAEC;IAED,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC;IAEhC,OAAO,CAAC,KAAK,CAAC,uBAAuB,GAAG,CAAC,GAAG,MAAM,OAAO,EAAE,CAAC,CAAC;IAE7D,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,0DAA0D;QAC1D,OAAO;YACL,OAAO,EAAE,OAAO;YAChB,UAAU;YACV,WAAW,EAAE,YAAY;SAC1B,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE;;;;;SAKJ,OAAO;;QAER;QACJ,UAAU;QACV,WAAW,EAAE,WAAW;KACzB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,4BAA4B,CAC1C,MAIC;IAED,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;IACnD,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,cAAc,CAAC;IACtE,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,KAAK,UAAU,GAAG,CAAC,CAAC,CAAC,sBAAsB,CAAC;IACvF,MAAM,OAAO,GAAG,SAAS,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,QAAQ,IAAI,OAAO,EAAE,CAAC;IAEnF,OAAO,gBAAgB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;AACrD,CAAC","sourcesContent":["import type { ExpandedRenderFunctionResult, RenderFunctionOptions } from '@ms-cloudpack/common-types';\nimport { isHtmlAcceptable } from './isHtmlAcceptable.js';\n\nconst statusCode = 500;\n\n/**\n * Log the error to the console and return an error response (500 status code).\n *\n * If it appears that the given request accepts HTML, it returns an HTML page,\n * or plain text otherwise.\n */\nexport function getErrorResponse(\n params: Pick<RenderFunctionOptions, 'req'> & {\n message: string;\n },\n): ExpandedRenderFunctionResult {\n const { message, req } = params;\n\n console.error(`App server: request ${req.url} - ${message}`);\n\n if (!isHtmlAcceptable(req)) {\n // If HTML is not acceptable, return a plain text response\n return {\n content: message,\n statusCode,\n contentType: 'text/plain',\n };\n }\n\n return {\n content: `<!DOCTYPE html>\n<html lang=\"en\">\n<head><title>Error</title></head>\n<body>\n <h1>Error</h1>\n <pre>${message}</pre>\n</body>\n</html>`,\n statusCode,\n contentType: 'text/html',\n };\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 */\nexport function getCustomRenderErrorResponse(\n params: Pick<RenderFunctionOptions, 'req' | 'route'> & {\n /** Error details (NOTE the format in the main function comment) */\n message: string;\n scriptPath: string | undefined;\n },\n): ExpandedRenderFunctionResult {\n const { message, req, route, scriptPath } = 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\n return getErrorResponse({ req, message: content });\n}\n"]}
1
+ {"version":3,"file":"getErrorResponse.js","sourceRoot":"","sources":["../../src/renderRoute/getErrorResponse.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGzD,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAEC;IAED,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC;IAEhC,OAAO,CAAC,KAAK,CAAC,uBAAuB,GAAG,CAAC,GAAG,MAAM,OAAO,EAAE,CAAC,CAAC;IAE7D,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,0DAA0D;QAC1D,OAAO;YACL,OAAO,EAAE,OAAO;YAChB,UAAU;YACV,WAAW,EAAE,YAAY;SAC1B,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE;;;;;SAKJ,OAAO;;QAER;QACJ,UAAU;QACV,WAAW,EAAE,WAAW;KACzB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,4BAA4B,CAC1C,MAIC;IAED,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;IACnD,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,cAAc,CAAC;IACtE,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,KAAK,UAAU,GAAG,CAAC,CAAC,CAAC,sBAAsB,CAAC;IACvF,MAAM,OAAO,GAAG,SAAS,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,QAAQ,IAAI,OAAO,EAAE,CAAC;IAEnF,OAAO,gBAAgB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;AACrD,CAAC","sourcesContent":["import type { ExpandedRenderFunctionResult } from '@ms-cloudpack/common-types';\nimport { isHtmlAcceptable } from './isHtmlAcceptable.js';\nimport type { RenderRouteOptions } from '../types/RenderRouteOptions.js';\n\nconst statusCode = 500;\n\n/**\n * Log the error to the console and return an error response (500 status code).\n *\n * If it appears that the given request accepts HTML, it returns an HTML page,\n * or plain text otherwise.\n */\nexport function getErrorResponse(\n params: Pick<RenderRouteOptions, 'req'> & {\n message: string;\n },\n): ExpandedRenderFunctionResult {\n const { message, req } = params;\n\n console.error(`App server: request ${req.url} - ${message}`);\n\n if (!isHtmlAcceptable(req)) {\n // If HTML is not acceptable, return a plain text response\n return {\n content: message,\n statusCode,\n contentType: 'text/plain',\n };\n }\n\n return {\n content: `<!DOCTYPE html>\n<html lang=\"en\">\n<head><title>Error</title></head>\n<body>\n <h1>Error</h1>\n <pre>${message}</pre>\n</body>\n</html>`,\n statusCode,\n contentType: 'text/html',\n };\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 */\nexport function getCustomRenderErrorResponse(\n params: Pick<RenderRouteOptions, 'req' | 'route'> & {\n /** Error details (NOTE the format in the main function comment) */\n message: string;\n scriptPath: string | undefined;\n },\n): ExpandedRenderFunctionResult {\n const { message, req, route, scriptPath } = 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\n return getErrorResponse({ req, message: content });\n}\n"]}
@@ -1,8 +1,13 @@
1
- import type { RenderFunctionOptions, ExpandedRenderFunctionResult } from '@ms-cloudpack/common-types';
1
+ import type { ExpandedRenderFunctionResult } from '@ms-cloudpack/common-types';
2
+ import { type IOptionalBrowserSettings } from 'happy-dom';
3
+ import type { RenderRouteOptions } from '../types/RenderRouteOptions.js';
4
+ import type { PartialContext } from '@ms-cloudpack/api-server';
5
+ /** `happy-dom` settings to disable scripts etc */
6
+ export declare const happyDomSettings: IOptionalBrowserSettings;
2
7
  /**
3
8
  * Modify the HTML response by injecting the import map, inline scripts, and entry/overlay scripts.
4
9
  *
5
10
  * Note that this just logs an error rather than returning an error response if something fails, for now.
6
11
  */
7
- export declare function injectScripts(options: RenderFunctionOptions, result: ExpandedRenderFunctionResult): Promise<void>;
12
+ export declare function injectScripts(options: RenderRouteOptions, result: ExpandedRenderFunctionResult, context: PartialContext<'session', 'importMap'>): Promise<void>;
8
13
  //# sourceMappingURL=injectScripts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"injectScripts.d.ts","sourceRoot":"","sources":["../../src/renderRoute/injectScripts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAItG;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE,4BAA4B,GACnC,OAAO,CAAC,IAAI,CAAC,CAsDf"}
1
+ {"version":3,"file":"injectScripts.d.ts","sourceRoot":"","sources":["../../src/renderRoute/injectScripts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAC/E,OAAO,EAAyB,KAAK,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAEjF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/D,kDAAkD;AAClD,eAAO,MAAM,gBAAgB,EAAE,wBAK9B,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,kBAAkB,EAC3B,MAAM,EAAE,4BAA4B,EACpC,OAAO,EAAE,cAAc,CAAC,SAAS,EAAE,WAAW,CAAC,GAC9C,OAAO,CAAC,IAAI,CAAC,CA8Df"}
@@ -1,12 +1,23 @@
1
1
  import { Window } from 'happy-dom';
2
2
  import HTMLSerializer from 'happy-dom/lib/html-serializer/HTMLSerializer.js';
3
+ /** `happy-dom` settings to disable scripts etc */
4
+ export const happyDomSettings = {
5
+ disableJavaScriptFileLoading: true,
6
+ disableJavaScriptEvaluation: true,
7
+ disableCSSFileLoading: true,
8
+ disableIframePageLoading: true,
9
+ };
3
10
  /**
4
11
  * Modify the HTML response by injecting the import map, inline scripts, and entry/overlay scripts.
5
12
  *
6
13
  * Note that this just logs an error rather than returning an error response if something fails, for now.
7
14
  */
8
- export async function injectScripts(options, result) {
9
- const { route, overlayScript, entryScripts, inlineScripts, importMap, baseUrl } = options;
15
+ export async function injectScripts(options, result, context) {
16
+ const { route, overlayScript, entryScripts, inlineScripts, baseUrl } = options;
17
+ const { importMap } = context.session;
18
+ if (typeof result.content !== 'string') {
19
+ return;
20
+ }
10
21
  const window = new Window({
11
22
  url: baseUrl,
12
23
  settings: {
@@ -26,6 +37,7 @@ export async function injectScripts(options, result) {
26
37
  catch (e) {
27
38
  // Trying to write a test for the above, it seemed very permissive, but catch just in case
28
39
  console.error(`Error parsing html response for rendering route "${JSON.stringify(route.match)}":\n${e.stack || e}`);
40
+ await window.happyDOM.close();
29
41
  return;
30
42
  }
31
43
  try {
@@ -48,11 +60,13 @@ export async function injectScripts(options, result) {
48
60
  }
49
61
  const serializer = new HTMLSerializer();
50
62
  result.content = serializer.serializeToString(document);
51
- await window.happyDOM.close();
52
63
  }
53
64
  catch (e) {
54
65
  console.error(`Error injecting scripts for route "${JSON.stringify(route.match)}":\n${e.stack || e}`);
55
66
  }
67
+ finally {
68
+ await window.happyDOM.close();
69
+ }
56
70
  }
57
71
  /**
58
72
  * Helper function to add a script to the document.
@@ -1 +1 @@
1
- {"version":3,"file":"injectScripts.js","sourceRoot":"","sources":["../../src/renderRoute/injectScripts.ts"],"names":[],"mappings":"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","sourcesContent":["import type { RenderFunctionOptions, ExpandedRenderFunctionResult } from '@ms-cloudpack/common-types';\nimport { Window, type Document } from 'happy-dom';\nimport HTMLSerializer from 'happy-dom/lib/html-serializer/HTMLSerializer.js';\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 */\nexport async function injectScripts(\n options: RenderFunctionOptions,\n result: ExpandedRenderFunctionResult,\n): 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"]}
1
+ {"version":3,"file":"injectScripts.js","sourceRoot":"","sources":["../../src/renderRoute/injectScripts.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ExpandedRenderFunctionResult } from '@ms-cloudpack/common-types';\nimport { Window, type Document, type IOptionalBrowserSettings } from 'happy-dom';\nimport HTMLSerializer from 'happy-dom/lib/html-serializer/HTMLSerializer.js';\nimport type { RenderRouteOptions } from '../types/RenderRouteOptions.js';\nimport type { PartialContext } from '@ms-cloudpack/api-server';\n\n/** `happy-dom` settings to disable scripts etc */\nexport const happyDomSettings: IOptionalBrowserSettings = {\n disableJavaScriptFileLoading: true,\n disableJavaScriptEvaluation: true,\n disableCSSFileLoading: true,\n disableIframePageLoading: true,\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 */\nexport async function injectScripts(\n options: RenderRouteOptions,\n result: ExpandedRenderFunctionResult,\n context: PartialContext<'session', 'importMap'>,\n): Promise<void> {\n const { route, overlayScript, entryScripts, inlineScripts, baseUrl } = options;\n const { importMap } = context.session;\n\n if (typeof result.content !== 'string') {\n return;\n }\n\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 await window.happyDOM.close();\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 } catch (e) {\n console.error(`Error injecting scripts for route \"${JSON.stringify(route.match)}\":\\n${(e as Error).stack || e}`);\n } finally {\n await window.happyDOM.close();\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"]}
@@ -1,8 +1,9 @@
1
1
  import { type EnsurePackageBundledContext } from '@ms-cloudpack/api-server';
2
- import type { ExpandedRenderFunctionResult, RenderFunctionOptions } from '@ms-cloudpack/common-types';
2
+ import type { ExpandedRenderFunctionResult } from '@ms-cloudpack/common-types';
3
+ import type { RenderRouteOptions } from '../types/RenderRouteOptions.js';
3
4
  /**
4
5
  * Get the response for the given route. If the route has a custom render script, use that.
5
6
  * Returns an error response if the file referenced by the script doesn't exist or throws an error.
6
7
  */
7
- export declare function renderRoute(options: RenderFunctionOptions, context: EnsurePackageBundledContext): Promise<ExpandedRenderFunctionResult | null>;
8
+ export declare function renderRoute(options: RenderRouteOptions, context: EnsurePackageBundledContext): Promise<ExpandedRenderFunctionResult | null>;
8
9
  //# sourceMappingURL=renderRoute.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"renderRoute.d.ts","sourceRoot":"","sources":["../../src/renderRoute/renderRoute.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,KAAK,EACV,4BAA4B,EAC5B,qBAAqB,EAEtB,MAAM,4BAA4B,CAAC;AAWpC;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,4BAA4B,GAAG,IAAI,CAAC,CAwG9C"}
1
+ {"version":3,"file":"renderRoute.d.ts","sourceRoot":"","sources":["../../src/renderRoute/renderRoute.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,KAAK,EAAE,4BAA4B,EAAwB,MAAM,4BAA4B,CAAC;AAUrG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEzE;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,4BAA4B,GAAG,IAAI,CAAC,CA2H9C"}
@@ -12,8 +12,8 @@ import { renderCustomScript } from './ssr/renderCustomScript.js';
12
12
  * Returns an error response if the file referenced by the script doesn't exist or throws an error.
13
13
  */
14
14
  export async function renderRoute(options, context) {
15
- const { route, session } = options;
16
- const { appPath } = session.config;
15
+ const { route } = options;
16
+ const { appPath, features } = context.session.config;
17
17
  // Get the script to render (if any). Prefer serverEntry if provided.
18
18
  // eslint-disable-next-line etc/no-deprecated
19
19
  let renderScriptPath = route.renderScript && path.resolve(appPath, route.renderScript);
@@ -45,7 +45,7 @@ export async function renderRoute(options, context) {
45
45
  }
46
46
  else if (javascriptExtensions.includes(scriptExt)) {
47
47
  // JS file: run its function
48
- rawResult = await renderCustomScript({ ...options, renderScriptPath });
48
+ rawResult = await renderCustomScript({ ...options, renderScriptPath }, context);
49
49
  }
50
50
  else {
51
51
  // Other file type: error. Technically we could just serve it as-is following the non-HTML
@@ -70,16 +70,24 @@ export async function renderRoute(options, context) {
70
70
  }
71
71
  }
72
72
  else {
73
- rawResult = await getDefaultHtmlResponse(options);
73
+ rawResult = getDefaultHtmlResponse(options);
74
74
  }
75
75
  if (rawResult === null) {
76
+ if (features?.enableSSR) {
77
+ // null is not valid in new SSR mode
78
+ return getCustomRenderErrorResponse({
79
+ message: `returned null, which is not supported with the "enableSSR" feature.`,
80
+ route,
81
+ req: options.req,
82
+ scriptPath: renderScriptPath,
83
+ });
84
+ }
76
85
  // This means the custom renderScript fully handled the request.
77
86
  return null;
78
87
  }
79
88
  if (rawResult === undefined) {
80
89
  return getCustomRenderErrorResponse({
81
- message: `returned undefined. If the script fully handled the request (sent a response), ` +
82
- `return null instead. Otherwise ensure that the function returns the content to be rendered.`,
90
+ message: `returned undefined. Ensure that the function returns the content to be rendered.`,
83
91
  route,
84
92
  req: options.req,
85
93
  scriptPath: renderScriptPath,
@@ -90,11 +98,20 @@ export async function renderRoute(options, context) {
90
98
  statusCode: 200,
91
99
  ...(typeof rawResult === 'string' ? { content: rawResult } : rawResult),
92
100
  };
101
+ // Add any custom headers (for success or error)
102
+ if (result.headers) {
103
+ for (const [key, value] of Object.entries(result.headers)) {
104
+ options.res.header(key, value);
105
+ }
106
+ }
93
107
  // If it's HTML and a success statusCode, inject the import map and appropriate scripts.
94
- if (result.statusCode === 200 && isHtmlType(result.contentType)) {
108
+ if (result.injectScripts !== false &&
109
+ result.statusCode === 200 &&
110
+ typeof result.content === 'string' &&
111
+ isHtmlType(result.contentType)) {
95
112
  if (isHtmlAcceptable(options.req)) {
96
113
  // Inject the import map and scripts.
97
- await injectScripts(options, result);
114
+ await injectScripts(options, result, context);
98
115
  }
99
116
  else {
100
117
  // It appears we're accidentally returning HTML when a different file type was requested.
@@ -1 +1 @@
1
- {"version":3,"file":"renderRoute.js","sourceRoot":"","sources":["../../src/renderRoute/renderRoute.ts"],"names":[],"mappings":"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","sourcesContent":["import { type EnsurePackageBundledContext } from '@ms-cloudpack/api-server';\nimport type {\n ExpandedRenderFunctionResult,\n RenderFunctionOptions,\n RenderFunctionResult,\n} from '@ms-cloudpack/common-types';\nimport { javascriptExtensions, sourceExtensions, typescriptExtensions } from '@ms-cloudpack/path-utilities';\nimport fsPromises from 'fs/promises';\nimport path from 'path';\nimport { getDefaultHtmlResponse } from './getDefaultHtmlResponse.js';\nimport { getCustomRenderErrorResponse, getErrorResponse } from './getErrorResponse.js';\nimport { injectScripts } from './injectScripts.js';\nimport { isHtmlAcceptable, isHtmlType } from './isHtmlAcceptable.js';\nimport { bundleServerEntry } from './ssr/bundleServerEntry.js';\nimport { renderCustomScript } from './ssr/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, req: options.req });\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 getCustomRenderErrorResponse({\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 getCustomRenderErrorResponse({\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.req)) {\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 getCustomRenderErrorResponse({\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\n/**\n * Read a static HTML file, or return an error response if the file can't be read.\n */\nasync function readStaticHtml(\n params: Pick<RenderFunctionOptions, 'req'> & {\n renderScriptPath: string;\n },\n): Promise<RenderFunctionResult> {\n const { renderScriptPath, req } = params;\n try {\n return (await fsPromises.readFile(renderScriptPath, 'utf8')) || '';\n } catch (e) {\n return getErrorResponse({\n req,\n message: `Error reading HTML file at \"${renderScriptPath}\":\\n${(e as Error).stack || e}`,\n });\n }\n}\n"]}
1
+ {"version":3,"file":"renderRoute.js","sourceRoot":"","sources":["../../src/renderRoute/renderRoute.ts"],"names":[],"mappings":"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","sourcesContent":["import { type EnsurePackageBundledContext } from '@ms-cloudpack/api-server';\nimport type { ExpandedRenderFunctionResult, RenderFunctionResult } from '@ms-cloudpack/common-types';\nimport { javascriptExtensions, sourceExtensions, typescriptExtensions } from '@ms-cloudpack/path-utilities';\nimport fsPromises from 'fs/promises';\nimport path from 'path';\nimport { getDefaultHtmlResponse } from './getDefaultHtmlResponse.js';\nimport { getCustomRenderErrorResponse, getErrorResponse } from './getErrorResponse.js';\nimport { injectScripts } from './injectScripts.js';\nimport { isHtmlAcceptable, isHtmlType } from './isHtmlAcceptable.js';\nimport { bundleServerEntry } from './ssr/bundleServerEntry.js';\nimport { renderCustomScript } from './ssr/renderCustomScript.js';\nimport type { RenderRouteOptions } from '../types/RenderRouteOptions.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: RenderRouteOptions,\n context: EnsurePackageBundledContext,\n): Promise<ExpandedRenderFunctionResult | null> {\n const { route } = options;\n const { appPath, features } = context.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, req: options.req });\n } else if (javascriptExtensions.includes(scriptExt)) {\n // JS file: run its function\n rawResult = await renderCustomScript({ ...options, renderScriptPath }, context);\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 getCustomRenderErrorResponse({\n message,\n route,\n req: options.req,\n scriptPath: renderScriptPath,\n });\n }\n } else {\n rawResult = getDefaultHtmlResponse(options);\n }\n\n if (rawResult === null) {\n if (features?.enableSSR) {\n // null is not valid in new SSR mode\n return getCustomRenderErrorResponse({\n message: `returned null, which is not supported with the \"enableSSR\" feature.`,\n route,\n req: options.req,\n scriptPath: renderScriptPath,\n });\n }\n // This means the custom renderScript fully handled the request.\n return null;\n }\n\n if (rawResult === undefined) {\n return getCustomRenderErrorResponse({\n message: `returned undefined. 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 // Add any custom headers (for success or error)\n if (result.headers) {\n for (const [key, value] of Object.entries(result.headers)) {\n options.res.header(key, value);\n }\n }\n\n // If it's HTML and a success statusCode, inject the import map and appropriate scripts.\n if (\n result.injectScripts !== false &&\n result.statusCode === 200 &&\n typeof result.content === 'string' &&\n isHtmlType(result.contentType)\n ) {\n if (isHtmlAcceptable(options.req)) {\n // Inject the import map and scripts.\n await injectScripts(options, result, context);\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 getCustomRenderErrorResponse({\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\n/**\n * Read a static HTML file, or return an error response if the file can't be read.\n */\nasync function readStaticHtml(\n params: Pick<RenderRouteOptions, 'req'> & {\n renderScriptPath: string;\n },\n): Promise<RenderFunctionResult> {\n const { renderScriptPath, req } = params;\n try {\n return (await fsPromises.readFile(renderScriptPath, 'utf8')) || '';\n } catch (e) {\n return getErrorResponse({\n req,\n message: `Error reading HTML file at \"${renderScriptPath}\":\\n${(e as Error).stack || e}`,\n });\n }\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { type EnsurePackageBundledContext } from '@ms-cloudpack/api-server';
2
- import type { RenderFunctionOptions, ExpandedRenderFunctionResult } from '@ms-cloudpack/common-types';
3
- export declare function bundleServerEntry(options: Pick<RenderFunctionOptions, 'definition' | 'route' | 'req'> & {
2
+ import type { ExpandedRenderFunctionResult } from '@ms-cloudpack/common-types';
3
+ import type { RenderRouteOptions } from '../../types/RenderRouteOptions.js';
4
+ export declare function bundleServerEntry(options: Pick<RenderRouteOptions, 'definition' | 'route' | 'req'> & {
4
5
  /** Relative serverEntry source path from the route */
5
6
  serverEntryPath: string;
6
7
  }, context: EnsurePackageBundledContext): Promise<string | ExpandedRenderFunctionResult>;
@@ -1 +1 @@
1
- {"version":3,"file":"bundleServerEntry.d.ts","sourceRoot":"","sources":["../../../src/renderRoute/ssr/bundleServerEntry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,2BAA2B,EAAwB,MAAM,0BAA0B,CAAC;AAClG,OAAO,KAAK,EAAE,qBAAqB,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAOtG,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,IAAI,CAAC,qBAAqB,EAAE,YAAY,GAAG,OAAO,GAAG,KAAK,CAAC,GAAG;IACrE,sDAAsD;IACtD,eAAe,EAAE,MAAM,CAAC;CACzB,EACD,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,MAAM,GAAG,4BAA4B,CAAC,CAwDhD"}
1
+ {"version":3,"file":"bundleServerEntry.d.ts","sourceRoot":"","sources":["../../../src/renderRoute/ssr/bundleServerEntry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,2BAA2B,EAAwB,MAAM,0BAA0B,CAAC;AAClG,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAM/E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAE5E,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,YAAY,GAAG,OAAO,GAAG,KAAK,CAAC,GAAG;IAClE,sDAAsD;IACtD,eAAe,EAAE,MAAM,CAAC;CACzB,EACD,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,MAAM,GAAG,4BAA4B,CAAC,CAwDhD"}
@@ -1 +1 @@
1
- {"version":3,"file":"bundleServerEntry.js","sourceRoot":"","sources":["../../../src/renderRoute/ssr/bundleServerEntry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAElG,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAGC,EACD,OAAoC;IAEpC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC;IAC5D,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC;IAErC,8FAA8F;IAC9F,8FAA8F;IAC9F,iGAAiG;IACjG,kEAAkE;IAClE,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CACzC;QACE,IAAI;QACJ,OAAO;QACP,iBAAiB,EAAE,MAAM;QACzB,iFAAiF;QACjF,gEAAgE;QAChE,WAAW,EAAE,CAAC,eAAe,CAAC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY;QACpF,+CAA+C;QAC/C,mBAAmB,EAAE,KAAK;KAC3B,EACD,OAAO,CACR,CAAC;IACF,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,EAAE,EAAE,WAAW,GAAG,EAAE,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC;IAEtE,IAAI,MAAM,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,4BAA4B,CAAC;YAClC,OAAO,EAAE,qBAAqB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;YAC/E,KAAK;YACL,GAAG;YACH,UAAU,EAAE,eAAe;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,IAAI,iBAAiB,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,eAAe,CAAC,EAAE,UAAU,CAAC;IAC9F,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,oEAAoE;QACpE,MAAM,2BAA2B,GAAG,wBAAwB,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACpG,iBAAiB;YACf,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,eAAe,CAAC,EAAE,UAAU;gBACrE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,KAAK,2BAA2B,CAAC,EAAE,UAAU,CAAC;IAC5G,CAAC;IAED,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,OAAO,4BAA4B,CAAC;YAClC,OAAO,EAAE,iFAAiF,eAAe,uBACvG,WAAW,CAAC,MAAM;gBAChB,CAAC,CAAC,YAAY,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,UAAU,YAAY,CAAC,CAAC,UAAU,IAAI,SAAS,GAAG,CAAC,CAAC;gBAC/F,CAAC,CAAC,MACN,EAAE;YACF,KAAK;YACL,GAAG;YACH,UAAU,EAAE,eAAe;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,2DAA2D;IAC3D,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;AACrD,CAAC","sourcesContent":["import { type EnsurePackageBundledContext, ensurePackageBundled } from '@ms-cloudpack/api-server';\nimport type { RenderFunctionOptions, ExpandedRenderFunctionResult } from '@ms-cloudpack/common-types';\nimport { environmentInfo } from '@ms-cloudpack/environment';\nimport { sourceToIntermediatePath } from '@ms-cloudpack/path-utilities';\nimport { bulletedList } from '@ms-cloudpack/task-reporter';\nimport path from 'path';\nimport { getCustomRenderErrorResponse } from '../getErrorResponse.js';\n\nexport async 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 // File watching is disabled in production mode for performance.\n shouldWatch: !environmentInfo.isJest && context.session.config.mode !== 'production',\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 getCustomRenderErrorResponse({\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 getCustomRenderErrorResponse({\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,\n scriptPath: serverEntryPath,\n });\n }\n\n // Translate relative-to-output path into an absolute path.\n return path.resolve(outputPath, serverEntryOutput);\n}\n"]}
1
+ {"version":3,"file":"bundleServerEntry.js","sourceRoot":"","sources":["../../../src/renderRoute/ssr/bundleServerEntry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAElG,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAGtE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAGC,EACD,OAAoC;IAEpC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC;IAC5D,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC;IAErC,8FAA8F;IAC9F,8FAA8F;IAC9F,iGAAiG;IACjG,kEAAkE;IAClE,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CACzC;QACE,IAAI;QACJ,OAAO;QACP,iBAAiB,EAAE,MAAM;QACzB,iFAAiF;QACjF,gEAAgE;QAChE,WAAW,EAAE,CAAC,eAAe,CAAC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY;QACpF,+CAA+C;QAC/C,mBAAmB,EAAE,KAAK;KAC3B,EACD,OAAO,CACR,CAAC;IACF,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,EAAE,EAAE,WAAW,GAAG,EAAE,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC;IAEtE,IAAI,MAAM,EAAE,MAAM,EAAE,CAAC;QACnB,OAAO,4BAA4B,CAAC;YAClC,OAAO,EAAE,qBAAqB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;YAC/E,KAAK;YACL,GAAG;YACH,UAAU,EAAE,eAAe;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,IAAI,iBAAiB,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,eAAe,CAAC,EAAE,UAAU,CAAC;IAC9F,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,oEAAoE;QACpE,MAAM,2BAA2B,GAAG,wBAAwB,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACpG,iBAAiB;YACf,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,eAAe,CAAC,EAAE,UAAU;gBACrE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,KAAK,2BAA2B,CAAC,EAAE,UAAU,CAAC;IAC5G,CAAC;IAED,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,OAAO,4BAA4B,CAAC;YAClC,OAAO,EAAE,iFAAiF,eAAe,uBACvG,WAAW,CAAC,MAAM;gBAChB,CAAC,CAAC,YAAY,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,UAAU,YAAY,CAAC,CAAC,UAAU,IAAI,SAAS,GAAG,CAAC,CAAC;gBAC/F,CAAC,CAAC,MACN,EAAE;YACF,KAAK;YACL,GAAG;YACH,UAAU,EAAE,eAAe;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,2DAA2D;IAC3D,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;AACrD,CAAC","sourcesContent":["import { type EnsurePackageBundledContext, ensurePackageBundled } from '@ms-cloudpack/api-server';\nimport type { ExpandedRenderFunctionResult } from '@ms-cloudpack/common-types';\nimport { environmentInfo } from '@ms-cloudpack/environment';\nimport { sourceToIntermediatePath } from '@ms-cloudpack/path-utilities';\nimport { bulletedList } from '@ms-cloudpack/task-reporter';\nimport path from 'path';\nimport { getCustomRenderErrorResponse } from '../getErrorResponse.js';\nimport type { RenderRouteOptions } from '../../types/RenderRouteOptions.js';\n\nexport async function bundleServerEntry(\n options: Pick<RenderRouteOptions, '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 // File watching is disabled in production mode for performance.\n shouldWatch: !environmentInfo.isJest && context.session.config.mode !== 'production',\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 getCustomRenderErrorResponse({\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 getCustomRenderErrorResponse({\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,\n scriptPath: serverEntryPath,\n });\n }\n\n // Translate relative-to-output path into an absolute path.\n return path.resolve(outputPath, serverEntryOutput);\n}\n"]}
@@ -1,10 +1,12 @@
1
- import type { RenderFunctionOptions, RenderFunctionResult } from '@ms-cloudpack/common-types';
1
+ import type { Context } from '@ms-cloudpack/api-server';
2
+ import type { RenderFunctionResult } from '@ms-cloudpack/common-types';
3
+ import type { RenderRouteOptions } from '../../types/RenderRouteOptions.js';
2
4
  /**
3
5
  * Load the default export from a JS file passed as the `renderScript` in a route,
4
6
  * and return either the result of running the function, or an error page if something fails.
5
7
  */
6
- export declare function renderCustomScript(params: RenderFunctionOptions & {
8
+ export declare function renderCustomScript(params: RenderRouteOptions & {
7
9
  /** Full path to the render script (bundled version if relevant) */
8
10
  renderScriptPath: string;
9
- }): Promise<RenderFunctionResult>;
11
+ }, context: Pick<Context, 'session'>): Promise<RenderFunctionResult>;
10
12
  //# sourceMappingURL=renderCustomScript.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"renderCustomScript.d.ts","sourceRoot":"","sources":["../../../src/renderRoute/ssr/renderCustomScript.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAIV,qBAAqB,EACrB,oBAAoB,EAErB,MAAM,4BAA4B,CAAC;AAMpC;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,qBAAqB,GAAG;IAC9B,mEAAmE;IACnE,gBAAgB,EAAE,MAAM,CAAC;CAC1B,GACA,OAAO,CAAC,oBAAoB,CAAC,CAyD/B"}
1
+ {"version":3,"file":"renderCustomScript.d.ts","sourceRoot":"","sources":["../../../src/renderRoute/ssr/renderCustomScript.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,KAAK,EAGV,oBAAoB,EAIrB,MAAM,4BAA4B,CAAC;AAIpC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAI5E;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,kBAAkB,GAAG;IAC3B,mEAAmE;IACnE,gBAAgB,EAAE,MAAM,CAAC;CAC1B,EAGD,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,GAChC,OAAO,CAAC,oBAAoB,CAAC,CA4E/B"}
@@ -6,8 +6,13 @@ import { runServerEntryInWorker } from './runServerEntryInWorker.js';
6
6
  * Load the default export from a JS file passed as the `renderScript` in a route,
7
7
  * and return either the result of running the function, or an error page if something fails.
8
8
  */
9
- export async function renderCustomScript(params) {
10
- const { renderScriptPath, ...options } = params;
9
+ export async function renderCustomScript(params,
10
+ // only config and importMap are used in the new SSR signature, so in the future this can be:
11
+ // PartialContext<'session', 'config' | 'importMap'>
12
+ context) {
13
+ const { renderScriptPath, req, res, ...options } = params;
14
+ const { session } = context;
15
+ const { config, importMap } = session;
11
16
  // Get the html factory function from a script default export.
12
17
  const renderScriptUrl = pathToFileURL(renderScriptPath).toString();
13
18
  // Note: because there isn't a way to purge the require cache, we need to add a cache breaker query param to the
@@ -22,18 +27,33 @@ export async function renderCustomScript(params) {
22
27
  catch {
23
28
  /* no-op */
24
29
  }
30
+ const ssrOptions = {
31
+ ...options,
32
+ appPath: config.appPath,
33
+ importMap: importMap || { imports: {} },
34
+ requestInfo: {
35
+ headers: req.headers,
36
+ hostname: req.hostname,
37
+ isEncrypted: !!req.socket.encrypted,
38
+ originalUrl: req.originalUrl,
39
+ params: req.params,
40
+ query: req.query,
41
+ url: req.url,
42
+ },
43
+ };
25
44
  const renderScriptUrlWithCacheBreaker = renderScriptUrl + cacheBreakerQueryParam;
26
- const { config } = options.session;
27
45
  if (config.features?.enableSSR) {
28
46
  return executeRenderScriptInSsrMode({
29
47
  renderScriptPath,
30
48
  renderScriptUrl: renderScriptUrlWithCacheBreaker,
31
49
  // TODO this should be a Node-specific import map
32
- importMap: options.importMap,
33
- options,
50
+ importMap: ssrOptions.importMap,
51
+ options: ssrOptions,
52
+ req,
34
53
  });
35
54
  }
36
- // Try importing the script
55
+ // Legacy custom render mode: import and run the script directly
56
+ // TODO improve the naming and error messages (use getCustomRenderErrorResponse)
37
57
  let createHtml;
38
58
  let errorMessage;
39
59
  try {
@@ -51,7 +71,7 @@ export async function renderCustomScript(params) {
51
71
  if (createHtml) {
52
72
  // Try running the script
53
73
  try {
54
- return await createHtml(options);
74
+ return await createHtml({ ...ssrOptions, session, req, res });
55
75
  }
56
76
  catch (e) {
57
77
  errorMessage = `Error running render script at "${renderScriptPath}":\n${e.stack || e}`;
@@ -59,25 +79,27 @@ export async function renderCustomScript(params) {
59
79
  }
60
80
  // Return an error page. Doing this instead of returning a default or empty response ensures that
61
81
  // the user is aware of any configuration issues.
62
- errorMessage ??= `Unknown error loading render script at "${renderScriptPath}".`;
63
- return getErrorResponse({ req: options.req, message: errorMessage });
82
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- always set by now
83
+ return getErrorResponse({ req, message: errorMessage });
64
84
  }
65
85
  async function executeRenderScriptInSsrMode(params) {
66
86
  const { renderScriptPath, renderScriptUrl, importMap, options } = params;
67
- const serverEntryResult = await runServerEntryInWorker({
68
- renderScriptUrl,
69
- importMap,
70
- renderParams: {},
71
- });
72
- const { errors, renderedContent } = serverEntryResult;
73
- if (errors?.length) {
87
+ try {
88
+ return await runServerEntryInWorker({
89
+ renderScriptUrl,
90
+ importMap,
91
+ renderParams: options,
92
+ });
93
+ }
94
+ catch (err) {
95
+ // note: err might not be an instance of Error because of serialization
96
+ const errorMessage = err?.stack || err?.message || String(err);
74
97
  return getCustomRenderErrorResponse({
75
- req: options.req,
98
+ req: params.req,
76
99
  route: options.route,
77
- message: `- error running script: ${errors.map((e) => e.text).join('\n')}`,
100
+ message: `- error running script: ${errorMessage}`,
78
101
  scriptPath: renderScriptPath,
79
102
  });
80
103
  }
81
- return { content: renderedContent, contentType: 'text/html', statusCode: 200 };
82
104
  }
83
105
  //# sourceMappingURL=renderCustomScript.js.map