@zenithbuild/cli 0.7.3 → 0.7.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 (84) hide show
  1. package/README.md +18 -13
  2. package/dist/adapters/adapter-netlify.d.ts +1 -1
  3. package/dist/adapters/adapter-netlify.js +56 -13
  4. package/dist/adapters/adapter-node.js +8 -0
  5. package/dist/adapters/adapter-static-export.d.ts +5 -0
  6. package/dist/adapters/adapter-static-export.js +115 -0
  7. package/dist/adapters/adapter-types.d.ts +3 -1
  8. package/dist/adapters/adapter-types.js +5 -2
  9. package/dist/adapters/adapter-vercel.d.ts +1 -1
  10. package/dist/adapters/adapter-vercel.js +70 -13
  11. package/dist/adapters/copy-hosted-page-runtime.d.ts +1 -0
  12. package/dist/adapters/copy-hosted-page-runtime.js +49 -0
  13. package/dist/adapters/resolve-adapter.js +4 -0
  14. package/dist/adapters/route-rules.d.ts +5 -0
  15. package/dist/adapters/route-rules.js +9 -0
  16. package/dist/adapters/validate-hosted-resource-routes.d.ts +1 -0
  17. package/dist/adapters/validate-hosted-resource-routes.js +13 -0
  18. package/dist/auth/route-auth.d.ts +6 -0
  19. package/dist/auth/route-auth.js +236 -0
  20. package/dist/build/compiler-runtime.d.ts +10 -9
  21. package/dist/build/compiler-runtime.js +58 -2
  22. package/dist/build/compiler-signal-expression.d.ts +1 -0
  23. package/dist/build/compiler-signal-expression.js +155 -0
  24. package/dist/build/expression-rewrites.d.ts +1 -6
  25. package/dist/build/expression-rewrites.js +61 -65
  26. package/dist/build/page-component-loop.d.ts +3 -13
  27. package/dist/build/page-component-loop.js +21 -46
  28. package/dist/build/page-ir-normalization.d.ts +0 -8
  29. package/dist/build/page-ir-normalization.js +13 -234
  30. package/dist/build/page-loop-state.d.ts +6 -9
  31. package/dist/build/page-loop-state.js +9 -8
  32. package/dist/build/page-loop.js +27 -22
  33. package/dist/build/scoped-identifier-rewrite.d.ts +37 -44
  34. package/dist/build/scoped-identifier-rewrite.js +28 -128
  35. package/dist/build/server-script.d.ts +3 -1
  36. package/dist/build/server-script.js +35 -5
  37. package/dist/build-output-manifest.d.ts +3 -2
  38. package/dist/build-output-manifest.js +3 -0
  39. package/dist/build.js +32 -18
  40. package/dist/component-instance-ir.js +158 -52
  41. package/dist/dev-build-session.js +20 -6
  42. package/dist/dev-server.js +152 -55
  43. package/dist/download-result.d.ts +14 -0
  44. package/dist/download-result.js +148 -0
  45. package/dist/framework-components/Image.zen +1 -1
  46. package/dist/images/materialization-plan.d.ts +1 -0
  47. package/dist/images/materialization-plan.js +6 -0
  48. package/dist/images/materialize.d.ts +5 -3
  49. package/dist/images/materialize.js +24 -109
  50. package/dist/images/router-manifest.d.ts +1 -0
  51. package/dist/images/router-manifest.js +49 -0
  52. package/dist/images/service.d.ts +13 -1
  53. package/dist/images/service.js +45 -15
  54. package/dist/index.js +8 -2
  55. package/dist/manifest.d.ts +15 -1
  56. package/dist/manifest.js +27 -7
  57. package/dist/preview.d.ts +13 -4
  58. package/dist/preview.js +261 -101
  59. package/dist/request-body.d.ts +1 -0
  60. package/dist/request-body.js +7 -0
  61. package/dist/request-origin.d.ts +2 -0
  62. package/dist/request-origin.js +45 -0
  63. package/dist/resource-manifest.d.ts +16 -0
  64. package/dist/resource-manifest.js +53 -0
  65. package/dist/resource-response.d.ts +34 -0
  66. package/dist/resource-response.js +71 -0
  67. package/dist/resource-route-module.d.ts +15 -0
  68. package/dist/resource-route-module.js +129 -0
  69. package/dist/route-check-support.d.ts +1 -0
  70. package/dist/route-check-support.js +4 -0
  71. package/dist/server-contract.d.ts +29 -6
  72. package/dist/server-contract.js +304 -42
  73. package/dist/server-error.d.ts +4 -0
  74. package/dist/server-error.js +36 -0
  75. package/dist/server-output.d.ts +4 -1
  76. package/dist/server-output.js +71 -10
  77. package/dist/server-runtime/node-server.js +67 -31
  78. package/dist/server-runtime/route-render.d.ts +27 -3
  79. package/dist/server-runtime/route-render.js +94 -53
  80. package/dist/server-script-composition.d.ts +13 -5
  81. package/dist/server-script-composition.js +29 -11
  82. package/dist/static-export-paths.d.ts +3 -0
  83. package/dist/static-export-paths.js +160 -0
  84. package/package.json +6 -3
