tova 0.3.4 → 0.3.6

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.
@@ -293,9 +293,19 @@ export class ServerCodegen extends BaseCodegen {
293
293
  // Check if rate limiting is needed
294
294
  const needsRateLimitStore = !!rateLimitConfig || routes.some(r => (r.decorators || []).some(d => d.name === 'rate_limit'));
295
295
 
296
+ // Fast mode: emit a minimal request handler when no complex features are used
297
+ const allRoutesStatic = routes.every(r => !r.path.includes(':') && !r.path.includes('*'));
298
+ const hasDynamicRoutes = !allRoutesStatic;
299
+ const isFastMode = !corsConfig && !authConfig && !sessionConfig && !rateLimitConfig &&
300
+ !errorHandler && !wsDecl && !staticDecl && !compressionConfig &&
301
+ middlewares.length === 0 && !dbConfig && subscriptions.length === 0 &&
302
+ backgroundJobs.length === 0 && sseDecls.length === 0 && !cacheConfig &&
303
+ routes.every(r => !(r.decorators || []).length && !r._groupMiddlewares?.length && !r._version);
304
+
296
305
  // ════════════════════════════════════════════════════════════
297
306
  // 1. Distributed Tracing
298
307
  // ════════════════════════════════════════════════════════════
308
+ if (!isFastMode) {
299
309
  lines.push('// ── Distributed Tracing ──');
300
310
  lines.push('import { AsyncLocalStorage } from "node:async_hooks";');
301
311
  lines.push('const __requestContext = new AsyncLocalStorage();');
@@ -308,12 +318,22 @@ export class ServerCodegen extends BaseCodegen {
308
318
  lines.push(' return store ? store.locals : {};');
309
319
  lines.push('}');
310
320
  lines.push('');
321
+ }
311
322
 
312
323
  // ════════════════════════════════════════════════════════════
313
324
  // 2. Env Validation (F6) — fail fast
314
325
  // ════════════════════════════════════════════════════════════
315
326
  if (envDecls.length > 0) {
316
327
  lines.push('// ── Env Validation ──');
328
+ // Collect all required env vars without defaults and validate presence before any exit
329
+ const requiredEnvs = envDecls.filter(d => !d.defaultValue);
330
+ if (requiredEnvs.length > 0) {
331
+ lines.push(`const __envErrors = [];`);
332
+ for (const decl of requiredEnvs) {
333
+ lines.push(`if (process.env.${decl.name} === undefined || process.env.${decl.name} === "") __envErrors.push("${decl.name}");`);
334
+ }
335
+ lines.push(`if (__envErrors.length > 0) { console.error("Missing required env vars: " + __envErrors.join(", ")); process.exit(1); }`);
336
+ }
317
337
  for (const decl of envDecls) {
318
338
  const envName = decl.name;
319
339
  const ta = decl.typeAnnotation;
@@ -323,8 +343,6 @@ export class ServerCodegen extends BaseCodegen {
323
343
  if (decl.defaultValue) {
324
344
  const defaultExpr = this.genExpression(decl.defaultValue);
325
345
  lines.push(` if (__raw === undefined || __raw === "") return ${defaultExpr};`);
326
- } else {
327
- lines.push(` if (__raw === undefined || __raw === "") { console.error("Required env var ${envName} is not set"); process.exit(1); }`);
328
346
  }
329
347
  switch (typeName) {
330
348
  case 'Int':
@@ -797,12 +815,18 @@ export class ServerCodegen extends BaseCodegen {
797
815
  // ════════════════════════════════════════════════════════════
798
816
  lines.push('// ── Router ──');
799
817
  lines.push('const __routes = [];');
818
+ lines.push('const __staticRoutes = new Map();'); // Fast lookup for static routes
800
819
  lines.push('function __addRoute(method, path, handler, version) {');
820
+ lines.push(' const isStatic = !path.includes(":") && !path.includes("*");');
821
+ lines.push(' if (isStatic) {');
822
+ lines.push(' const key = method + " " + path;');
823
+ lines.push(' __staticRoutes.set(key, { method, handler, _path: path, _version: version || null });');
824
+ lines.push(' }');
801
825
  lines.push(' let pattern = path');
802
826
  lines.push(' .replace(/\\*([a-zA-Z_][a-zA-Z0-9_]*)/g, "(?<$1>.+)")');
803
827
  lines.push(' .replace(/\\*$/g, "(.*)")');
804
828
  lines.push(' .replace(/:([^/]+)/g, "(?<$1>[^/]+)");');
805
- lines.push(' __routes.push({ method, regex: new RegExp(`^${pattern}$`), handler, _path: path, _version: version || null });');
829
+ lines.push(' __routes.push({ method, regex: new RegExp(`^${pattern}$`), handler, _path: path, _version: version || null, _isStatic: isStatic });');
806
830
  lines.push('}');
807
831
  lines.push('');
808
832
 
@@ -830,13 +854,12 @@ export class ServerCodegen extends BaseCodegen {
830
854
  lines.push('}');
831
855
  } else {
832
856
  lines.push('// ── CORS ──');
833
- lines.push('function __getCorsHeaders() {');
834
- lines.push(' return {');
835
- lines.push(' "Access-Control-Allow-Origin": "*",');
836
- lines.push(' "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",');
837
- lines.push(' "Access-Control-Allow-Headers": "Content-Type, Authorization",');
838
- lines.push(' };');
839
- lines.push('}');
857
+ lines.push('const __corsHeadersConst = Object.freeze({');
858
+ lines.push(' "Access-Control-Allow-Origin": "*",');
859
+ lines.push(' "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",');
860
+ lines.push(' "Access-Control-Allow-Headers": "Content-Type, Authorization",');
861
+ lines.push('});');
862
+ lines.push('function __getCorsHeaders() { return __corsHeadersConst; }');
840
863
  }
841
864
  lines.push('');
842
865
 
@@ -879,12 +902,9 @@ export class ServerCodegen extends BaseCodegen {
879
902
  lines.push(' const __sig = await crypto.subtle.sign("HMAC", __authKey, new TextEncoder().encode(__sigData));');
880
903
  lines.push(' const __expectedSig = btoa(String.fromCharCode(...new Uint8Array(__sig)))');
881
904
  lines.push(' .replace(/\\+/g, "-").replace(/\\//g, "_").replace(/=+$/, "");');
882
- lines.push(' const __sigBuf = new TextEncoder().encode(__expectedSig);');
883
- lines.push(' const __tokBuf = new TextEncoder().encode(parts[2]);');
884
- lines.push(' if (__sigBuf.length !== __tokBuf.length) return null;');
885
- lines.push(' let __mismatch = 0;');
886
- lines.push(' for (let i = 0; i < __sigBuf.length; i++) __mismatch |= __sigBuf[i] ^ __tokBuf[i];');
887
- lines.push(' if (__mismatch !== 0) return null;');
905
+ lines.push(' const __sigBuf = Buffer.from(__expectedSig);');
906
+ lines.push(' const __tokBuf = Buffer.from(parts[2]);');
907
+ lines.push(' if (__sigBuf.length !== __tokBuf.length || !require("crypto").timingSafeEqual(__sigBuf, __tokBuf)) return null;');
888
908
  lines.push(' const __payload = JSON.parse(atob(parts[1].replace(/-/g, "+").replace(/_/g, "/")));');
889
909
  lines.push(' if (__payload.exp && __payload.exp < Math.floor(Date.now() / 1000)) return null;');
890
910
  lines.push(' return __payload;');
@@ -935,9 +955,8 @@ export class ServerCodegen extends BaseCodegen {
935
955
  lines.push('setInterval(() => {');
936
956
  lines.push(' const now = Date.now();');
937
957
  lines.push(' for (const [key, entry] of __rateLimitStore) {');
938
- lines.push(' if (entry.timestamps.length === 0 || now - entry.timestamps[entry.timestamps.length - 1] > 60000) {');
939
- lines.push(' __rateLimitStore.delete(key);');
940
- lines.push(' }');
958
+ lines.push(' entry.timestamps = entry.timestamps.filter(t => now - t < 120000);');
959
+ lines.push(' if (entry.timestamps.length === 0) __rateLimitStore.delete(key);');
941
960
  lines.push(' }');
942
961
  lines.push('}, 60000);');
943
962
  lines.push('');
@@ -2180,6 +2199,7 @@ export class ServerCodegen extends BaseCodegen {
2180
2199
  // ════════════════════════════════════════════════════════════
2181
2200
  // 18. Logging, Static files, WebSocket
2182
2201
  // ════════════════════════════════════════════════════════════
2202
+ if (!isFastMode) {
2183
2203
  lines.push('// ── Structured Logging ──');
2184
2204
  lines.push('let __reqCounter = 0;');
2185
2205
  lines.push('function __genRequestId() {');
@@ -2199,6 +2219,7 @@ export class ServerCodegen extends BaseCodegen {
2199
2219
  lines.push(' if (__logFile) __logFile.write(entry + "\\n");');
2200
2220
  lines.push('}');
2201
2221
  lines.push('');
2222
+ }
2202
2223
 
2203
2224
  if (staticDecl) {
2204
2225
  lines.push('// ── Static File Serving ──');
@@ -2348,14 +2369,77 @@ export class ServerCodegen extends BaseCodegen {
2348
2369
  lines.push('');
2349
2370
  }
2350
2371
 
2372
+ if (!isFastMode) {
2351
2373
  lines.push('// ── Graceful Drain ──');
2352
2374
  lines.push('let __activeRequests = 0;');
2353
2375
  lines.push('let __shuttingDown = false;');
2354
2376
  lines.push('');
2377
+ }
2355
2378
 
2356
2379
  // ════════════════════════════════════════════════════════════
2357
2380
  // 21. Request Handler — with global rate limit check (F2)
2358
2381
  // ════════════════════════════════════════════════════════════
2382
+ if (isFastMode) {
2383
+ // Fast mode: emit direct handler references for static routes
2384
+ if (allRoutesStatic && routes.length <= 16) {
2385
+ for (let ri = 0; ri < routes.length; ri++) {
2386
+ const method = routes[ri].method.toUpperCase();
2387
+ const path = routes[ri].path;
2388
+ lines.push(`const __fh${ri} = __staticRoutes.get(${JSON.stringify(method + ' ' + path)}).handler;`);
2389
+ }
2390
+ }
2391
+ // Fast mode: minimal handler with no AsyncLocalStorage, no logging, no request IDs
2392
+ lines.push('// ── Request Handler (fast mode) ──');
2393
+ lines.push('function __handleRequest(req) {');
2394
+ lines.push(' const __rawUrl = req.url;');
2395
+ lines.push(' const __pStart = __rawUrl.indexOf("/", 12);');
2396
+ lines.push(' const __qIdx = __rawUrl.indexOf("?", __pStart);');
2397
+ lines.push(' const __pathname = __qIdx === -1 ? __rawUrl.slice(__pStart) : __rawUrl.slice(__pStart, __qIdx);');
2398
+ lines.push(' const __method = req.method;');
2399
+
2400
+ // OPTIONS fast path
2401
+ lines.push(' if (__method === "OPTIONS") return new Response(null, { status: 204, headers: __corsHeadersConst });');
2402
+
2403
+ // Static route dispatch — emit direct if/else chain using named handler refs
2404
+ if (allRoutesStatic && routes.length <= 16) {
2405
+ for (let ri = 0; ri < routes.length; ri++) {
2406
+ const method = routes[ri].method.toUpperCase();
2407
+ const path = routes[ri].path;
2408
+ const handlerVar = `__fh${ri}`;
2409
+ lines.push(` if (__method === ${JSON.stringify(method)} && __pathname === ${JSON.stringify(path)}) return ${handlerVar}(req, {});`);
2410
+ }
2411
+ // HEAD fallback for GET routes
2412
+ for (let ri = 0; ri < routes.length; ri++) {
2413
+ if (routes[ri].method.toUpperCase() === 'GET') {
2414
+ const handlerVar = `__fh${ri}`;
2415
+ lines.push(` if (__method === "HEAD" && __pathname === ${JSON.stringify(routes[ri].path)}) return ${handlerVar}(req, {});`);
2416
+ }
2417
+ }
2418
+ } else {
2419
+ // Fallback to Map lookup for larger route sets or dynamic routes
2420
+ lines.push(' const __staticKey = __method + " " + __pathname;');
2421
+ lines.push(' const __sr = __staticRoutes.get(__staticKey);');
2422
+ lines.push(' if (__sr) return __sr.handler(req, {});');
2423
+ if (hasDynamicRoutes) {
2424
+ lines.push(' for (const route of __routes) {');
2425
+ lines.push(' if (route._isStatic) continue;');
2426
+ lines.push(' if (__method === route.method || (route.method === "GET" && __method === "HEAD")) {');
2427
+ lines.push(' const match = __pathname.match(route.regex);');
2428
+ lines.push(' if (match) return route.handler(req, match.groups || {});');
2429
+ lines.push(' }');
2430
+ lines.push(' }');
2431
+ }
2432
+ }
2433
+
2434
+ // Client HTML fallback
2435
+ lines.push(' if (__pathname === "/" && typeof __clientHTML !== "undefined") {');
2436
+ lines.push(' return new Response(__clientHTML, { status: 200, headers: { "Content-Type": "text/html" } });');
2437
+ lines.push(' }');
2438
+ lines.push(' return new Response("Not Found", { status: 404 });');
2439
+ lines.push('}');
2440
+ lines.push('');
2441
+ } else {
2442
+ // Full mode: original handler with all features
2359
2443
  lines.push('// ── Request Handler ──');
2360
2444
  lines.push('async function __handleRequest(req) {');
2361
2445
 
@@ -2364,7 +2448,10 @@ export class ServerCodegen extends BaseCodegen {
2364
2448
  lines.push(' }');
2365
2449
  lines.push(' __activeRequests++;');
2366
2450
 
2367
- lines.push(' const url = new URL(req.url);');
2451
+ lines.push(' const __rawUrl = req.url;');
2452
+ lines.push(' const __pStart = __rawUrl.indexOf("/", 12);'); // skip "http://x:p" or "https://x:p"
2453
+ lines.push(' const __qIdx = __rawUrl.indexOf("?", __pStart);');
2454
+ lines.push(' const __pathname = __qIdx === -1 ? __rawUrl.slice(__pStart) : __rawUrl.slice(__pStart, __qIdx);');
2368
2455
  lines.push(' const __rid = req.headers.get("X-Request-Id") || __genRequestId();');
2369
2456
  lines.push(' const __startTime = Date.now();');
2370
2457
  lines.push(' const __cors = __getCorsHeaders(req);');
@@ -2437,16 +2524,77 @@ export class ServerCodegen extends BaseCodegen {
2437
2524
 
2438
2525
  // Static file serving
2439
2526
  if (staticDecl) {
2440
- lines.push(` if (url.pathname.startsWith(__staticPrefix)) {`);
2441
- lines.push(' const __staticRes = await __serveStatic(url.pathname, req);');
2527
+ lines.push(` if (__pathname.startsWith(__staticPrefix)) {`);
2528
+ lines.push(' const __staticRes = await __serveStatic(__pathname, req);');
2442
2529
  lines.push(' if (__staticRes) return __staticRes;');
2443
2530
  lines.push(' }');
2444
2531
  }
2445
2532
 
2446
- // Route matching
2533
+ // Route matching — fast path for static routes (no params/wildcards)
2534
+ lines.push(' const __staticKey = req.method + " " + __pathname;');
2535
+ lines.push(' const __staticRoute = __staticRoutes.get(__staticKey) || (req.method === "HEAD" && __staticRoutes.get("GET " + __pathname));');
2536
+ lines.push(' if (__staticRoute) {');
2537
+ lines.push(' const route = __staticRoute;');
2538
+ lines.push(' const match = { groups: {} };');
2539
+
2540
+ // Emit static route handler (same structure as dynamic)
2541
+ if (globalMiddlewares.length > 0) {
2542
+ lines.push(' const __handler = async (__req) => route.handler(__req, {});');
2543
+ lines.push(' const __chain = __middlewares.reduceRight(');
2544
+ lines.push(' (next, mw) => async (__req) => mw(__req, next),');
2545
+ lines.push(' __handler');
2546
+ lines.push(' );');
2547
+ lines.push(' try {');
2548
+ lines.push(' const res = await __chain(req);');
2549
+ lines.push(' __log("info", `${req.method} ${__pathname}`, { rid: __rid, status: res.status, ms: Date.now() - __startTime });');
2550
+ lines.push(' const headers = new Headers(res.headers);');
2551
+ lines.push(' for (const [k, v] of Object.entries(__cors)) headers.set(k, v);');
2552
+ lines.push(' return new Response(res.body, { status: res.status, headers });');
2553
+ lines.push(' } catch (err) {');
2554
+ lines.push(' if (err.message === "__BODY_TOO_LARGE__") return Response.json({ error: "Payload Too Large" }, { status: 413, headers: __cors });');
2555
+ if (errorHandler) {
2556
+ lines.push(' try {');
2557
+ lines.push(' const errRes = await __errorHandler(err, req);');
2558
+ lines.push(' if (errRes instanceof Response) {');
2559
+ lines.push(' const headers = new Headers(errRes.headers);');
2560
+ lines.push(' for (const [k, v] of Object.entries(__cors)) headers.set(k, v);');
2561
+ lines.push(' return new Response(errRes.body, { status: errRes.status, headers });');
2562
+ lines.push(' }');
2563
+ lines.push(' return Response.json(errRes, { status: 500, headers: __cors });');
2564
+ lines.push(' } catch { /**/ }');
2565
+ }
2566
+ lines.push(' __log("error", `Unhandled error: ${err.message}`, { error: err.stack || err.message });');
2567
+ lines.push(' return Response.json({ error: "Internal Server Error" }, { status: 500, headers: __cors });');
2568
+ lines.push(' }');
2569
+ } else {
2570
+ lines.push(' try {');
2571
+ lines.push(' const res = await route.handler(req, {});');
2572
+ lines.push(' __log("info", `${req.method} ${__pathname}`, { rid: __rid, status: res.status, ms: Date.now() - __startTime });');
2573
+ lines.push(' for (const [k, v] of Object.entries(__cors)) res.headers.set(k, v);');
2574
+ lines.push(' return res;');
2575
+ lines.push(' } catch (err) {');
2576
+ lines.push(' if (err.message === "__BODY_TOO_LARGE__") return Response.json({ error: "Payload Too Large" }, { status: 413, headers: __cors });');
2577
+ if (errorHandler) {
2578
+ lines.push(' try {');
2579
+ lines.push(' const errRes = await __errorHandler(err, req);');
2580
+ lines.push(' if (errRes instanceof Response) {');
2581
+ lines.push(' for (const [k, v] of Object.entries(__cors)) errRes.headers.set(k, v);');
2582
+ lines.push(' return errRes;');
2583
+ lines.push(' }');
2584
+ lines.push(' return Response.json(errRes, { status: 500, headers: __cors });');
2585
+ lines.push(' } catch { /**/ }');
2586
+ }
2587
+ lines.push(' __log("error", `Unhandled error: ${err.message}`, { error: err.stack || err.message });');
2588
+ lines.push(' return Response.json({ error: "Internal Server Error" }, { status: 500, headers: __cors });');
2589
+ lines.push(' }');
2590
+ }
2591
+ lines.push(' }');
2592
+
2593
+ // Fallback: regex-based matching for dynamic routes
2447
2594
  lines.push(' for (const route of __routes) {');
2595
+ lines.push(' if (route._isStatic) continue;'); // Skip static routes already handled
2448
2596
  lines.push(' if (req.method === route.method || (route.method === "GET" && req.method === "HEAD" && !__routes.some(r => r.method === "HEAD" && r.regex.source === route.regex.source))) {');
2449
- lines.push(' const match = url.pathname.match(route.regex);');
2597
+ lines.push(' const match = __pathname.match(route.regex);');
2450
2598
  lines.push(' if (match) {');
2451
2599
 
2452
2600
  if (globalMiddlewares.length > 0) {
@@ -2457,19 +2605,17 @@ export class ServerCodegen extends BaseCodegen {
2457
2605
  lines.push(' );');
2458
2606
  lines.push(' try {');
2459
2607
  lines.push(' const res = await __chain(req);');
2460
- lines.push(' __log("info", `${req.method} ${url.pathname}`, { rid: __rid, status: res.status, ms: Date.now() - __startTime });');
2461
- lines.push(' const headers = new Headers(res.headers);');
2462
- lines.push(' for (const [k, v] of Object.entries(__cors)) headers.set(k, v);');
2463
- lines.push(' return new Response(res.body, { status: res.status, headers });');
2608
+ lines.push(' __log("info", `${req.method} ${__pathname}`, { rid: __rid, status: res.status, ms: Date.now() - __startTime });');
2609
+ lines.push(' for (const [k, v] of Object.entries(__cors)) res.headers.set(k, v);');
2610
+ lines.push(' return res;');
2464
2611
  lines.push(' } catch (err) {');
2465
2612
  lines.push(' if (err.message === "__BODY_TOO_LARGE__") return Response.json({ error: "Payload Too Large" }, { status: 413, headers: __cors });');
2466
2613
  if (errorHandler) {
2467
2614
  lines.push(' try {');
2468
2615
  lines.push(' const errRes = await __errorHandler(err, req);');
2469
2616
  lines.push(' if (errRes instanceof Response) {');
2470
- lines.push(' const headers = new Headers(errRes.headers);');
2471
- lines.push(' for (const [k, v] of Object.entries(__cors)) headers.set(k, v);');
2472
- lines.push(' return new Response(errRes.body, { status: errRes.status, headers });');
2617
+ lines.push(' for (const [k, v] of Object.entries(__cors)) errRes.headers.set(k, v);');
2618
+ lines.push(' return errRes;');
2473
2619
  lines.push(' }');
2474
2620
  lines.push(' return Response.json(errRes, { status: 500, headers: __cors });');
2475
2621
  lines.push(' } catch { /**/ }');
@@ -2480,19 +2626,17 @@ export class ServerCodegen extends BaseCodegen {
2480
2626
  } else {
2481
2627
  lines.push(' try {');
2482
2628
  lines.push(' const res = await route.handler(req, match.groups || {});');
2483
- lines.push(' __log("info", `${req.method} ${url.pathname}`, { rid: __rid, status: res.status, ms: Date.now() - __startTime });');
2484
- lines.push(' const headers = new Headers(res.headers);');
2485
- lines.push(' for (const [k, v] of Object.entries(__cors)) headers.set(k, v);');
2486
- lines.push(' return new Response(res.body, { status: res.status, headers });');
2629
+ lines.push(' __log("info", `${req.method} ${__pathname}`, { rid: __rid, status: res.status, ms: Date.now() - __startTime });');
2630
+ lines.push(' for (const [k, v] of Object.entries(__cors)) res.headers.set(k, v);');
2631
+ lines.push(' return res;');
2487
2632
  lines.push(' } catch (err) {');
2488
2633
  lines.push(' if (err.message === "__BODY_TOO_LARGE__") return Response.json({ error: "Payload Too Large" }, { status: 413, headers: __cors });');
2489
2634
  if (errorHandler) {
2490
2635
  lines.push(' try {');
2491
2636
  lines.push(' const errRes = await __errorHandler(err, req);');
2492
2637
  lines.push(' if (errRes instanceof Response) {');
2493
- lines.push(' const headers = new Headers(errRes.headers);');
2494
- lines.push(' for (const [k, v] of Object.entries(__cors)) headers.set(k, v);');
2495
- lines.push(' return new Response(errRes.body, { status: errRes.status, headers });');
2638
+ lines.push(' for (const [k, v] of Object.entries(__cors)) errRes.headers.set(k, v);');
2639
+ lines.push(' return errRes;');
2496
2640
  lines.push(' }');
2497
2641
  lines.push(' return Response.json(errRes, { status: 500, headers: __cors });');
2498
2642
  lines.push(' } catch { /**/ }');
@@ -2507,11 +2651,11 @@ export class ServerCodegen extends BaseCodegen {
2507
2651
  lines.push(' }');
2508
2652
 
2509
2653
  // Serve client HTML at root
2510
- lines.push(' if (url.pathname === "/" && typeof __clientHTML !== "undefined") {');
2654
+ lines.push(' if (__pathname === "/" && typeof __clientHTML !== "undefined") {');
2511
2655
  lines.push(' return new Response(__clientHTML, { status: 200, headers: { "Content-Type": "text/html", ...(__cors) } });');
2512
2656
  lines.push(' }');
2513
2657
  lines.push(' const __notFound = Response.json({ error: "Not Found" }, { status: 404, headers: __cors });');
2514
- lines.push(' __log("warn", "Not Found", { rid: __rid, method: req.method, path: url.pathname, status: 404, ms: Date.now() - __startTime });');
2658
+ lines.push(' __log("warn", "Not Found", { rid: __rid, method: req.method, path: __pathname, status: 404, ms: Date.now() - __startTime });');
2515
2659
  lines.push(' return __notFound;');
2516
2660
 
2517
2661
  if (sessionConfig) {
@@ -2537,6 +2681,7 @@ export class ServerCodegen extends BaseCodegen {
2537
2681
  }
2538
2682
  lines.push('}');
2539
2683
  lines.push('');
2684
+ } // end else (full mode)
2540
2685
 
2541
2686
  // ════════════════════════════════════════════════════════════
2542
2687
  // 22. Bun.serve()
@@ -2615,6 +2760,9 @@ export class ServerCodegen extends BaseCodegen {
2615
2760
  // 24. Graceful Shutdown — on_stop hooks (F3) + clearInterval (F8)
2616
2761
  // ════════════════════════════════════════════════════════════
2617
2762
  lines.push('// ── Graceful Shutdown ──');
2763
+ if (isFastMode) {
2764
+ lines.push('function __shutdown() { __server.stop(); process.exit(0); }');
2765
+ } else {
2618
2766
  lines.push('async function __shutdown() {');
2619
2767
  lines.push(` console.log(\`Tova server${label} shutting down...\`);`);
2620
2768
  lines.push(' __shuttingDown = true;');
@@ -2661,6 +2809,7 @@ export class ServerCodegen extends BaseCodegen {
2661
2809
  lines.push(' if (__logFile) __logFile.end();');
2662
2810
  lines.push(' process.exit(0);');
2663
2811
  lines.push('}');
2812
+ } // end else (full shutdown)
2664
2813
  lines.push('process.on("SIGINT", __shutdown);');
2665
2814
  lines.push('process.on("SIGTERM", __shutdown);');
2666
2815