@faapi/faapi 0.0.0-canary.f5b23c6 → 1.0.0-canary.3a00f3e

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.
package/dist/index.js CHANGED
@@ -1043,6 +1043,87 @@ async function loadConfig(rootDir, dist) {
1043
1043
  return null;
1044
1044
  }
1045
1045
 
1046
+ // src/cli/loadEnv.ts
1047
+ import fs2 from "fs";
1048
+ import path3 from "path";
1049
+ function resolveEnv() {
1050
+ return process.env.NODE_ENV || "development";
1051
+ }
1052
+ function getEnvFiles(env) {
1053
+ return [".env", ".env.local", `.env.${env}`, `.env.${env}.local`];
1054
+ }
1055
+ function parseEnvFile(content, fileVars) {
1056
+ const result = {};
1057
+ const lines = content.replace(/\r\n/g, "\n").split("\n");
1058
+ for (const line of lines) {
1059
+ const trimmed = line.trim();
1060
+ if (!trimmed || trimmed.startsWith("#")) continue;
1061
+ const match = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(trimmed);
1062
+ if (!match) continue;
1063
+ const [, key, rawValue] = match;
1064
+ const value = parseValue(rawValue, { ...fileVars, ...result });
1065
+ result[key] = value;
1066
+ }
1067
+ return result;
1068
+ }
1069
+ function parseValue(raw, env) {
1070
+ if (raw === "") return "";
1071
+ if (raw[0] === "'") {
1072
+ const end = raw.indexOf("'", 1);
1073
+ return end === -1 ? raw.slice(1) : raw.slice(1, end);
1074
+ }
1075
+ if (raw[0] === '"') {
1076
+ const match = /^"((?:\\.|[^"\\])*)"/.exec(raw);
1077
+ const inner = match ? match[1] : raw.slice(1);
1078
+ return expandEscapesAndVars(inner, env);
1079
+ }
1080
+ const commentMatch = /^(.*?)(\s+#.*)$/.exec(raw);
1081
+ const value = commentMatch ? commentMatch[1] : raw;
1082
+ return value.trim();
1083
+ }
1084
+ function expandEscapesAndVars(str, env) {
1085
+ const escaped = str.replace(/\\(.)/g, (_, ch) => {
1086
+ switch (ch) {
1087
+ case "n":
1088
+ return "\n";
1089
+ case "r":
1090
+ return "\r";
1091
+ case "t":
1092
+ return " ";
1093
+ case "\\":
1094
+ return "\\";
1095
+ case '"':
1096
+ return '"';
1097
+ default:
1098
+ return ch;
1099
+ }
1100
+ });
1101
+ return escaped.replace(
1102
+ /\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g,
1103
+ (_, braced, plain) => {
1104
+ const varName = braced || plain;
1105
+ return env[varName] ?? process.env[varName] ?? "";
1106
+ }
1107
+ );
1108
+ }
1109
+ function loadEnv(rootDir) {
1110
+ const env = resolveEnv();
1111
+ const files = getEnvFiles(env);
1112
+ const merged = {};
1113
+ for (const file of files) {
1114
+ const filePath = path3.join(rootDir, file);
1115
+ if (!fs2.existsSync(filePath)) continue;
1116
+ const content = fs2.readFileSync(filePath, "utf-8");
1117
+ const parsed = parseEnvFile(content, merged);
1118
+ Object.assign(merged, parsed);
1119
+ }
1120
+ for (const [key, value] of Object.entries(merged)) {
1121
+ if (process.env[key] === void 0) {
1122
+ process.env[key] = value;
1123
+ }
1124
+ }
1125
+ }
1126
+
1046
1127
  // src/errors/FaapiError.ts
