@geekmidas/constructs 3.0.2 → 3.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/{AmazonApiGatewayEndpointAdaptor-B30TqZRX.d.mts → AmazonApiGatewayEndpointAdaptor-CS-m4WZy.d.mts} +11 -2
  3. package/dist/AmazonApiGatewayEndpointAdaptor-CS-m4WZy.d.mts.map +1 -0
  4. package/dist/{AmazonApiGatewayEndpointAdaptor-ciUHVvPX.mjs → AmazonApiGatewayEndpointAdaptor-Cmm9d_Po.mjs} +20 -2
  5. package/dist/AmazonApiGatewayEndpointAdaptor-Cmm9d_Po.mjs.map +1 -0
  6. package/dist/{AmazonApiGatewayEndpointAdaptor-jDYqkgFE.cjs → AmazonApiGatewayEndpointAdaptor-D7mhDtSL.cjs} +20 -2
  7. package/dist/AmazonApiGatewayEndpointAdaptor-D7mhDtSL.cjs.map +1 -0
  8. package/dist/{AmazonApiGatewayEndpointAdaptor-D4lAuXA-.d.cts → AmazonApiGatewayEndpointAdaptor-DisDeEF0.d.cts} +11 -2
  9. package/dist/AmazonApiGatewayEndpointAdaptor-DisDeEF0.d.cts.map +1 -0
  10. package/dist/{AmazonApiGatewayV1EndpointAdaptor-B5xFYauV.d.cts → AmazonApiGatewayV1EndpointAdaptor-D6wNqjth.d.cts} +3 -3
  11. package/dist/AmazonApiGatewayV1EndpointAdaptor-D6wNqjth.d.cts.map +1 -0
  12. package/dist/{AmazonApiGatewayV1EndpointAdaptor-CG1UJoAK.d.mts → AmazonApiGatewayV1EndpointAdaptor-DSMe7dZ4.d.mts} +3 -3
  13. package/dist/AmazonApiGatewayV1EndpointAdaptor-DSMe7dZ4.d.mts.map +1 -0
  14. package/dist/{AmazonApiGatewayV1EndpointAdaptor-CW0BCUkm.cjs → AmazonApiGatewayV1EndpointAdaptor-DstIoQBv.cjs} +4 -4
  15. package/dist/{AmazonApiGatewayV1EndpointAdaptor-CW0BCUkm.cjs.map → AmazonApiGatewayV1EndpointAdaptor-DstIoQBv.cjs.map} +1 -1
  16. package/dist/{AmazonApiGatewayV1EndpointAdaptor-COLHHAsd.mjs → AmazonApiGatewayV1EndpointAdaptor-zrlehWUG.mjs} +4 -4
  17. package/dist/{AmazonApiGatewayV1EndpointAdaptor-COLHHAsd.mjs.map → AmazonApiGatewayV1EndpointAdaptor-zrlehWUG.mjs.map} +1 -1
  18. package/dist/{AmazonApiGatewayV2EndpointAdaptor-ofjuxcPV.d.mts → AmazonApiGatewayV2EndpointAdaptor-ByljgCnY.d.mts} +3 -3
  19. package/dist/AmazonApiGatewayV2EndpointAdaptor-ByljgCnY.d.mts.map +1 -0
  20. package/dist/{AmazonApiGatewayV2EndpointAdaptor-C7ZrwcTX.cjs → AmazonApiGatewayV2EndpointAdaptor-DaAHSnqs.cjs} +7 -9
  21. package/dist/AmazonApiGatewayV2EndpointAdaptor-DaAHSnqs.cjs.map +1 -0
  22. package/dist/{AmazonApiGatewayV2EndpointAdaptor-Cl9ERGyi.mjs → AmazonApiGatewayV2EndpointAdaptor-Dj5v-I6S.mjs} +6 -9
  23. package/dist/AmazonApiGatewayV2EndpointAdaptor-Dj5v-I6S.mjs.map +1 -0
  24. package/dist/{AmazonApiGatewayV2EndpointAdaptor-OkwjABOz.d.cts → AmazonApiGatewayV2EndpointAdaptor-PcMFUEF9.d.cts} +3 -3
  25. package/dist/AmazonApiGatewayV2EndpointAdaptor-PcMFUEF9.d.cts.map +1 -0
  26. package/dist/{Endpoint-BiPM0glm.d.mts → Endpoint-BIvS-rKH.d.mts} +6 -6
  27. package/dist/Endpoint-BIvS-rKH.d.mts.map +1 -0
  28. package/dist/{Endpoint-C9N6CmvB.d.cts → Endpoint-BPh52sXZ.d.cts} +6 -6
  29. package/dist/Endpoint-BPh52sXZ.d.cts.map +1 -0
  30. package/dist/Endpoint-BcxvF4F3.cjs.map +1 -1
  31. package/dist/Endpoint-DvY3aqAy.mjs.map +1 -1
  32. package/dist/{EndpointBuilder-B0Aj5jbB.d.mts → EndpointBuilder-3xH8Gllw.d.mts} +4 -3
  33. package/dist/EndpointBuilder-3xH8Gllw.d.mts.map +1 -0
  34. package/dist/{EndpointBuilder-Bel6RS7W.d.cts → EndpointBuilder-BmVTFp1A.d.cts} +4 -3
  35. package/dist/EndpointBuilder-BmVTFp1A.d.cts.map +1 -0
  36. package/dist/{EndpointBuilder-D1RtrBu1.mjs → EndpointBuilder-CMzbGG2c.mjs} +5 -1
  37. package/dist/{EndpointBuilder-D1RtrBu1.mjs.map → EndpointBuilder-CMzbGG2c.mjs.map} +1 -1
  38. package/dist/{EndpointBuilder-fXmTxRyW.cjs → EndpointBuilder-QdDf3x87.cjs} +5 -1
  39. package/dist/{EndpointBuilder-fXmTxRyW.cjs.map → EndpointBuilder-QdDf3x87.cjs.map} +1 -1
  40. package/dist/{EndpointFactory-DBfTbSTX.cjs → EndpointFactory-AsfUsn-v.cjs} +2 -2
  41. package/dist/{EndpointFactory-DBfTbSTX.cjs.map → EndpointFactory-AsfUsn-v.cjs.map} +1 -1
  42. package/dist/{EndpointFactory-DkB8yxdQ.mjs → EndpointFactory-CkPaFZA0.mjs} +2 -2
  43. package/dist/{EndpointFactory-DkB8yxdQ.mjs.map → EndpointFactory-CkPaFZA0.mjs.map} +1 -1
  44. package/dist/{EndpointFactory-CWIeWCRG.d.mts → EndpointFactory-DKHnUYl8.d.mts} +3 -3
  45. package/dist/{EndpointFactory-CXvakOkn.d.cts.map → EndpointFactory-DKHnUYl8.d.mts.map} +1 -1
  46. package/dist/{EndpointFactory-CXvakOkn.d.cts → EndpointFactory-DaDXPRAg.d.cts} +3 -3
  47. package/dist/{EndpointFactory-CWIeWCRG.d.mts.map → EndpointFactory-DaDXPRAg.d.cts.map} +1 -1
  48. package/dist/{HonoEndpointAdaptor-C3AdQ0xS.d.mts → HonoEndpointAdaptor-Cb45sqM9.d.mts} +4 -4
  49. package/dist/{HonoEndpointAdaptor-BCql3gLP.d.cts.map → HonoEndpointAdaptor-Cb45sqM9.d.mts.map} +1 -1
  50. package/dist/{HonoEndpointAdaptor-y9fCIxaz.mjs → HonoEndpointAdaptor-D7N5oiO0.mjs} +6 -3
  51. package/dist/HonoEndpointAdaptor-D7N5oiO0.mjs.map +1 -0
  52. package/dist/{HonoEndpointAdaptor-CavBd6P2.cjs → HonoEndpointAdaptor-Jbptyuxv.cjs} +6 -3
  53. package/dist/HonoEndpointAdaptor-Jbptyuxv.cjs.map +1 -0
  54. package/dist/{HonoEndpointAdaptor-BCql3gLP.d.cts → HonoEndpointAdaptor-VStaYQHr.d.cts} +2 -2
  55. package/dist/{HonoEndpointAdaptor-C3AdQ0xS.d.mts.map → HonoEndpointAdaptor-VStaYQHr.d.cts.map} +1 -1
  56. package/dist/{TestEndpointAdaptor-DjQSuxRq.cjs → TestEndpointAdaptor-BKoJpTGT.cjs} +5 -2
  57. package/dist/TestEndpointAdaptor-BKoJpTGT.cjs.map +1 -0
  58. package/dist/{TestEndpointAdaptor-qSWV8dpS.d.mts → TestEndpointAdaptor-Bt-uW6SC.d.mts} +2 -2
  59. package/dist/{TestEndpointAdaptor-CCf3Dg0u.d.cts.map → TestEndpointAdaptor-Bt-uW6SC.d.mts.map} +1 -1
  60. package/dist/{TestEndpointAdaptor-CDOhCmIk.mjs → TestEndpointAdaptor-DHVaHmhw.mjs} +5 -2
  61. package/dist/TestEndpointAdaptor-DHVaHmhw.mjs.map +1 -0
  62. package/dist/{TestEndpointAdaptor-CCf3Dg0u.d.cts → TestEndpointAdaptor-UKhHj1RR.d.cts} +2 -2
  63. package/dist/{TestEndpointAdaptor-qSWV8dpS.d.mts.map → TestEndpointAdaptor-UKhHj1RR.d.cts.map} +1 -1
  64. package/dist/adaptors/aws.cjs +4 -4
  65. package/dist/adaptors/aws.d.cts +5 -5
  66. package/dist/adaptors/aws.d.mts +5 -5
  67. package/dist/adaptors/aws.mjs +4 -4
  68. package/dist/adaptors/hono.cjs +2 -2
  69. package/dist/adaptors/hono.d.cts +3 -3
  70. package/dist/adaptors/hono.d.mts +3 -3
  71. package/dist/adaptors/hono.mjs +2 -2
  72. package/dist/adaptors/testing.cjs +1 -1
  73. package/dist/adaptors/testing.d.cts +3 -3
  74. package/dist/adaptors/testing.d.mts +3 -3
  75. package/dist/adaptors/testing.mjs +1 -1
  76. package/dist/crons/Cron.d.cts +1 -1
  77. package/dist/crons/Cron.d.mts +1 -1
  78. package/dist/crons/CronBuilder.d.cts +1 -1
  79. package/dist/crons/CronBuilder.d.mts +1 -1
  80. package/dist/crons/index.d.cts +5 -5
  81. package/dist/crons/index.d.mts +5 -5
  82. package/dist/endpoints/AmazonApiGatewayEndpointAdaptor.cjs +1 -1
  83. package/dist/endpoints/AmazonApiGatewayEndpointAdaptor.d.cts +3 -3
  84. package/dist/endpoints/AmazonApiGatewayEndpointAdaptor.d.mts +3 -3
  85. package/dist/endpoints/AmazonApiGatewayEndpointAdaptor.mjs +1 -1
  86. package/dist/endpoints/AmazonApiGatewayV1EndpointAdaptor.cjs +3 -3
  87. package/dist/endpoints/AmazonApiGatewayV1EndpointAdaptor.d.cts +4 -4
  88. package/dist/endpoints/AmazonApiGatewayV1EndpointAdaptor.d.mts +4 -4
  89. package/dist/endpoints/AmazonApiGatewayV1EndpointAdaptor.mjs +3 -3
  90. package/dist/endpoints/AmazonApiGatewayV2EndpointAdaptor.cjs +2 -3
  91. package/dist/endpoints/AmazonApiGatewayV2EndpointAdaptor.d.cts +4 -4
  92. package/dist/endpoints/AmazonApiGatewayV2EndpointAdaptor.d.mts +4 -4
  93. package/dist/endpoints/AmazonApiGatewayV2EndpointAdaptor.mjs +2 -3
  94. package/dist/endpoints/Endpoint.d.cts +2 -2
  95. package/dist/endpoints/Endpoint.d.mts +2 -2
  96. package/dist/endpoints/EndpointBuilder.cjs +1 -1
  97. package/dist/endpoints/EndpointBuilder.d.cts +3 -3
  98. package/dist/endpoints/EndpointBuilder.d.mts +3 -3
  99. package/dist/endpoints/EndpointBuilder.mjs +1 -1
  100. package/dist/endpoints/EndpointFactory.cjs +2 -2
  101. package/dist/endpoints/EndpointFactory.d.cts +4 -4
  102. package/dist/endpoints/EndpointFactory.d.mts +4 -4
  103. package/dist/endpoints/EndpointFactory.mjs +2 -2
  104. package/dist/endpoints/HonoEndpointAdaptor.cjs +2 -2
  105. package/dist/endpoints/HonoEndpointAdaptor.d.cts +3 -3
  106. package/dist/endpoints/HonoEndpointAdaptor.d.mts +3 -3
  107. package/dist/endpoints/HonoEndpointAdaptor.mjs +2 -2
  108. package/dist/endpoints/TestEndpointAdaptor.cjs +1 -1
  109. package/dist/endpoints/TestEndpointAdaptor.d.cts +3 -3
  110. package/dist/endpoints/TestEndpointAdaptor.d.mts +3 -3
  111. package/dist/endpoints/TestEndpointAdaptor.mjs +1 -1
  112. package/dist/endpoints/audit.d.cts +2 -2
  113. package/dist/endpoints/audit.d.mts +2 -2
  114. package/dist/endpoints/helpers.d.cts +2 -2
  115. package/dist/endpoints/helpers.d.mts +2 -2
  116. package/dist/endpoints/index.cjs +2 -2
  117. package/dist/endpoints/index.d.cts +7 -7
  118. package/dist/endpoints/index.d.mts +7 -7
  119. package/dist/endpoints/index.mjs +2 -2
  120. package/dist/endpoints/lazyAccessors.d.cts +3 -3
  121. package/dist/endpoints/lazyAccessors.d.mts +3 -3
  122. package/dist/endpoints/parseHonoQuery.cjs +1 -1
  123. package/dist/endpoints/parseHonoQuery.d.cts +2 -2
  124. package/dist/endpoints/parseHonoQuery.d.cts.map +1 -1
  125. package/dist/endpoints/parseHonoQuery.d.mts +2 -2
  126. package/dist/endpoints/parseHonoQuery.d.mts.map +1 -1
  127. package/dist/endpoints/parseHonoQuery.mjs +1 -1
  128. package/dist/endpoints/parseQueryParams.cjs +1 -1
  129. package/dist/endpoints/parseQueryParams.d.cts +2 -2
  130. package/dist/endpoints/parseQueryParams.d.cts.map +1 -1
  131. package/dist/endpoints/parseQueryParams.d.mts +2 -2
  132. package/dist/endpoints/parseQueryParams.d.mts.map +1 -1
  133. package/dist/endpoints/parseQueryParams.mjs +1 -1
  134. package/dist/endpoints/processAudits.d.cts +2 -2
  135. package/dist/endpoints/processAudits.d.mts +2 -2
  136. package/dist/endpoints/rls.d.cts +2 -2
  137. package/dist/endpoints/rls.d.mts +2 -2
  138. package/dist/functions/index.d.cts +1 -1
  139. package/dist/functions/index.d.mts +1 -1
  140. package/dist/index-BfeupgMl.d.cts +12 -0
  141. package/dist/{index-BRoc67OX.d.cts.map → index-BfeupgMl.d.cts.map} +1 -1
  142. package/dist/index-CtMNRkV7.d.mts +12 -0
  143. package/dist/{index-_5DYCNAt.d.mts.map → index-CtMNRkV7.d.mts.map} +1 -1
  144. package/dist/{lazyAccessors-DXkJpnyX.d.mts → lazyAccessors-Ba8HxeIC.d.cts} +2 -2
  145. package/dist/{lazyAccessors-DXkJpnyX.d.mts.map → lazyAccessors-Ba8HxeIC.d.cts.map} +1 -1
  146. package/dist/{lazyAccessors-DdZaA716.d.cts → lazyAccessors-D9tzUZaz.d.mts} +2 -2
  147. package/dist/{lazyAccessors-DdZaA716.d.cts.map → lazyAccessors-D9tzUZaz.d.mts.map} +1 -1
  148. package/dist/parseHonoQuery-BBs8CX-H.cjs +24 -0
  149. package/dist/parseHonoQuery-BBs8CX-H.cjs.map +1 -0
  150. package/dist/parseHonoQuery-Cp8CII0P.mjs +18 -0
  151. package/dist/parseHonoQuery-Cp8CII0P.mjs.map +1 -0
  152. package/dist/parseQueryParams-B4RK1za3.cjs +31 -0
  153. package/dist/parseQueryParams-B4RK1za3.cjs.map +1 -0
  154. package/dist/parseQueryParams-DS3Qmrbd.mjs +25 -0
  155. package/dist/parseQueryParams-DS3Qmrbd.mjs.map +1 -0
  156. package/dist/subscribers/index.d.cts +2 -2
  157. package/dist/subscribers/index.d.mts +2 -2
  158. package/dist/subscribers/index.d.mts.map +1 -1
  159. package/package.json +15 -13
  160. package/src/endpoints/AmazonApiGatewayEndpointAdaptor.ts +32 -1
  161. package/src/endpoints/AmazonApiGatewayV1EndpointAdaptor.ts +5 -1
  162. package/src/endpoints/AmazonApiGatewayV2EndpointAdaptor.ts +8 -18
  163. package/src/endpoints/Endpoint.ts +7 -4
  164. package/src/endpoints/EndpointBuilder.ts +6 -1
  165. package/src/endpoints/EndpointFactory.ts +1 -1
  166. package/src/endpoints/HonoEndpointAdaptor.ts +9 -0
  167. package/src/endpoints/TestEndpointAdaptor.ts +3 -0
  168. package/src/endpoints/__tests__/AmazonApiGatewayV1EndpointAdaptor.spec.ts +119 -7
  169. package/src/endpoints/__tests__/AmazonApiGatewayV2EndpointAdaptor.events.spec.ts +4 -2
  170. package/src/endpoints/__tests__/AmazonApiGatewayV2EndpointAdaptor.spec.ts +146 -7
  171. package/src/endpoints/__tests__/EndpointBuilder.spec.ts +35 -0
  172. package/src/endpoints/__tests__/HonoEndpointAdaptor.spec.ts +109 -4
  173. package/src/endpoints/__tests__/TestEndpointAdaptor.spec.ts +139 -0
  174. package/src/endpoints/parseHonoQuery.ts +9 -46
  175. package/src/endpoints/parseQueryParams.ts +12 -33
  176. package/dist/AmazonApiGatewayEndpointAdaptor-B30TqZRX.d.mts.map +0 -1
  177. package/dist/AmazonApiGatewayEndpointAdaptor-D4lAuXA-.d.cts.map +0 -1
  178. package/dist/AmazonApiGatewayEndpointAdaptor-ciUHVvPX.mjs.map +0 -1
  179. package/dist/AmazonApiGatewayEndpointAdaptor-jDYqkgFE.cjs.map +0 -1
  180. package/dist/AmazonApiGatewayV1EndpointAdaptor-B5xFYauV.d.cts.map +0 -1
  181. package/dist/AmazonApiGatewayV1EndpointAdaptor-CG1UJoAK.d.mts.map +0 -1
  182. package/dist/AmazonApiGatewayV2EndpointAdaptor-C7ZrwcTX.cjs.map +0 -1
  183. package/dist/AmazonApiGatewayV2EndpointAdaptor-Cl9ERGyi.mjs.map +0 -1
  184. package/dist/AmazonApiGatewayV2EndpointAdaptor-OkwjABOz.d.cts.map +0 -1
  185. package/dist/AmazonApiGatewayV2EndpointAdaptor-ofjuxcPV.d.mts.map +0 -1
  186. package/dist/Endpoint-BiPM0glm.d.mts.map +0 -1
  187. package/dist/Endpoint-C9N6CmvB.d.cts.map +0 -1
  188. package/dist/EndpointBuilder-B0Aj5jbB.d.mts.map +0 -1
  189. package/dist/EndpointBuilder-Bel6RS7W.d.cts.map +0 -1
  190. package/dist/HonoEndpointAdaptor-CavBd6P2.cjs.map +0 -1
  191. package/dist/HonoEndpointAdaptor-y9fCIxaz.mjs.map +0 -1
  192. package/dist/TestEndpointAdaptor-CDOhCmIk.mjs.map +0 -1
  193. package/dist/TestEndpointAdaptor-DjQSuxRq.cjs.map +0 -1
  194. package/dist/index-BRoc67OX.d.cts +0 -12
  195. package/dist/index-_5DYCNAt.d.mts +0 -12
  196. package/dist/parseHonoQuery-D4MhxTRc.cjs +0 -39
  197. package/dist/parseHonoQuery-D4MhxTRc.cjs.map +0 -1
  198. package/dist/parseHonoQuery-DpK3sGPc.mjs +0 -33
  199. package/dist/parseHonoQuery-DpK3sGPc.mjs.map +0 -1
  200. package/dist/parseQueryParams-BSNkjmZ9.cjs +0 -40
  201. package/dist/parseQueryParams-BSNkjmZ9.cjs.map +0 -1
  202. package/dist/parseQueryParams-UMTRnRrW.mjs +0 -34
  203. package/dist/parseQueryParams-UMTRnRrW.mjs.map +0 -1
