@ms-cloudpack/app-server 0.20.5 → 0.20.7

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 (44) hide show
  1. package/lib/createRoutes.d.ts.map +1 -1
  2. package/lib/createRoutes.js +5 -2
  3. package/lib/createRoutes.js.map +1 -1
  4. package/lib/renderRoute/getCustomRenderErrorHandler.d.ts +37 -0
  5. package/lib/renderRoute/getCustomRenderErrorHandler.d.ts.map +1 -0
  6. package/lib/renderRoute/getCustomRenderErrorHandler.js +51 -0
  7. package/lib/renderRoute/getCustomRenderErrorHandler.js.map +1 -0
  8. package/lib/renderRoute/renderRoute.js +30 -45
  9. package/lib/renderRoute/renderRoute.js.map +1 -1
  10. package/lib/renderRoute/ssr/bundleServerEntry.d.ts +8 -2
  11. package/lib/renderRoute/ssr/bundleServerEntry.d.ts.map +1 -1
  12. package/lib/renderRoute/ssr/bundleServerEntry.js +28 -21
  13. package/lib/renderRoute/ssr/bundleServerEntry.js.map +1 -1
  14. package/lib/renderRoute/ssr/renderCustomScript.d.ts +2 -0
  15. package/lib/renderRoute/ssr/renderCustomScript.d.ts.map +1 -1
  16. package/lib/renderRoute/ssr/renderCustomScript.js +15 -69
  17. package/lib/renderRoute/ssr/renderCustomScript.js.map +1 -1
  18. package/lib/renderRoute/ssr/runServerEntryInWorker.d.ts +5 -4
  19. package/lib/renderRoute/ssr/runServerEntryInWorker.d.ts.map +1 -1
  20. package/lib/renderRoute/ssr/runServerEntryInWorker.js +17 -6
  21. package/lib/renderRoute/ssr/runServerEntryInWorker.js.map +1 -1
  22. package/lib/renderRoute/ssr/types/RunScriptResult.d.ts +12 -0
  23. package/lib/renderRoute/ssr/types/RunScriptResult.d.ts.map +1 -0
  24. package/lib/renderRoute/ssr/types/RunScriptResult.js +2 -0
  25. package/lib/renderRoute/ssr/types/RunScriptResult.js.map +1 -0
  26. package/lib/renderRoute/ssr/types/RunServerEntryOptions.d.ts +2 -2
  27. package/lib/renderRoute/ssr/types/RunServerEntryOptions.d.ts.map +1 -1
  28. package/lib/renderRoute/ssr/types/RunServerEntryOptions.js.map +1 -1
  29. package/lib/renderRoute/ssr/worker/importAndRunScript.d.ts +16 -0
  30. package/lib/renderRoute/ssr/worker/importAndRunScript.d.ts.map +1 -0
  31. package/lib/renderRoute/ssr/worker/importAndRunScript.js +70 -0
  32. package/lib/renderRoute/ssr/worker/importAndRunScript.js.map +1 -0
  33. package/lib/renderRoute/ssr/worker/runServerEntry.d.ts +7 -4
  34. package/lib/renderRoute/ssr/worker/runServerEntry.d.ts.map +1 -1
  35. package/lib/renderRoute/ssr/worker/runServerEntry.js +68 -30
  36. package/lib/renderRoute/ssr/worker/runServerEntry.js.map +1 -1
  37. package/lib/startServers.d.ts.map +1 -1
  38. package/lib/startServers.js +1 -0
  39. package/lib/startServers.js.map +1 -1
  40. package/package.json +9 -9
  41. package/lib/renderRoute/getErrorResponse.d.ts +0 -21
  42. package/lib/renderRoute/getErrorResponse.d.ts.map +0 -1
  43. package/lib/renderRoute/getErrorResponse.js +0 -44
  44. package/lib/renderRoute/getErrorResponse.js.map +0 -1
@@ -1,7 +1,5 @@
1
- import fsPromises from 'fs/promises';
2
- import { pathToFileURL } from 'url';
3
- import { getCustomRenderErrorResponse, getErrorResponse } from '../getErrorResponse.js';
4
1
  import { runServerEntryInWorker } from './runServerEntryInWorker.js';
2
+ import { importAndRunScript } from './worker/importAndRunScript.js';
5
3
  /**
6
4
  * Load the default export from a JS file passed as the `renderScript` in a route,
7
5
  * and return either the result of running the function, or an error page if something fails.
@@ -10,23 +8,9 @@ export async function renderCustomScript(params,
10
8
  // only config and importMap are used in the new SSR signature, so in the future this can be:
11
9
  // PartialContext<'session', 'config' | 'importMap'>
12
10
  context) {
13
- const { renderScriptPath, req, res, ...options } = params;
11
+ const { renderScriptPath, req, res, handleCustomRenderError, ...options } = params;
14
12
  const { session } = context;
15
13
  const { config, importMap } = session;
16
- // Get the html factory function from a script default export.
17
- const renderScriptUrl = pathToFileURL(renderScriptPath).toString();
18
- // Note: because there isn't a way to purge the require cache, we need to add a cache breaker query param to the
19
- // script path to ensure we get the latest version of the script. This could be improved by using the git hash of
20
- // the file if it exists, or a hash of the content, or even the timestamp of the file.
21
- // TODO: this won't work to update anything the file imports, and doesn't seem to work for serverEntry at all...
22
- let cacheBreakerQueryParam = `?t=${Date.now()}`;
23
- try {
24
- const { mtime } = await fsPromises.stat(renderScriptPath);
25
- cacheBreakerQueryParam = `?t=${mtime.getTime()}`;
26
- }
27
- catch {
28
- /* no-op */
29
- }
30
14
  const ssrOptions = {
31
15
  ...options,
32
16
  appPath: config.appPath,
@@ -41,65 +25,27 @@ context) {
41
25
  url: req.url,
42
26
  },
43
27
  };