1047
1128
  var FaapiError = class extends Error {
1048
1129
  constructor(code, message, statusCode) {
@@ -1069,14 +1150,14 @@ var ValidationError = class extends FaapiError {
1069
1150
  issues;
1070
1151
  };
1071
1152
  var RouteNotFoundError = class extends FaapiError {
1072
- constructor(path9) {
1073
- super("ROUTE_NOT_FOUND", `Route not found: ${path9}`, 404);
1153
+ constructor(path10) {
1154
+ super("ROUTE_NOT_FOUND", `Route not found: ${path10}`, 404);
1074
1155
  this.name = "RouteNotFoundError";
1075
1156
  }
1076
1157
  };
1077
1158
  var MethodNotAllowedError = class extends FaapiError {
1078
- constructor(method, path9, allowedMethods) {
1079
- super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${path9}`, 405);
1159
+ constructor(method, path10, allowedMethods) {
1160
+ super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${path10}`, 405);
1080
1161
  this.allowedMethods = allowedMethods;
1081
1162
  this.name = "MethodNotAllowedError";
1082
1163
  }
@@ -1095,182 +1176,6 @@ var ModuleLoadError = class extends FaapiError {
1095
1176
  }
1096
1177
  };
1097
1178
 
1098
- // src/cli/createAppCore.ts
1099
- import fs4 from "fs";
1100
- import path7 from "path";
1101
- import { PassThrough } from "stream";
1102
-
1103
- // src/router/sortRoutes.ts
1104
- function sortRoutes(routes) {
1105
- return [...routes].sort((a, b) => {
1106
- if (a.isDynamic !== b.isDynamic) {
1107
- return a.isDynamic ? 1 : -1;
1108
- }
1109
- if (a.isCatchAll !== b.isCatchAll) {
1110
- return a.isCatchAll ? 1 : -1;
1111
- }
1112
- const aSegments = a.urlPath.split("/").filter(Boolean).length;
1113
- const bSegments = b.urlPath.split("/").filter(Boolean).length;
1114
- if (aSegments !== bSegments) {
1115
- return aSegments - bSegments;
1116
- }
1117
- return a.urlPath.localeCompare(b.urlPath);
1118
- });
1119
- }
1120
-
1121
- // src/router/detectRouteConflicts.ts
1122
- function detectRouteConflicts(routes) {
1123
- const map = /* @__PURE__ */ new Map();
1124
- for (const route of routes) {
1125
- const key = `${route.method} ${route.urlPath}`;
1126
- const existing = map.get(key);
1127
- if (existing) {
1128
- existing.files.push(route.filePath);
1129
- } else {
1130
- map.set(key, {
1131
- method: route.method,
1132
- urlPath: route.urlPath,
1133
- files: [route.filePath]
1134
- });
1135
- }
1136
- }
1137
- const conflicts = [];
1138
- for (const conflict of map.values()) {
1139
- if (conflict.files.length > 1) {
1140
- conflicts.push(conflict);
1141
- }
1142
- }
1143
- return conflicts;
1144
- }
1145
-
1146
- // src/server/createServer.ts
1147
- import {
1148
- createServer as createHttpServer
1149
- } from "http";
1150
- import { createSecureServer as createHttp2SecureServer } from "http2";
1151
- import { readFileSync } from "fs";
1152
- import { Readable as Readable2 } from "stream";
1153
- import path5 from "path";
1154
-
1155
- // src/router/matchRoute.ts
1156
- function matchRoute(routes, method, path9) {
1157
- for (const route of routes) {
1158
- if (route.method !== method) {
1159
- continue;
1160
- }
1161
- if (!route.isDynamic) {
1162
- if (route.urlPath === path9) {
1163
- return { route, params: {} };
1164
- }
1165
- continue;
1166
- }
1167
- const params = matchDynamicPath(route.urlPath, path9, route.paramNames, route.isCatchAll);
1168
- if (params !== null) {
1169
- return { route, params };
1170
- }
1171
- }
1172
- return null;
1173
- }
1174
- function matchWsRoute(wsRoutes, path9) {
1175
- for (const route of wsRoutes) {
1176
- if (!route.isDynamic) {
1177
- if (route.urlPath === path9) {
1178
- return { route, params: {} };
1179
- }
1180
- continue;
1181
- }
1182
- const params = matchDynamicPath(route.urlPath, path9, route.paramNames, route.isCatchAll);
1183
- if (params !== null) {
1184
- return { route, params };
1185
- }
1186
- }
1187
- return null;
1188
- }
1189
- function matchDynamicPath(pattern, path9, paramNames, isCatchAll) {
1190
- const patternSegments = pattern.split("/").filter(Boolean);
1191
- const pathSegments = path9.split("/").filter(Boolean);
1192
- if (isCatchAll) {
1193
- const nonCatchAllCount = patternSegments.length - 1;
1194
- if (pathSegments.length <= nonCatchAllCount) {
1195
- return null;
1196
- }
1197
- const params2 = {};
1198
- for (let i = 0; i < nonCatchAllCount; i++) {
1199
- const patternSeg = patternSegments[i];
1200
- const pathSeg = pathSegments[i];
1201
- if (patternSeg.startsWith(":")) {
1202
- const paramName = patternSeg.slice(1);
1203
- params2[paramName] = pathSeg;
1204
- } else if (patternSeg !== pathSeg) {
1205
- return null;
1206
- }
1207
- }
1208
- const catchAllValue = pathSegments.slice(nonCatchAllCount).join("/");
1209
- const catchAllParamName = patternSegments[nonCatchAllCount].slice(4);
1210
- params2[catchAllParamName] = catchAllValue;
1211
- if (Object.keys(params2).length !== paramNames.length) {
1212
- return null;
1213
- }
1214
- return params2;
1215
- }
1216
- if (patternSegments.length !== pathSegments.length) {
1217
- return null;
1218
- }
1219
- const params = {};
1220
- for (let i = 0; i < patternSegments.length; i++) {
1221
- const patternSeg = patternSegments[i];
1222
- const pathSeg = pathSegments[i];
1223
- if (patternSeg.startsWith(":")) {
1224
- const paramName = patternSeg.slice(1);
1225
- params[paramName] = pathSeg;
1226
- } else if (patternSeg !== pathSeg) {
1227
- return null;
1228
- }
1229
- }
1230
- if (Object.keys(params).length !== paramNames.length) {
1231
- return null;
1232
- }
1233
- return params;
1234
- }
1235
-
1236
- // src/loader/resolveExports.ts
1237
- function resolveExport(module, exportName) {
1238
- if (exportName in module && typeof module[exportName] !== "undefined") {
1239
- return module[exportName];
1240
- }
1241
- const defaultExport = module.default;
1242
- if (defaultExport !== null && typeof defaultExport === "object") {
1243
- const value = defaultExport[exportName];
1244
- if (value !== void 0) {
1245
- return value;
1246
- }
1247
- }
1248
- return void 0;
1249
- }
1250
-
1251
- // src/loader/validateRouteModule.ts
1252
- function validateRouteModule(value, method, filePath) {
1253
- if (typeof value !== "function") {
1254
- throw new Error(
1255
- `Route module "${filePath}" does not export a valid handler for method "${method}". Expected a function, got ${typeof value}.`
1256
- );
1257
- }
1258
- }
1259
-
1260
- // src/loader/loadRouteModule.ts
1261
- async function loadRouteModule(filePath, method) {
1262
- let module;
1263
- try {
1264
- module = await importWithCacheBust(filePath);
1265
- } catch (err) {
1266
- const reason = err instanceof Error ? err.message : String(err);
1267
- throw new Error(`Failed to load route module "${filePath}": ${reason}`, { cause: err });
1268
- }
1269
- const handler = resolveExport(module, method);
1270
- validateRouteModule(handler, method, filePath);
1271
- return { handler, method };
1272
- }
1273
-
1274
1179
  // src/runtime/sse.ts
1275
1180
  function encodeSseEvent(event) {
1276
1181
  let out = "";
@@ -1337,6 +1242,11 @@ function createSseWriter() {
1337
1242
  const text = encodeSseEvent(event);
1338
1243
  controller.enqueue(encoder.encode(text));
1339
1244
  },
1245
+ sendRaw(chunk) {
1246
+ if (closed || !controller) return;
1247
+ const bytes = typeof chunk === "string" ? encoder.encode(chunk) : chunk;
1248
+ controller.enqueue(bytes);
1249
+ },
1340
1250
  sendError(error) {
1341
1251
  if (closed || !controller) return;
1342
1252
  const message = error instanceof Error ? error.message : String(error);
@@ -1475,13 +1385,429 @@ function createContext(request, params, config = {}, ip = "") {
1475
1385
  return ctx;
1476
1386
  }
1477
1387
 
1478
- // src/utils/queryToObject.ts
1479
- function queryToObject(params) {
1480
- const result = {};
1481
- for (const [key, value] of params) {
1482
- result[key] = value;
1388
+ // src/utils/isPlainObject.ts
1389
+ function isPlainObject(value) {
1390
+ if (value === null || typeof value !== "object") {
1391
+ return false;
1392
+ }
1393
+ if (Array.isArray(value)) {
1394
+ return false;
1395
+ }
1396
+ const proto = Object.getPrototypeOf(value);
1397
+ return proto === null || proto === Object.prototype;
1398
+ }
1399
+
1400
+ // src/response/toResponse.ts
1401
+ async function toResponse(value, meta) {
1402
+ if (value instanceof Promise) {
1403
+ return toResponse(await value, meta);
1404
+ }
1405
+ const applyMeta = (headers2) => {
1406
+ if (!meta) return;
1407
+ for (const [key, val] of Object.entries(meta.headers)) {
1408
+ headers2.set(key, val);
1409
+ }
1410
+ for (const cookie of meta.setCookies ?? []) {
1411
+ headers2.append("set-cookie", cookie);
1412
+ }
1413
+ };
1414
+ if (value instanceof Response) {
1415
+ if (meta && (meta.status !== void 0 || Object.keys(meta.headers).length > 0 || meta.setCookies.length > 0)) {
1416
+ const headers2 = new Headers(value.headers);
1417
+ applyMeta(headers2);
1418
+ return new Response(value.body, {
1419
+ status: meta.status ?? value.status,
1420
+ headers: headers2
1421
+ });
1422
+ }
1423
+ return value;
1424
+ }
1425
+ if (value === null || value === void 0) {
1426
+ const status = meta?.status ?? 204;
1427
+ const headers2 = new Headers();
1428
+ applyMeta(headers2);
1429
+ return new Response(null, { status, headers: headers2 });
1430
+ }
1431
+ if (typeof ReadableStream !== "undefined" && value instanceof ReadableStream) {
1432
+ const headers2 = new Headers({ "Content-Type": "application/octet-stream" });
1433
+ applyMeta(headers2);
1434
+ return new Response(value, {
1435
+ status: meta?.status ?? 200,
1436
+ headers: headers2
1437
+ });
1438
+ }
1439
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) {
1440
+ const headers2 = new Headers({ "Content-Type": "application/octet-stream" });
1441
+ applyMeta(headers2);
1442
+ return new Response(value, {
1443
+ status: meta?.status ?? 200,
1444
+ headers: headers2
1445
+ });
1446
+ }
1447
+ if (value instanceof Uint8Array) {
1448
+ const headers2 = new Headers({ "Content-Type": "application/octet-stream" });
1449
+ applyMeta(headers2);
1450
+ return new Response(value, {
1451
+ status: meta?.status ?? 200,
1452
+ headers: headers2
1453
+ });
1454
+ }
1455
+ if (isPlainObject(value) || Array.isArray(value)) {
1456
+ const body2 = JSON.stringify(value);
1457
+ const headers2 = new Headers({ "Content-Type": "application/json" });
1458
+ applyMeta(headers2);
1459
+ return new Response(body2, {
1460
+ status: meta?.status ?? 200,
1461
+ headers: headers2
1462
+ });
1463
+ }
1464
+ if (typeof value === "string") {
1465
+ const headers2 = new Headers({ "Content-Type": "text/plain" });
1466
+ applyMeta(headers2);
1467
+ return new Response(value, {
1468
+ status: meta?.status ?? 200,
1469
+ headers: headers2
1470
+ });
1471
+ }
1472
+ if (typeof value === "number" || typeof value === "boolean") {
1473
+ const headers2 = new Headers({ "Content-Type": "text/plain" });
1474
+ applyMeta(headers2);
1475
+ return new Response(String(value), {
1476
+ status: meta?.status ?? 200,
1477
+ headers: headers2
1478
+ });
1479
+ }
1480
+ const body = JSON.stringify(value);
1481
+ const headers = new Headers({ "Content-Type": "application/json" });
1482
+ applyMeta(headers);
1483
+ return new Response(body, {
1484
+ status: meta?.status ?? 200,
1485
+ headers
1486
+ });
1487
+ }
1488
+
1489
+ // src/utils/queryToObject.ts
1490
+ function queryToObject(params) {
1491
+ const result = {};
1492
+ for (const [key, value] of params) {
1493
+ result[key] = value;
1494
+ }
1495
+ return result;
1496
+ }
1497
+
1498
+ // src/injection/injectParams.ts
1499
+ function getBuiltinInjectionValue(type, ctx, body) {
1500
+ switch (type) {
1501
+ case "query":
1502
+ return queryToObject(ctx.query);
1503
+ case "params":
1504
+ return ctx.params;
1505
+ case "headers":
1506
+ return ctx.headers;
1507
+ case "context":
1508
+ return ctx;
1509
+ case "cookies":
1510
+ return ctx.cookies;
1511
+ case "ip":
1512
+ return ctx.ip;
1513
+ case "body":
1514
+ return body;
1515
+ // form 与 body 共享解析结果(resolveInput 已按 Content-Type 解析 form-urlencoded)
1516
+ // 差异仅在 schema 校验(form coerce=true,由 collectRouteSchemaSources 标记)
1517
+ case "form":
1518
+ return body;
1519
+ case "files":
1520
+ if (body && typeof body === "object" && "files" in body) {
1521
+ return body.files;
1522
+ }
1523
+ return [];
1524
+ case "fields":
1525
+ if (body && typeof body === "object" && "fields" in body) {
1526
+ return body.fields;
1527
+ }
1528
+ return {};
1529
+ default:
1530
+ return void 0;
1531
+ }
1532
+ }
1533
+ async function injectParamsAsync(handler, ctx, body, injectors) {
1534
+ const injections = resolveInjection(handler);
1535
+ if (injections.length === 0) {
1536
+ return await handler();
1537
+ }
1538
+ const args = await Promise.all(
1539
+ injections.map(async (injection) => {
1540
+ if (injection.type !== "unknown") {
1541
+ return getBuiltinInjectionValue(injection.type, ctx, body);
1542
+ }
1543
+ if (injectors && injection.name in injectors) {
1544
+ return await injectors[injection.name](ctx);
1545
+ }
1546
+ return void 0;
1547
+ })
1548
+ );
1549
+ return await handler(...args);
1550
+ }
1551
+
1552
+ // src/runtime/invokeHandler.ts
1553
+ function mergeMeta(response, meta) {
1554
+ const hasMeta = meta.status !== void 0 || Object.keys(meta.headers).length > 0 || meta.setCookies.length > 0;
1555
+ if (!hasMeta) return response;
1556
+ const headers = new Headers(response.headers);
1557
+ for (const [key, value] of Object.entries(meta.headers)) {
1558
+ headers.set(key, value);
1559
+ }
1560
+ for (const cookie of meta.setCookies) {
1561
+ headers.append("set-cookie", cookie);
1562
+ }
1563
+ return new Response(response.body, {
1564
+ status: meta.status ?? response.status,
1565
+ headers
1566
+ });
1567
+ }
1568
+ async function compose(middlewares, ctx, finalHandler) {
1569
+ const meta = ctx.meta;
1570
+ let index = -1;
1571
+ async function dispatch(i) {
1572
+ if (i <= index) {
1573
+ throw new Error("next() called multiple times");
1574
+ }
1575
+ index = i;
1576
+ if (i >= middlewares.length) {
1577
+ return await finalHandler();
1578
+ }
1579
+ const mw = middlewares[i];
1580
+ let innerResponse;
1581
+ const next = async () => {
1582
+ innerResponse = await dispatch(i + 1);
1583
+ return innerResponse;
1584
+ };
1585
+ const result = await mw(ctx, next);
1586
+ if (result instanceof Response) {
1587
+ return mergeMeta(result, meta);
1588
+ }
1589
+ if (innerResponse !== void 0) {
1590
+ return innerResponse;
1591
+ }
1592
+ throw new Error("\u4E2D\u95F4\u4EF6\u5FC5\u987B await next() \u6216\u8FD4\u56DE Response");
1593
+ }
1594
+ return await dispatch(0);
1595
+ }
1596
+ async function invokeHandler(handler, ctx, body, middlewares, injectors) {
1597
+ const meta = ctx.meta;
1598
+ const pickSseAndAutoClose = () => {
1599
+ const sseWriter = ctx.__sseWriter;
1600
+ if (!sseWriter) return null;
1601
+ if (!sseWriter.closed && !sseWriter.aborted) {
1602
+ sseWriter.close();
1603
+ }
1604
+ return mergeMeta(sseWriter.response, meta);
1605
+ };
1606
+ const autoCloseSseOnError = () => {
1607
+ const sseWriter = ctx.__sseWriter;
1608
+ if (sseWriter && !sseWriter.closed && !sseWriter.aborted) {
1609
+ sseWriter.close();
1610
+ }
1611
+ };
1612
+ if (!middlewares || middlewares.length === 0) {
1613
+ try {
1614
+ const result = await injectParamsAsync(handler, ctx, body, injectors);
1615
+ const sseResponse = pickSseAndAutoClose();
1616
+ if (sseResponse) return sseResponse;
1617
+ return toResponse(result, meta);
1618
+ } catch (err) {
1619
+ autoCloseSseOnError();
1620
+ throw err;
1621
+ }
1622
+ }
1623
+ const finalHandler = async () => {
1624
+ try {
1625
+ const result = await injectParamsAsync(handler, ctx, body, injectors);
1626
+ const sseResponse = pickSseAndAutoClose();
1627
+ if (sseResponse) return sseResponse;
1628
+ return toResponse(result, meta);
1629
+ } catch (err) {
1630
+ autoCloseSseOnError();
1631
+ throw err;
1632
+ }
1633
+ };
1634
+ return await compose(middlewares, ctx, finalHandler);
1635
+ }
1636
+
1637
+ // src/cli/createAppCore.ts
1638
+ import fs5 from "fs";
1639
+ import path8 from "path";
1640
+ import { PassThrough } from "stream";
1641
+
1642
+ // src/router/sortRoutes.ts
1643
+ function sortRoutes(routes) {
1644
+ return [...routes].sort((a, b) => {
1645
+ if (a.isDynamic !== b.isDynamic) {
1646
+ return a.isDynamic ? 1 : -1;
1647
+ }
1648
+ if (a.isCatchAll !== b.isCatchAll) {
1649
+ return a.isCatchAll ? 1 : -1;
1650
+ }
1651
+ const aSegments = a.urlPath.split("/").filter(Boolean).length;
1652
+ const bSegments = b.urlPath.split("/").filter(Boolean).length;
1653
+ if (aSegments !== bSegments) {
1654
+ return aSegments - bSegments;
1655
+ }
1656
+ return a.urlPath.localeCompare(b.urlPath);
1657
+ });
1658
+ }
1659
+
1660
+ // src/router/detectRouteConflicts.ts
1661
+ function detectRouteConflicts(routes) {
1662
+ const map = /* @__PURE__ */ new Map();
1663
+ for (const route of routes) {
1664
+ const key = `${route.method} ${route.urlPath}`;
1665
+ const existing = map.get(key);
1666
+ if (existing) {
1667
+ existing.files.push(route.filePath);
1668
+ } else {
1669
+ map.set(key, {
1670
+ method: route.method,
1671
+ urlPath: route.urlPath,
1672
+ files: [route.filePath]
1673
+ });
1674
+ }
1675
+ }
1676
+ const conflicts = [];
1677
+ for (const conflict of map.values()) {
1678
+ if (conflict.files.length > 1) {
1679
+ conflicts.push(conflict);
1680
+ }
1681
+ }
1682
+ return conflicts;
1683
+ }
1684
+
1685
+ // src/server/createServer.ts
1686
+ import {
1687
+ createServer as createHttpServer
1688
+ } from "http";
1689
+ import { createSecureServer as createHttp2SecureServer } from "http2";
1690
+ import { readFileSync } from "fs";
1691
+ import { Readable as Readable2 } from "stream";
1692
+ import path6 from "path";
1693
+
1694
+ // src/router/matchRoute.ts
1695
+ function matchRoute(routes, method, path10) {
1696
+ for (const route of routes) {
1697
+ if (route.method !== method) {
1698
+ continue;
1699
+ }
1700
+ if (!route.isDynamic) {
1701
+ if (route.urlPath === path10) {
1702
+ return { route, params: {} };
1703
+ }
1704
+ continue;
1705
+ }
1706
+ const params = matchDynamicPath(route.urlPath, path10, route.paramNames, route.isCatchAll);
1707
+ if (params !== null) {
1708
+ return { route, params };
1709
+ }
1710
+ }
1711
+ return null;
1712
+ }
1713
+ function matchWsRoute(wsRoutes, path10) {
1714
+ for (const route of wsRoutes) {
1715
+ if (!route.isDynamic) {
1716
+ if (route.urlPath === path10) {
1717
+ return { route, params: {} };
1718
+ }
1719
+ continue;
1720
+ }
1721
+ const params = matchDynamicPath(route.urlPath, path10, route.paramNames, route.isCatchAll);
1722
+ if (params !== null) {
1723
+ return { route, params };
1724
+ }
1725
+ }
1726
+ return null;
1727
+ }
1728
+ function matchDynamicPath(pattern, path10, paramNames, isCatchAll) {
1729
+ const patternSegments = pattern.split("/").filter(Boolean);
1730
+ const pathSegments = path10.split("/").filter(Boolean);
1731
+ if (isCatchAll) {
1732
+ const nonCatchAllCount = patternSegments.length - 1;
1733
+ if (pathSegments.length <= nonCatchAllCount) {
1734
+ return null;
1735
+ }
1736
+ const params2 = {};
1737
+ for (let i = 0; i < nonCatchAllCount; i++) {
1738
+ const patternSeg = patternSegments[i];
1739
+ const pathSeg = pathSegments[i];
1740
+ if (patternSeg.startsWith(":")) {
1741
+ const paramName = patternSeg.slice(1);
1742
+ params2[paramName] = pathSeg;
1743
+ } else if (patternSeg !== pathSeg) {
1744
+ return null;
1745
+ }
1746
+ }
1747
+ const catchAllValue = pathSegments.slice(nonCatchAllCount).join("/");
1748
+ const catchAllParamName = patternSegments[nonCatchAllCount].slice(4);
1749
+ params2[catchAllParamName] = catchAllValue;
1750
+ if (Object.keys(params2).length !== paramNames.length) {
1751
+ return null;
1752
+ }
1753
+ return params2;
1754
+ }
1755
+ if (patternSegments.length !== pathSegments.length) {
1756
+ return null;
1757
+ }
1758
+ const params = {};
1759
+ for (let i = 0; i < patternSegments.length; i++) {
1760
+ const patternSeg = patternSegments[i];
1761
+ const pathSeg = pathSegments[i];
1762
+ if (patternSeg.startsWith(":")) {
1763
+ const paramName = patternSeg.slice(1);
1764
+ params[paramName] = pathSeg;
1765
+ } else if (patternSeg !== pathSeg) {
1766
+ return null;
1767
+ }
1768
+ }
1769
+ if (Object.keys(params).length !== paramNames.length) {
1770
+ return null;
1771
+ }
1772
+ return params;
1773
+ }
1774
+
1775
+ // src/loader/resolveExports.ts
1776
+ function resolveExport(module, exportName) {
1777
+ if (exportName in module && typeof module[exportName] !== "undefined") {
1778
+ return module[exportName];
1779
+ }
1780
+ const defaultExport = module.default;
1781
+ if (defaultExport !== null && typeof defaultExport === "object") {
1782
+ const value = defaultExport[exportName];
1783
+ if (value !== void 0) {
1784
+ return value;
1785
+ }
1786
+ }
1787
+ return void 0;
1788
+ }
1789
+
1790
+ // src/loader/validateRouteModule.ts
1791
+ function validateRouteModule(value, method, filePath) {
1792
+ if (typeof value !== "function") {
1793
+ throw new Error(
1794
+ `Route module "${filePath}" does not export a valid handler for method "${method}". Expected a function, got ${typeof value}.`
1795
+ );
1796
+ }
1797
+ }
1798
+
1799
+ // src/loader/loadRouteModule.ts
1800
+ async function loadRouteModule(filePath, method) {
1801
+ let module;
1802
+ try {
1803
+ module = await importWithCacheBust(filePath);
1804
+ } catch (err) {
1805
+ const reason = err instanceof Error ? err.message : String(err);
1806
+ throw new Error(`Failed to load route module "${filePath}": ${reason}`, { cause: err });
1483
1807
  }
1484
- return result;
1808
+ const handler = resolveExport(module, method);
1809
+ validateRouteModule(handler, method, filePath);
1810
+ return { handler, method };
1485
1811
  }
1486
1812
 
1487
1813
  // src/utils/parseJsonBody.ts
@@ -1564,246 +1890,6 @@ async function resolveInput(method, request) {
1564
1890
  return queryToObject(url.searchParams);
1565
1891
  }
1566
1892
 
1567
- // src/utils/isPlainObject.ts
1568
- function isPlainObject(value) {
1569
- if (value === null || typeof value !== "object") {
1570
- return false;
1571
- }
1572
- if (Array.isArray(value)) {
1573
- return false;
1574
- }
1575
- const proto = Object.getPrototypeOf(value);
1576
- return proto === null || proto === Object.prototype;
1577
- }
1578
-
1579
- // src/response/toResponse.ts
1580
- async function toResponse(value, meta) {
1581
- if (value instanceof Promise) {
1582
- return toResponse(await value, meta);
1583
- }
1584
- const applyMeta = (headers2) => {
1585
- if (!meta) return;
1586
- for (const [key, val] of Object.entries(meta.headers)) {
1587
- headers2.set(key, val);
1588
- }
1589
- for (const cookie of meta.setCookies ?? []) {
1590
- headers2.append("set-cookie", cookie);
1591
- }
1592
- };
1593
- if (value instanceof Response) {
1594
- if (meta && (meta.status !== void 0 || Object.keys(meta.headers).length > 0 || meta.setCookies.length > 0)) {
1595
- const headers2 = new Headers(value.headers);
1596
- applyMeta(headers2);
1597
- return new Response(value.body, {
1598
- status: meta.status ?? value.status,
1599
- headers: headers2
1600
- });
1601
- }
1602
- return value;
1603
- }
1604
- if (value === null || value === void 0) {
1605
- const status = meta?.status ?? 204;
1606
- const headers2 = new Headers();
1607
- applyMeta(headers2);
1608
- return new Response(null, { status, headers: headers2 });
1609
- }
1610
- if (typeof ReadableStream !== "undefined" && value instanceof ReadableStream) {
1611
- const headers2 = new Headers({ "Content-Type": "application/octet-stream" });
1612
- applyMeta(headers2);
1613
- return new Response(value, {
1614
- status: meta?.status ?? 200,
1615
- headers: headers2
1616
- });
1617
- }
1618
- if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) {
1619
- const headers2 = new Headers({ "Content-Type": "application/octet-stream" });
1620
- applyMeta(headers2);
1621
- return new Response(value, {
1622
- status: meta?.status ?? 200,
1623
- headers: headers2
1624
- });
1625
- }
1626
- if (value instanceof Uint8Array) {
1627
- const headers2 = new Headers({ "Content-Type": "application/octet-stream" });
1628
- applyMeta(headers2);
1629
- return new Response(value, {
1630
- status: meta?.status ?? 200,
1631
- headers: headers2
1632
- });
1633
- }
1634
- if (isPlainObject(value) || Array.isArray(value)) {
1635
- const body2 = JSON.stringify(value);
1636
- const headers2 = new Headers({ "Content-Type": "application/json" });
1637
- applyMeta(headers2);
1638
- return new Response(body2, {
1639
- status: meta?.status ?? 200,
1640
- headers: headers2
1641
- });
1642
- }
1643
- if (typeof value === "string") {
1644
- const headers2 = new Headers({ "Content-Type": "text/plain" });
1645
- applyMeta(headers2);
1646
- return new Response(value, {
1647
- status: meta?.status ?? 200,
1648
- headers: headers2
1649
- });
1650
- }
1651
- if (typeof value === "number" || typeof value === "boolean") {
1652
- const headers2 = new Headers({ "Content-Type": "text/plain" });
1653
- applyMeta(headers2);
1654
- return new Response(String(value), {
1655
- status: meta?.status ?? 200,
1656
- headers: headers2
1657
- });
1658
- }
1659
- const body = JSON.stringify(value);
1660
- const headers = new Headers({ "Content-Type": "application/json" });
1661
- applyMeta(headers);
1662
- return new Response(body, {
1663
- status: meta?.status ?? 200,
1664
- headers
1665
- });
1666
- }
1667
-
1668
- // src/injection/injectParams.ts
1669
- function getBuiltinInjectionValue(type, ctx, body) {
1670
- switch (type) {
1671
- case "query":
1672
- return queryToObject(ctx.query);
1673
- case "params":
1674
- return ctx.params;
1675
- case "headers":
1676
- return ctx.headers;
1677
- case "context":
1678
- return ctx;
1679
- case "cookies":
1680
- return ctx.cookies;
1681
- case "ip":
1682
- return ctx.ip;
1683
- case "body":
1684
- return body;
1685
- // form 与 body 共享解析结果(resolveInput 已按 Content-Type 解析 form-urlencoded)
1686
- // 差异仅在 schema 校验(form coerce=true,由 collectRouteSchemaSources 标记)
1687
- case "form":
1688
- return body;
1689
- case "files":
1690
- if (body && typeof body === "object" && "files" in body) {
1691
- return body.files;
1692
- }
1693
- return [];
1694
- case "fields":
1695
- if (body && typeof body === "object" && "fields" in body) {
1696
- return body.fields;
1697
- }
1698
- return {};
1699
- default:
1700
- return void 0;
1701
- }
1702
- }
1703
- async function injectParamsAsync(handler, ctx, body, injectors) {
1704
- const injections = resolveInjection(handler);
1705
- if (injections.length === 0) {
1706
- return await handler();
1707
- }
1708
- const args = await Promise.all(
1709
- injections.map(async (injection) => {
1710
- if (injection.type !== "unknown") {
1711
- return getBuiltinInjectionValue(injection.type, ctx, body);
1712
- }
1713
- if (injectors && injection.name in injectors) {
1714
- return await injectors[injection.name](ctx);
1715
- }
1716
- return void 0;
1717
- })
1718
- );
1719
- return await handler(...args);
1720
- }
1721
-
1722
- // src/runtime/invokeHandler.ts
1723
- function mergeMeta(response, meta) {
1724
- const hasMeta = meta.status !== void 0 || Object.keys(meta.headers).length > 0 || meta.setCookies.length > 0;
1725
- if (!hasMeta) return response;
1726
- const headers = new Headers(response.headers);
1727
- for (const [key, value] of Object.entries(meta.headers)) {
1728
- headers.set(key, value);
1729
- }
1730
- for (const cookie of meta.setCookies) {
1731
- headers.append("set-cookie", cookie);
1732
- }
1733
- return new Response(response.body, {
1734
- status: meta.status ?? response.status,
1735
- headers
1736
- });
1737
- }
1738
- async function compose(middlewares, ctx, finalHandler) {
1739
- const meta = ctx.meta;
1740
- let index = -1;
1741
- async function dispatch(i) {
1742
- if (i <= index) {
1743
- throw new Error("next() called multiple times");
1744
- }
1745
- index = i;
1746
- if (i >= middlewares.length) {
1747
- return await finalHandler();
1748
- }
1749
- const mw = middlewares[i];
1750
- let innerResponse;
1751
- const next = async () => {
1752
- innerResponse = await dispatch(i + 1);
1753
- return innerResponse;
1754
- };
1755
- const result = await mw(ctx, next);
1756
- if (result instanceof Response) {
1757
- return mergeMeta(result, meta);
1758
- }
1759
- if (innerResponse !== void 0) {
1760
- return innerResponse;
1761
- }
1762
- throw new Error("\u4E2D\u95F4\u4EF6\u5FC5\u987B await next() \u6216\u8FD4\u56DE Response");
1763
- }
1764
- return await dispatch(0);
1765
- }
1766
- async function invokeHandler(handler, ctx, body, middlewares, injectors) {
1767
- const meta = ctx.meta;
1768
- const pickSseAndAutoClose = () => {
1769
- const sseWriter = ctx.__sseWriter;
1770
- if (!sseWriter) return null;
1771
- if (!sseWriter.closed && !sseWriter.aborted) {
1772
- sseWriter.close();
1773
- }
1774
- return mergeMeta(sseWriter.response, meta);
1775
- };
1776
- const autoCloseSseOnError = () => {
1777
- const sseWriter = ctx.__sseWriter;
1778
- if (sseWriter && !sseWriter.closed && !sseWriter.aborted) {
1779
- sseWriter.close();
1780
- }
1781
- };
1782
- if (!middlewares || middlewares.length === 0) {
1783
- try {
1784
- const result = await injectParamsAsync(handler, ctx, body, injectors);
1785
- const sseResponse = pickSseAndAutoClose();
1786
- if (sseResponse) return sseResponse;
1787
- return toResponse(result, meta);
1788
- } catch (err) {
1789
- autoCloseSseOnError();
1790
- throw err;
1791
- }
1792
- }
1793
- const finalHandler = async () => {
1794
- try {
1795
- const result = await injectParamsAsync(handler, ctx, body, injectors);
1796
- const sseResponse = pickSseAndAutoClose();
1797
- if (sseResponse) return sseResponse;
1798
- return toResponse(result, meta);
1799
- } catch (err) {
1800
- autoCloseSseOnError();
1801
- throw err;
1802
- }
1803
- };
1804
- return await compose(middlewares, ctx, finalHandler);
1805
- }
1806
-
1807
1893
  // src/response/sendNodeResponse.ts
1808
1894
  import { Readable } from "stream";
1809
1895
  async function sendNodeResponse(response, res) {
@@ -1872,9 +1958,9 @@ async function validateInput(schemaPath, method, inputType, input) {
1872
1958
  function mapZodIssues(error) {
1873
1959
  return error.issues.map((issue) => {
1874
1960
  const code = mapZodCode(issue.code, issue.message);
1875
- const path9 = issue.path.map(String).join(".") || "";
1961
+ const path10 = issue.path.map(String).join(".") || "";
1876
1962
  return {
1877
- path: path9,
1963
+ path: path10,
1878
1964
  code,
1879
1965
  expected: issue.expected ?? mapExpectedFromMessage(issue.message),
1880
1966
  received: issue.received ?? mapReceivedFromMessage(issue.message),
@@ -1934,7 +2020,7 @@ function getClientIp(req) {
1934
2020
 
1935
2021
  // src/server/handleWsUpgrade.ts
1936
2022
  import { WebSocketServer, WebSocket } from "ws";
1937
- import path3 from "path";
2023
+ import path4 from "path";
1938
2024
 
1939
2025
  // src/errors/formatErrorResponse.ts
1940
2026
  function formatErrorResponse(error) {
@@ -2107,7 +2193,7 @@ function attachWebSocket(options) {
2107
2193
  const finalHandler = async () => {
2108
2194
  let handlers;
2109
2195
  try {
2110
- const absoluteFilePath = path3.resolve(rootDir, route.filePath);
2196
+ const absoluteFilePath = path4.resolve(rootDir, route.filePath);
2111
2197
  handlers = await loadWsHandler(absoluteFilePath, ctx);
2112
2198
  } catch (err) {
2113
2199
  const reason = err instanceof Error ? err.message : String(err);
@@ -2153,8 +2239,8 @@ function attachWebSocket(options) {
2153
2239
  }
2154
2240
 
2155
2241
  // src/cli/generateSchemaFiles.ts
2156
- import path4 from "path";
2157
- import fs2 from "fs/promises";
2242
+ import path5 from "path";
2243
+ import fs3 from "fs/promises";
2158
2244
 
2159
2245
  // src/ast/generateZodSchema.ts
2160
2246
  var CodeGenContext = class {
@@ -2463,7 +2549,7 @@ function getSchemaOutputPath(sourceFile, dist, rootDir) {
2463
2549
  }
2464
2550
  const idx = rel.lastIndexOf("/");
2465
2551
  const relDir = idx >= 0 ? rel.slice(0, idx) : "";
2466
- return path4.resolve(rootDir, dist, relDir, "zod.js");
2552
+ return path5.resolve(rootDir, dist, relDir, "zod.js");
2467
2553
  }
2468
2554
  function getRuntimeSchemaPath(filePath, dist, rootDir) {
2469
2555
  let rel = filePath.replace(/\\/g, "/");
@@ -2474,7 +2560,7 @@ function getRuntimeSchemaPath(filePath, dist, rootDir) {
2474
2560
  }
2475
2561
  const idx = rel.lastIndexOf("/");
2476
2562
  const relDir = idx >= 0 ? rel.slice(0, idx) : "";
2477
- return path4.resolve(rootDir, dist, relDir, "zod.js");
2563
+ return path5.resolve(rootDir, dist, relDir, "zod.js");
2478
2564
  }
2479
2565
  function getHelpersImportPath(relDir) {
2480
2566
  if (!relDir) return `./${HELPERS_FILENAME}`;
@@ -2524,7 +2610,7 @@ async function generateSchemaFiles(routes, rootDir, dist) {
2524
2610
  }
2525
2611
  const fileEntries = [];
2526
2612
  for (const [filePath, fileSources] of sourcesByFile) {
2527
- const relFile = path4.relative(rootDir, filePath).replace(/\\/g, "/");
2613
+ const relFile = path5.relative(rootDir, filePath).replace(/\\/g, "/");
2528
2614
  const outputPath = getSchemaOutputPath(relFile, dist, rootDir);
2529
2615
  const allTypes = allTypesByFile.get(filePath) ?? /* @__PURE__ */ new Map();
2530
2616
  let relForDir = relFile;
@@ -2539,7 +2625,7 @@ async function generateSchemaFiles(routes, rootDir, dist) {
2539
2625
  }
2540
2626
  const allSourceCode = fileEntries.map((e) => e.source).join("\n");
2541
2627
  if (usesCoerceHelpers(allSourceCode)) {
2542
- const helpersPath = path4.resolve(rootDir, dist, HELPERS_FILENAME);
2628
+ const helpersPath = path5.resolve(rootDir, dist, HELPERS_FILENAME);
2543
2629
  await writeSchemaFile(helpersPath, generateHelpersFileSource());
2544
2630
  }
2545
2631
  await Promise.all(
@@ -2547,8 +2633,8 @@ async function generateSchemaFiles(routes, rootDir, dist) {
2547
2633
  );
2548
2634
  }
2549
2635
  async function writeSchemaFile(outputPath, source) {
2550
- await fs2.mkdir(path4.dirname(outputPath), { recursive: true });
2551
- await fs2.writeFile(outputPath, source, "utf-8");
2636
+ await fs3.mkdir(path5.dirname(outputPath), { recursive: true });
2637
+ await fs3.writeFile(outputPath, source, "utf-8");
2552
2638
  }
2553
2639
 
2554
2640
  // src/server/createServer.ts
@@ -2596,15 +2682,15 @@ function limitStreamSize(stream, maxSize) {
2596
2682
  }
2597
2683
  });
2598
2684
  }
2599
- function findAllowedMethods(routes, path9) {
2685
+ function findAllowedMethods(routes, path10) {
2600
2686
  const methods = /* @__PURE__ */ new Set();
2601
2687
  for (const route of routes) {
2602
- if (route.urlPath === path9) {
2688
+ if (route.urlPath === path10) {
2603
2689
  methods.add(route.method);
2604
2690
  continue;
2605
2691
  }
2606
2692
  if (route.isDynamic) {
2607
- const params = matchDynamicPath(route.urlPath, path9, route.paramNames, route.isCatchAll);
2693
+ const params = matchDynamicPath(route.urlPath, path10, route.paramNames, route.isCatchAll);
2608
2694
  if (params !== null) {
2609
2695
  methods.add(route.method);
2610
2696
  }
@@ -2690,7 +2776,7 @@ async function handleRequest(routes, rootDir, dist, req, res, configMiddlewares,
2690
2776
  }
2691
2777
  ctx.params = match.params;
2692
2778
  const { route } = match;
2693
- const absoluteFilePath = path5.resolve(rootDir, route.filePath);
2779
+ const absoluteFilePath = path6.resolve(rootDir, route.filePath);
2694
2780
  const routeModule = await loadRouteModule(absoluteFilePath, route.method);
2695
2781
  const input = await resolveInput(route.method, request);
2696
2782
  const inputType = getInputTypeForMethod(route.method);
@@ -2764,8 +2850,8 @@ function applyPluginWrappers(server, handlerWrappers, upgradeWrappers) {
2764
2850
  }
2765
2851
 
2766
2852
  // src/cli/generateRoutes.ts
2767
- import fs3 from "fs";
2768
- import path6 from "path";
2853
+ import fs4 from "fs";
2854
+ import path7 from "path";
2769
2855
 
2770
2856
  // src/middleware/loadMiddlewares.ts
2771
2857
  var middlewareCache = /* @__PURE__ */ new Map();
@@ -2921,7 +3007,7 @@ function resolveDeclaration(decl) {
2921
3007
  }
2922
3008
 
2923
3009
  // src/cli/createAppCore.ts
2924
- var DEFAULT_DIST = ".faapi/build";
3010
+ var DEFAULT_DIST = "dist";
2925
3011
  var DEFAULT_PORT = 3e3;
2926
3012
  var ROUTES_FILE = "faapi-routes.js";
2927
3013
  var PATTERNS = ["src/api/**/*.ts"];
@@ -2943,8 +3029,8 @@ function isFaapiConfigKey(key) {
2943
3029
  async function createAppBase(options) {
2944
3030
  const rootDir = options?.rootDir ?? process.cwd();
2945
3031
  const dist = options?.dist ?? process.env.FAAPI_DIST ?? DEFAULT_DIST;
2946
- const routesPath = path7.resolve(rootDir, dist, ROUTES_FILE);
2947
- if (!fs4.existsSync(routesPath)) {
3032
+ const routesPath = path8.resolve(rootDir, dist, ROUTES_FILE);
3033
+ if (!fs5.existsSync(routesPath)) {
2948
3034
  throw new Error(
2949
3035
  `[faapi] ${dist}/${ROUTES_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
2950
3036
  );
@@ -3147,8 +3233,8 @@ async function createAppBase(options) {
3147
3233
 
3148
3234
  // src/router/scanRoutes.ts
3149
3235
  import fg from "fast-glob";
3150
- import path8 from "path";
3151
- import fs5 from "fs";
3236
+ import path9 from "path";
3237
+ import fs6 from "fs";
3152
3238
 
3153
3239
  // src/router/constants.ts
3154
3240
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
@@ -3158,9 +3244,9 @@ function isHttpMethod(value) {
3158
3244
  }
3159
3245
 
3160
3246
  // src/utils/normalizePath.ts
3161
- function normalizePath(path9) {
3162
- if (!path9) return "";
3163
- let result = path9.replace(/\\/g, "/");
3247
+ function normalizePath(path10) {
3248
+ if (!path10) return "";
3249
+ let result = path10.replace(/\\/g, "/");
3164
3250
  result = result.replace(/\/+/g, "/");
3165
3251
  result = result.replace(/\/+$/, "");
3166
3252
  if (result && !result.startsWith("/")) {
@@ -3209,38 +3295,38 @@ function filePathToUrlPath(filePath) {
3209
3295
  // src/router/scanRoutes.ts
3210
3296
  var APP_DIR = "src";
3211
3297
  function toProdAbsPath(sourceAbsPath, rootDir, dist) {
3212
- let rel = path8.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
3298
+ let rel = path9.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
3213
3299
  if (rel.startsWith(`${APP_DIR}/`)) {
3214
3300
  rel = rel.slice(APP_DIR.length + 1);
3215
3301
  }
3216
3302
  const prodRel = `${dist}/${rel.replace(/\.ts$/, ".js")}`;
3217
- return path8.resolve(rootDir, prodRel);
3303
+ return path9.resolve(rootDir, prodRel);
3218
3304
  }
3219
3305
  async function findMergedMiddlewares(routeFilePath, rootDir, dist) {
3220
- const routeDir = path8.dirname(routeFilePath);
3221
- const resolvedRoot = path8.resolve(rootDir);
3306
+ const routeDir = path9.dirname(routeFilePath);
3307
+ const resolvedRoot = path9.resolve(rootDir);
3222
3308
  const mwPaths = [];
3223
- let currentDir = path8.resolve(rootDir, routeDir);
3309
+ let currentDir = path9.resolve(rootDir, routeDir);
3224
3310
  while (true) {
3225
3311
  if (dist) {
3226
- const mwPath = path8.join(currentDir, "middlewares.js");
3227
- const absMwPath = path8.resolve(rootDir, mwPath);
3312
+ const mwPath = path9.join(currentDir, "middlewares.js");
3313
+ const absMwPath = path9.resolve(rootDir, mwPath);
3228
3314
  const prodAbsMwPath = toProdAbsPath(absMwPath, rootDir, dist);
3229
- if (fs5.existsSync(prodAbsMwPath)) {
3315
+ if (fs6.existsSync(prodAbsMwPath)) {
3230
3316
  mwPaths.push(prodAbsMwPath);
3231
3317
  }
3232
3318
  } else {
3233
3319
  for (const ext of [".ts", ".js"]) {
3234
- const mwPath = path8.join(currentDir, `middlewares${ext}`);
3235
- const absMwPath = path8.resolve(rootDir, mwPath);
3236
- if (fs5.existsSync(absMwPath)) {
3320
+ const mwPath = path9.join(currentDir, `middlewares${ext}`);
3321
+ const absMwPath = path9.resolve(rootDir, mwPath);
3322
+ if (fs6.existsSync(absMwPath)) {
3237
3323
  mwPaths.push(absMwPath);
3238
3324
  break;
3239
3325
  }
3240
3326
  }
3241
3327
  }
3242
3328
  if (currentDir === resolvedRoot) break;
3243
- const parentDir = path8.dirname(currentDir);
3329
+ const parentDir = path9.dirname(currentDir);
3244
3330
  if (parentDir === currentDir) break;
3245
3331
  currentDir = parentDir;
3246
3332
  }
@@ -3302,7 +3388,7 @@ async function scanRoutes(rootDir, patterns, dist) {
3302
3388
  const normalizedFile = file.replace(/\\/g, "/");
3303
3389
  const fileName = normalizedFile.split("/").pop();
3304
3390
  if (fileName === "handler.ts" || fileName === "handler.js") {
3305
- const absPath = path8.resolve(rootDir, normalizedFile);
3391
+ const absPath = path9.resolve(rootDir, normalizedFile);
3306
3392
  const importPath = dist ? toProdAbsPath(absPath, rootDir, dist) : absPath;
3307
3393
  const urlPath = filePathToUrlPath(normalizedFile);
3308
3394
  const paramNames = extractParamNames(urlPath);
@@ -3373,6 +3459,7 @@ export {
3373
3459
  collectRouteSchemaSources,
3374
3460
  cors,
3375
3461
  createProdApp as createApp,
3462
+ createContext,
3376
3463
  createDevApp,
3377
3464
  createProdApp,
3378
3465
  createProgram,
@@ -3380,7 +3467,9 @@ export {
3380
3467
  getInputTypeForMethod,
3381
3468
  helmet,
3382
3469
  invalidateProgramCache,
3470
+ invokeHandler,
3383
3471
  loadConfig,
3472
+ loadEnv,
3384
3473
  logger,
3385
3474
  resolveTypeNode
3386
3475
  };