@@ -59,7 +59,11 @@ export class AmazonApiGatewayV1Endpoint<
59
59
  }
60
60
 
61
61
  return {
62
- body: e.body ? JSON.parse(e.body) : undefined,
62
+ body: AmazonApiGatewayEndpoint.decodeBody(
63
+ e.body,
64
+ e.isBase64Encoded,
65
+ e.headers?.['Content-Type'] ?? e.headers?.['content-type'],
66
+ ),
63
67
  query: parseQueryParams(mergedParams),
64
68
  params: e.pathParameters || {},
65
69
  };
@@ -4,6 +4,7 @@ import type { Logger } from '@geekmidas/logger';
4
4
  import type { Service } from '@geekmidas/services';
5
5
  import type { StandardSchemaV1 } from '@standard-schema/spec';
6
6
  import type { APIGatewayProxyEventV2, Context } from 'aws-lambda';
7
+ import qs from 'qs';
7
8
  import type { HttpMethod } from '../types';
8
9
  import {
9
10
  AmazonApiGatewayEndpoint,
@@ -14,7 +15,6 @@ import {
14
15
  } from './AmazonApiGatewayEndpointAdaptor';
15
16
  import type { CookieFn, Endpoint, EndpointSchemas } from './Endpoint';
16
17
  import { createApiGatewayCookies } from './lazyAccessors';
17
- import { parseQueryParams } from './parseQueryParams';
18
18
 
19
19
  export class AmazonApiGatewayV2Endpoint<
20
20
  TRoute extends string,
@@ -45,25 +45,15 @@ export class AmazonApiGatewayV2Endpoint<
45
45
  }