@@ -3,6 +3,7 @@ import { cp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
3
3
  import { createRequire } from 'node:module';
4
4
  import { basename, dirname, extname, join, relative, resolve } from 'node:path';
5
5
  import { fileURLToPath, pathToFileURL } from 'node:url';
6
+ import { loadResourceRouteManifest } from './resource-manifest.js';
6
7
  const PACKAGE_REQUIRE = createRequire(import.meta.url);
7
8
  const RELATIVE_SPECIFIER_RE = /((?:import|export)\s+(?:[^'"]*?\s+from\s+)?|import\s*\()\s*(['"])([^'"]+)\2/g;
8
9
  const SERVER_RUNTIME_FILES = [
@@ -14,6 +15,10 @@ const SERVER_RUNTIME_FILES = [
14
15
  from: new URL('./server-contract.js', import.meta.url),
15
16
  to: 'server-contract.js'
16
17
  },
18
+ {
19
+ from: new URL('./auth/route-auth.js', import.meta.url),
20
+ to: 'auth/route-auth.js'
21
+ },
17
22
  {
18
23
  from: new URL('./base-path.js', import.meta.url),
19
24
  to: 'base-path.js'
@@ -29,8 +34,32 @@ const SERVER_RUNTIME_FILES = [
29
34
  {
30
35
  from: new URL('./images/shared.js', import.meta.url),
31
36
  to: 'images/shared.js'
37
+ },
38
+ {
39
+ from: new URL('./images/runtime.js', import.meta.url),
40
+ to: 'images/runtime.js'
41
+ },
42
+ {
43
+ from: new URL('./images/service.js', import.meta.url),
44
+ to: 'images/service.js'
45
+ },
46
+ {
47
+ from: new URL('./server-error.js', import.meta.url),
48
+ to: 'server-error.js'
49
+ },
50
+ {
51
+ from: new URL('./resource-response.js', import.meta.url),
52
+ to: 'resource-response.js'
53
+ },
54
+ {
55
+ from: new URL('./download-result.js', import.meta.url),
56
+ to: 'download-result.js'
32
57
  }
33
58
  ];
59
+ const SPECIAL_SERVER_SPECIFIERS = new Map([
60
+ ['zenith:server-contract', 'server-contract.js'],
61
+ ['zenith:route-auth', 'auth/route-auth.js']
62
+ ]);
34
63
  function normalizeRouteName(routePath) {
35
64
  if (routePath === '/') {
36
65
  return 'index';
@@ -144,7 +173,7 @@ function outputPathForSource(projectRoot, modulesRoot, sourcePath) {
144
173
  : relativePath.replace(/\.(tsx|ts|mts|cts|jsx|js|mjs|cjs)$/i, '.js');
145
174
  return join(modulesRoot, nextRelative);
146
175
  }
147
- async function compileImportedModule({ projectRoot, modulesRoot, sourcePath, ts, seen }) {
176
+ async function compileImportedModule({ projectRoot, modulesRoot, serverDir, sourcePath, ts, seen }) {
148
177
  if (seen.has(sourcePath)) {
149
178
  return outputPathForSource(projectRoot, modulesRoot, sourcePath);
150
179
  }
@@ -158,6 +187,12 @@ async function compileImportedModule({ projectRoot, modulesRoot, sourcePath, ts,
158
187
  const source = await readFile(sourcePath, 'utf8');
159
188
  let output = transpileSource(ts, source, sourcePath);
160
189
  for (const specifier of gatherSpecifiers(output)) {
190
+ const specialSpecifierPath = SPECIAL_SERVER_SPECIFIERS.get(specifier);
191
+ if (specialSpecifierPath) {
192
+ const nextSpecifier = relative(dirname(outPath), join(serverDir, specialSpecifierPath)).replaceAll('\\', '/');
193
+ output = replaceSpecifier(output, specifier, nextSpecifier.startsWith('.') ? nextSpecifier : `./${nextSpecifier}`);
194
+ continue;
195
+ }
161
196
  if (!isRelativeSpecifier(specifier)) {
162
197
  continue;
163
198
  }
@@ -168,6 +203,7 @@ async function compileImportedModule({ projectRoot, modulesRoot, sourcePath, ts,
168
203
  const compiledDependencyPath = await compileImportedModule({
169
204
  projectRoot,
170
205
  modulesRoot,
206
+ serverDir,
171
207
  sourcePath: resolvedPath,
172
208
  ts,
173
209
  seen
@@ -178,12 +214,18 @@ async function compileImportedModule({ projectRoot, modulesRoot, sourcePath, ts,
178
214
  await writeFile(outPath, output, 'utf8');
179
215
  return outPath;
180
216
  }
181
- async function writeRouteModulePackage({ projectRoot, routeDir, route }) {
217
+ async function writeRouteModulePackage({ projectRoot, serverDir, routeDir, route }) {
182
218
  const ts = resolveTypeScriptApi(projectRoot);
183
219
  const modulesRoot = join(routeDir, 'modules');
184
220
  const seen = new Set();
185
221
  let entryOutput = transpileSource(ts, route.server_script || '', route.server_script_path || 'route-entry.ts');
186
222
  for (const specifier of gatherSpecifiers(entryOutput)) {
223
+ const specialSpecifierPath = SPECIAL_SERVER_SPECIFIERS.get(specifier);
224
+ if (specialSpecifierPath) {
225
+ const nextSpecifier = relative(join(routeDir, 'route'), join(serverDir, specialSpecifierPath)).replaceAll('\\', '/');
226
+ entryOutput = replaceSpecifier(entryOutput, specifier, nextSpecifier.startsWith('.') ? nextSpecifier : `./${nextSpecifier}`);
227
+ continue;
228
+ }
187
229
  if (!isRelativeSpecifier(specifier)) {
188
230
  continue;
189
231
  }
@@ -194,6 +236,7 @@ async function writeRouteModulePackage({ projectRoot, routeDir, route }) {
194
236
  const compiledDependencyPath = await compileImportedModule({
195
237
  projectRoot,
196
238
  modulesRoot,
239
+ serverDir,
197
240
  sourcePath: resolvedPath,
198
241
  ts,
199
242
  seen
@@ -230,8 +273,15 @@ export async function writeServerOutput({ coreOutputDir, staticDir, projectRoot,
230
273
  catch {
231
274
  routerManifest = { routes: [] };
232
275
  }
233
- const routes = Array.isArray(routerManifest.routes) ? routerManifest.routes : [];
234
- const serverRoutes = routes.filter((route) => route.server_script && route.prerender !== true);
276
+ const resourceManifest = await loadResourceRouteManifest(staticDir, basePath);
277
+ const pageRoutes = Array.isArray(routerManifest.routes) ? routerManifest.routes : [];
278
+ const serverRoutes = pageRoutes
279
+ .filter((route) => route.server_script && route.prerender !== true)
280
+ .map((route) => ({ ...route, route_kind: 'page' }))
281
+ .concat((Array.isArray(resourceManifest.routes) ? resourceManifest.routes : []).map((route) => ({
282
+ ...route,
283
+ route_kind: 'resource'
284
+ })));
235
285
  await mkdir(serverDir, { recursive: true });
236
286
  await copyRuntimeFiles(serverDir);
237
287
  const imageManifestSource = join(staticDir, '_zenith', 'image', 'manifest.json');
@@ -240,8 +290,10 @@ export async function writeServerOutput({ coreOutputDir, staticDir, projectRoot,
240
290
  const name = normalizeRouteName(route.path);
241
291
  const routeDir = join(serverDir, 'routes', name);
242
292
  await mkdir(routeDir, { recursive: true });
243
- const htmlSourcePath = join(staticDir, String(route.output || '').replace(/^\//, ''));
244
- await copyOptionalFile(htmlSourcePath, join(routeDir, 'route', 'page.html'));
293
+ if (route.route_kind !== 'resource') {
294
+ const htmlSourcePath = join(staticDir, String(route.output || '').replace(/^\//, ''));
295
+ await copyOptionalFile(htmlSourcePath, join(routeDir, 'route', 'page.html'));
296
+ }
245
297
  let pageAssetFile = null;
246
298
  if (typeof route.page_asset === 'string' && route.page_asset.length > 0) {
247
299
  const assetSourcePath = join(staticDir, route.page_asset.replace(/^\//, ''));
@@ -251,18 +303,20 @@ export async function writeServerOutput({ coreOutputDir, staticDir, projectRoot,
251
303
  }
252
304
  }
253
305
  let imageManifestFile = null;
254
- if (await copyOptionalFile(imageManifestSource, join(routeDir, 'route', 'image-manifest.json'))) {
306
+ if (route.route_kind !== 'resource' && await copyOptionalFile(imageManifestSource, join(routeDir, 'route', 'image-manifest.json'))) {
255
307
  imageManifestFile = 'image-manifest.json';
256
308
  }
257
309
  await writeRouteModulePackage({
258
310
  projectRoot,
311
+ serverDir,
259
312
  routeDir,
260
313
  route
261
314
  });
262
315
  const meta = {
263
316
  name,
264
317
  path: route.path,
265
- output: route.output,
318
+ route_kind: route.route_kind || 'page',
319
+ output: route.output || null,
266
320
  base_path: basePath,
267
321
  page_asset: route.page_asset || null,
268
322
  page_asset_file: pageAssetFile,
@@ -270,12 +324,19 @@ export async function writeServerOutput({ coreOutputDir, staticDir, projectRoot,
270
324
  server_script_path: route.server_script_path || null,
271
325
  guard_module_ref: route.guard_module_ref || null,
272
326
  load_module_ref: route.load_module_ref || null,
327
+ action_module_ref: route.action_module_ref || null,
273
328
  has_guard: route.has_guard === true,
274
329
  has_load: route.has_load === true,
275
- params: extractRouteParams(route.path),
276
- image_manifest_file: imageManifestFile,
330
+ has_action: route.has_action === true,
331
+ params: Array.isArray(route.params) && route.params.length > 0
332
+ ? [...route.params]
333
+ : extractRouteParams(route.path),
334
+ image_manifest_file: route.route_kind === 'resource' ? null : imageManifestFile,
277
335
  image_config: config?.images || {}
278
336
  };
337
+ if (route.route_kind !== 'resource' && Array.isArray(route.image_materialization) && route.image_materialization.length > 0) {
338
+ meta.image_materialization = route.image_materialization;
339
+ }
279
340
  await writeFile(join(routeDir, 'route.json'), `${JSON.stringify(meta, null, 2)}\n`, 'utf8');
280
341
  emittedRoutes.push(meta);
281
342
  }
@@ -5,7 +5,9 @@ import { dirname, extname, join, normalize, resolve, sep } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { appLocalRedirectLocation, imageEndpointPath, normalizeBasePath, routeCheckPath, stripBasePath } from '../base-path.js';
7
7
  import { handleImageRequest } from '../images/service.js';
8
- import { executeRouteRequest, renderRouteRequest } from './route-render.js';
8
+ import { createTrustedOriginResolver } from '../request-origin.js';
9
+ import { defaultRouteDenyMessage, logServerException, sanitizeRouteResult } from '../server-error.js';
10
+ import { executeRouteRequest, renderResourceRouteRequest, renderRouteRequest } from './route-render.js';
9
11
  import { resolveRequestRoute } from './resolve-request-route.js';
10
12
  const __filename = fileURLToPath(import.meta.url);
11
13
  const __dirname = dirname(__filename);
@@ -68,18 +70,6 @@ function toStaticFilePath(staticDir, pathname) {
68
70
  }
69
71
  return resolveWithinRoot(staticDir, resolvedPath);
70
72
  }
71
- function publicHost(host) {
72
- if (host === '0.0.0.0' || host === '::') {
73
- return '127.0.0.1';
74
- }
75
- return host;
76
- }
77
- function createRequestBase(req, fallbackOrigin) {
78
- if (typeof req.headers.host === 'string' && req.headers.host.length > 0) {
79
- return `http://${req.headers.host}`;
80
- }
81
- return fallbackOrigin;
82
- }
83
73
  async function createWebRequest(req, url) {
84
74
  const init = {
85
75
  method: req.method || 'GET',
@@ -107,9 +97,23 @@ async function createWebRequest(req, url) {
107
97
  }
108
98
  return new Request(url.toString(), init);
109
99
  }
100
+ function getSetCookieValues(response) {
101
+ if (typeof response?.headers?.getSetCookie === 'function') {
102
+ return response.headers.getSetCookie();
103
+ }
104
+ const value = response?.headers?.get?.('set-cookie');
105
+ return typeof value === 'string' && value.length > 0 ? [value] : [];
106
+ }
110
107
  async function sendFetchResponse(res, response, method) {
111
108
  res.statusCode = response.status;
109
+ const setCookies = getSetCookieValues(response);
110
+ if (setCookies.length > 0) {
111
+ res.setHeader('set-cookie', setCookies);
112
+ }
112
113
  for (const [key, value] of response.headers.entries()) {
114
+ if (key.toLowerCase() === 'set-cookie') {
115
+ continue;
116
+ }
113
117
  res.setHeader(key, value);
114
118
  }
115
119
  if (String(method || 'GET').toUpperCase() === 'HEAD') {
@@ -165,13 +169,16 @@ async function loadRuntimeContext(options = {}) {
165
169
  base_path: '/'
166
170
  });
167
171
  const serverManifest = await readJson(join(serverDir, 'manifest.json'), { routes: [] });
172
+ const allServerRoutes = Array.isArray(serverManifest.routes) ? serverManifest.routes : [];
168
173
  return {
169
174
  distDir,
170
175
  serverDir,
171
176
  staticDir: resolve(serverDir, config.static_dir || '../static'),
172
177
  buildManifest,
173
178
  buildRoutes: Array.isArray(buildManifest.routes) ? buildManifest.routes : [],
174
- serverRoutes: Array.isArray(serverManifest.routes) ? serverManifest.routes : [],
179
+ serverRoutes: allServerRoutes,
180
+ pageServerRoutes: allServerRoutes.filter((route) => route?.route_kind !== 'resource'),
181
+ resourceServerRoutes: allServerRoutes.filter((route) => route?.route_kind === 'resource'),
175
182
  images: config.images || {},
176
183
  basePath: normalizeBasePath(config.base_path || '/')
177
184
  };
@@ -216,7 +223,7 @@ async function handleRouteCheck(req, res, url, context) {
216
223
  }
217
224
  let result = { kind: 'allow' };
218
225
  let routeId = buildResolved.route.path || '';
219
- const serverResolved = resolveRequestRoute(canonicalTargetUrl, context.serverRoutes);
226
+ const serverResolved = resolveRequestRoute(canonicalTargetUrl, context.pageServerRoutes);
220
227
  if (serverResolved.matched && serverResolved.route) {
221
228
  routeId = serverResolved.route.route_id || serverResolved.route.name || serverResolved.route.path || routeId;
222
229
  try {
@@ -232,10 +239,11 @@ async function handleRouteCheck(req, res, url, context) {
232
239
  result = normalizeRouteCheckResult(execution.result, targetUrl, context.basePath);
233
240
  }
234
241
  catch (error) {
242
+ logServerException('node route-check failed', error);
235
243
  result = {
236
244
  kind: 'deny',
237
245
  status: 500,
238
- message: String(error)
246
+ message: defaultRouteDenyMessage(500)
239
247
  };
240
248
  }
241
249
  }
@@ -247,13 +255,13 @@ async function handleRouteCheck(req, res, url, context) {
247
255
  Vary: 'Cookie'
248
256
  });
249
257
  res.end(JSON.stringify({
250
- result,
258
+ result: sanitizeRouteResult(result),
251
259
  routeId,
252
260
  to: targetUrl.toString()
253
261
  }));
254
262
  }
255
263
  async function handleNodeRequest(req, res, context, serverOrigin) {
256
- const url = new URL(req.url || '/', createRequestBase(req, serverOrigin));
264
+ const url = new URL(req.url || '/', serverOrigin);
257
265
  const canonicalPath = stripBasePath(url.pathname, context.basePath);
258
266
  if (url.pathname === routeCheckPath(context.basePath)) {
259
267
  await handleRouteCheck(req, res, url, context);
@@ -284,7 +292,20 @@ async function handleNodeRequest(req, res, context, serverOrigin) {
284
292
  await sendStaticFile(res, assetPath, req.method);
285
293
  return;
286
294
  }
287
- const serverResolved = resolveRequestRoute(canonicalUrl, context.serverRoutes);
295
+ const resourceResolved = resolveRequestRoute(canonicalUrl, context.resourceServerRoutes);
296
+ if (resourceResolved.matched && resourceResolved.route) {
297
+ const routeDir = join(context.serverDir, 'routes', resourceResolved.route.name);
298
+ const request = await createWebRequest(req, url);
299
+ const response = await renderResourceRouteRequest({
300
+ request,
301
+ route: resourceResolved.route,
302
+ params: resourceResolved.params,
303
+ routeModulePath: join(routeDir, 'route', 'entry.js')
304
+ });
305
+ await sendFetchResponse(res, response, req.method);
306
+ return;
307
+ }
308
+ const serverResolved = resolveRequestRoute(canonicalUrl, context.pageServerRoutes);
288
309
  if (serverResolved.matched && serverResolved.route) {
289
310
  const routeDir = join(context.serverDir, 'routes', serverResolved.route.name);
290
311
  const request = await createWebRequest(req, url);
@@ -294,9 +315,6 @@ async function handleNodeRequest(req, res, context, serverOrigin) {
294
315
  params: serverResolved.params,
295
316
  routeModulePath: join(routeDir, 'route', 'entry.js'),
296
317
  shellHtmlPath: join(routeDir, 'route', 'page.html'),
297
- pageAssetPath: serverResolved.route.page_asset_file
298
- ? join(routeDir, 'route', serverResolved.route.page_asset_file)
299
- : null,
300
318
  imageManifestPath: serverResolved.route.image_manifest_file
301
319
  ? join(routeDir, 'route', serverResolved.route.image_manifest_file)
302
320
  : null,
@@ -321,32 +339,50 @@ async function handleNodeRequest(req, res, context, serverOrigin) {
321
339
  res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
322
340
  res.end('404 Not Found');
323
341
  }
324
- export async function createRequestHandler(options = {}) {
325
- const context = await loadRuntimeContext(options);
326
- const host = publicHost(options.host || '127.0.0.1');
327
- const port = Number.isInteger(options.port) ? options.port : 3000;
328
- const serverOrigin = `http://${host}:${port}`;
342
+ function createNodeRequestHandler(context, resolveServerOrigin) {
329
343
  return async (req, res) => {
330
344
  try {
331
- await handleNodeRequest(req, res, context, serverOrigin);
345
+ await handleNodeRequest(req, res, context, resolveServerOrigin());
332
346
  }
333
347
  catch (error) {
348
+ logServerException('node request handler failed', error);
334
349
  res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
335
- res.end(String(error));
350
+ res.end(defaultRouteDenyMessage(500));
336
351
  }
337
352
  };
338
353
  }
354
+ export async function createRequestHandler(options = {}) {
355
+ const context = await loadRuntimeContext(options);
356
+ const resolveServerOrigin = createTrustedOriginResolver({
357
+ publicOrigin: options.publicOrigin,
358
+ host: options.host || '127.0.0.1',
359
+ port: Number.isInteger(options.port) ? options.port : undefined,
360
+ label: 'createRequestHandler()'
361
+ });
362
+ resolveServerOrigin();
363
+ return createNodeRequestHandler(context, resolveServerOrigin);
364
+ }
339
365
  export async function createNodeServer(options = {}) {
340
366
  const { port = 3000, host = '127.0.0.1' } = options;
341
- const handler = await createRequestHandler({ ...options, port, host });
367
+ const context = await loadRuntimeContext(options);
368
+ let actualPort = Number.isInteger(port) && port > 0 ? port : 0;
369
+ const resolveServerOrigin = createTrustedOriginResolver({
370
+ publicOrigin: options.publicOrigin,
371
+ host,
372
+ getPort: () => actualPort,
373
+ label: 'createNodeServer()'
374
+ });
375
+ const handler = createNodeRequestHandler(context, resolveServerOrigin);
342
376
  const server = createServer((req, res) => {
343
377
  void handler(req, res);
344
378
  });
345
379
  return new Promise((resolveServer) => {
346
380
  server.listen(port, host, () => {
381
+ const address = server.address();
382
+ actualPort = address && typeof address === 'object' ? address.port : port;
347
383
  resolveServer({
348
384
  server,
349
- port: server.address().port,
385
+ port: actualPort,
350
386
  close: () => server.close()
351
387
  });
352
388
  });
@@ -8,7 +8,7 @@ export function extractInternalParams(requestUrl: any, route: any): {};
8
8
  * routeModulePath: string,
9
9
  * guardOnly?: boolean
10
10
  * }} options
11
- * @returns {Promise<{ publicUrl: URL, result: { kind: string, [key: string]: unknown }, trace: { guard: string, load: string } }>}
11
+ * @returns {Promise<{ publicUrl: URL, result: { kind: string, [key: string]: unknown }, trace: { guard: string, action: string, load: string }, status?: number, setCookies?: string[] }>}
12
12
  */
13
13
  export function executeRouteRequest(options: {
14
14
  request: Request;
@@ -30,8 +30,11 @@ export function executeRouteRequest(options: {
30
30
  };
31
31
  trace: {
32
32
  guard: string;
33
+ action: string;
33
34
  load: string;
34
35
  };
36
+ status?: number;
37
+ setCookies?: string[];
35
38
  }>;
36
39
  /**
37
40
  * @param {{
@@ -40,7 +43,6 @@ export function executeRouteRequest(options: {
40
43
  * params: Record<string, string>,
41
44
  * routeModulePath: string,
42
45
  * shellHtmlPath: string,
43
- * pageAssetPath?: string | null,
44
46
  * imageManifestPath?: string | null,
45
47
  * imageConfig?: Record<string, unknown>
46
48
  * }} options
@@ -58,7 +60,29 @@ export function renderRouteRequest(options: {
58
60
  params: Record<string, string>;
59
61
  routeModulePath: string;
60
62
  shellHtmlPath: string;
61
- pageAssetPath?: string | null;
62
63
  imageManifestPath?: string | null;
63
64
  imageConfig?: Record<string, unknown>;
64
65
  }): Promise<Response>;
66
+ /**
67
+ * @param {{
68
+ * request: Request,
69
+ * route: { path: string, params?: string[], route_id?: string | null, server_script_path?: string | null, file?: string | null, route_kind?: string | null, base_path?: string | null },
70
+ * params: Record<string, string>,
71
+ * routeModulePath: string
72
+ * }} options
73
+ * @returns {Promise<Response>}
74
+ */
75
+ export function renderResourceRouteRequest(options: {
76
+ request: Request;
77
+ route: {
78
+ path: string;
79
+ params?: string[];
80
+ route_id?: string | null;
81
+ server_script_path?: string | null;
82
+ file?: string | null;
83
+ route_kind?: string | null;
84
+ base_path?: string | null;
85
+ };
86
+ params: Record<string, string>;
87
+ routeModulePath: string;
88
+ }): Promise<Response>;