@vesk/vesk-cli 0.2.69 → 0.2.71

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 (2) hide show
  1. package/dist/cli.js +268 -106
  2. package/package.json +5 -5
package/dist/cli.js CHANGED
@@ -49909,11 +49909,26 @@ function resolveCssUrls(opts) {
49909
49909
 
49910
49910
  // ../adapter/src/ssr-function.ts
49911
49911
  init_default_pages();
49912
- function resolveErrorFile(sourceDir, appDir) {
49912
+ function resolveBoundaryFile(sourceDir, appDir, file) {
49913
49913
  const rel = relative(appDir, sourceDir).split("/").filter(Boolean);
49914
49914
  for (let depth = rel.length; depth >= 0; depth--) {
49915
49915
  const dir = depth === 0 ? appDir : join5(appDir, ...rel.slice(0, depth));
49916
- const p = join5(dir, "error.vsk");
49916
+ const p = join5(dir, file);
49917
+ if (existsSync8(p)) return p;
49918
+ }
49919
+ return null;
49920
+ }
49921
+ function resolveErrorFile(sourceDir, appDir) {
49922
+ return resolveBoundaryFile(sourceDir, appDir, "error.vsk");
49923
+ }
49924
+ function resolveNotFoundFile(sourceDir, appDir) {
49925
+ return resolveBoundaryFile(sourceDir, appDir, "not-found.vsk");
49926
+ }
49927
+ function resolveBoundaryFileForUrl(pathname, appDir, file) {
49928
+ const segments = pathname.split("/").filter(Boolean);
49929
+ for (let depth = segments.length; depth >= 0; depth--) {
49930
+ const dir = join5(appDir, ...segments.slice(0, depth));
49931
+ const p = join5(dir, file);
49917
49932
  if (existsSync8(p)) return p;
49918
49933
  }
49919
49934
  return null;
@@ -50053,6 +50068,12 @@ function generateSsrFunction(routeNode, appDir, outDir, componentMap, options) {
50053
50068
  const errorVars = errorPath ? `const _errorComp = ${JSON.stringify(errorComp)};
50054
50069
  const _errorCompiled = ${hydrateInvoke(precompilePlan(errorPath))};
50055
50070
  ` : "const _errorComp = null;\nconst _errorCompiled = null;\n";
50071
+ const notFoundPath = resolveNotFoundFile(routeNode.sourceDir, appDir);
50072
+ const notFoundSrc = notFoundPath ? readFileSync7(notFoundPath, "utf-8") : null;
50073
+ const notFoundComp = notFoundPath ? extractCompName(notFoundSrc) || "NotFound" : null;
50074
+ const notFoundVars = notFoundPath ? `const _notFoundComp = ${JSON.stringify(notFoundComp)};
50075
+ const _notFoundCompiled = ${hydrateInvoke(precompilePlan(notFoundPath))};
50076
+ ` : "const _notFoundComp = null;\nconst _notFoundCompiled = null;\n";
50056
50077
  let src2 = "";
50057
50078
  if (layoutDecls.length > 0) {
50058
50079
  src2 = `const _pageComp = ${JSON.stringify(pageComp)};
@@ -50065,13 +50086,13 @@ const _errorCompiled = ${hydrateInvoke(precompilePlan(errorPath))};
50065
50086
  ${layoutDecls.map((d) => " " + d.compiled + ",").join("\n")}
50066
50087
  ];
50067
50088
  `;
50068
- src2 += errorVars;
50089
+ src2 += errorVars + notFoundVars;
50069
50090
  } else {
50070
50091
  src2 = `const _comp = ${JSON.stringify(pageComp)};
50071
50092
  `;
50072
50093
  src2 += `const _compiled = (() => { try { return hydratePrecompile(${pagePlan}); } catch { return null; } })();
50073
50094
  `;
50074
- src2 += errorVars;
50095
+ src2 += errorVars + notFoundVars;
50075
50096
  }
50076
50097
  const urlParts = routeNode.fullPath.split("/").filter(Boolean);
50077
50098
  const paramExprs = buildParamExtraction(routeNode, urlParts);
@@ -50113,30 +50134,66 @@ ${layoutDecls.map((d) => " " + d.compiled + ",").join("\n")}
50113
50134
  " }",
50114
50135
  "}",
50115
50136
  "",
50137
+ // Returns null rather than throwing when there is no boundary, so the
50138
+ // caller can re-throw the original NotFoundError and let the server answer
50139
+ // — a page with no not-found.vsk anywhere must still get a 404.
50140
+ "async function __renderNotFoundBody(props) {",
50141
+ " if (!_notFoundComp) return null;",
50142
+ " try {",
50143
+ " const result = await renderPage('', _notFoundComp, props, __componentRegistry, { hydrate: true, cached: _notFoundCompiled });",
50144
+ " return result.body;",
50145
+ " } catch {",
50146
+ " return null;",
50147
+ " }",
50148
+ "}",
50149
+ "",
50150
+ // The layout wrap, shared by the page / error / not-found paths. Extracted
50151
+ // because three call sites that each re-implemented it is three chances to
50152
+ // forget the head merge.
50153
+ "async function __wrapLayouts(params, body, head) {",
50154
+ " let _b = body;",
50155
+ ' let _h = head || "";',
50156
+ " for (let _i = _layoutCompList.length - 1; _i > 0; _i--) {",
50157
+ " const _inner = await renderPage('', _layoutCompList[_i], { params, children: _b }, __componentRegistry, { hydrate: true, cached: _layoutCompiledList[_i] });",
50158
+ " _b = _inner.body;",
50159
+ " if (_inner.head) _h = _inner.head + _h;",
50160
+ " }",
50161
+ " return renderFullPage('', _layoutCompList[0], { params, children: _b }, __componentRegistry, { hydrate: true, cached: _layoutCompiledList[0]" + bakedOptions + clientScriptOption + dataScriptOption + ", pageHead: _h });",
50162
+ "}",
50163
+ "",
50116
50164
  "async function __renderHtml(params, requestUrl) {",
50117
50165
  " return withSsrStore(async () => {",
50118
50166
  " resetPersistedStoreKeys();",
50119
50167
  " let page;",
50120
50168
  " let caughtError = null;",
50169
+ " let notFound = false;",
50121
50170
  " const __expose = process.env.NODE_ENV !== 'production';",
50122
50171
  " try {",
50123
50172
  " page = await renderPage('', _pageComp, { params }, __componentRegistry, { hydrate: true, cached: _pageCompiled });",
50124
50173
  " } catch (err) {",
50125
- " if (err && (err.name === 'NotFoundError' || err.name === 'Redirect')) throw err;",
50126
- " caughtError = err;",
50127
- " const message = err && typeof err === 'object' && 'message' in err ? String(err.message) : String(err);",
50128
- " const stack = err && typeof err === 'object' && 'stack' in err ? String(err.stack) : '';",
50129
- " page = { body: await __renderErrorBody({ params, statusCode: 500, error: __expose ? message : 'Internal Server Error', stack: __expose ? stack : '', url: requestUrl || '' }), head: '' };",
50174
+ " if (err && err.name === 'Redirect') throw err;",
50175
+ " if (err && err.name === 'NotFoundError') {",
50176
+ // The boundary is chosen per-route at build time (resolveNotFoundFile walks
50177
+ // this route's own directory up to the app root), so a nested
50178
+ // app/admin/not-found.vsk answers /admin/* and the root one answers the
50179
+ // rest — matching the client router's deepest-first chain walk.
50180
+ " const nfBody = await __renderNotFoundBody({ params, url: requestUrl || '' });",
50181
+ " if (nfBody !== null) {",
50182
+ " notFound = true;",
50183
+ " page = { body: nfBody, head: '' };",
50184
+ " } else {",
50185
+ " throw err;",
50186
+ " }",
50187
+ " } else {",
50188
+ " caughtError = err;",
50189
+ " const message = err && typeof err === 'object' && 'message' in err ? String(err.message) : String(err);",
50190
+ " const stack = err && typeof err === 'object' && 'stack' in err ? String(err.stack) : '';",
50191
+ " page = { body: await __renderErrorBody({ params, statusCode: 500, error: __expose ? message : 'Internal Server Error', stack: __expose ? stack : '', url: requestUrl || '' }), head: '' };",
50192
+ " }",
50130
50193
  " }",
50131
50194
  " let _body = (caughtError ? '<!--vesk-ssr-error:' + (caughtError && typeof caughtError === 'object' && 'message' in caughtError ? encodeURIComponent(__expose ? String(caughtError.message) : 'Internal Server Error') : '') + '-->' : '') + page.body;",
50132
- " let _head = page.head || '';",
50133
- " for (let _i = _layoutCompList.length - 1; _i > 0; _i--) {",
50134
- " const _inner = await renderPage('', _layoutCompList[_i], { params, children: _body }, __componentRegistry, { hydrate: true, cached: _layoutCompiledList[_i] });",
50135
- " _body = _inner.body;",
50136
- " if (_inner.head) _head = _inner.head + _head;",
50137
- " }",
50138
- " const html = await renderFullPage('', _layoutCompList[0], { params, children: _body }, __componentRegistry, { hydrate: true, cached: _layoutCompiledList[0]" + bakedOptions + clientScriptOption + dataScriptOption + ", pageHead: _head });",
50139
- " return new Response(__injectStoreInit(html), { headers: { 'Content-Type': 'text/html' }, status: caughtError ? 500 : 200 });",
50195
+ " const html = await __wrapLayouts(params, _body, page.head || '');",
50196
+ " return new Response(__injectStoreInit(html), { headers: { 'Content-Type': 'text/html' }, status: notFound ? 404 : (caughtError ? 500 : 200) });",
50140
50197
  " });",
50141
50198
  "}",
50142
50199
  ""
@@ -52505,6 +52562,7 @@ function generateManifest(routes, ssrRoutes, apiRoutes, staticRoutes, middleware
52505
52562
  if (r._isrTags) entry.tags = r._isrTags;
52506
52563
  routeEntries.push(entry);
52507
52564
  }
52565
+ routeEntries.sort((a, b) => compareSpecificity(a.path, b.path));
52508
52566
  const apiEntries = [];
52509
52567
  for (const r of apiRoutes) {
52510
52568
  const urlParts = r.fullPath.split("/").filter(Boolean);
@@ -52952,6 +53010,142 @@ function installResponsePipeline(res, req, options = {}) {
52952
53010
  });
52953
53011
  }
52954
53012
 
53013
+ // ../adapter/src/web-request.ts
53014
+ import { Buffer as Buffer2 } from "node:buffer";
53015
+ function bodyTooLarge(maxBytes) {
53016
+ const err = new Error(`Request body exceeds limit (${maxBytes} bytes)`);
53017
+ err.status = 413;
53018
+ return err;
53019
+ }
53020
+ function makeWebRequest(nodeReq, url, maxBodyBytes = DEFAULT_MAX_BODY_BYTES) {
53021
+ const parsedUrl = new URL(url, `http://${nodeReq.headers.host || "localhost"}`);
53022
+ const method = nodeReq.method || "GET";
53023
+ let cachedBody = null;
53024
+ let streamTaken = false;
53025
+ let streamEnded = false;
53026
+ const endWaiters = [];
53027
+ const settleEnd = (body) => {
53028
+ if (body !== null) cachedBody = body;
53029
+ streamEnded = true;
53030
+ for (const resolve41 of endWaiters.splice(0)) resolve41();
53031
+ };
53032
+ function createBodyStream() {
53033
+ streamTaken = true;
53034
+ const cache2 = [];
53035
+ let total = 0;
53036
+ return new ReadableStream({
53037
+ start(controller) {
53038
+ nodeReq.on("data", (chunk) => {
53039
+ total += chunk.length;
53040
+ if (total > maxBodyBytes) {
53041
+ settleEnd(Buffer2.concat(cache2));
53042
+ cache2.length = 0;
53043
+ try {
53044
+ controller.error(bodyTooLarge(maxBodyBytes));
53045
+ } catch {
53046
+ }
53047
+ nodeReq.destroy();
53048
+ return;
53049
+ }
53050
+ cache2.push(chunk);
53051
+ try {
53052
+ controller.enqueue(new Uint8Array(chunk));
53053
+ } catch {
53054
+ }
53055
+ });
53056
+ nodeReq.on("end", () => {
53057
+ settleEnd(Buffer2.concat(cache2));
53058
+ cache2.length = 0;
53059
+ try {
53060
+ controller.close();
53061
+ } catch {
53062
+ }
53063
+ });
53064
+ nodeReq.on("error", (e) => {
53065
+ settleEnd(Buffer2.concat(cache2));
53066
+ cache2.length = 0;
53067
+ try {
53068
+ controller.error(e);
53069
+ } catch {
53070
+ }
53071
+ });
53072
+ }
53073
+ });
53074
+ }
53075
+ async function getBody() {
53076
+ if (cachedBody) return cachedBody;
53077
+ if (streamTaken) {
53078
+ while (!streamEnded) await new Promise((r) => endWaiters.push(r));
53079
+ return cachedBody || Buffer2.alloc(0);
53080
+ }
53081
+ const chunks = [];
53082
+ let total = 0;
53083
+ for await (const chunk of nodeReq) {
53084
+ total += chunk.byteLength;
53085
+ if (total > maxBodyBytes) throw bodyTooLarge(maxBodyBytes);
53086
+ chunks.push(Buffer2.from(chunk));
53087
+ }
53088
+ cachedBody = Buffer2.concat(chunks);
53089
+ return cachedBody;
53090
+ }
53091
+ const webRequest = new Request(parsedUrl, {
53092
+ method,
53093
+ headers: nodeReq.headers,
53094
+ body: null
53095
+ });
53096
+ let bodyStream = null;
53097
+ Object.defineProperty(webRequest, "body", {
53098
+ configurable: true,
53099
+ enumerable: false,
53100
+ get: () => {
53101
+ if (bodyStream) return bodyStream;
53102
+ if (method === "GET" || method === "HEAD") return null;
53103
+ bodyStream = createBodyStream();
53104
+ return bodyStream;
53105
+ }
53106
+ });
53107
+ webRequest.json = async () => {
53108
+ try {
53109
+ return JSON.parse((await getBody()).toString());
53110
+ } catch {
53111
+ return null;
53112
+ }
53113
+ };
53114
+ webRequest.text = async () => (await getBody()).toString("utf-8");
53115
+ webRequest.formData = async () => {
53116
+ const body = await getBody();
53117
+ const ct = String(nodeReq.headers["content-type"] || "");
53118
+ if (ct.includes("multipart/form-data")) {
53119
+ const temp = new Request("http://localhost", { method: "POST", headers: nodeReq.headers, body });
53120
+ return temp.formData();
53121
+ }
53122
+ const fd = new FormData();
53123
+ if (ct.includes("x-www-form-urlencoded")) {
53124
+ for (const [k, v] of new URLSearchParams(body.toString()).entries()) fd.append(k, v);
53125
+ }
53126
+ return fd;
53127
+ };
53128
+ webRequest.clone = () => webRequest;
53129
+ const cookies2 = {};
53130
+ for (const part of String(nodeReq.headers.cookie || "").split(";")) {
53131
+ const eq = part.indexOf("=");
53132
+ if (eq === -1) continue;
53133
+ const name = part.slice(0, eq).trim();
53134
+ if (!name) continue;
53135
+ const raw2 = part.slice(eq + 1).trim();
53136
+ try {
53137
+ cookies2[name] = decodeURIComponent(raw2);
53138
+ } catch {
53139
+ cookies2[name] = raw2;
53140
+ }
53141
+ }
53142
+ webRequest.cookies = cookies2;
53143
+ const query = {};
53144
+ for (const [k, v] of parsedUrl.searchParams.entries()) query[k] = v;
53145
+ webRequest.query = query;
53146
+ return webRequest;
53147
+ }
53148
+
52955
53149
  // ../adapter/src/prod-server.ts
52956
53150
  init_platform_output();
52957
53151
  var __dirname4 = dirname11(fileURLToPath3(import.meta.url));
@@ -53009,65 +53203,10 @@ function pumpRemaining(res, reader) {
53009
53203
  res.end();
53010
53204
  })();
53011
53205
  }
53012
- function bodyTooLarge(maxBytes) {
53013
- const err = new Error(`Request body exceeds limit (${maxBytes} bytes)`);
53014
- err.status = 413;
53015
- return err;
53016
- }
53017
53206
  function errorStatus(e, fallback) {
53018
53207
  const s = e?.status ?? e?.statusCode;
53019
53208
  return typeof s === "number" && s >= 400 && s < 600 ? s : fallback;
53020
53209
  }
53021
- function makeWebRequest(nodeReq, url, maxBodyBytes = DEFAULT_MAX_BODY_BYTES) {
53022
- const parsedUrl = new URL(url, `http://${nodeReq.headers.host || "localhost"}`);
53023
- const method = nodeReq.method || "GET";
53024
- let _bodyBuffer = null;
53025
- async function getBody() {
53026
- if (_bodyBuffer) return _bodyBuffer;
53027
- const chunks = [];
53028
- let total = 0;
53029
- for await (const chunk of nodeReq) {
53030
- total += chunk.byteLength;
53031
- if (total > maxBodyBytes) throw bodyTooLarge(maxBodyBytes);
53032
- chunks.push(Buffer.from(chunk));
53033
- }
53034
- _bodyBuffer = Buffer.concat(chunks);
53035
- return _bodyBuffer;
53036
- }
53037
- const webRequest = new Request(parsedUrl, { method, headers: nodeReq.headers, body: null });
53038
- webRequest.json = async () => {
53039
- try {
53040
- return JSON.parse((await getBody()).toString());
53041
- } catch {
53042
- return null;
53043
- }
53044
- };
53045
- webRequest.text = async () => (await getBody()).toString("utf-8");
53046
- webRequest.formData = async () => {
53047
- const body = await getBody();
53048
- const ct = String(nodeReq.headers["content-type"] || "");
53049
- if (ct.includes("multipart/form-data")) {
53050
- const temp = new Request("http://localhost", { method: "POST", headers: nodeReq.headers, body });
53051
- return temp.formData();
53052
- }
53053
- const fd = new FormData();
53054
- if (ct.includes("x-www-form-urlencoded")) {
53055
- for (const [k, v] of new URLSearchParams(body.toString()).entries()) fd.append(k, v);
53056
- }
53057
- return fd;
53058
- };
53059
- webRequest.clone = () => webRequest;
53060
- const cookies2 = {};
53061
- for (const [k, v] of String(nodeReq.headers.cookie || "").split(";").map((s) => s.trim()).filter(Boolean)) {
53062
- const eq = k.indexOf("=");
53063
- if (eq > -1) cookies2[k.slice(0, eq)] = decodeURIComponent(k.slice(eq + 1));
53064
- }
53065
- webRequest.cookies = cookies2;
53066
- const query = {};
53067
- for (const [k, v] of parsedUrl.searchParams.entries()) query[k] = v;
53068
- webRequest.query = query;
53069
- return webRequest;
53070
- }
53071
53210
  async function startProdServer(outDir, options) {
53072
53211
  const port = options?.port || 3e3;
53073
53212
  const host = options?.host || "127.0.0.1";
@@ -53138,15 +53277,22 @@ async function startProdServer(outDir, options) {
53138
53277
  }
53139
53278
  }
53140
53279
  const functionCache = /* @__PURE__ */ new Map();
53280
+ const functionFailures = /* @__PURE__ */ new Map();
53281
+ function functionFailure(funcPath) {
53282
+ return functionFailures.get(funcPath);
53283
+ }
53141
53284
  async function loadFunction(funcPath) {
53142
53285
  if (functionCache.has(funcPath)) return functionCache.get(funcPath);
53286
+ if (functionFailures.has(funcPath)) return null;
53143
53287
  const fullPath = resolve14(outDir, funcPath);
53144
53288
  if (!existsSync16(fullPath)) return null;
53145
53289
  try {
53146
53290
  const mod = await import(`${fullPath}?t=${Date.now()}`);
53147
53291
  functionCache.set(funcPath, mod);
53148
53292
  return mod;
53149
- } catch {
53293
+ } catch (e) {
53294
+ console.error(`vesk start: failed to load ${funcPath}:`, e instanceof Error ? e.message : e);
53295
+ functionFailures.set(funcPath, e);
53150
53296
  return null;
53151
53297
  }
53152
53298
  }
@@ -53409,18 +53555,24 @@ async function startProdServer(outDir, options) {
53409
53555
  const params = matchPath(route.path, url.pathname);
53410
53556
  if (params) {
53411
53557
  const mod = await loadFunction(route.function);
53412
- if (mod) {
53413
- try {
53414
- const webRequest = makeWebRequest(req, url.href, maxBodyBytes);
53415
- const response = await withSecurityScope(() => mod.handle(webRequest));
53416
- await deliverResponse(res, response);
53417
- return;
53418
- } catch (e) {
53419
- const status = errorStatus(e, 500);
53420
- res.writeHead(status, { "Content-Type": "application/json" });
53421
- res.end(JSON.stringify({ error: status === 500 ? "Internal Server Error" : e instanceof Error ? e.message : String(e) }));
53558
+ if (!mod) {
53559
+ if (functionFailure(route.function)) {
53560
+ res.writeHead(500, { "Content-Type": "application/json" });
53561
+ res.end(JSON.stringify({ error: "Internal Server Error" }));
53422
53562
  return;
53423
53563
  }
53564
+ continue;
53565
+ }
53566
+ try {
53567
+ const webRequest = makeWebRequest(req, url.href, maxBodyBytes);
53568
+ const response = await withSecurityScope(() => mod.handle(webRequest));
53569
+ await deliverResponse(res, response);
53570
+ return;
53571
+ } catch (e) {
53572
+ const status = errorStatus(e, 500);
53573
+ res.writeHead(status, { "Content-Type": "application/json" });
53574
+ res.end(JSON.stringify({ error: status === 500 ? "Internal Server Error" : e instanceof Error ? e.message : String(e) }));
53575
+ return;
53424
53576
  }
53425
53577
  }
53426
53578
  }
@@ -53430,17 +53582,29 @@ async function startProdServer(outDir, options) {
53430
53582
  const pageCssUrls = resolveCssUrls({
53431
53583
  enabled: hasBuiltGlobalCss(outDir)
53432
53584
  });
53433
- const nfPath = resolve14(appDir, "not-found.vsk");
53434
- let notFoundHtml = null;
53435
- if (existsSync16(nfPath)) {
53436
- try {
53437
- const runtimePath = resolve14(outDir, "server", "runtime.js");
53438
- const { renderFullPage: renderFullPage2, storeDataScriptGlobal } = await import(runtimePath);
53439
- const src2 = readFileSync18(nfPath, "utf-8");
53440
- const compName = resolveComponentName(src2) || "NotFound";
53441
- notFoundHtml = await renderFullPage2(src2, compName, { params: {}, url: url.pathname }, /* @__PURE__ */ new Map(), { hydrate: true, cssUrls: pageCssUrls, security: securityConfig?.security || {}, externalDataScript: storeDataScriptGlobal, sourcePath: nfPath, headExtra: bakedHeadExtra });
53442
- } catch {
53585
+ const boundaryCache = /* @__PURE__ */ new Map();
53586
+ async function renderBoundaryFile(file, pathname, props) {
53587
+ const boundaryPath = resolveBoundaryFileForUrl(pathname, appDir, file);
53588
+ if (!boundaryPath) return null;
53589
+ const key = `${file}:${boundaryPath}`;
53590
+ let html = boundaryCache.has(key) ? boundaryCache.get(key) : null;
53591
+ if (html === null) {
53592
+ try {
53593
+ const runtimePath = resolve14(outDir, "server", "runtime.js");
53594
+ const { renderFullPage: renderFullPage2, storeDataScriptGlobal } = await import(runtimePath);
53595
+ const src2 = readFileSync18(boundaryPath, "utf-8");
53596
+ const compName = resolveComponentName(src2) || (file === "error.vsk" ? "Error" : "NotFound");
53597
+ html = await renderFullPage2(src2, compName, props, /* @__PURE__ */ new Map(), { hydrate: true, cssUrls: pageCssUrls, clientScriptUrl: buildConfig?.assets?.client?.url || "/_vesk/static/client.js", clientScriptIntegrity: buildConfig?.assets?.client?.integrity, security: securityConfig?.security || {}, externalDataScript: storeDataScriptGlobal, sourcePath: boundaryPath, headExtra: bakedHeadExtra });
53598
+ } catch {
53599
+ return null;
53600
+ }
53443
53601
  }
53602
+ return html;
53603
+ }
53604
+ let notFoundHtml = null;
53605
+ {
53606
+ const rendered = await renderBoundaryFile("not-found.vsk", url.pathname, { params: {}, url: url.pathname });
53607
+ notFoundHtml = rendered;
53444
53608
  }
53445
53609
  for (const route of buildConfig.routes) {
53446
53610
  if (route.type === "ssr") {
@@ -53510,19 +53674,13 @@ async function startProdServer(outDir, options) {
53510
53674
  if (eventsMod) {
53511
53675
  await executeError(err, { url: url.href, routePath: route.path, target: "node", status: 500 }, eventsMod);
53512
53676
  }
53513
- const errPath = resolve14(appDir, "error.vsk");
53514
- let errorHtml = null;
53515
- if (existsSync16(errPath)) {
53516
- try {
53517
- const runtimePath = resolve14(outDir, "server", "runtime.js");
53518
- const { renderFullPage: renderFullPage2, storeDataScriptGlobal } = await import(runtimePath);
53519
- const src2 = readFileSync18(errPath, "utf-8");
53520
- const compName = resolveComponentName(src2) || "Error";
53521
- const expose = process.env.NODE_ENV !== "production";
53522
- errorHtml = await renderFullPage2(src2, compName, { error: expose ? err.message : "Internal Server Error", stack: expose ? err.stack : "", statusCode: 500, url: url.pathname }, /* @__PURE__ */ new Map(), { hydrate: true, cssUrls: pageCssUrls, clientScriptUrl: buildConfig?.assets?.client?.url || "/_vesk/static/client.js", clientScriptIntegrity: buildConfig?.assets?.client?.integrity, security: securityConfig?.security || {}, externalDataScript: storeDataScriptGlobal, sourcePath: errPath, headExtra: bakedHeadExtra });
53523
- } catch {
53524
- }
53525
- }
53677
+ const expose = process.env.NODE_ENV !== "production";
53678
+ const errorHtml = await renderBoundaryFile("error.vsk", url.pathname, {
53679
+ error: expose ? err.message : "Internal Server Error",
53680
+ stack: expose ? err.stack : "",
53681
+ statusCode: 500,
53682
+ url: url.pathname
53683
+ });
53526
53684
  res.writeHead(500, { "Content-Type": "text/html" });
53527
53685
  res.end(errorHtml || renderDefaultErrorPage({ message: process.env.NODE_ENV !== "production" ? err.message : void 0, url: url.pathname, headExtra: bakedHeadExtra }));
53528
53686
  return;
@@ -65074,6 +65232,10 @@ var PACKAGES = {
65074
65232
  // servers on fresh checkouts (CI).
65075
65233
  "plugin-pwa": { name: "@vesk/plugin-pwa", entry: "index" },
65076
65234
  "plugin-tailwind": { name: "@vesk/plugin-tailwind", entry: "index" },
65235
+ // The multipart plugin ships as a real published package: an app route imports
65236
+ // it by name, so its dist must exist for the API-function bundler to resolve it
65237
+ // on a fresh checkout (CI).
65238
+ "plugin-multipart": { name: "@vesk/plugin-multipart", entry: "index" },
65077
65239
  // testing last — it depends on the compiler.
65078
65240
  testing: { name: "@vesk/testing", entry: "index" }
65079
65241
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vesk/vesk-cli",
3
- "version": "0.2.69",
3
+ "version": "0.2.71",
4
4
  "type": "module",
5
5
  "description": "Vesk — compiler-first framework for the post-VDOM web. vesk dev, vesk build.",
6
6
  "bin": {
@@ -8,10 +8,10 @@
8
8
  },
9
9
  "main": "./dist/cli.js",
10
10
  "dependencies": {
11
- "@vesk/adapter": "^0.2.69",
12
- "@vesk/compiler": "^0.2.69",
13
- "@vesk/plugin-tailwind": "^0.2.69",
14
- "@vesk/runtime": "^0.2.69",
11
+ "@vesk/adapter": "^0.2.71",
12
+ "@vesk/compiler": "^0.2.71",
13
+ "@vesk/plugin-tailwind": "^0.2.71",
14
+ "@vesk/runtime": "^0.2.71",
15
15
  "esrap": "^2.3.0",
16
16
  "typescript": "^5.8.0",
17
17
  "ws": "^8.18.0",