46
46
 
47
47
  override getInput(e: APIGatewayProxyEventV2): GetInputResponse {
48
- // API Gateway V2 handles arrays as comma-separated values
49
- const queryParams = e.queryStringParameters || {};
50
- const processedParams: Record<string, string | string[]> = {};
51
-
52
- for (const [key, value] of Object.entries(queryParams)) {
53
- if (value !== undefined) {
54
- // Check if value contains comma and could be an array
55
- // Be careful not to split values that legitimately contain commas
56
- if (value.includes(',') && !value.includes('"')) {
57
- processedParams[key] = value.split(',').map((v) => v.trim());
58
- } else {
59
- processedParams[key] = value;
60
- }
61
- }
62
- }
48
+ const raw = AmazonApiGatewayEndpoint.decodeBody(
49
+ e.body,
50
+ e.isBase64Encoded,
51
+ e.headers?.['content-type'],
52
+ );
63
53
 
64
54
  return {
65
- body: e.body ? JSON.parse(e.body) : undefined,
66
- query: parseQueryParams(processedParams),
55
+ body: raw,
56
+ query: qs.parse(e.rawQueryString) as Record<string, any>,
67
57
  params: e.pathParameters || {},
68
58
  };
69
59
  }
@@ -113,7 +113,8 @@ export class Endpoint<
113
113
  public getSession: SessionFn<TServices, TLogger, TSession, TDatabase> = () =>
