@faapi/faapi 6.18.1 → 6.20.0

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.
@@ -930,7 +930,14 @@ interface FaapiContext {
930
930
  */
931
931
  log: Logger;
932
932
  request: Request;
933
- params: Record<string, string>;
933
+ /**
934
+ * 动态路由参数
935
+ *
936
+ * handler 声明了 `params: XxxParams` 类型时,管线按声明类型校验并回写
937
+ * 转换后的值(number/boolean 字段不再是字符串);未声明时保持 URL 原始
938
+ * 字符串。以 handler 形参上的接口声明为准读取(注入即本对象)。
939
+ */
940
+ params: Record<string, string | number | boolean>;
934
941
  query: URLSearchParams;
935
942
  headers: Headers;
936
943
  method: string;
package/dist/testing.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as FaapiContext, F as FaapiMiddleware, I as InjectorMap, R as RouteManifest, W as WsRouteManifest, C as CorsOptions, H as HelmetOptions } from './routeTypes-BAhPIzcG.js';
1
+ import { a as FaapiContext, F as FaapiMiddleware, I as InjectorMap, R as RouteManifest, W as WsRouteManifest, C as CorsOptions, H as HelmetOptions } from './routeTypes-DTqdo28V.js';
2
2
  import { Server } from 'node:http';
3
3
  import { WebSocket } from 'ws';
4
4
 
package/dist/testing.js CHANGED
@@ -1108,8 +1108,10 @@ function getAgentHandle(ctx) {
1108
1108
  // src/injection/injectParams.ts
1109
1109
  function getBuiltinInjectionValue(type, ctx, body) {
1110
1110
  switch (type) {
1111
- case "query":
1112
- return queryToObject(ctx.query);
1111
+ case "query": {
1112
+ const validated = ctx.__validatedQuery;
1113
+ return validated ?? queryToObject(ctx.query);
1114
+ }
1113
1115
  case "params":
1114
1116
  return ctx.params;
1115
1117
  case "headers":
@@ -2663,6 +2665,37 @@ function collectRouteSchemaSources(routes, rootDir) {
2663
2665
  typeInfo,
2664
2666
  coerce: isForm || void 0
2665
2667
  });
2668
+ const paramsArg = meta.params.find((p) => p.type === "params");
2669
+ if (paramsArg?.typeName) {
2670
+ sources.push({
2671
+ urlPath: entry.urlPath,
2672
+ filePath,
2673
+ schemaName: getSchemaName(method, "params"),
2674
+ typeInfo: resolver.resolve(paramsArg.typeName) ?? null
2675
+ });
2676
+ }
2677
+ if (inputType === "body") {
2678
+ const queryArg = meta.params.find((p) => p.type === "query");
2679
+ if (queryArg?.typeName) {
2680
+ sources.push({
2681
+ urlPath: entry.urlPath,
2682
+ filePath,
2683
+ schemaName: getSchemaName(method, "query"),
2684
+ typeInfo: resolver.resolve(queryArg.typeName) ?? null
2685
+ });
2686
+ }
2687
+ } else if (hasBody(method)) {
2688
+ const bodyArg = meta.params.find((p) => p.type === "body") ?? meta.params.find((p) => p.type === "form");
2689
+ if (bodyArg?.typeName) {
2690
+ sources.push({
2691
+ urlPath: entry.urlPath,
2692
+ filePath,
2693
+ schemaName: getSchemaName(method, "body"),
2694
+ typeInfo: resolver.resolve(bodyArg.typeName) ?? null,
2695
+ coerce: bodyArg.type === "form" || void 0
2696
+ });
2697
+ }
2698
+ }
2666
2699
  }
2667
2700
  }
2668
2701
  return { sources, resolversByFile };
@@ -4962,7 +4995,48 @@ function createRoutePipeline(opts) {
4962
4995
  if (!result.valid) {
4963
4996
  throw new ValidationError("\u53C2\u6570\u6821\u9A8C\u5931\u8D25", result.issues);
4964
4997
  }
4965
- const body = inputType === "query" && hasBody(route.method) ? await resolveBodyForQueryMethod(request) : hasBody(route.method) ? result.data : void 0;
4998
+ if (inputType === "query" && result.data !== input && typeof result.data === "object" && result.data !== null) {
4999
+ ctx.__validatedQuery = {
5000
+ ...input,
5001
+ ...result.data
5002
+ };
5003
+ }
5004
+ const paramsResult = await validateInput(schemaPath, route.method, "params", match.params);
5005
+ if (!paramsResult.valid) {
5006
+ throw new ValidationError("\u53C2\u6570\u6821\u9A8C\u5931\u8D25", paramsResult.issues);
5007
+ }
5008
+ if (paramsResult.data !== match.params) {
5009
+ ctx.params = typeof paramsResult.data === "object" && paramsResult.data !== null ? {
5010
+ ...match.params,
5011
+ ...paramsResult.data
5012
+ } : match.params;
5013
+ }
5014
+ if (inputType === "body") {
5015
+ const rawQuery = queryToObject(url.searchParams);
5016
+ const queryResult = await validateInput(schemaPath, route.method, "query", rawQuery);
5017
+ if (!queryResult.valid) {
5018
+ throw new ValidationError("\u53C2\u6570\u6821\u9A8C\u5931\u8D25", queryResult.issues);
5019
+ }
5020
+ if (queryResult.data !== rawQuery && typeof queryResult.data === "object" && queryResult.data !== null) {
5021
+ ctx.__validatedQuery = {
5022
+ ...rawQuery,
5023
+ ...queryResult.data
5024
+ };
5025
+ }
5026
+ }
5027
+ let body;
5028
+ if (inputType === "query" && hasBody(route.method)) {
5029
+ const parsed = await resolveBodyForQueryMethod(request);
5030
+ if (parsed !== void 0) {
5031
+ const bodyResult = await validateInput(schemaPath, route.method, "body", parsed);
5032
+ if (!bodyResult.valid) {
5033
+ throw new ValidationError("\u53C2\u6570\u6821\u9A8C\u5931\u8D25", bodyResult.issues);
5034
+ }
5035
+ body = bodyResult.data;
5036
+ }
5037
+ } else if (hasBody(route.method)) {
5038
+ body = result.data;
5039
+ }
4966
5040
  if (route.middlewares === void 0 && route.injectors === void 0 && route.middlewarePaths) {
4967
5041
  await ensureMiddlewaresCompiled(route.middlewarePaths, rootDir);
4968
5042
  const bundle = await loadMergedMiddlewares(route.middlewarePaths);