44
- const renderScriptUrlWithCacheBreaker = renderScriptUrl + cacheBreakerQueryParam;
28
+ // Run the function, either in a child process or directly.
29
+ // It's expected that either of these will return an error with appropriate context, not throw.
30
+ // (RenderFunctionResult is a superset of ServerEntryFunctionResult.)
31
+ let result;
45
32
  if (config.features?.enableSSR) {
46
- return executeRenderScriptInSsrMode({
33
+ result = await runServerEntryInWorker({
47
34
  renderScriptPath,
48
- renderScriptUrl: renderScriptUrlWithCacheBreaker,
49
35
  // TODO this should be a Node-specific import map
50
36
  importMap: ssrOptions.importMap,
51
- options: ssrOptions,
52
- req,
37
+ renderParams: ssrOptions,
53
38
  });
54
39
  }
55
- // Legacy custom render mode: import and run the script directly
56
- // TODO improve the naming and error messages (use getCustomRenderErrorResponse)
57
- let createHtml;
58
- let errorMessage;
59
- try {
60
- const importResult = (await import(renderScriptUrlWithCacheBreaker)).default;
61
- if (typeof importResult === 'function') {
62
- createHtml = importResult;
63
- }
64
- else {
65
- errorMessage = `The render script at "${renderScriptPath}" does not export a default function.`;
66
- }
67
- }
68
- catch (e) {
69
- errorMessage = `Error importing render script at "${renderScriptUrl}":\n${e.stack || e}`;
70
- }
71
- if (createHtml) {
72
- // Try running the script
73
- try {
74
- return await createHtml({ ...ssrOptions, session, req, res });
75
- }
76
- catch (e) {
77
- errorMessage = `Error running render script at "${renderScriptPath}":\n${e.stack || e}`;
78
- }
79
- }
80
- // Return an error page. Doing this instead of returning a default or empty response ensures that
81
- // the user is aware of any configuration issues.
82
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- always set by now
83
- return getErrorResponse({ req, message: errorMessage });
84
- }
85
- async function executeRenderScriptInSsrMode(params) {
86
- const { renderScriptPath, renderScriptUrl, importMap, options } = params;
87
- try {
88
- return await runServerEntryInWorker({
89
- renderScriptUrl,
90
- importMap,
91
- renderParams: options,
92
- });
40
+ else {
41
+ result = await importAndRunScript(renderScriptPath, { ...ssrOptions, session, req, res });
93
42
  }
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);
97
- return getCustomRenderErrorResponse({
98
- req: params.req,
99
- route: options.route,
100
- message: `- error running script: ${errorMessage}`,
101
- scriptPath: renderScriptPath,
43
+ if (result && typeof result === 'object' && 'error' in result) {
44
+ return handleCustomRenderError({
45
+ message: `:\n${result.error}`,
46
+ bundledPath: renderScriptPath,
102
47
  });
103
48
  }
49
+ return result;
104
50
  }
105
51
  //# sourceMappingURL=renderCustomScript.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"renderCustomScript.js","sourceRoot":"","sources":["../../../src/renderRoute/ssr/renderCustomScript.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context } from '@ms-cloudpack/api-server';\nimport type {\n ImportMap,\n RenderFunction,\n RenderFunctionResult,\n RenderFunctionScript,\n ServerEntryFunctionOptions,\n ServerEntryFunctionResult,\n} from '@ms-cloudpack/common-types';\nimport fsPromises from 'fs/promises';\nimport type { TLSSocket } from 'tls';\nimport { pathToFileURL } from 'url';\nimport type { RenderRouteOptions } from '../../types/RenderRouteOptions.js';\nimport { getCustomRenderErrorResponse, getErrorResponse } from '../getErrorResponse.js';\nimport { runServerEntryInWorker } from './runServerEntryInWorker.js';\n\n/**\n * Load the default export from a JS file passed as the `renderScript` in a route,\n * and return either the result of running the function, or an error page if something fails.\n */\nexport async function renderCustomScript(\n params: RenderRouteOptions & {\n /** Full path to the render script (bundled version if relevant) */\n renderScriptPath: string;\n },\n // only config and importMap are used in the new SSR signature, so in the future this can be:\n // PartialContext<'session', 'config' | 'importMap'>\n context: Pick<Context, 'session'>,\n): Promise<RenderFunctionResult> {\n const { renderScriptPath, req, res, ...options } = params;\n const { session } = context;\n const { config, importMap } = session;\n\n // Get the html factory function from a script default export.\n const renderScriptUrl = pathToFileURL(renderScriptPath).toString();\n\n // Note: because there isn't a way to purge the require cache, we need to add a cache breaker query param to the\n // script path to ensure we get the latest version of the script. This could be improved by using the git hash of\n // the file if it exists, or a hash of the content, or even the timestamp of the file.\n // TODO: this won't work to update anything the file imports, and doesn't seem to work for serverEntry at all...\n let cacheBreakerQueryParam = `?t=${Date.now()}`;\n try {\n const { mtime } = await fsPromises.stat(renderScriptPath);\n cacheBreakerQueryParam = `?t=${mtime.getTime()}`;\n } catch {\n /* no-op */\n }\n\n const ssrOptions: ServerEntryFunctionOptions = {\n ...options,\n appPath: config.appPath,\n importMap: importMap || { imports: {} },\n requestInfo: {\n headers: req.headers,\n hostname: req.hostname,\n isEncrypted: !!(req.socket as TLSSocket).encrypted,\n originalUrl: req.originalUrl,\n params: req.params,\n query: req.query,\n url: req.url,\n },\n };\n\n const renderScriptUrlWithCacheBreaker = renderScriptUrl + cacheBreakerQueryParam;\n\n if (config.features?.enableSSR) {\n return executeRenderScriptInSsrMode({\n renderScriptPath,\n renderScriptUrl: renderScriptUrlWithCacheBreaker,\n // TODO this should be a Node-specific import map\n importMap: ssrOptions.importMap,\n options: ssrOptions,\n req,\n });\n }\n\n // Legacy custom render mode: import and run the script directly\n // TODO improve the naming and error messages (use getCustomRenderErrorResponse)\n let createHtml: RenderFunction | undefined;\n let errorMessage: string | undefined;\n try {\n const importResult = ((await import(renderScriptUrlWithCacheBreaker)) as RenderFunctionScript).default;\n if (typeof importResult === 'function') {\n createHtml = importResult;\n } else {\n errorMessage = `The render script at \"${renderScriptPath}\" does not export a default function.`;\n }\n } catch (e) {\n errorMessage = `Error importing render script at \"${renderScriptUrl}\":\\n${(e as Error).stack || e}`;\n }\n\n if (createHtml) {\n // Try running the script\n try {\n return await createHtml({ ...ssrOptions, session, req, res });\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 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- always set by now\n return getErrorResponse({ req, message: errorMessage! });\n}\n\nasync function executeRenderScriptInSsrMode(\n params: Pick<RenderRouteOptions, 'req'> & {\n renderScriptPath: string;\n renderScriptUrl: string;\n importMap: ImportMap;\n options: ServerEntryFunctionOptions;\n },\n): Promise<ServerEntryFunctionResult> {\n const { renderScriptPath, renderScriptUrl, importMap, options } = params;\n try {\n return await runServerEntryInWorker({\n renderScriptUrl,\n importMap,\n renderParams: options,\n });\n } catch (err) {\n // note: err might not be an instance of Error because of serialization\n const errorMessage = (err as Error)?.stack || (err as Error)?.message || String(err);\n return getCustomRenderErrorResponse({\n req: params.req,\n route: options.route,\n message: `- error running script: ${errorMessage}`,\n scriptPath: renderScriptPath,\n });\n }\n}\n"]}
1
+ {"version":3,"file":"renderCustomScript.js","sourceRoot":"","sources":["../../../src/renderRoute/ssr/renderCustomScript.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAErE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAGpE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAIC;AACD,6FAA6F;AAC7F,oDAAoD;AACpD,OAAiC;IAEjC,MAAM,EAAE,gBAAgB,EAAE,GAAG,EAAE,GAAG,EAAE,uBAAuB,EAAE,GAAG,OAAO,EAAE,GAAG,MAAM,CAAC;IACnF,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAC5B,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;IAEtC,MAAM,UAAU,GAA+B;QAC7C,GAAG,OAAO;QACV,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,SAAS,EAAE,SAAS,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;QACvC,WAAW,EAAE;YACX,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,WAAW,EAAE,CAAC,CAAE,GAAG,CAAC,MAAoB,CAAC,SAAS;YAClD,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,GAAG,EAAE,GAAG,CAAC,GAAG;SACb;KACF,CAAC;IAEF,2DAA2D;IAC3D,+FAA+F;IAC/F,qEAAqE;IACrE,IAAI,MAA6C,CAAC;IAClD,IAAI,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE,CAAC;QAC/B,MAAM,GAAG,MAAM,sBAAsB,CAAC;YACpC,gBAAgB;YAChB,iDAAiD;YACjD,SAAS,EAAE,UAAU,CAAC,SAAS;YAC/B,YAAY,EAAE,UAAU;SACzB,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,MAAM,kBAAkB,CAAiB,gBAAgB,EAAE,EAAE,GAAG,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5G,CAAC;IAED,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC;QAC9D,OAAO,uBAAuB,CAAC;YAC7B,OAAO,EAAE,MAAM,MAAM,CAAC,KAAK,EAAE;YAC7B,WAAW,EAAE,gBAAgB;SAC9B,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import type { Context } from '@ms-cloudpack/api-server';\nimport type { RenderFunction, RenderFunctionResult, ServerEntryFunctionOptions } from '@ms-cloudpack/common-types';\nimport type { TLSSocket } from 'tls';\nimport type { CustomRenderErrorHandler } from '../getCustomRenderErrorHandler.js';\nimport { runServerEntryInWorker } from './runServerEntryInWorker.js';\nimport type { RenderRouteOptions } from '../../types/RenderRouteOptions.js';\nimport { importAndRunScript } from './worker/importAndRunScript.js';\nimport type { RunScriptResult } from './types/RunScriptResult.js';\n\n/**\n * Load the default export from a JS file passed as the `renderScript` in a route,\n * and return either the result of running the function, or an error page if something fails.\n */\nexport async function renderCustomScript(\n params: RenderRouteOptions & {\n /** Full path to the render script (bundled version if relevant) */\n renderScriptPath: string;\n handleCustomRenderError: CustomRenderErrorHandler;\n },\n // only config and importMap are used in the new SSR signature, so in the future this can be:\n // PartialContext<'session', 'config' | 'importMap'>\n context: Pick<Context, 'session'>,\n): Promise<RenderFunctionResult> {\n const { renderScriptPath, req, res, handleCustomRenderError, ...options } = params;\n const { session } = context;\n const { config, importMap } = session;\n\n const ssrOptions: ServerEntryFunctionOptions = {\n ...options,\n appPath: config.appPath,\n importMap: importMap || { imports: {} },\n requestInfo: {\n headers: req.headers,\n hostname: req.hostname,\n isEncrypted: !!(req.socket as TLSSocket).encrypted,\n originalUrl: req.originalUrl,\n params: req.params,\n query: req.query,\n url: req.url,\n },\n };\n\n // Run the function, either in a child process or directly.\n // It's expected that either of these will return an error with appropriate context, not throw.\n // (RenderFunctionResult is a superset of ServerEntryFunctionResult.)\n let result: RunScriptResult<RenderFunctionResult>;\n if (config.features?.enableSSR) {\n result = await runServerEntryInWorker({\n renderScriptPath,\n // TODO this should be a Node-specific import map\n importMap: ssrOptions.importMap,\n renderParams: ssrOptions,\n });\n } else {\n result = await importAndRunScript<RenderFunction>(renderScriptPath, { ...ssrOptions, session, req, res });\n }\n\n if (result && typeof result === 'object' && 'error' in result) {\n return handleCustomRenderError({\n message: `:\\n${result.error}`,\n bundledPath: renderScriptPath,\n });\n }\n return result;\n}\n"]}
@@ -1,12 +1,13 @@
1
1
  import type { ServerEntryFunctionResult } from '@ms-cloudpack/common-types';
2
2
  import type { RunServerEntryOptions } from './types/RunServerEntryOptions.js';
3
+ import type { RunScriptResult } from './types/RunScriptResult.js';
3
4
  /**
4
5
  * Run server entry in a child process pool. (Despite the naming, this does not use worker threads.)
5
- * Allows exceptions from the script to propagate.
6
+ *
7
+ * It should catch all exceptions and return them with appropriate extra text explaining the
8
+ * context where the error occurred (import vs run + the stack if relevant).
6
9
  */
7
- export declare function runServerEntryInWorker(options: RunServerEntryOptions & {
8
- maxWorkers?: number;
9
- }): Promise<ServerEntryFunctionResult>;
10
+ export declare function runServerEntryInWorker(options: RunServerEntryOptions): Promise<RunScriptResult<ServerEntryFunctionResult>>;
10
11
  /**
11
12
  * Dispose the server entry runner child processes.
12
13
  */
@@ -1 +1 @@
1
- {"version":3,"file":"runServerEntryInWorker.d.ts","sourceRoot":"","sources":["../../../src/renderRoute/ssr/runServerEntryInWorker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAG5E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAM9E;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,qBAAqB,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACvD,OAAO,CAAC,yBAAyB,CAAC,CAiBpC;AAED;;GAEG;AACH,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,IAAI,CAAC,CAGlE"}
1
+ {"version":3,"file":"runServerEntryInWorker.d.ts","sourceRoot":"","sources":["../../../src/renderRoute/ssr/runServerEntryInWorker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAG5E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAC9E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAMlE;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,yBAAyB,CAAC,CAAC,CAyBrD;AAED;;GAEG;AACH,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,IAAI,CAAC,CAGlE"}
@@ -1,10 +1,12 @@
1
1
  import { WorkerPool } from '@ms-cloudpack/worker-pool';
2
2
  import path from 'path';
3
- const workerPath = path.join(import.meta.dirname, 'worker/ssrProcessEntry.js');
3
+ const workerPath = path.join(import.meta.dirname, 'worker/ssrWorkerEntry.js');
4
4
  let workerPool;
5
5
  /**
6
6
  * Run server entry in a child process pool. (Despite the naming, this does not use worker threads.)
7
- * Allows exceptions from the script to propagate.
7
+ *
8
+ * It should catch all exceptions and return them with appropriate extra text explaining the
9
+ * context where the error occurred (import vs run + the stack if relevant).
8
10
  */
9
11
  export async function runServerEntryInWorker(options) {
10
12
  workerPool ??= new WorkerPool({
@@ -18,10 +20,19 @@ export async function runServerEntryInWorker(options) {
18
20
  // OOM crashes if cloudpack start is left running for a long time.
19
21
  inactiveTimeout: 5 * 60 * 1000,
20
22
  });
21
- return workerPool.execute({
22
- method: 'runServerEntry',
23
- args: [options],
24
- });
23
+ try {
24
+ return await workerPool.execute({
25
+ method: 'runServerEntry',
26
+ args: [options],
27
+ });
28
+ }
29
+ catch (err) {
30
+ // runServerEntry should catch errors directly from its code, so an error here might be
31
+ // some issue with the worker
32
+ return {
33
+ error: `Error running script in child process: ${err.stack || err}`,
34
+ };
35
+ }
25
36
  }
26
37
  /**
27
38
  * Dispose the server entry runner child processes.
@@ -1 +1 @@
1
- {"version":3,"file":"runServerEntryInWorker.js","sourceRoot":"","sources":["../../../src/renderRoute/ssr/runServerEntryInWorker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,2BAA2B,CAAC,CAAC;AAE/E,IAAI,UAAkC,CAAC;AAEvC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAAwD;IAExD,UAAU,KAAK,IAAI,UAAU,CAAC;QAC5B,UAAU,EAAE,SAAS;QACrB,IAAI,EAAE,uBAAuB;QAC7B,SAAS,EAAE,UAAU;QACrB,iFAAiF;QACjF,mEAAmE;QACnE,UAAU,EAAE,CAAC;QACb,6FAA6F;QAC7F,kEAAkE;QAClE,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI;KAC/B,CAAC,CAAC;IAEH,OAAO,UAAU,CAAC,OAAO,CAA4B;QACnD,MAAM,EAAE,gBAAgB;QACxB,IAAI,EAAE,CAAC,OAAO,CAAC;KAChB,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B;IAChD,MAAM,UAAU,EAAE,OAAO,EAAE,CAAC;IAC5B,UAAU,GAAG,SAAS,CAAC;AACzB,CAAC","sourcesContent":["import type { ServerEntryFunctionResult } from '@ms-cloudpack/common-types';\nimport { WorkerPool } from '@ms-cloudpack/worker-pool';\nimport path from 'path';\nimport type { RunServerEntryOptions } from './types/RunServerEntryOptions.js';\n\nconst workerPath = path.join(import.meta.dirname, 'worker/ssrProcessEntry.js');\n\nlet workerPool: WorkerPool | undefined;\n\n/**\n * Run server entry in a child process pool. (Despite the naming, this does not use worker threads.)\n * Allows exceptions from the script to propagate.\n */\nexport async function runServerEntryInWorker(\n options: RunServerEntryOptions & { maxWorkers?: number },\n): Promise<ServerEntryFunctionResult> {\n workerPool ??= new WorkerPool({\n workerType: 'process',\n name: 'serverEntry execution',\n entryPath: workerPath,\n // Currently we're unlikely to be server-rendering more than one thing at a time.\n // Keep this low unless we find a case where it needs to be higher.\n maxWorkers: 3,\n // Stop inactive processes after 5 minutes. This is intended to prevent one possible cause of\n // OOM crashes if cloudpack start is left running for a long time.\n inactiveTimeout: 5 * 60 * 1000,\n });\n\n return workerPool.execute<ServerEntryFunctionResult>({\n method: 'runServerEntry',\n args: [options],\n });\n}\n\n/**\n * Dispose the server entry runner child processes.\n */\nexport async function disposeServerEntryWorkerPool(): Promise<void> {\n await workerPool?.dispose();\n workerPool = undefined;\n}\n"]}
1
+ {"version":3,"file":"runServerEntryInWorker.js","sourceRoot":"","sources":["../../../src/renderRoute/ssr/runServerEntryInWorker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,IAAI,MAAM,MAAM,CAAC;AAIxB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,0BAA0B,CAAC,CAAC;AAE9E,IAAI,UAAkC,CAAC;AAEvC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAA8B;IAE9B,UAAU,KAAK,IAAI,UAAU,CAAC;QAC5B,UAAU,EAAE,SAAS;QACrB,IAAI,EAAE,uBAAuB;QAC7B,SAAS,EAAE,UAAU;QACrB,iFAAiF;QACjF,mEAAmE;QACnE,UAAU,EAAE,CAAC;QACb,6FAA6F;QAC7F,kEAAkE;QAClE,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI;KAC/B,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,OAAO,MAAM,UAAU,CAAC,OAAO,CAA4B;YACzD,MAAM,EAAE,gBAAgB;YACxB,IAAI,EAAE,CAAC,OAAO,CAAC;SAChB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,uFAAuF;QACvF,6BAA6B;QAC7B,OAAO;YACL,KAAK,EAAE,0CAA2C,GAAa,CAAC,KAAK,IAAI,GAAG,EAAE;SAC/E,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B;IAChD,MAAM,UAAU,EAAE,OAAO,EAAE,CAAC;IAC5B,UAAU,GAAG,SAAS,CAAC;AACzB,CAAC","sourcesContent":["import type { ServerEntryFunctionResult } from '@ms-cloudpack/common-types';\nimport { WorkerPool } from '@ms-cloudpack/worker-pool';\nimport path from 'path';\nimport type { RunServerEntryOptions } from './types/RunServerEntryOptions.js';\nimport type { RunScriptResult } from './types/RunScriptResult.js';\n\nconst workerPath = path.join(import.meta.dirname, 'worker/ssrWorkerEntry.js');\n\nlet workerPool: WorkerPool | undefined;\n\n/**\n * Run server entry in a child process pool. (Despite the naming, this does not use worker threads.)\n *\n * It should catch all exceptions and return them with appropriate extra text explaining the\n * context where the error occurred (import vs run + the stack if relevant).\n */\nexport async function runServerEntryInWorker(\n options: RunServerEntryOptions,\n): Promise<RunScriptResult<ServerEntryFunctionResult>> {\n workerPool ??= new WorkerPool({\n workerType: 'process',\n name: 'serverEntry execution',\n entryPath: workerPath,\n // Currently we're unlikely to be server-rendering more than one thing at a time.\n // Keep this low unless we find a case where it needs to be higher.\n maxWorkers: 3,\n // Stop inactive processes after 5 minutes. This is intended to prevent one possible cause of\n // OOM crashes if cloudpack start is left running for a long time.\n inactiveTimeout: 5 * 60 * 1000,\n });\n\n try {\n return await workerPool.execute<ServerEntryFunctionResult>({\n method: 'runServerEntry',\n args: [options],\n });\n } catch (err) {\n // runServerEntry should catch errors directly from its code, so an error here might be\n // some issue with the worker\n return {\n error: `Error running script in child process: ${(err as Error).stack || err}`,\n };\n }\n}\n\n/**\n * Dispose the server entry runner child processes.\n */\nexport async function disposeServerEntryWorkerPool(): Promise<void> {\n await workerPool?.dispose();\n workerPool = undefined;\n}\n"]}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Result of `importAndRunScript` function that runs the `serverEntry` or `renderScript`.
3
+ */
4
+ export type RunScriptResult<TResult> = TResult | {
5
+ /**
6
+ * Error message if there was an issue importing or running the script.
7
+ * This is formatted as a sentence and assumes the final version will be processed through a
8
+ * `CustomRenderErrorHandler` to add the route and path info.
9
+ */
10
+ error: string;
11
+ };
12
+ //# sourceMappingURL=RunScriptResult.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RunScriptResult.d.ts","sourceRoot":"","sources":["../../../../src/renderRoute/ssr/types/RunScriptResult.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,eAAe,CAAC,OAAO,IAC/B,OAAO,GACP;IACE;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;CACf,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=RunScriptResult.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RunScriptResult.js","sourceRoot":"","sources":["../../../../src/renderRoute/ssr/types/RunScriptResult.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Result of `importAndRunScript` function that runs the `serverEntry` or `renderScript`.\n */\nexport type RunScriptResult<TResult> =\n | TResult\n | {\n /**\n * Error message if there was an issue importing or running the script.\n * This is formatted as a sentence and assumes the final version will be processed through a\n * `CustomRenderErrorHandler` to add the route and path info.\n */\n error: string;\n };\n"]}
@@ -6,8 +6,8 @@ export type RunServerEntryOptions = {
6
6
  * (as of writing, it reuses the browser one)
7
7
  */
8
8
  importMap: ImportMap;
9
- /** Absolute file URL for the render script (bundled if relevant) */
10
- renderScriptUrl: string;
9
+ /** Absolute path to the render script (bundled if relevant) */
10
+ renderScriptPath: string;
11
11
  /** Params to pass to the user's render function */
12
12
  renderParams: ServerEntryFunctionOptions;
13
13
  };
@@ -1 +1 @@
1
- {"version":3,"file":"RunServerEntryOptions.d.ts","sourceRoot":"","sources":["../../../../src/renderRoute/ssr/types/RunServerEntryOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;OAGG;IACH,SAAS,EAAE,SAAS,CAAC;IACrB,oEAAoE;IACpE,eAAe,EAAE,MAAM,CAAC;IACxB,mDAAmD;IACnD,YAAY,EAAE,0BAA0B,CAAC;CAC1C,CAAC"}
1
+ {"version":3,"file":"RunServerEntryOptions.d.ts","sourceRoot":"","sources":["../../../../src/renderRoute/ssr/types/RunServerEntryOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;OAGG;IACH,SAAS,EAAE,SAAS,CAAC;IACrB,+DAA+D;IAC/D,gBAAgB,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,YAAY,EAAE,0BAA0B,CAAC;CAC1C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"RunServerEntryOptions.js","sourceRoot":"","sources":["../../../../src/renderRoute/ssr/types/RunServerEntryOptions.ts"],"names":[],"mappings":"","sourcesContent":["import type { ServerEntryFunctionOptions } from '@ms-cloudpack/common-types';\nimport type { ImportMap } from '@ms-cloudpack/import-map';\n\nexport type RunServerEntryOptions = {\n /**\n * TODO: This should be the import map specific to the render script in Node\n * (as of writing, it reuses the browser one)\n */\n importMap: ImportMap;\n /** Absolute file URL for the render script (bundled if relevant) */\n renderScriptUrl: string;\n /** Params to pass to the user's render function */\n renderParams: ServerEntryFunctionOptions;\n};\n"]}
1
+ {"version":3,"file":"RunServerEntryOptions.js","sourceRoot":"","sources":["../../../../src/renderRoute/ssr/types/RunServerEntryOptions.ts"],"names":[],"mappings":"","sourcesContent":["import type { ServerEntryFunctionOptions } from '@ms-cloudpack/common-types';\nimport type { ImportMap } from '@ms-cloudpack/import-map';\n\nexport type RunServerEntryOptions = {\n /**\n * TODO: This should be the import map specific to the render script in Node\n * (as of writing, it reuses the browser one)\n */\n importMap: ImportMap;\n /** Absolute path to the render script (bundled if relevant) */\n renderScriptPath: string;\n /** Params to pass to the user's render function */\n renderParams: ServerEntryFunctionOptions;\n};\n"]}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Import the `serverEntry` or `renderScript` and run the render function.
3
+ *
4
+ * It should catch all exceptions and return them with appropriate extra text explaining the
5
+ * context where the error occurred (import vs run + the stack if relevant).
6
+ *
7
+ * @param {string} scriptPath Absolute path to the script.
8
+ * @param {Parameters<F>[0]} options
9
+ * @returns {Promise<RunScriptResult<ReturnType<F>>>} The rendered result or an error message.
10
+ * @template {ServerEntryFunction | RenderFunction} F
11
+ */
12
+ export function importAndRunScript<F extends ServerEntryFunction | RenderFunction>(scriptPath: string, options: Parameters<F>[0]): Promise<RunScriptResult<ReturnType<F>>>;
13
+ import type { ServerEntryFunction } from '@ms-cloudpack/common-types';
14
+ import type { RenderFunction } from '@ms-cloudpack/common-types';
15
+ import type { RunScriptResult } from '../types/RunScriptResult.js';
16
+ //# sourceMappingURL=importAndRunScript.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"importAndRunScript.d.ts","sourceRoot":"","sources":["../../../../src/renderRoute/ssr/worker/importAndRunScript.js"],"names":[],"mappings":"AAOA;;;;;;;;;;GAUG;AACH,mCAFoD,CAAC,SAAvC,mBAAmB,GAAG,cAAe,cAHxC,MAAM,WACN,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GACd,OAAO,CAAC,gBAAgB,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAyDnD;yCAvEuD,4BAA4B;oCAA5B,4BAA4B;qCAChD,6BAA6B"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @import { RenderFunction, ServerEntryFunction } from '@ms-cloudpack/common-types';
3
+ * @import { RunScriptResult } from '../types/RunScriptResult.js';
4
+ */
5
+ import fs from 'fs';
6
+ import { pathToFileURL } from 'url';
7
+ /**
8
+ * Import the `serverEntry` or `renderScript` and run the render function.
9
+ *
10
+ * It should catch all exceptions and return them with appropriate extra text explaining the
11
+ * context where the error occurred (import vs run + the stack if relevant).
12
+ *
13
+ * @param {string} scriptPath Absolute path to the script.
14
+ * @param {Parameters<F>[0]} options
15
+ * @returns {Promise<RunScriptResult<ReturnType<F>>>} The rendered result or an error message.
16
+ * @template {ServerEntryFunction | RenderFunction} F
17
+ */
18
+ export async function importAndRunScript(scriptPath, options) {
19
+ // Explicitly check if it exists, for a better error message.
20
+ if (!fs.existsSync(scriptPath)) {
21
+ return { error: 'File does not exist.' };
22
+ }
23
+ // Because there isn't a way to purge the require cache, we need to add a cache breaker query param to the
24
+ // script path to ensure we get the latest version of the script. This could be improved by using the git hash of
25
+ // the file if it exists, or a hash of the content, or even the timestamp of the file.
26
+ // TODO: this won't work to update anything the file imports, and doesn't seem to work for serverEntry at all...
27
+ let cacheBreakerQueryParam = `?t=${Date.now()}`;
28
+ try {
29
+ const { mtime } = fs.statSync(scriptPath);
30
+ cacheBreakerQueryParam = `?t=${mtime.getTime()}`;
31
+ }
32
+ catch {
33
+ /* no-op */
34
+ }
35
+ const scriptUrl = pathToFileURL(scriptPath).toString() + cacheBreakerQueryParam;
36
+ // Import the script and try to find the render function.
37
+ let /** @type {F | undefined} */ renderFunction;
38
+ try {
39
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- misses jsdoc cast
40
+ const { default: defaultExport, render } = /** @type {{ default?: F | { render?: F }; render?: F; }} */ (await import(scriptUrl));
41
+ renderFunction =
42
+ typeof render === 'function'
43
+ ? render
44
+ : typeof defaultExport === 'function'
45
+ ? defaultExport
46
+ : // less likely case for CJS interop quirks
47
+ defaultExport && typeof defaultExport.render === 'function'
48
+ ? defaultExport.render
49
+ : undefined;
50
+ }
51
+ catch (error) {
52
+ // Include the stack here because this might be an error running the script.
53
+ // (It shouldn't be a module not found error since we checked existence above.)
54
+ return { error: `Error importing script: ${ /** @type {Error} */(error).stack || error}` };
55
+ }
56
+ if (!renderFunction) {
57
+ return { error: 'Render function not found in script (it must be the default export or a named export "render").' };
58
+ }
59
+ // Try running the script
60
+ try {
61
+ // Type inference is getting lost for some reason
62
+ return /** @type {ReturnType<F>} */ (await renderFunction(/** @type {*} */ (options)));
63
+ }
64
+ catch (error) {
65
+ return {
66
+ error: `Render function threw an error:\n${ /** @type {Error} */(error).stack || error}`,
67
+ };
68
+ }
69
+ }
70
+ //# sourceMappingURL=importAndRunScript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"importAndRunScript.js","sourceRoot":"","sources":["../../../../src/renderRoute/ssr/worker/importAndRunScript.js"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,UAAU,EAAE,OAAO;IAC1D,6DAA6D;IAC7D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC;IAC3C,CAAC;IAED,0GAA0G;IAC1G,iHAAiH;IACjH,sFAAsF;IACtF,gHAAgH;IAChH,IAAI,sBAAsB,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IAChD,IAAI,CAAC;QACH,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QAC1C,sBAAsB,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,WAAW;IACb,CAAC;IACD,MAAM,SAAS,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC,QAAQ,EAAE,GAAG,sBAAsB,CAAC;IAEhF,yDAAyD;IACzD,IAAI,4BAA4B,CAAC,cAAc,CAAC;IAChD,IAAI,CAAC;QACH,wFAAwF;QACxF,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,4DAA4D,CAAC,CACtG,MAAM,MAAM,CAAC,SAAS,CAAC,CACxB,CAAC;QACF,cAAc;YACZ,OAAO,MAAM,KAAK,UAAU;gBAC1B,CAAC,CAAC,MAAM;gBACR,CAAC,CAAC,OAAO,aAAa,KAAK,UAAU;oBACnC,CAAC,CAAC,aAAa;oBACf,CAAC,CAAC,0CAA0C;wBAC1C,aAAa,IAAI,OAAO,aAAa,CAAC,MAAM,KAAK,UAAU;4BAC3D,CAAC,CAAC,aAAa,CAAC,MAAM;4BACtB,CAAC,CAAC,SAAS,CAAC;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,4EAA4E;QAC5E,+EAA+E;QAC/E,OAAO,EAAE,KAAK,EAAE,2BAA2B,CAAA,oBAAqB,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,KAAK,EAAE,EAAE,CAAC;IAC7F,CAAC;IAED,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,OAAO,EAAE,KAAK,EAAE,iGAAiG,EAAE,CAAC;IACtH,CAAC;IAED,yBAAyB;IACzB,IAAI,CAAC;QACH,iDAAiD;QACjD,OAAO,4BAA4B,CAAC,CAAC,MAAM,cAAc,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACzF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,KAAK,EAAE,oCAAoC,CAAA,oBAAqB,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,KAAK,EAAE;SACzF,CAAC;IACJ,CAAC;AACH,CAAC","sourcesContent":["/**\n * @import { RenderFunction, ServerEntryFunction } from '@ms-cloudpack/common-types';\n * @import { RunScriptResult } from '../types/RunScriptResult.js';\n */\nimport fs from 'fs';\nimport { pathToFileURL } from 'url';\n\n/**\n * Import the `serverEntry` or `renderScript` and run the render function.\n *\n * It should catch all exceptions and return them with appropriate extra text explaining the\n * context where the error occurred (import vs run + the stack if relevant).\n *\n * @param {string} scriptPath Absolute path to the script.\n * @param {Parameters<F>[0]} options\n * @returns {Promise<RunScriptResult<ReturnType<F>>>} The rendered result or an error message.\n * @template {ServerEntryFunction | RenderFunction} F\n */\nexport async function importAndRunScript(scriptPath, options) {\n // Explicitly check if it exists, for a better error message.\n if (!fs.existsSync(scriptPath)) {\n return { error: 'File does not exist.' };\n }\n\n // Because there isn't a way to purge the require cache, we need to add a cache breaker query param to the\n // script path to ensure we get the latest version of the script. This could be improved by using the git hash of\n // the file if it exists, or a hash of the content, or even the timestamp of the file.\n // TODO: this won't work to update anything the file imports, and doesn't seem to work for serverEntry at all...\n let cacheBreakerQueryParam = `?t=${Date.now()}`;\n try {\n const { mtime } = fs.statSync(scriptPath);\n cacheBreakerQueryParam = `?t=${mtime.getTime()}`;\n } catch {\n /* no-op */\n }\n const scriptUrl = pathToFileURL(scriptPath).toString() + cacheBreakerQueryParam;\n\n // Import the script and try to find the render function.\n let /** @type {F | undefined} */ renderFunction;\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- misses jsdoc cast\n const { default: defaultExport, render } = /** @type {{ default?: F | { render?: F }; render?: F; }} */ (\n await import(scriptUrl)\n );\n renderFunction =\n typeof render === 'function'\n ? render\n : typeof defaultExport === 'function'\n ? defaultExport\n : // less likely case for CJS interop quirks\n defaultExport && typeof defaultExport.render === 'function'\n ? defaultExport.render\n : undefined;\n } catch (error) {\n // Include the stack here because this might be an error running the script.\n // (It shouldn't be a module not found error since we checked existence above.)\n return { error: `Error importing script: ${/** @type {Error} */ (error).stack || error}` };\n }\n\n if (!renderFunction) {\n return { error: 'Render function not found in script (it must be the default export or a named export \"render\").' };\n }\n\n // Try running the script\n try {\n // Type inference is getting lost for some reason\n return /** @type {ReturnType<F>} */ (await renderFunction(/** @type {*} */ (options)));\n } catch (error) {\n return {\n error: `Render function threw an error:\\n${/** @type {Error} */ (error).stack || error}`,\n };\n }\n}\n"]}
@@ -1,10 +1,13 @@
1
1
  /**
2
2
  * Register the import map loader with `options.importMap`, then import and run the render script.
3
- * Throws if the script path is invalid, and allows exceptions from within the script to propagate.
3
+ *
4
+ * It should catch all exceptions and return them with appropriate extra text explaining the
5
+ * context where the error occurred (import vs run + the stack if relevant).
4
6
  * @param {RunServerEntryOptions} options
5
- * @returns {Promise<RenderFunctionResult>}
7
+ * @returns {Promise<RunScriptResult<ServerEntryFunctionResult>>}
6
8
  */
7
- export function runServerEntry(options: RunServerEntryOptions): Promise<RenderFunctionResult>;
9
+ export function runServerEntry(options: RunServerEntryOptions): Promise<RunScriptResult<ServerEntryFunctionResult>>;
8
10
  import type { RunServerEntryOptions } from '../types/RunServerEntryOptions.js';
9
- import type { RenderFunctionResult } from '@ms-cloudpack/common-types';
11
+ import type { ServerEntryFunctionResult } from '@ms-cloudpack/common-types';
12
+ import type { RunScriptResult } from '../types/RunScriptResult.js';
10
13
  //# sourceMappingURL=runServerEntry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runServerEntry.d.ts","sourceRoot":"","sources":["../../../../src/renderRoute/ssr/worker/runServerEntry.js"],"names":[],"mappings":"AAuBA;;;;;GAKG;AACH,wCAHW,qBAAqB,GACnB,OAAO,CAAC,oBAAoB,CAAC,CA4BzC;2CAjDyC,mCAAmC;0CADnE,4BAA4B"}
1
+ {"version":3,"file":"runServerEntry.d.ts","sourceRoot":"","sources":["../../../../src/renderRoute/ssr/worker/runServerEntry.js"],"names":[],"mappings":"AAyBA;;;;;;;GAOG;AACH,wCAHW,qBAAqB,GACnB,OAAO,CAAC,gBAAgB,yBAAyB,CAAC,CAAC,CA4C/D;2CArEyC,mCAAmC;+CADnE,4BAA4B;qCAEF,6BAA6B"}
@@ -1,14 +1,16 @@
1
1
  /**
2
2
  * @import {
3
3
  * ImportMap,
4
- * RenderFunctionResult,
5
- * ServerEntryFunctionScript
4
+ * ServerEntryFunctionOptions,
5
+ * ServerEntryFunctionResult,
6
6
  * } from '@ms-cloudpack/common-types';
7
7
  * @import { RunServerEntryOptions } from '../types/RunServerEntryOptions.js';
8
+ * @import { RunScriptResult } from '../types/RunScriptResult.js';
8
9
  * @import { ImportMapLoaderContext, UpdateImportMapMessage, UpdateImportMapResult } from './importMapLoader.js';
9
10
  */
10
11
  import { register } from 'node:module';
11
12
  import { MessageChannel } from 'node:worker_threads';
13
+ import { importAndRunScript } from './importAndRunScript.js';
12
14
  let hasInitialized = false;
13
15
  // See updateImportMap comment
14
16
  const channel = new MessageChannel();
@@ -20,67 +22,84 @@ parentPort.unref();
20
22
  loaderPort.unref();
21
23
  /**
22
24
  * Register the import map loader with `options.importMap`, then import and run the render script.
23
- * Throws if the script path is invalid, and allows exceptions from within the script to propagate.
25
+ *
26
+ * It should catch all exceptions and return them with appropriate extra text explaining the
27
+ * context where the error occurred (import vs run + the stack if relevant).
24
28
  * @param {RunServerEntryOptions} options
25
- * @returns {Promise<RenderFunctionResult>}
29
+ * @returns {Promise<RunScriptResult<ServerEntryFunctionResult>>}
26
30
  */
27
31
  export async function runServerEntry(options) {
28
- const { importMap, renderScriptUrl, renderParams } = options;
29
- if (!hasInitialized) {
30
- // Register the custom loader, which is responsible for handling "bare" import resolution
31
- // using the import map where necessary.
32
- hasInitialized = true;
33
- const /** @type {ImportMapLoaderContext} */ context = { port: loaderPort };
34
- register('./importMapLoader.js', {
35
- parentURL: import.meta.url,
36
- data: context,
37
- transferList: [loaderPort],
38
- });
32
+ const { importMap, renderScriptPath, renderParams } = options;
33
+ try {
34
+ if (!hasInitialized) {
35
+ // Register the custom loader, which is responsible for handling "bare" import resolution
36
+ // using the import map where necessary.
37
+ hasInitialized = true;
38
+ const /** @type {ImportMapLoaderContext} */ context = { port: loaderPort };
39
+ register('./importMapLoader.js', {
40
+ parentURL: import.meta.url,
41
+ data: context,
42
+ transferList: [loaderPort],
43
+ });
44
+ }
45
+ // Send the latest import map to the loader
46
+ const updateImportMapResult = await updateImportMap(importMap);
47
+ if (updateImportMapResult.error) {
48
+ // jsdoc type inference failure??
49
+ return { error: updateImportMapResult.error };
50
+ }
39
51
  }
40
- // Send the latest import map to the loader
41
- await updateImportMap(importMap);
42
- // Import the render script, which will use the custom import map if relevant
43
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- missed jsdoc cast
44
- const render = /** @type {ServerEntryFunctionScript} */ (await import(renderScriptUrl)).default;
45
- if (typeof render !== 'function') {
46
- throw new Error(`The serverEntry script does not export a default function.`);
52
+ catch (err) {
53
+ return {
54
+ error: `Error updating import map before running serverEntry: ${ /** @type {Error} */(err).stack || err}`,
55
+ };
47
56
  }
48
- return await render(renderParams);
57
+ // Import the render script, which will use the custom import map if relevant
58
+ // (jsdoc doesn't have a direct way to provide the type parameter)
59
+ return /** @type {Promise<RunScriptResult<ServerEntryFunctionResult>>} */ (importAndRunScript(renderScriptPath,
60
+ /** @type {ServerEntryFunctionOptions} */ ({
61
+ ...renderParams,
62
+ // Use proxies to throw errors on unsupported sub-property usage to help with migration.
63
+ req: getInvalidPropProxy('req'),
64
+ res: getInvalidPropProxy('res'),
65
+ session: getInvalidPropProxy('session'),
66
+ })));
49
67
  }
50
68
  /**
51
69
  * We have to update the import map for importMapLoader.js via message ports, since the
52
70
  * customization hooks run in a separate thread.
53
71
  * @param {ImportMap} importMap
54
- * @returns {Promise<void>}
72
+ * @returns {Promise<{ error?: string }>} Empty object on success, or message on caught error.
73
+ * It might also throw an exception if `postMessage()` fails somehow.
55
74
  */
56
75
  function updateImportMap(importMap) {
57
- return new Promise((resolve, reject) => {
76
+ return new Promise((resolve) => {
58
77
  // Before sending the message, add one-off handlers for all cases to be safe.
59
78
  // This is just sending an object and waiting for confirmation, so errors are very unlikely.
60
79
  const onMessage = (/** @type {UpdateImportMapResult} */ result) => {
61
80
  handlersOff();
62
81
  if (result === true) {
63
- resolve();
82
+ resolve({});
64
83
  }
65
84
  else if (result === false) {
66
85
  // extremely unlikely
67
- reject(new Error('Failed to update import map before running serverEntry'));
86
+ resolve({ error: 'Failed to update import map before running serverEntry' });
68
87
  }
69
88
  else {
70
89
  // even less likely
71
- reject(new Error(`Unexpected message from import map loader: ${JSON.stringify(message)}`));
90
+ resolve({ error: `Unexpected message from import map loader: ${JSON.stringify(message)}` });
72
91
  }
73
92
  };
74
93
  parentPort.once('message', onMessage);
75
94
  const onError = (/** @type {*} */ error) => {
76
95
  handlersOff();
77
- reject(new Error(`Error updating import map before running serverEntry: ${error}`));
96
+ resolve({ error: `Error updating import map before running serverEntry: ${error}` });
78
97
  };
79
98
  parentPort.once('error', onError);
80
99
  // This should happen almost instantly, so conservatively time out after 1s
81
100
  const timeout = setTimeout(() => {
82
101
  handlersOff();
83
- reject(new Error('Timed out waiting for import map update confirmation from loader'));
102
+ resolve({ error: 'Timed out waiting for import map update confirmation from loader' });
84
103
  }, 1000);
85
104
  const handlersOff = () => {
86
105
  parentPort.off('message', onMessage);
@@ -91,4 +110,23 @@ function updateImportMap(importMap) {
91
110
  parentPort.postMessage(message);
92
111
  });
93
112
  }
113
+ /**
114
+ * Creates a proxy that throws an error when sub-properties of old non-SSR-friendly
115
+ * `RenderFunctionOptions` props are accessed. (The error is only thrown on sub-property access
116
+ * in case the top-level props are destructured but not actually used, and so that we don't need to
117
+ * make the whole options object into a proxy since that could have unintended side effects.)
118
+ * @param {string} propName The name of the `RenderFunctionOptions` property being accessed.
119
+ * @returns {unknown} the proxy
120
+ */
121
+ function getInvalidPropProxy(propName) {
122
+ return new Proxy({}, {
123
+ get() {
124
+ // This will be caught and logged by an error handler which adds in info about the
125
+ // route and file the error came from
126
+ throw new Error(`"${propName}" is not supported with the "enableSSR" feature. ` +
127
+ 'Please see the migration guide at https://microsoft.github.io/cloudpack/reference/custom-rendering#migration, ' +
128
+ 'or disable the feature for now.');
129
+ },
130
+ });
131
+ }
94
132
  //# sourceMappingURL=runServerEntry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"runServerEntry.js","sourceRoot":"","sources":["../../../../src/renderRoute/ssr/worker/runServerEntry.js"],"names":[],"mappings":"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","sourcesContent":["/**\n * @import {\n * ImportMap,\n * RenderFunctionResult,\n * ServerEntryFunctionScript\n * } from '@ms-cloudpack/common-types';\n * @import { RunServerEntryOptions } from '../types/RunServerEntryOptions.js';\n * @import { ImportMapLoaderContext, UpdateImportMapMessage, UpdateImportMapResult } from './importMapLoader.js';\n */\nimport { register } from 'node:module';\nimport { MessageChannel } from 'node:worker_threads';\n\nlet hasInitialized = false;\n\n// See updateImportMap comment\nconst channel = new MessageChannel();\n/** Port used by the parent to communicate with the loader */\nconst parentPort = channel.port1;\n/** Port used by the loader to communicate with the parent */\nconst loaderPort = channel.port2;\nparentPort.unref();\nloaderPort.unref();\n\n/**\n * Register the import map loader with `options.importMap`, then import and run the render script.\n * Throws if the script path is invalid, and allows exceptions from within the script to propagate.\n * @param {RunServerEntryOptions} options\n * @returns {Promise<RenderFunctionResult>}\n */\nexport async function runServerEntry(options) {\n const { importMap, renderScriptUrl, renderParams } = options;\n\n if (!hasInitialized) {\n // Register the custom loader, which is responsible for handling \"bare\" import resolution\n // using the import map where necessary.\n hasInitialized = true;\n const /** @type {ImportMapLoaderContext} */ context = { port: loaderPort };\n register('./importMapLoader.js', {\n parentURL: import.meta.url,\n data: context,\n transferList: [loaderPort],\n });\n }\n\n // Send the latest import map to the loader\n await updateImportMap(importMap);\n\n // Import the render script, which will use the custom import map if relevant\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- missed jsdoc cast\n const render = /** @type {ServerEntryFunctionScript} */ (await import(renderScriptUrl)).default;\n if (typeof render !== 'function') {\n throw new Error(`The serverEntry script does not export a default function.`);\n }\n\n return await render(renderParams);\n}\n\n/**\n * We have to update the import map for importMapLoader.js via message ports, since the\n * customization hooks run in a separate thread.\n * @param {ImportMap} importMap\n * @returns {Promise<void>}\n */\nfunction updateImportMap(importMap) {\n return new Promise((resolve, reject) => {\n // Before sending the message, add one-off handlers for all cases to be safe.\n // This is just sending an object and waiting for confirmation, so errors are very unlikely.\n const onMessage = (/** @type {UpdateImportMapResult} */ result) => {\n handlersOff();\n if (result === true) {\n resolve();\n } else if (result === false) {\n // extremely unlikely\n reject(new Error('Failed to update import map before running serverEntry'));\n } else {\n // even less likely\n reject(new Error(`Unexpected message from import map loader: ${JSON.stringify(message)}`));\n }\n };\n\n parentPort.once('message', onMessage);\n const onError = (/** @type {*} */ error) => {\n handlersOff();\n reject(new Error(`Error updating import map before running serverEntry: ${error}`));\n };\n parentPort.once('error', onError);\n\n // This should happen almost instantly, so conservatively time out after 1s\n const timeout = setTimeout(() => {\n handlersOff();\n reject(new Error('Timed out waiting for import map update confirmation from loader'));\n }, 1000);\n\n const handlersOff = () => {\n parentPort.off('message', onMessage);\n parentPort.off('error', onError);\n clearTimeout(timeout);\n };\n\n const /** @type {UpdateImportMapMessage} */ message = { importMap };\n parentPort.postMessage(message);\n });\n}\n"]}
1
+ {"version":3,"file":"runServerEntry.js","sourceRoot":"","sources":["../../../../src/renderRoute/ssr/worker/runServerEntry.js"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D,IAAI,cAAc,GAAG,KAAK,CAAC;AAE3B,8BAA8B;AAC9B,MAAM,OAAO,GAAG,IAAI,cAAc,EAAE,CAAC;AACrC,6DAA6D;AAC7D,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC;AACjC,6DAA6D;AAC7D,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC;AACjC,UAAU,CAAC,KAAK,EAAE,CAAC;AACnB,UAAU,CAAC,KAAK,EAAE,CAAC;AAEnB;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAO;IAC1C,MAAM,EAAE,SAAS,EAAE,gBAAgB,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC;IAE9D,IAAI,CAAC;QACH,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,yFAAyF;YACzF,wCAAwC;YACxC,cAAc,GAAG,IAAI,CAAC;YACtB,MAAM,qCAAqC,CAAC,OAAO,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;YAC3E,QAAQ,CAAC,sBAAsB,EAAE;gBAC/B,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG;gBAC1B,IAAI,EAAE,OAAO;gBACb,YAAY,EAAE,CAAC,UAAU,CAAC;aAC3B,CAAC,CAAC;QACL,CAAC;QAED,2CAA2C;QAC3C,MAAM,qBAAqB,GAAG,MAAM,eAAe,CAAC,SAAS,CAAC,CAAC;QAC/D,IAAI,qBAAqB,CAAC,KAAK,EAAE,CAAC;YAChC,iCAAiC;YACjC,OAAO,EAAE,KAAK,EAAE,qBAAqB,CAAC,KAAK,EAAE,CAAC;QAChD,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,KAAK,EAAE,yDAAyD,CAAA,oBAAqB,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,GAAG,EAAE;SAC1G,CAAC;IACJ,CAAC;IAED,6EAA6E;IAC7E,kEAAkE;IAClE,OAAO,kEAAkE,CAAC,CACxE,kBAAkB,CAChB,gBAAgB;IAChB,yCAAyC,CAAC,CAAC;QACzC,GAAG,YAAY;QACf,wFAAwF;QACxF,GAAG,EAAE,mBAAmB,CAAC,KAAK,CAAC;QAC/B,GAAG,EAAE,mBAAmB,CAAC,KAAK,CAAC;QAC/B,OAAO,EAAE,mBAAmB,CAAC,SAAS,CAAC;KACxC,CAAC,CACH,CACF,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAS,eAAe,CAAC,SAAS;IAChC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,6EAA6E;QAC7E,4FAA4F;QAC5F,MAAM,SAAS,GAAG,CAAC,oCAAoC,CAAC,MAAM,EAAE,EAAE;YAChE,WAAW,EAAE,CAAC;YACd,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACpB,OAAO,CAAC,EAAE,CAAC,CAAC;YACd,CAAC;iBAAM,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;gBAC5B,qBAAqB;gBACrB,OAAO,CAAC,EAAE,KAAK,EAAE,wDAAwD,EAAE,CAAC,CAAC;YAC/E,CAAC;iBAAM,CAAC;gBACN,mBAAmB;gBACnB,OAAO,CAAC,EAAE,KAAK,EAAE,8CAA8C,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;YAC9F,CAAC;QACH,CAAC,CAAC;QAEF,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,CAAC,gBAAgB,CAAC,KAAK,EAAE,EAAE;YACzC,WAAW,EAAE,CAAC;YACd,OAAO,CAAC,EAAE,KAAK,EAAE,yDAAyD,KAAK,EAAE,EAAE,CAAC,CAAC;QACvF,CAAC,CAAC;QACF,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAElC,2EAA2E;QAC3E,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YAC9B,WAAW,EAAE,CAAC;YACd,OAAO,CAAC,EAAE,KAAK,EAAE,kEAAkE,EAAE,CAAC,CAAC;QACzF,CAAC,EAAE,IAAI,CAAC,CAAC;QAET,MAAM,WAAW,GAAG,GAAG,EAAE;YACvB,UAAU,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YACrC,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACjC,YAAY,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC,CAAC;QAEF,MAAM,qCAAqC,CAAC,OAAO,GAAG,EAAE,SAAS,EAAE,CAAC;QACpE,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,mBAAmB,CAAC,QAAQ;IACnC,OAAO,IAAI,KAAK,CACd,EAAE,EACF;QACE,GAAG;YACD,kFAAkF;YAClF,qCAAqC;YACrC,MAAM,IAAI,KAAK,CACb,IAAI,QAAQ,mDAAmD;gBAC7D,gHAAgH;gBAChH,iCAAiC,CACpC,CAAC;QACJ,CAAC;KACF,CACF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * @import {\n * ImportMap,\n * ServerEntryFunctionOptions,\n * ServerEntryFunctionResult,\n * } from '@ms-cloudpack/common-types';\n * @import { RunServerEntryOptions } from '../types/RunServerEntryOptions.js';\n * @import { RunScriptResult } from '../types/RunScriptResult.js';\n * @import { ImportMapLoaderContext, UpdateImportMapMessage, UpdateImportMapResult } from './importMapLoader.js';\n */\nimport { register } from 'node:module';\nimport { MessageChannel } from 'node:worker_threads';\nimport { importAndRunScript } from './importAndRunScript.js';\n\nlet hasInitialized = false;\n\n// See updateImportMap comment\nconst channel = new MessageChannel();\n/** Port used by the parent to communicate with the loader */\nconst parentPort = channel.port1;\n/** Port used by the loader to communicate with the parent */\nconst loaderPort = channel.port2;\nparentPort.unref();\nloaderPort.unref();\n\n/**\n * Register the import map loader with `options.importMap`, then import and run the render script.\n *\n * It should catch all exceptions and return them with appropriate extra text explaining the\n * context where the error occurred (import vs run + the stack if relevant).\n * @param {RunServerEntryOptions} options\n * @returns {Promise<RunScriptResult<ServerEntryFunctionResult>>}\n */\nexport async function runServerEntry(options) {\n const { importMap, renderScriptPath, renderParams } = options;\n\n try {\n if (!hasInitialized) {\n // Register the custom loader, which is responsible for handling \"bare\" import resolution\n // using the import map where necessary.\n hasInitialized = true;\n const /** @type {ImportMapLoaderContext} */ context = { port: loaderPort };\n register('./importMapLoader.js', {\n parentURL: import.meta.url,\n data: context,\n transferList: [loaderPort],\n });\n }\n\n // Send the latest import map to the loader\n const updateImportMapResult = await updateImportMap(importMap);\n if (updateImportMapResult.error) {\n // jsdoc type inference failure??\n return { error: updateImportMapResult.error };\n }\n } catch (err) {\n return {\n error: `Error updating import map before running serverEntry: ${/** @type {Error} */ (err).stack || err}`,\n };\n }\n\n // Import the render script, which will use the custom import map if relevant\n // (jsdoc doesn't have a direct way to provide the type parameter)\n return /** @type {Promise<RunScriptResult<ServerEntryFunctionResult>>} */ (\n importAndRunScript(\n renderScriptPath,\n /** @type {ServerEntryFunctionOptions} */ ({\n ...renderParams,\n // Use proxies to throw errors on unsupported sub-property usage to help with migration.\n req: getInvalidPropProxy('req'),\n res: getInvalidPropProxy('res'),\n session: getInvalidPropProxy('session'),\n }),\n )\n );\n}\n\n/**\n * We have to update the import map for importMapLoader.js via message ports, since the\n * customization hooks run in a separate thread.\n * @param {ImportMap} importMap\n * @returns {Promise<{ error?: string }>} Empty object on success, or message on caught error.\n * It might also throw an exception if `postMessage()` fails somehow.\n */\nfunction updateImportMap(importMap) {\n return new Promise((resolve) => {\n // Before sending the message, add one-off handlers for all cases to be safe.\n // This is just sending an object and waiting for confirmation, so errors are very unlikely.\n const onMessage = (/** @type {UpdateImportMapResult} */ result) => {\n handlersOff();\n if (result === true) {\n resolve({});\n } else if (result === false) {\n // extremely unlikely\n resolve({ error: 'Failed to update import map before running serverEntry' });\n } else {\n // even less likely\n resolve({ error: `Unexpected message from import map loader: ${JSON.stringify(message)}` });\n }\n };\n\n parentPort.once('message', onMessage);\n const onError = (/** @type {*} */ error) => {\n handlersOff();\n resolve({ error: `Error updating import map before running serverEntry: ${error}` });\n };\n parentPort.once('error', onError);\n\n // This should happen almost instantly, so conservatively time out after 1s\n const timeout = setTimeout(() => {\n handlersOff();\n resolve({ error: 'Timed out waiting for import map update confirmation from loader' });\n }, 1000);\n\n const handlersOff = () => {\n parentPort.off('message', onMessage);\n parentPort.off('error', onError);\n clearTimeout(timeout);\n };\n\n const /** @type {UpdateImportMapMessage} */ message = { importMap };\n parentPort.postMessage(message);\n });\n}\n\n/**\n * Creates a proxy that throws an error when sub-properties of old non-SSR-friendly\n * `RenderFunctionOptions` props are accessed. (The error is only thrown on sub-property access\n * in case the top-level props are destructured but not actually used, and so that we don't need to\n * make the whole options object into a proxy since that could have unintended side effects.)\n * @param {string} propName The name of the `RenderFunctionOptions` property being accessed.\n * @returns {unknown} the proxy\n */\nfunction getInvalidPropProxy(propName) {\n return new Proxy(\n {},\n {\n get() {\n // This will be caught and logged by an error handler which adds in info about the\n // route and file the error came from\n throw new Error(\n `\"${propName}\" is not supported with the \"enableSSR\" feature. ` +\n 'Please see the migration guide at https://microsoft.github.io/cloudpack/reference/custom-rendering#migration, ' +\n 'or disable the feature for now.',\n );\n },\n },\n );\n}\n"]}