114
114
  ({}) as TSession;
115
115
  /** Function to determine if the request is authorized */
116
- public authorize: AuthorizeFn<TServices, TLogger, TSession> = () => true;
116
+ public authorize: AuthorizeFn<TServices, TLogger, TSession, TInput> = () =>
117
+ true;
117
118
  /** Optional rate limiting configuration */
118
119
  public rateLimit?: RateLimitConfig;
119
120
  /** Optional authorizer for this endpoint */
@@ -749,7 +750,7 @@ export interface EndpointOptions<
749
750
  TAuditAction
750
751
  >;
751
752
  /** Optional authorization check function */
752
- authorize: AuthorizeFn<TServices, TLogger, TSession> | undefined;
753
+ authorize: AuthorizeFn<TServices, TLogger, TSession, TInput> | undefined;
753
754
  /** Optional description for documentation */
754
755
  description: string | undefined;
755
756
  /** Optional tags for OpenAPI documentation */
@@ -813,13 +814,14 @@ export type AuthorizeContext<
813
814
  TServices extends Service[] = [],
814
815
  TLogger extends Logger = Logger,
815
816
  TSession = unknown,
817
+ TInput extends EndpointSchemas | undefined = undefined,
816
818
  > = {
817
819
  services: ServiceRecord<TServices>;
818
820
  logger: TLogger;
819
821
  header: HeaderFn;
820
822
  cookie: CookieFn;
821
823
  session: TSession;
822
- };
824
+ } & InferComposableStandardSchema<TInput>;
823
825
  /**
824
826
  * Function type for endpoint authorization checks.
825
827
  *
@@ -841,8 +843,9 @@ export type AuthorizeFn<
841
843
  TServices extends Service[] = [],
842
844
  TLogger extends Logger = Logger,
843
845
  TSession = unknown,
846
+ TInput extends EndpointSchemas | undefined = undefined,
844
847
  > = (
845
- ctx: AuthorizeContext<TServices, TLogger, TSession>,
848
+ ctx: AuthorizeContext<TServices, TLogger, TSession, TInput>,
846
849
  ) => Promise<boolean> | boolean;
847
850
 
848
851
  /**
@@ -63,7 +63,7 @@ export class EndpointBuilder<
63
63
  protected _memorySize?: number;
64
64
  _getSession: SessionFn<TServices, TLogger, TSession, TDatabase> = () =>
65
65
  ({}) as TSession;
66
- _authorize: AuthorizeFn<TServices, TLogger, TSession> = () => true;
66
+ _authorize: AuthorizeFn<TServices, TLogger, TSession, TInput> = () => true;
67
67
  _rateLimit?: RateLimitConfig;
68
68
  _availableAuthorizers: Authorizer[] = [];
69
69
  _authorizerName?: TAuthorizers[number];
@@ -263,6 +263,11 @@ export class EndpointBuilder<
263
263
  return this;
264
264
  }
265
265
 
266
+ authorize(fn: AuthorizeFn<TServices, TLogger, TSession, TInput>): this {
267
+ this._authorize = fn;
268
+ return this;
269
+ }
270
+
266
271
  rateLimit(config: RateLimitConfig): this {
267
272
  this._rateLimit = config;
268
273
  return this;
@@ -988,7 +988,7 @@ export class EndpointFactory<
988
988
  >(fullPath, method);
989
989
 
990
990
  if (this.defaultAuthorizeFn) {
991
- builder._authorize = this.defaultAuthorizeFn;
991
+ builder._authorize = this.defaultAuthorizeFn as any;
992
992
  }
993
993
  if (this.defaultServices.length) {
994
994
  // Create a copy to avoid sharing references between builders
@@ -393,6 +393,15 @@ export class HonoEndpoint<
393
393
  services,
394
394
  logger,
395
395
  session,
396
+ body: features.hasBodyValidation
397
+ ? (c.req.valid as any)('json')
398
+ : undefined,
399
+ query: features.hasQueryValidation
400
+ ? (c.req.valid as any)('query')
401
+ : undefined,
402
+ params: features.hasParamValidation
403
+ ? (c.req.valid as any)('param')
404
+ : undefined,
396
405
  });
397
406
 
398
407
  if (!isAuthorized) {
@@ -216,6 +216,9 @@ export class TestEndpointAdaptor<
216
216
  services: ctx.services,
217
217
  logger,
218
218
  session,
219
+ body,
220
+ query,
221
+ params,
219
222
  });
220
223
 
221
224
  if (!isAuthorized) {
@@ -45,6 +45,118 @@ describe('AmazonApiGatewayV1Endpoint', () => {
45
45
  envParser = new EnvironmentParser({});
46
46
  });
47
47
 
48
+ describe('getInput', () => {
49
+ it('should parse JSON body when content-type is application/json', () => {
50
+ const endpoint = new Endpoint({
51
+ route: '/test',
52
+ method: 'GET',
53
+ fn: async () => ({ success: true }),
54
+ input: {},
55
+ output: z.object({ success: z.boolean() }),
56
+ services: [],
57
+ logger: mockLogger,
58
+ timeout: undefined,
59
+ memorySize: undefined,
60
+ status: undefined,
61
+ getSession: undefined,
62
+ authorize: undefined,
63
+ description: 'Test endpoint',
64
+ });
65
+ const adapter = new AmazonApiGatewayV1Endpoint(envParser, endpoint);
66
+
67
+ const event = createMockV1Event({
68
+ headers: { 'Content-Type': 'application/json' },
69
+ body: JSON.stringify({ name: 'test' }),
70
+ });
71
+
72
+ const result = adapter.getInput(event);
73
+ expect(result.body).toEqual({ name: 'test' });
74
+ });
75
+
76
+ it('should decode base64-encoded JSON body', () => {
77
+ const endpoint = new Endpoint({
78
+ route: '/test',
79
+ method: 'GET',
80
+ fn: async () => ({ success: true }),
81
+ input: {},
82
+ output: z.object({ success: z.boolean() }),
83
+ services: [],
84
+ logger: mockLogger,
85
+ timeout: undefined,
86
+ memorySize: undefined,
87
+ status: undefined,
88
+ getSession: undefined,
89
+ authorize: undefined,
90
+ description: 'Test endpoint',
91
+ });
92
+ const adapter = new AmazonApiGatewayV1Endpoint(envParser, endpoint);
93
+
94
+ const event = createMockV1Event({
95
+ headers: { 'Content-Type': 'application/json' },
96
+ body: Buffer.from(JSON.stringify({ name: 'test' })).toString('base64'),
97
+ isBase64Encoded: true,
98
+ });
99
+
100
+ const result = adapter.getInput(event);
101
+ expect(result.body).toEqual({ name: 'test' });
102
+ });
103
+
104
+ it('should return raw string for non-JSON content-type', () => {
105
+ const endpoint = new Endpoint({
106
+ route: '/test',
107
+ method: 'POST',
108
+ fn: async () => ({ success: true }),
109
+ input: {},
110
+ output: z.object({ success: z.boolean() }),
111
+ services: [],
112
+ logger: mockLogger,
113
+ timeout: undefined,
114
+ memorySize: undefined,
115
+ status: undefined,
116
+ getSession: undefined,
117
+ authorize: undefined,
118
+ description: 'Test endpoint',
119
+ });
120
+ const adapter = new AmazonApiGatewayV1Endpoint(envParser, endpoint);
121
+
122
+ const event = createMockV1Event({
123
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
124
+ body: 'amount=100&currency=ZAR',
125
+ });
126
+
127
+ const result = adapter.getInput(event);
128
+ expect(result.body).toBe('amount=100&currency=ZAR');
129
+ });
130
+
131
+ it('should decode base64 and return string for form-urlencoded content', () => {
132
+ const endpoint = new Endpoint({
133
+ route: '/test',
134
+ method: 'POST',
135
+ fn: async () => ({ success: true }),
136
+ input: {},
137
+ output: z.object({ success: z.boolean() }),
138
+ services: [],
139
+ logger: mockLogger,
140
+ timeout: undefined,
141
+ memorySize: undefined,
142
+ status: undefined,
143
+ getSession: undefined,
144
+ authorize: undefined,
145
+ description: 'Test endpoint',
146
+ });
147
+ const adapter = new AmazonApiGatewayV1Endpoint(envParser, endpoint);
148
+
149
+ const event = createMockV1Event({
150
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
151
+ body: Buffer.from('amount=100&currency=ZAR').toString('base64'),
152
+ isBase64Encoded: true,
153
+ });
154
+
155
+ const result = adapter.getInput(event);
156
+ expect(result.body).toBe('amount=100&currency=ZAR');
157
+ });
158
+ });
159
+
48
160
  describe('handler', () => {
49
161
  it('should handle a simple GET request', async () => {
50
162
  const endpoint = new Endpoint({
@@ -775,7 +887,7 @@ describe('AmazonApiGatewayV1Endpoint', () => {
775
887
  });
776
888
 
777
889
  describe('combined inputs', () => {
778
- it('should handle array query parameters', async () => {
890
+ it('should handle array query parameters with bracket notation', async () => {
779
891
  const querySchema = z.object({
780
892
  tags: z.array(z.string()),
781
893
  page: z.coerce.number().default(1),
@@ -808,9 +920,9 @@ describe('AmazonApiGatewayV1Endpoint', () => {
808
920
  const handler = adapter.handler;
809
921
 
810
922
  const event = createMockV1Event({
811
- queryStringParameters: { tags: 'nodejs', page: '2' },
923
+ queryStringParameters: { 'tags[]': 'nodejs', page: '2' },
812
924
  multiValueQueryStringParameters: {
813
- tags: ['nodejs', 'typescript', 'javascript'],
925
+ 'tags[]': ['nodejs', 'typescript', 'javascript'],
814
926
  page: ['2'],
815
927
  },
816
928
  });
@@ -825,7 +937,7 @@ describe('AmazonApiGatewayV1Endpoint', () => {
825
937
  });
826
938
  });
827
939
 
828
- it('should handle object query parameters with dot notation', async () => {
940
+ it('should handle object query parameters with bracket notation', async () => {
829
941
  const querySchema = z.object({
830
942
  filter: z.object({
831
943
  name: z.string(),
@@ -867,9 +979,9 @@ describe('AmazonApiGatewayV1Endpoint', () => {
867
979
 
868
980
  const event = createMockV1Event({
869
981
  queryStringParameters: {
870
- 'filter.name': 'john',
871
- 'filter.status': 'active',
872
- 'filter.priority': '1',
982
+ 'filter[name]': 'john',
983
+ 'filter[status]': 'active',
984
+ 'filter[priority]': '1',
873
985
  sort: 'priority',
874
986
  },
875
987
  });
@@ -635,7 +635,7 @@ describe('AmazonApiGatewayV2Endpoint Events', () => {
635
635
  expect(mockPublisher.publish).not.toHaveBeenCalled();
636
636
  });
637
637
 
638
- it('should handle comma-separated array query parameters', async () => {
638
+ it('should handle bracket notation array query parameters', async () => {
639
639
  const mockPublisher: EventPublisher<TestEvent> = {
640
640
  publish: vi.fn().mockResolvedValue(undefined),
641
641
  };
@@ -702,8 +702,10 @@ describe('AmazonApiGatewayV2Endpoint Events', () => {
702
702
  },
703
703
  },
704
704
  pathParameters: { id: '123' },
705
+ rawQueryString:
706
+ 'tags%5B%5D=javascript&tags%5B%5D=typescript&tags%5B%5D=nodejs',
705
707
  queryStringParameters: {
706
- tags: 'javascript, typescript, nodejs', // V2 handles arrays as comma-separated
708
+ 'tags[]': 'javascript,typescript,nodejs',
707
709
  },
708
710
  });
709
711
  const context = createMockContext();
@@ -3,6 +3,7 @@ import { createMockContext, createMockV2Event } from '@geekmidas/testkit/aws';
3
3
  import type { Context } from 'aws-lambda';
4
4
  import { beforeEach, describe, expect, it } from 'vitest';
5
5
  import { z } from 'zod';
6
+ import { AmazonApiGatewayEndpoint } from '../AmazonApiGatewayEndpointAdaptor';
6
7
  import { AmazonApiGatewayV2Endpoint } from '../AmazonApiGatewayV2EndpointAdaptor';
7
8
  import { e } from '../EndpointFactory';
8
9
 
@@ -36,6 +37,78 @@ describe('AmazonApiGatewayV2Endpoint', () => {
36
37
  });
37
38
  });
38
39
 
40
+ it('should parse JSON body when content-type is application/json', () => {
41
+ const endpoint = e.get('/test').handle(() => ({ success: true }));
42
+ const adapter = new AmazonApiGatewayV2Endpoint(envParser, endpoint);
43
+
44
+ const event = createMockV2Event({
45
+ headers: { 'content-type': 'application/json' },
46
+ body: JSON.stringify({ name: 'test' }),
47
+ });
48
+
49
+ const result = adapter.getInput(event);
50
+
51
+ expect(result.body).toEqual({ name: 'test' });
52
+ });
53
+
54
+ it('should decode base64-encoded JSON body', () => {
55
+ const endpoint = e.get('/test').handle(() => ({ success: true }));
56
+ const adapter = new AmazonApiGatewayV2Endpoint(envParser, endpoint);
57
+
58
+ const event = createMockV2Event({
59
+ headers: { 'content-type': 'application/json' },
60
+ body: Buffer.from(JSON.stringify({ name: 'test' })).toString('base64'),
61
+ isBase64Encoded: true,
62
+ });
63
+
64
+ const result = adapter.getInput(event);
65
+
66
+ expect(result.body).toEqual({ name: 'test' });
67
+ });
68
+
69
+ it('should return base64-decoded string for non-JSON content-type', () => {
70
+ const endpoint = e.get('/test').handle(() => ({ success: true }));
71
+ const adapter = new AmazonApiGatewayV2Endpoint(envParser, endpoint);
72
+
73
+ const event = createMockV2Event({
74
+ headers: { 'content-type': 'application/x-www-form-urlencoded' },
75
+ body: Buffer.from('amount=100&currency=ZAR').toString('base64'),
76
+ isBase64Encoded: true,
77
+ });
78
+
79
+ const result = adapter.getInput(event);
80
+
81
+ expect(result.body).toBe('amount=100&currency=ZAR');
82
+ });
83
+
84
+ it('should return raw string when content-type is not JSON', () => {
85
+ const endpoint = e.get('/test').handle(() => ({ success: true }));
86
+ const adapter = new AmazonApiGatewayV2Endpoint(envParser, endpoint);
87
+
88
+ const event = createMockV2Event({
89
+ headers: { 'content-type': 'text/plain' },
90
+ body: 'plain-text-body',
91
+ });
92
+
93
+ const result = adapter.getInput(event);
94
+
95
+ expect(result.body).toBe('plain-text-body');
96
+ });
97
+
98
+ it('should default to JSON parsing when no content-type header', () => {
99
+ const endpoint = e.get('/test').handle(() => ({ success: true }));
100
+ const adapter = new AmazonApiGatewayV2Endpoint(envParser, endpoint);
101
+
102
+ const event = createMockV2Event({
103
+ headers: {},
104
+ body: JSON.stringify({ name: 'test' }),
105
+ });
106
+
107
+ const result = adapter.getInput(event);
108
+
109
+ expect(result.body).toEqual({ name: 'test' });
110
+ });
111
+
39
112
  it('should handle missing body, query, and params', () => {
40
113
  const endpoint = e.get('/test').handle(() => ({ success: true }));
41
114
  const adapter = new AmazonApiGatewayV2Endpoint(envParser, endpoint);
@@ -139,7 +212,7 @@ describe('AmazonApiGatewayV2Endpoint', () => {
139
212
  });
140
213
  });
141
214
 
142
- it('should handle array query parameters (comma-separated)', async () => {
215
+ it('should handle array query parameters with bracket notation', async () => {
143
216
  const endpoint = e
144
217
  .get('/search')
145
218
  .query(
@@ -164,9 +237,10 @@ describe('AmazonApiGatewayV2Endpoint', () => {
164
237
  const event = createMockV2Event({
165
238
  routeKey: 'GET /search',
166
239
  rawPath: '/search',
167
- rawQueryString: 'tags=nodejs,typescript,javascript&limit=20',
240
+ rawQueryString:
241
+ 'tags%5B%5D=nodejs&tags%5B%5D=typescript&tags%5B%5D=javascript&limit=20',
168
242
  queryStringParameters: {
169
- tags: 'nodejs,typescript,javascript',
243
+ 'tags[]': 'nodejs,typescript,javascript',
170
244
  limit: '20',
171
245
  },
172
246
  });
@@ -182,7 +256,7 @@ describe('AmazonApiGatewayV2Endpoint', () => {
182
256
  });
183
257
  });
184
258
 
185
- it('should handle object query parameters with dot notation', async () => {
259
+ it('should handle object query parameters with bracket notation', async () => {
186
260
  const endpoint = e
187
261
  .get('/search')
188
262
  .query(
@@ -210,10 +284,11 @@ describe('AmazonApiGatewayV2Endpoint', () => {
210
284
  const event = createMockV2Event({
211
285
  routeKey: 'GET /search',
212
286
  rawPath: '/search',
213
- rawQueryString: 'filter.category=electronics&filter.active=true',
287
+ rawQueryString:
288
+ 'filter%5Bcategory%5D=electronics&filter%5Bactive%5D=true',
214
289
  queryStringParameters: {
215
- 'filter.category': 'electronics',
216
- 'filter.active': 'true',
290
+ 'filter[category]': 'electronics',
291
+ 'filter[active]': 'true',
217
292
  },
218
293
  });
219
294
 
@@ -488,3 +563,67 @@ describe('AmazonApiGatewayV2Endpoint', () => {
488
563
  });
489
564
  });
490
565
  });
566
+
567
+ describe('AmazonApiGatewayEndpoint.decodeBody', () => {
568
+ const decodeBody = AmazonApiGatewayEndpoint.decodeBody;
569
+
570
+ it('should return undefined for null/undefined body', () => {
571
+ expect(decodeBody(undefined, false, 'application/json')).toBeUndefined();
572
+ expect(decodeBody(null, false, 'application/json')).toBeUndefined();
573
+ expect(decodeBody('', false, 'application/json')).toBeUndefined();
574
+ });
575
+
576
+ it('should JSON.parse when content-type is application/json', () => {
577
+ const result = decodeBody('{"name":"test"}', false, 'application/json');
578
+ expect(result).toEqual({ name: 'test' });
579
+ });
580
+
581
+ it('should JSON.parse when content-type includes application/json with charset', () => {
582
+ const result = decodeBody(
583
+ '{"name":"test"}',
584
+ false,
585
+ 'application/json; charset=utf-8',
586
+ );
587
+ expect(result).toEqual({ name: 'test' });
588
+ });
589
+
590
+ it('should decode base64 then JSON.parse for base64-encoded JSON', () => {
591
+ const encoded = Buffer.from('{"name":"test"}').toString('base64');
592
+ const result = decodeBody(encoded, true, 'application/json');
593
+ expect(result).toEqual({ name: 'test' });
594
+ });
595
+
596
+ it('should return raw string for non-JSON content-type', () => {
597
+ const result = decodeBody(
598
+ 'amount=100&currency=ZAR',
599
+ false,
600
+ 'application/x-www-form-urlencoded',
601
+ );
602
+ expect(result).toBe('amount=100&currency=ZAR');
603
+ });
604
+
605
+ it('should decode base64 and return string for non-JSON content-type', () => {
606
+ const encoded = Buffer.from('amount=100&currency=ZAR').toString('base64');
607
+ const result = decodeBody(
608
+ encoded,
609
+ true,
610
+ 'application/x-www-form-urlencoded',
611
+ );
612
+ expect(result).toBe('amount=100&currency=ZAR');
613
+ });
614
+
615
+ it('should return raw string for text/plain', () => {
616
+ const result = decodeBody('hello world', false, 'text/plain');
617
+ expect(result).toBe('hello world');
618
+ });
619
+
620
+ it('should default to JSON parsing when content-type is undefined', () => {
621
+ const result = decodeBody('{"name":"test"}', false, undefined);
622
+ expect(result).toEqual({ name: 'test' });
623
+ });
624
+
625
+ it('should not JSON.parse a string that happens to be valid JSON when content-type is not JSON', () => {
626
+ const result = decodeBody('{"looks":"like json"}', false, 'text/plain');
627
+ expect(result).toBe('{"looks":"like json"}');
628
+ });
629
+ });
@@ -379,6 +379,41 @@ describe('EndpointBuilder', () => {
379
379
  expect(endpoint.authorize).toBe(customAuth);
380
380
  });
381
381
 
382
+ it('should set authorize via .authorize() method', () => {
383
+ const authFn = () => true;
384
+ const builder = new EndpointBuilder('/test', 'GET');
385
+ const result = builder.authorize(authFn);
386
+
387
+ expect(result).toBe(builder);
388
+ expect((builder as any)._authorize).toBe(authFn);
389
+ });
390
+
391
+ it('should pass .authorize() function to endpoint', () => {
392
+ const authFn = async () => false;
393
+ const endpoint = new EndpointBuilder('/test', 'POST')
394
+ .authorize(authFn)
395
+ .handle(async () => ({}));
396
+
397
+ expect(endpoint.authorize).toBe(authFn);
398
+ });
399
+
400
+ it('should chain .authorize() with body, query, and params', () => {
401
+ const endpoint = new EndpointBuilder('/users/:id', 'POST')
402
+ .body(z.object({ role: z.string() }))
403
+ .query(z.object({ verbose: z.string() }))
404
+ .params(z.object({ id: z.string() }))
405
+ .authorize(({ body, query, params }) => {
406
+ return (
407
+ body.role === 'admin' &&
408
+ params.id !== '' &&
409
+ query.verbose === 'true'
410
+ );
411
+ })
412
+ .handle(async () => ({}));
413
+
414
+ expect(endpoint.authorize).toBeDefined();
415
+ });
416
+
382
417
  it('should allow setting custom session extractor', () => {
383
418
  const customSession = async () => ({ userId: '123', role: 'admin' });
384
419
  const builder = new EndpointBuilder('/test', 'GET');