@geekmidas/constructs 0.0.11 → 0.0.13
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/README.md +448 -0
- package/dist/AWSLambdaFunction-B-Oxr8qt.d.cts +30 -0
- package/dist/{AWSLambdaFunction-DcI_77u8.cjs → AWSLambdaFunction-C-fuCLA3.cjs} +28 -5
- package/dist/AWSLambdaFunction-C-fuCLA3.cjs.map +1 -0
- package/dist/AWSLambdaFunction-CAm9r5ZX.d.mts +30 -0
- package/dist/{AWSLambdaFunction-BTSGYZSx.mjs → AWSLambdaFunction-H65WfXLt.mjs} +28 -5
- package/dist/AWSLambdaFunction-H65WfXLt.mjs.map +1 -0
- package/dist/{AWSLambdaSubscriberAdaptor-mLOpS63x.d.mts → AWSLambdaSubscriberAdaptor-Cknp_nn1.d.cts} +4 -4
- package/dist/{AWSLambdaSubscriberAdaptor-G8y3YkWj.mjs → AWSLambdaSubscriberAdaptor-CyFh7MN8.mjs} +1 -1
- package/dist/{AWSLambdaSubscriberAdaptor-G8y3YkWj.mjs.map → AWSLambdaSubscriberAdaptor-CyFh7MN8.mjs.map} +1 -1
- package/dist/{AWSLambdaSubscriberAdaptor-DX5NFdWf.d.cts → AWSLambdaSubscriberAdaptor-DpHzp-AM.d.mts} +4 -4
- package/dist/{AWSLambdaSubscriberAdaptor-CmPZ10JF.cjs → AWSLambdaSubscriberAdaptor-Dum5bkw3.cjs} +1 -1
- package/dist/{AWSLambdaSubscriberAdaptor-CmPZ10JF.cjs.map → AWSLambdaSubscriberAdaptor-Dum5bkw3.cjs.map} +1 -1
- package/dist/{AmazonApiGatewayEndpointAdaptor-CATWBIJo.d.mts → AmazonApiGatewayEndpointAdaptor-4hPy5vty.d.mts} +9 -8
- package/dist/{AmazonApiGatewayEndpointAdaptor-vi0D3dWg.mjs → AmazonApiGatewayEndpointAdaptor-C6Jk5HSy.mjs} +51 -16
- package/dist/AmazonApiGatewayEndpointAdaptor-C6Jk5HSy.mjs.map +1 -0
- package/dist/{AmazonApiGatewayEndpointAdaptor-BTqB3c1_.cjs → AmazonApiGatewayEndpointAdaptor-CI9L7Ucn.cjs} +51 -16
- package/dist/AmazonApiGatewayEndpointAdaptor-CI9L7Ucn.cjs.map +1 -0
- package/dist/{AmazonApiGatewayEndpointAdaptor-BwtDtS-i.d.cts → AmazonApiGatewayEndpointAdaptor-ro0RMLzr.d.cts} +8 -7
- package/dist/{AmazonApiGatewayV1EndpointAdaptor-D2_u_zR6.mjs → AmazonApiGatewayV1EndpointAdaptor-BMy8DdNJ.mjs} +3 -3
- package/dist/{AmazonApiGatewayV1EndpointAdaptor-D2_u_zR6.mjs.map → AmazonApiGatewayV1EndpointAdaptor-BMy8DdNJ.mjs.map} +1 -1
- package/dist/{AmazonApiGatewayV1EndpointAdaptor-B1AVk2MM.d.cts → AmazonApiGatewayV1EndpointAdaptor-BWJWKqQT.d.cts} +3 -3
- package/dist/{AmazonApiGatewayV1EndpointAdaptor-nwrtG5L9.cjs → AmazonApiGatewayV1EndpointAdaptor-DYL1bCBS.cjs} +3 -3
- package/dist/{AmazonApiGatewayV1EndpointAdaptor-nwrtG5L9.cjs.map → AmazonApiGatewayV1EndpointAdaptor-DYL1bCBS.cjs.map} +1 -1
- package/dist/{AmazonApiGatewayV1EndpointAdaptor-2Rd0LLz4.d.mts → AmazonApiGatewayV1EndpointAdaptor-hyR-WwyP.d.mts} +4 -4
- package/dist/{AmazonApiGatewayV2EndpointAdaptor-DoWAJ9oN.mjs → AmazonApiGatewayV2EndpointAdaptor-BU5wQMOe.mjs} +3 -3
- package/dist/{AmazonApiGatewayV2EndpointAdaptor-DoWAJ9oN.mjs.map → AmazonApiGatewayV2EndpointAdaptor-BU5wQMOe.mjs.map} +1 -1
- package/dist/{AmazonApiGatewayV2EndpointAdaptor-FSIdXYV6.cjs → AmazonApiGatewayV2EndpointAdaptor-CPLCMeaN.cjs} +3 -3
- package/dist/{AmazonApiGatewayV2EndpointAdaptor-FSIdXYV6.cjs.map → AmazonApiGatewayV2EndpointAdaptor-CPLCMeaN.cjs.map} +1 -1
- package/dist/{AmazonApiGatewayV2EndpointAdaptor-DmY_QfjO.d.cts → AmazonApiGatewayV2EndpointAdaptor-D1Irdggp.d.cts} +3 -3
- package/dist/{AmazonApiGatewayV2EndpointAdaptor-cBDfwdBx.d.mts → AmazonApiGatewayV2EndpointAdaptor-DX3SuI5S.d.mts} +4 -4
- package/dist/{Authorizer-Bbk9ziuG.d.mts → Authorizer-BTmly8ps.d.cts} +1 -1
- package/dist/{Authorizer-iXsSB600.d.cts → Authorizer-pmPvIVgv.d.mts} +1 -1
- package/dist/{BaseFunctionBuilder-DSqF1r3a.mjs → BaseFunctionBuilder-B5gkW0Kt.mjs} +16 -2
- package/dist/BaseFunctionBuilder-B5gkW0Kt.mjs.map +1 -0
- package/dist/{BaseFunctionBuilder-bTDKP7bd.cjs → BaseFunctionBuilder-C5Se7pdL.cjs} +16 -2
- package/dist/BaseFunctionBuilder-C5Se7pdL.cjs.map +1 -0
- package/dist/{BaseFunctionBuilder-DOcm1heZ.d.mts → BaseFunctionBuilder-CbDnPZpD.d.mts} +13 -4
- package/dist/{BaseFunctionBuilder-hWNVKXQB.d.cts → BaseFunctionBuilder-DUZMbEr3.d.cts} +13 -4
- package/dist/{Construct-XCGKof_O.cjs → Construct-BYSPikVm.cjs} +8 -3
- package/dist/Construct-BYSPikVm.cjs.map +1 -0
- package/dist/{Construct-rZQg2yus.mjs → Construct-LWeB1rSQ.mjs} +8 -3
- package/dist/Construct-LWeB1rSQ.mjs.map +1 -0
- package/dist/{Construct-iDmbkZ3o.d.mts → Construct-ZPqE0vhn.d.mts} +5 -3
- package/dist/{Construct-zc8BkRSp.d.cts → Construct-dI_rgdSp.d.cts} +5 -3
- package/dist/Construct.cjs +1 -1
- package/dist/Construct.d.cts +1 -1
- package/dist/Construct.d.mts +1 -1
- package/dist/Construct.mjs +1 -1
- package/dist/{Cron-CYIhd0VE.cjs → Cron-Bi3QOge_.cjs} +5 -5
- package/dist/Cron-Bi3QOge_.cjs.map +1 -0
- package/dist/{Cron-D5HGEt6g.d.mts → Cron-COdfP0Jd.d.cts} +5 -5
- package/dist/{Cron-DbJNPTbP.d.cts → Cron-D8cn_ahj.d.mts} +5 -5
- package/dist/{Cron-BnO1uXhh.mjs → Cron-Dy_HW2Vv.mjs} +5 -5
- package/dist/Cron-Dy_HW2Vv.mjs.map +1 -0
- package/dist/{CronBuilder-BxKwHCdJ.mjs → CronBuilder-Bl3A2Zp4.mjs} +15 -6
- package/dist/CronBuilder-Bl3A2Zp4.mjs.map +1 -0
- package/dist/{CronBuilder-D-546XtP.d.cts → CronBuilder-DntF6H3A.d.cts} +17 -12
- package/dist/{CronBuilder-vRiZb1_N.d.mts → CronBuilder-DoMnSs_0.d.mts} +17 -12
- package/dist/{CronBuilder-D_3AiPFO.cjs → CronBuilder-Dv_w7Yri.cjs} +15 -6
- package/dist/CronBuilder-Dv_w7Yri.cjs.map +1 -0
- package/dist/{Endpoint-DqZkc6ft.d.mts → Endpoint-Bbs_sFvg.d.mts} +120 -23
- package/dist/{Endpoint-BpTSQn7W.d.cts → Endpoint-Bu8Phz6y.d.cts} +120 -23
- package/dist/{Endpoint-BdIGWw1H.cjs → Endpoint-DDpF7NO1.cjs} +22 -10
- package/dist/Endpoint-DDpF7NO1.cjs.map +1 -0
- package/dist/{Endpoint-ChzcIFdV.mjs → Endpoint-S6Yh2_PN.mjs} +22 -10
- package/dist/Endpoint-S6Yh2_PN.mjs.map +1 -0
- package/dist/EndpointBuilder-CPxmF_w7.d.cts +103 -0
- package/dist/EndpointBuilder-Csfyfjd7.d.mts +103 -0
- package/dist/{EndpointBuilder-dmMiNRm9.cjs → EndpointBuilder-DpGmObMb.cjs} +68 -5
- package/dist/EndpointBuilder-DpGmObMb.cjs.map +1 -0
- package/dist/{EndpointBuilder-yo40Qkf_.mjs → EndpointBuilder-aE2E6WTx.mjs} +68 -5
- package/dist/EndpointBuilder-aE2E6WTx.mjs.map +1 -0
- package/dist/{EndpointFactory-D_mmO7Gu.d.mts → EndpointFactory-Bf9JSWzI.d.cts} +12 -11
- package/dist/{EndpointFactory-CNbcr8u_.cjs → EndpointFactory-BfH6mjJ3.cjs} +3 -3
- package/dist/EndpointFactory-BfH6mjJ3.cjs.map +1 -0
- package/dist/{EndpointFactory-JPhk0Qom.d.cts → EndpointFactory-Cgk1R0oO.d.mts} +12 -11
- package/dist/{EndpointFactory-LD3upVQK.mjs → EndpointFactory-D4leYk1N.mjs} +3 -3
- package/dist/EndpointFactory-D4leYk1N.mjs.map +1 -0
- package/dist/{Function-DDaSfL_E.cjs → Function-DagDbeXo.cjs} +7 -6
- package/dist/Function-DagDbeXo.cjs.map +1 -0
- package/dist/{Function-Cl9fiR3a.mjs → Function-DfKsM5Kx.mjs} +7 -6
- package/dist/Function-DfKsM5Kx.mjs.map +1 -0
- package/dist/{Function-C-5_23Wi.d.mts → Function-V9M9UVHp.d.mts} +25 -7
- package/dist/{Function-CW-WcZTu.d.cts → Function-VI1TB3Mh.d.cts} +25 -7
- package/dist/{FunctionBuilder-vW12WFOw.mjs → FunctionBuilder-CVT7bG2o.mjs} +27 -6
- package/dist/FunctionBuilder-CVT7bG2o.mjs.map +1 -0
- package/dist/FunctionBuilder-CjVEFTYC.d.cts +38 -0
- package/dist/FunctionBuilder-D1ofSeMd.d.mts +38 -0
- package/dist/{FunctionBuilder-LQuBiqRc.cjs → FunctionBuilder-DXvG_XD-.cjs} +27 -6
- package/dist/FunctionBuilder-DXvG_XD-.cjs.map +1 -0
- package/dist/FunctionExecutionWrapper-Bubnr0zA.mjs +101 -0
- package/dist/FunctionExecutionWrapper-Bubnr0zA.mjs.map +1 -0
- package/dist/FunctionExecutionWrapper-CwtwYozd.d.cts +48 -0
- package/dist/FunctionExecutionWrapper-DkNycmOh.cjs +107 -0
- package/dist/FunctionExecutionWrapper-DkNycmOh.cjs.map +1 -0
- package/dist/FunctionExecutionWrapper-rhbIYT0Q.d.mts +48 -0
- package/dist/{HonoEndpointAdaptor-BVQtyvna.d.cts → HonoEndpointAdaptor-B34kNobH.d.cts} +8 -7
- package/dist/{HonoEndpointAdaptor-W-disJnm.d.mts → HonoEndpointAdaptor-BDpi32mH.d.mts} +9 -8
- package/dist/{HonoEndpointAdaptor-Bc61IS6Q.cjs → HonoEndpointAdaptor-CfLRHHFw.cjs} +55 -20
- package/dist/HonoEndpointAdaptor-CfLRHHFw.cjs.map +1 -0
- package/dist/{HonoEndpointAdaptor-NR3h5gml.mjs → HonoEndpointAdaptor-DuyE06nH.mjs} +55 -20
- package/dist/HonoEndpointAdaptor-DuyE06nH.mjs.map +1 -0
- package/dist/{Subscriber-C2OPATI8.cjs → Subscriber-Bdh8rMSL.cjs} +2 -2
- package/dist/{Subscriber-C2OPATI8.cjs.map → Subscriber-Bdh8rMSL.cjs.map} +1 -1
- package/dist/{Subscriber-BVq3ST4C.d.cts → Subscriber-BhzqUzs-.d.cts} +2 -2
- package/dist/{Subscriber-BKftCzwT.mjs → Subscriber-CJOWwaw1.mjs} +2 -2
- package/dist/{Subscriber-BKftCzwT.mjs.map → Subscriber-CJOWwaw1.mjs.map} +1 -1
- package/dist/{Subscriber-BuHaqWo4.d.mts → Subscriber-s6yfjeOc.d.mts} +2 -2
- package/dist/{SubscriberBuilder-2asHtGbl.d.mts → SubscriberBuilder-BCVkp-ga.d.cts} +3 -3
- package/dist/{SubscriberBuilder-yDfpHPZE.mjs → SubscriberBuilder-BWQmiYd8.mjs} +2 -2
- package/dist/{SubscriberBuilder-yDfpHPZE.mjs.map → SubscriberBuilder-BWQmiYd8.mjs.map} +1 -1
- package/dist/{SubscriberBuilder-yikoQ8ND.cjs → SubscriberBuilder-DieD_60p.cjs} +2 -2
- package/dist/{SubscriberBuilder-yikoQ8ND.cjs.map → SubscriberBuilder-DieD_60p.cjs.map} +1 -1
- package/dist/{SubscriberBuilder-CHIRyNjy.d.cts → SubscriberBuilder-aCua5_wA.d.mts} +3 -3
- package/dist/{TestEndpointAdaptor-DpmNXp7G.mjs → TestEndpointAdaptor-BEyZa0Yg.mjs} +51 -18
- package/dist/TestEndpointAdaptor-BEyZa0Yg.mjs.map +1 -0
- package/dist/{TestEndpointAdaptor-DA1D6l-U.cjs → TestEndpointAdaptor-C8425RJ0.cjs} +51 -18
- package/dist/TestEndpointAdaptor-C8425RJ0.cjs.map +1 -0
- package/dist/{TestEndpointAdaptor-BrE5Rs4U.d.cts → TestEndpointAdaptor-H5To8PH7.d.cts} +6 -5
- package/dist/{TestEndpointAdaptor-B10J-cI-.d.mts → TestEndpointAdaptor-jxn68ayg.d.mts} +7 -6
- package/dist/adaptors/aws.cjs +14 -13
- package/dist/adaptors/aws.d.cts +14 -14
- package/dist/adaptors/aws.d.mts +15 -15
- package/dist/adaptors/aws.mjs +14 -13
- package/dist/adaptors/hono.cjs +10 -9
- package/dist/adaptors/hono.d.cts +8 -8
- package/dist/adaptors/hono.d.mts +9 -9
- package/dist/adaptors/hono.mjs +10 -9
- package/dist/adaptors/testing.cjs +8 -7
- package/dist/adaptors/testing.d.cts +8 -8
- package/dist/adaptors/testing.d.mts +9 -9
- package/dist/adaptors/testing.mjs +8 -7
- package/dist/crons/Cron.cjs +6 -6
- package/dist/crons/Cron.d.cts +6 -6
- package/dist/crons/Cron.d.mts +6 -6
- package/dist/crons/Cron.mjs +6 -6
- package/dist/crons/CronBuilder.cjs +7 -7
- package/dist/crons/CronBuilder.d.cts +7 -7
- package/dist/crons/CronBuilder.d.mts +7 -7
- package/dist/crons/CronBuilder.mjs +7 -7
- package/dist/crons/index.cjs +7 -7
- package/dist/crons/index.d.cts +11 -11
- package/dist/crons/index.d.mts +8 -8
- package/dist/crons/index.mjs +7 -7
- package/dist/endpoints/AmazonApiGatewayEndpointAdaptor.cjs +8 -7
- package/dist/endpoints/AmazonApiGatewayEndpointAdaptor.d.cts +8 -8
- package/dist/endpoints/AmazonApiGatewayEndpointAdaptor.d.mts +9 -9
- package/dist/endpoints/AmazonApiGatewayEndpointAdaptor.mjs +8 -7
- package/dist/endpoints/AmazonApiGatewayV1EndpointAdaptor.cjs +10 -9
- package/dist/endpoints/AmazonApiGatewayV1EndpointAdaptor.d.cts +9 -9
- package/dist/endpoints/AmazonApiGatewayV1EndpointAdaptor.d.mts +10 -10
- package/dist/endpoints/AmazonApiGatewayV1EndpointAdaptor.mjs +10 -9
- package/dist/endpoints/AmazonApiGatewayV2EndpointAdaptor.cjs +10 -9
- package/dist/endpoints/AmazonApiGatewayV2EndpointAdaptor.d.cts +9 -9
- package/dist/endpoints/AmazonApiGatewayV2EndpointAdaptor.d.mts +10 -10
- package/dist/endpoints/AmazonApiGatewayV2EndpointAdaptor.mjs +10 -9
- package/dist/endpoints/Authorizer.d.cts +1 -1
- package/dist/endpoints/Authorizer.d.mts +1 -1
- package/dist/endpoints/Endpoint.cjs +6 -6
- package/dist/endpoints/Endpoint.d.cts +7 -7
- package/dist/endpoints/Endpoint.d.mts +8 -8
- package/dist/endpoints/Endpoint.mjs +6 -6
- package/dist/endpoints/EndpointBuilder.cjs +7 -7
- package/dist/endpoints/EndpointBuilder.d.cts +8 -8
- package/dist/endpoints/EndpointBuilder.d.mts +9 -9
- package/dist/endpoints/EndpointBuilder.mjs +7 -7
- package/dist/endpoints/EndpointFactory.cjs +8 -8
- package/dist/endpoints/EndpointFactory.d.cts +9 -9
- package/dist/endpoints/EndpointFactory.d.mts +10 -10
- package/dist/endpoints/EndpointFactory.mjs +8 -8
- package/dist/endpoints/HonoEndpointAdaptor.cjs +10 -9
- package/dist/endpoints/HonoEndpointAdaptor.d.cts +8 -8
- package/dist/endpoints/HonoEndpointAdaptor.d.mts +9 -9
- package/dist/endpoints/HonoEndpointAdaptor.mjs +10 -9
- package/dist/endpoints/TestEndpointAdaptor.cjs +8 -7
- package/dist/endpoints/TestEndpointAdaptor.d.cts +8 -8
- package/dist/endpoints/TestEndpointAdaptor.d.mts +9 -9
- package/dist/endpoints/TestEndpointAdaptor.mjs +8 -7
- package/dist/endpoints/audit.cjs +0 -0
- package/dist/endpoints/audit.d.cts +9 -0
- package/dist/endpoints/audit.d.mts +9 -0
- package/dist/endpoints/audit.mjs +0 -0
- package/dist/endpoints/helpers.cjs +7 -7
- package/dist/endpoints/helpers.d.cts +7 -7
- package/dist/endpoints/helpers.d.mts +8 -8
- package/dist/endpoints/helpers.mjs +7 -7
- package/dist/endpoints/index.cjs +8 -8
- package/dist/endpoints/index.cjs.map +1 -1
- package/dist/endpoints/index.d.cts +12 -12
- package/dist/endpoints/index.d.mts +13 -13
- package/dist/endpoints/index.mjs +8 -8
- package/dist/endpoints/index.mjs.map +1 -1
- package/dist/endpoints/parseHonoQuery.cjs +1 -1
- package/dist/endpoints/parseHonoQuery.mjs +1 -1
- package/dist/endpoints/parseQueryParams.cjs +1 -1
- package/dist/endpoints/parseQueryParams.mjs +1 -1
- package/dist/endpoints/processAudits.cjs +5 -0
- package/dist/endpoints/processAudits.d.cts +74 -0
- package/dist/endpoints/processAudits.d.mts +74 -0
- package/dist/endpoints/processAudits.mjs +3 -0
- package/dist/functions/AWSLambdaFunction.cjs +6 -6
- package/dist/functions/AWSLambdaFunction.d.cts +4 -4
- package/dist/functions/AWSLambdaFunction.d.mts +4 -4
- package/dist/functions/AWSLambdaFunction.mjs +6 -6
- package/dist/functions/BaseFunctionBuilder.cjs +2 -2
- package/dist/functions/BaseFunctionBuilder.d.cts +2 -2
- package/dist/functions/BaseFunctionBuilder.d.mts +2 -2
- package/dist/functions/BaseFunctionBuilder.mjs +2 -2
- package/dist/functions/Function.cjs +2 -2
- package/dist/functions/Function.d.cts +2 -2
- package/dist/functions/Function.d.mts +2 -2
- package/dist/functions/Function.mjs +2 -2
- package/dist/functions/FunctionBuilder.cjs +4 -4
- package/dist/functions/FunctionBuilder.d.cts +4 -4
- package/dist/functions/FunctionBuilder.d.mts +4 -4
- package/dist/functions/FunctionBuilder.mjs +4 -4
- package/dist/functions/FunctionExecutionWrapper.cjs +5 -5
- package/dist/functions/FunctionExecutionWrapper.d.cts +3 -3
- package/dist/functions/FunctionExecutionWrapper.d.mts +3 -3
- package/dist/functions/FunctionExecutionWrapper.mjs +5 -5
- package/dist/functions/TestFunctionAdaptor.cjs +38 -5
- package/dist/functions/TestFunctionAdaptor.cjs.map +1 -1
- package/dist/functions/TestFunctionAdaptor.d.cts +10 -7
- package/dist/functions/TestFunctionAdaptor.d.mts +10 -7
- package/dist/functions/TestFunctionAdaptor.mjs +38 -5
- package/dist/functions/TestFunctionAdaptor.mjs.map +1 -1
- package/dist/functions/index.cjs +5 -5
- package/dist/functions/index.d.cts +5 -5
- package/dist/functions/index.d.mts +5 -5
- package/dist/functions/index.mjs +5 -5
- package/dist/{functions-yg097ymU.cjs → functions-FCb-wWFC.cjs} +2 -2
- package/dist/{functions-yg097ymU.cjs.map → functions-FCb-wWFC.cjs.map} +1 -1
- package/dist/functions-JhRsNoAZ.mjs +8 -0
- package/dist/{functions-HCoZ4YKF.mjs.map → functions-JhRsNoAZ.mjs.map} +1 -1
- package/dist/{helpers-CHsIWAEx.mjs → helpers-2CLKTnRm.mjs} +2 -2
- package/dist/{helpers-CHsIWAEx.mjs.map → helpers-2CLKTnRm.mjs.map} +1 -1
- package/dist/{helpers-Cq6wjo7s.cjs → helpers-Khuhi_Qx.cjs} +2 -2
- package/dist/{helpers-Cq6wjo7s.cjs.map → helpers-Khuhi_Qx.cjs.map} +1 -1
- package/dist/index-Bomy7R9z.d.mts +10 -0
- package/dist/index-DRYfZHb3.d.cts +10 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{parseHonoQuery-DopC24vB.cjs → parseHonoQuery-CT8Cvin-.cjs} +1 -1
- package/dist/{parseHonoQuery-DopC24vB.cjs.map → parseHonoQuery-CT8Cvin-.cjs.map} +1 -1
- package/dist/{parseHonoQuery-znDKBhdE.mjs → parseHonoQuery-CwFKw2ua.mjs} +1 -1
- package/dist/{parseHonoQuery-znDKBhdE.mjs.map → parseHonoQuery-CwFKw2ua.mjs.map} +1 -1
- package/dist/{parseQueryParams-BJaRh3OB.mjs → parseQueryParams-CHINupbZ.mjs} +1 -1
- package/dist/{parseQueryParams-BJaRh3OB.mjs.map → parseQueryParams-CHINupbZ.mjs.map} +1 -1
- package/dist/{parseQueryParams-BzPop4I1.cjs → parseQueryParams-CwvXXwkW.cjs} +1 -1
- package/dist/{parseQueryParams-BzPop4I1.cjs.map → parseQueryParams-CwvXXwkW.cjs.map} +1 -1
- package/dist/processAudits-BFokHhCO.cjs +174 -0
- package/dist/processAudits-BFokHhCO.cjs.map +1 -0
- package/dist/processAudits-DfcB-X-4.mjs +156 -0
- package/dist/processAudits-DfcB-X-4.mjs.map +1 -0
- package/dist/publisher-Bw4770Hi.mjs.map +1 -1
- package/dist/publisher-lFQleddL.cjs.map +1 -1
- package/dist/publisher.d.cts +3 -2
- package/dist/publisher.d.mts +3 -2
- package/dist/subscribers/AWSLambdaSubscriberAdaptor.cjs +1 -1
- package/dist/subscribers/AWSLambdaSubscriberAdaptor.d.cts +3 -3
- package/dist/subscribers/AWSLambdaSubscriberAdaptor.d.mts +3 -3
- package/dist/subscribers/AWSLambdaSubscriberAdaptor.mjs +1 -1
- package/dist/subscribers/Subscriber.cjs +2 -2
- package/dist/subscribers/Subscriber.d.cts +2 -2
- package/dist/subscribers/Subscriber.d.mts +2 -2
- package/dist/subscribers/Subscriber.mjs +2 -2
- package/dist/subscribers/SubscriberBuilder.cjs +3 -3
- package/dist/subscribers/SubscriberBuilder.d.cts +3 -3
- package/dist/subscribers/SubscriberBuilder.d.mts +3 -3
- package/dist/subscribers/SubscriberBuilder.mjs +3 -3
- package/dist/subscribers/index.cjs +3 -3
- package/dist/subscribers/index.d.cts +5 -5
- package/dist/subscribers/index.d.mts +5 -5
- package/dist/subscribers/index.mjs +3 -3
- package/dist/{types-CVq20-fE.d.mts → types-DKf0juBf.d.mts} +1 -1
- package/dist/types.d.mts +1 -1
- package/package.json +15 -11
- package/src/Construct.ts +8 -0
- package/src/crons/Cron.ts +12 -3
- package/src/crons/CronBuilder.ts +85 -13
- package/src/crons/__tests__/CronBuilder.state-isolation.spec.ts +2 -2
- package/src/endpoints/AmazonApiGatewayEndpointAdaptor.ts +112 -20
- package/src/endpoints/Endpoint.ts +208 -39
- package/src/endpoints/EndpointBuilder.ts +291 -21
- package/src/endpoints/EndpointFactory.ts +5 -1
- package/src/endpoints/HonoEndpointAdaptor.ts +144 -33
- package/src/endpoints/TestEndpointAdaptor.ts +116 -21
- package/src/endpoints/__tests__/AmazonApiGatewayV1EndpointAdaptor.events.spec.ts +8 -0
- package/src/endpoints/__tests__/AmazonApiGatewayV1EndpointAdaptor.spec.ts +29 -0
- package/src/endpoints/__tests__/AmazonApiGatewayV2EndpointAdaptor.audits.spec.ts +626 -0
- package/src/endpoints/__tests__/AmazonApiGatewayV2EndpointAdaptor.events.spec.ts +9 -0
- package/src/endpoints/__tests__/AmazonApiGatewayV2EndpointAdaptor.kysely-audit.integration.spec.ts +757 -0
- package/src/endpoints/__tests__/Endpoint.cookies.spec.ts +3 -1
- package/src/endpoints/__tests__/Endpoint.manifest.spec.ts +1 -1
- package/src/endpoints/__tests__/Endpoint.spec.ts +19 -0
- package/src/endpoints/__tests__/EndpointBuilder.audit.spec.ts +448 -0
- package/src/endpoints/__tests__/EndpointFactory.authorizers.spec.ts +51 -14
- package/src/endpoints/__tests__/EndpointFactory.reference-audit.spec.ts +8 -8
- package/src/endpoints/__tests__/EndpointFactory.state-isolation.spec.ts +11 -11
- package/src/endpoints/__tests__/HonoEndpointAdaptor.audit-transactions.spec.ts +703 -0
- package/src/endpoints/__tests__/HonoEndpointAdaptor.audits.spec.ts +632 -0
- package/src/endpoints/__tests__/HonoEndpointAdaptor.events.spec.ts +8 -0
- package/src/endpoints/__tests__/HonoEndpointAdaptor.kysely-audit.integration.spec.ts +1063 -0
- package/src/endpoints/__tests__/HonoEndpointAdaptor.openapi.spec.ts +4 -0
- package/src/endpoints/__tests__/HonoEndpointAdaptor.spec.ts +25 -0
- package/src/endpoints/audit.ts +87 -0
- package/src/endpoints/index.ts +1 -0
- package/src/endpoints/processAudits.ts +348 -0
- package/src/functions/AWSLambdaFunction.ts +125 -12
- package/src/functions/BaseFunctionBuilder.ts +98 -7
- package/src/functions/Function.ts +83 -15
- package/src/functions/FunctionBuilder.ts +217 -17
- package/src/functions/FunctionExecutionWrapper.ts +133 -2
- package/src/functions/TestFunctionAdaptor.ts +94 -8
- package/src/functions/__tests__/AWSLambdaFunctionAdaptor.spec.ts +82 -0
- package/src/functions/__tests__/Function.audits.spec.ts +393 -0
- package/src/functions/__tests__/Function.spec.ts +76 -0
- package/src/functions/__tests__/FunctionBuilder.state-isolation.spec.ts +54 -7
- package/src/publisher.ts +12 -1
- package/dist/AWSLambdaFunction-BTSGYZSx.mjs.map +0 -1
- package/dist/AWSLambdaFunction-Bel0NFTl.d.mts +0 -27
- package/dist/AWSLambdaFunction-DcI_77u8.cjs.map +0 -1
- package/dist/AWSLambdaFunction-xyCenuD7.d.cts +0 -27
- package/dist/AmazonApiGatewayEndpointAdaptor-BTqB3c1_.cjs.map +0 -1
- package/dist/AmazonApiGatewayEndpointAdaptor-vi0D3dWg.mjs.map +0 -1
- package/dist/BaseFunctionBuilder-DSqF1r3a.mjs.map +0 -1
- package/dist/BaseFunctionBuilder-bTDKP7bd.cjs.map +0 -1
- package/dist/Construct-XCGKof_O.cjs.map +0 -1
- package/dist/Construct-rZQg2yus.mjs.map +0 -1
- package/dist/Cron-BnO1uXhh.mjs.map +0 -1
- package/dist/Cron-CYIhd0VE.cjs.map +0 -1
- package/dist/CronBuilder-BxKwHCdJ.mjs.map +0 -1
- package/dist/CronBuilder-D_3AiPFO.cjs.map +0 -1
- package/dist/Endpoint-BdIGWw1H.cjs.map +0 -1
- package/dist/Endpoint-ChzcIFdV.mjs.map +0 -1
- package/dist/EndpointBuilder-Cx6pUcCn.d.mts +0 -55
- package/dist/EndpointBuilder-Xka8ULR1.d.cts +0 -55
- package/dist/EndpointBuilder-dmMiNRm9.cjs.map +0 -1
- package/dist/EndpointBuilder-yo40Qkf_.mjs.map +0 -1
- package/dist/EndpointFactory-CNbcr8u_.cjs.map +0 -1
- package/dist/EndpointFactory-LD3upVQK.mjs.map +0 -1
- package/dist/Function-Cl9fiR3a.mjs.map +0 -1
- package/dist/Function-DDaSfL_E.cjs.map +0 -1
- package/dist/FunctionBuilder-LQuBiqRc.cjs.map +0 -1
- package/dist/FunctionBuilder-fU1H7tFN.d.cts +0 -26
- package/dist/FunctionBuilder-udObIGV0.d.mts +0 -26
- package/dist/FunctionBuilder-vW12WFOw.mjs.map +0 -1
- package/dist/FunctionExecutionWrapper-BBIoHUt6.cjs +0 -42
- package/dist/FunctionExecutionWrapper-BBIoHUt6.cjs.map +0 -1
- package/dist/FunctionExecutionWrapper-DYBy9OP3.d.mts +0 -24
- package/dist/FunctionExecutionWrapper-Dtyhid1Q.mjs +0 -36
- package/dist/FunctionExecutionWrapper-Dtyhid1Q.mjs.map +0 -1
- package/dist/FunctionExecutionWrapper-Dwr2ZKzC.d.cts +0 -24
- package/dist/HonoEndpointAdaptor-Bc61IS6Q.cjs.map +0 -1
- package/dist/HonoEndpointAdaptor-NR3h5gml.mjs.map +0 -1
- package/dist/TestEndpointAdaptor-DA1D6l-U.cjs.map +0 -1
- package/dist/TestEndpointAdaptor-DpmNXp7G.mjs.map +0 -1
- package/dist/functions-HCoZ4YKF.mjs +0 -8
- package/dist/index-RtxMVjUZ.d.mts +0 -9
- package/dist/index-fiIGH95t.d.cts +0 -9
|
@@ -0,0 +1,1063 @@
|
|
|
1
|
+
import type { AuditableAction } from '@geekmidas/audit';
|
|
2
|
+
import {
|
|
3
|
+
type AuditLogTable,
|
|
4
|
+
KyselyAuditStorage,
|
|
5
|
+
} from '@geekmidas/audit/kysely';
|
|
6
|
+
import { EnvironmentParser } from '@geekmidas/envkit';
|
|
7
|
+
import type { Logger } from '@geekmidas/logger';
|
|
8
|
+
import type { Service } from '@geekmidas/services';
|
|
9
|
+
import { ServiceDiscovery } from '@geekmidas/services';
|
|
10
|
+
import { Hono } from 'hono';
|
|
11
|
+
import {
|
|
12
|
+
CamelCasePlugin,
|
|
13
|
+
type Generated,
|
|
14
|
+
Kysely,
|
|
15
|
+
PostgresDialect,
|
|
16
|
+
sql,
|
|
17
|
+
} from 'kysely';
|
|
18
|
+
import pg from 'pg';
|
|
19
|
+
import {
|
|
20
|
+
afterAll,
|
|
21
|
+
afterEach,
|
|
22
|
+
beforeAll,
|
|
23
|
+
beforeEach,
|
|
24
|
+
describe,
|
|
25
|
+
expect,
|
|
26
|
+
it,
|
|
27
|
+
vi,
|
|
28
|
+
} from 'vitest';
|
|
29
|
+
import { z } from 'zod';
|
|
30
|
+
import { TEST_DATABASE_CONFIG } from '../../../../testkit/test/globalSetup';
|
|
31
|
+
import { Endpoint, type EndpointContext } from '../Endpoint';
|
|
32
|
+
import { HonoEndpoint } from '../HonoEndpointAdaptor';
|
|
33
|
+
import type { MappedAudit } from '../audit';
|
|
34
|
+
|
|
35
|
+
// Database schema
|
|
36
|
+
interface TestDatabase {
|
|
37
|
+
auditLogs: AuditLogTable;
|
|
38
|
+
users: {
|
|
39
|
+
id: Generated<number>;
|
|
40
|
+
name: string;
|
|
41
|
+
email: string;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Audit action types
|
|
46
|
+
type TestAuditAction =
|
|
47
|
+
| AuditableAction<'user.created', { userId: number; email: string }>
|
|
48
|
+
| AuditableAction<'user.updated', { userId: number; changes: string[] }>
|
|
49
|
+
| AuditableAction<'user.deleted', { userId: number }>;
|
|
50
|
+
|
|
51
|
+
describe('HonoEndpoint Kysely Audit Integration', () => {
|
|
52
|
+
let db: Kysely<TestDatabase>;
|
|
53
|
+
let auditStorage: KyselyAuditStorage<TestDatabase>;
|
|
54
|
+
let mockLogger: Logger;
|
|
55
|
+
|
|
56
|
+
const createMockLogger = (): Logger => ({
|
|
57
|
+
debug: vi.fn(),
|
|
58
|
+
info: vi.fn(),
|
|
59
|
+
warn: vi.fn(),
|
|
60
|
+
error: vi.fn(),
|
|
61
|
+
fatal: vi.fn(),
|
|
62
|
+
trace: vi.fn(),
|
|
63
|
+
child: vi.fn(function (this: Logger) {
|
|
64
|
+
return this;
|
|
65
|
+
}),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const createServiceDiscovery = (logger: Logger) => {
|
|
69
|
+
const envParser = new EnvironmentParser({});
|
|
70
|
+
ServiceDiscovery.reset();
|
|
71
|
+
return ServiceDiscovery.getInstance(logger, envParser);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
beforeAll(async () => {
|
|
75
|
+
db = new Kysely<TestDatabase>({
|
|
76
|
+
dialect: new PostgresDialect({
|
|
77
|
+
pool: new pg.Pool({
|
|
78
|
+
...TEST_DATABASE_CONFIG,
|
|
79
|
+
database: 'postgres',
|
|
80
|
+
}),
|
|
81
|
+
}),
|
|
82
|
+
plugins: [new CamelCasePlugin()],
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Create audit_logs table
|
|
86
|
+
await db.schema
|
|
87
|
+
.createTable('auditLogs')
|
|
88
|
+
.ifNotExists()
|
|
89
|
+
.addColumn('id', 'varchar(32)', (col) => col.primaryKey())
|
|
90
|
+
.addColumn('type', 'varchar', (col) => col.notNull())
|
|
91
|
+
.addColumn('operation', 'varchar', (col) => col.notNull())
|
|
92
|
+
.addColumn('table', 'varchar')
|
|
93
|
+
.addColumn('entityId', 'varchar')
|
|
94
|
+
.addColumn('oldValues', 'jsonb')
|
|
95
|
+
.addColumn('newValues', 'jsonb')
|
|
96
|
+
.addColumn('payload', 'jsonb')
|
|
97
|
+
.addColumn('timestamp', 'timestamp', (col) =>
|
|
98
|
+
col.defaultTo(sql`now()`).notNull(),
|
|
99
|
+
)
|
|
100
|
+
.addColumn('actorId', 'varchar')
|
|
101
|
+
.addColumn('actorType', 'varchar')
|
|
102
|
+
.addColumn('actorData', 'jsonb')
|
|
103
|
+
.addColumn('metadata', 'jsonb')
|
|
104
|
+
.execute();
|
|
105
|
+
|
|
106
|
+
// Create users table
|
|
107
|
+
await db.schema
|
|
108
|
+
.createTable('users')
|
|
109
|
+
.ifNotExists()
|
|
110
|
+
.addColumn('id', 'serial', (col) => col.primaryKey())
|
|
111
|
+
.addColumn('name', 'varchar', (col) => col.notNull())
|
|
112
|
+
.addColumn('email', 'varchar', (col) => col.notNull().unique())
|
|
113
|
+
.execute();
|
|
114
|
+
|
|
115
|
+
auditStorage = new KyselyAuditStorage({
|
|
116
|
+
db,
|
|
117
|
+
tableName: 'auditLogs',
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
beforeEach(() => {
|
|
122
|
+
mockLogger = createMockLogger();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
afterEach(async () => {
|
|
126
|
+
await db.deleteFrom('auditLogs').execute();
|
|
127
|
+
await db.deleteFrom('users').execute();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
afterAll(async () => {
|
|
131
|
+
await db.schema.dropTable('auditLogs').ifExists().execute();
|
|
132
|
+
await db.schema.dropTable('users').ifExists().execute();
|
|
133
|
+
await db.destroy();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('declarative audits with real database', () => {
|
|
137
|
+
it('should write declarative audit to database on successful request', async () => {
|
|
138
|
+
const serviceDiscovery = createServiceDiscovery(mockLogger);
|
|
139
|
+
|
|
140
|
+
const auditStorageService: Service<
|
|
141
|
+
'auditStorage',
|
|
142
|
+
KyselyAuditStorage<TestDatabase>
|
|
143
|
+
> = {
|
|
144
|
+
serviceName: 'auditStorage' as const,
|
|
145
|
+
register: vi.fn().mockResolvedValue(auditStorage),
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const outputSchema = z.object({ id: z.number(), email: z.string() });
|
|
149
|
+
|
|
150
|
+
const audits: MappedAudit<TestAuditAction, typeof outputSchema>[] = [
|
|
151
|
+
{
|
|
152
|
+
type: 'user.created',
|
|
153
|
+
payload: (response) => ({
|
|
154
|
+
userId: response.id,
|
|
155
|
+
email: response.email,
|
|
156
|
+
}),
|
|
157
|
+
},
|
|
158
|
+
];
|
|
159
|
+
|
|
160
|
+
const endpoint = new Endpoint({
|
|
161
|
+
route: '/users',
|
|
162
|
+
method: 'POST',
|
|
163
|
+
fn: async () => {
|
|
164
|
+
return { id: 1, email: 'test@example.com' };
|
|
165
|
+
},
|
|
166
|
+
input: undefined,
|
|
167
|
+
output: outputSchema,
|
|
168
|
+
services: [],
|
|
169
|
+
logger: mockLogger,
|
|
170
|
+
timeout: undefined,
|
|
171
|
+
memorySize: undefined,
|
|
172
|
+
status: 201,
|
|
173
|
+
getSession: undefined,
|
|
174
|
+
authorize: undefined,
|
|
175
|
+
description: undefined,
|
|
176
|
+
events: [],
|
|
177
|
+
publisherService: undefined,
|
|
178
|
+
auditorStorageService: auditStorageService,
|
|
179
|
+
audits,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const adaptor = new HonoEndpoint(endpoint);
|
|
183
|
+
const app = new Hono();
|
|
184
|
+
HonoEndpoint.applyEventMiddleware(app, serviceDiscovery);
|
|
185
|
+
adaptor.addRoute(serviceDiscovery, app);
|
|
186
|
+
|
|
187
|
+
const response = await app.request('/users', {
|
|
188
|
+
method: 'POST',
|
|
189
|
+
body: JSON.stringify({}),
|
|
190
|
+
headers: { 'Content-Type': 'application/json' },
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
expect(response.status).toBe(201);
|
|
194
|
+
|
|
195
|
+
// Verify audit was written to the real database
|
|
196
|
+
const auditsInDb = await db.selectFrom('auditLogs').selectAll().execute();
|
|
197
|
+
|
|
198
|
+
expect(auditsInDb).toHaveLength(1);
|
|
199
|
+
expect(auditsInDb[0].type).toBe('user.created');
|
|
200
|
+
expect(auditsInDb[0].payload).toEqual({
|
|
201
|
+
userId: 1,
|
|
202
|
+
email: 'test@example.com',
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('should not write audit when handler fails', async () => {
|
|
207
|
+
const serviceDiscovery = createServiceDiscovery(mockLogger);
|
|
208
|
+
|
|
209
|
+
const auditStorageService: Service<
|
|
210
|
+
'auditStorage',
|
|
211
|
+
KyselyAuditStorage<TestDatabase>
|
|
212
|
+
> = {
|
|
213
|
+
serviceName: 'auditStorage' as const,
|
|
214
|
+
register: vi.fn().mockResolvedValue(auditStorage),
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const outputSchema = z.object({ id: z.number(), email: z.string() });
|
|
218
|
+
|
|
219
|
+
const audits: MappedAudit<TestAuditAction, typeof outputSchema>[] = [
|
|
220
|
+
{
|
|
221
|
+
type: 'user.created',
|
|
222
|
+
payload: (response) => ({
|
|
223
|
+
userId: response.id,
|
|
224
|
+
email: response.email,
|
|
225
|
+
}),
|
|
226
|
+
},
|
|
227
|
+
];
|
|
228
|
+
|
|
229
|
+
const endpoint = new Endpoint({
|
|
230
|
+
route: '/users',
|
|
231
|
+
method: 'POST',
|
|
232
|
+
fn: async () => {
|
|
233
|
+
throw new Error('Handler failed');
|
|
234
|
+
},
|
|
235
|
+
input: undefined,
|
|
236
|
+
output: outputSchema,
|
|
237
|
+
services: [],
|
|
238
|
+
logger: mockLogger,
|
|
239
|
+
timeout: undefined,
|
|
240
|
+
memorySize: undefined,
|
|
241
|
+
status: 201,
|
|
242
|
+
getSession: undefined,
|
|
243
|
+
authorize: undefined,
|
|
244
|
+
description: undefined,
|
|
245
|
+
events: [],
|
|
246
|
+
publisherService: undefined,
|
|
247
|
+
auditorStorageService: auditStorageService,
|
|
248
|
+
audits,
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const adaptor = new HonoEndpoint(endpoint);
|
|
252
|
+
const app = new Hono();
|
|
253
|
+
HonoEndpoint.applyEventMiddleware(app, serviceDiscovery);
|
|
254
|
+
adaptor.addRoute(serviceDiscovery, app);
|
|
255
|
+
|
|
256
|
+
const response = await app.request('/users', {
|
|
257
|
+
method: 'POST',
|
|
258
|
+
body: JSON.stringify({}),
|
|
259
|
+
headers: { 'Content-Type': 'application/json' },
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
expect(response.status).toBe(500);
|
|
263
|
+
|
|
264
|
+
// Verify no audit was written
|
|
265
|
+
const auditsInDb = await db.selectFrom('auditLogs').selectAll().execute();
|
|
266
|
+
|
|
267
|
+
expect(auditsInDb).toHaveLength(0);
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
describe('manual audits with real database', () => {
|
|
272
|
+
it('should write manual audits from handler to database', async () => {
|
|
273
|
+
const serviceDiscovery = createServiceDiscovery(mockLogger);
|
|
274
|
+
|
|
275
|
+
const auditStorageService: Service<
|
|
276
|
+
'auditStorage',
|
|
277
|
+
KyselyAuditStorage<TestDatabase>
|
|
278
|
+
> = {
|
|
279
|
+
serviceName: 'auditStorage' as const,
|
|
280
|
+
register: vi.fn().mockResolvedValue(auditStorage),
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const outputSchema = z.object({ id: z.number(), email: z.string() });
|
|
284
|
+
|
|
285
|
+
const endpoint = new Endpoint({
|
|
286
|
+
route: '/users',
|
|
287
|
+
method: 'POST',
|
|
288
|
+
fn: async (
|
|
289
|
+
ctx: EndpointContext<
|
|
290
|
+
undefined,
|
|
291
|
+
[],
|
|
292
|
+
Logger,
|
|
293
|
+
unknown,
|
|
294
|
+
TestAuditAction,
|
|
295
|
+
undefined,
|
|
296
|
+
KyselyAuditStorage<TestDatabase>
|
|
297
|
+
>,
|
|
298
|
+
) => {
|
|
299
|
+
// Manual audit in handler - auditor is guaranteed to exist when TAuditStorage is configured
|
|
300
|
+
ctx.auditor.audit('user.created', {
|
|
301
|
+
userId: 42,
|
|
302
|
+
email: 'manual@example.com',
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
return { id: 42, email: 'manual@example.com' };
|
|
306
|
+
},
|
|
307
|
+
input: undefined,
|
|
308
|
+
output: outputSchema,
|
|
309
|
+
services: [],
|
|
310
|
+
logger: mockLogger,
|
|
311
|
+
timeout: undefined,
|
|
312
|
+
memorySize: undefined,
|
|
313
|
+
status: 201,
|
|
314
|
+
getSession: undefined,
|
|
315
|
+
authorize: undefined,
|
|
316
|
+
description: undefined,
|
|
317
|
+
events: [],
|
|
318
|
+
publisherService: undefined,
|
|
319
|
+
auditorStorageService: auditStorageService,
|
|
320
|
+
audits: [],
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
const adaptor = new HonoEndpoint(endpoint);
|
|
324
|
+
const app = new Hono();
|
|
325
|
+
HonoEndpoint.applyEventMiddleware(app, serviceDiscovery);
|
|
326
|
+
adaptor.addRoute(serviceDiscovery, app);
|
|
327
|
+
|
|
328
|
+
const response = await app.request('/users', {
|
|
329
|
+
method: 'POST',
|
|
330
|
+
body: JSON.stringify({}),
|
|
331
|
+
headers: { 'Content-Type': 'application/json' },
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
expect(response.status).toBe(201);
|
|
335
|
+
|
|
336
|
+
// Verify manual audit was written
|
|
337
|
+
const auditsInDb = await db.selectFrom('auditLogs').selectAll().execute();
|
|
338
|
+
|
|
339
|
+
expect(auditsInDb).toHaveLength(1);
|
|
340
|
+
expect(auditsInDb[0].type).toBe('user.created');
|
|
341
|
+
expect(auditsInDb[0].payload).toEqual({
|
|
342
|
+
userId: 42,
|
|
343
|
+
email: 'manual@example.com',
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('should not write manual audit when handler fails after audit call', async () => {
|
|
348
|
+
const serviceDiscovery = createServiceDiscovery(mockLogger);
|
|
349
|
+
|
|
350
|
+
const auditStorageService: Service<
|
|
351
|
+
'auditStorage',
|
|
352
|
+
KyselyAuditStorage<TestDatabase>
|
|
353
|
+
> = {
|
|
354
|
+
serviceName: 'auditStorage' as const,
|
|
355
|
+
register: vi.fn().mockResolvedValue(auditStorage),
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const outputSchema = z.object({ id: z.number(), email: z.string() });
|
|
359
|
+
|
|
360
|
+
const endpoint = new Endpoint({
|
|
361
|
+
route: '/users',
|
|
362
|
+
method: 'POST',
|
|
363
|
+
fn: async (
|
|
364
|
+
ctx: EndpointContext<
|
|
365
|
+
undefined,
|
|
366
|
+
[],
|
|
367
|
+
Logger,
|
|
368
|
+
unknown,
|
|
369
|
+
TestAuditAction,
|
|
370
|
+
undefined,
|
|
371
|
+
KyselyAuditStorage<TestDatabase>
|
|
372
|
+
>,
|
|
373
|
+
) => {
|
|
374
|
+
// Manual audit before failure - auditor is guaranteed to exist
|
|
375
|
+
ctx.auditor.audit('user.created', {
|
|
376
|
+
userId: 99,
|
|
377
|
+
email: 'shouldnotexist@example.com',
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// Fail after audit
|
|
381
|
+
throw new Error('Handler failed after audit');
|
|
382
|
+
},
|
|
383
|
+
input: undefined,
|
|
384
|
+
output: outputSchema,
|
|
385
|
+
services: [],
|
|
386
|
+
logger: mockLogger,
|
|
387
|
+
timeout: undefined,
|
|
388
|
+
memorySize: undefined,
|
|
389
|
+
status: 201,
|
|
390
|
+
getSession: undefined,
|
|
391
|
+
authorize: undefined,
|
|
392
|
+
description: undefined,
|
|
393
|
+
events: [],
|
|
394
|
+
publisherService: undefined,
|
|
395
|
+
auditorStorageService: auditStorageService,
|
|
396
|
+
audits: [],
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const adaptor = new HonoEndpoint(endpoint);
|
|
400
|
+
const app = new Hono();
|
|
401
|
+
HonoEndpoint.applyEventMiddleware(app, serviceDiscovery);
|
|
402
|
+
adaptor.addRoute(serviceDiscovery, app);
|
|
403
|
+
|
|
404
|
+
const response = await app.request('/users', {
|
|
405
|
+
method: 'POST',
|
|
406
|
+
body: JSON.stringify({}),
|
|
407
|
+
headers: { 'Content-Type': 'application/json' },
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
expect(response.status).toBe(500);
|
|
411
|
+
|
|
412
|
+
// Verify no audit was written (transaction rolled back)
|
|
413
|
+
const auditsInDb = await db.selectFrom('auditLogs').selectAll().execute();
|
|
414
|
+
|
|
415
|
+
expect(auditsInDb).toHaveLength(0);
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
describe('transactional consistency with real database', () => {
|
|
420
|
+
it('should commit both user insert and audit on success', async () => {
|
|
421
|
+
const serviceDiscovery = createServiceDiscovery(mockLogger);
|
|
422
|
+
|
|
423
|
+
const databaseService: Service<'database', Kysely<TestDatabase>> = {
|
|
424
|
+
serviceName: 'database' as const,
|
|
425
|
+
register: vi.fn().mockResolvedValue(db),
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
const auditStorageService: Service<
|
|
429
|
+
'auditStorage',
|
|
430
|
+
KyselyAuditStorage<TestDatabase>
|
|
431
|
+
> = {
|
|
432
|
+
serviceName: 'auditStorage' as const,
|
|
433
|
+
register: vi.fn().mockResolvedValue(auditStorage),
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
const outputSchema = z.object({ id: z.number(), email: z.string() });
|
|
437
|
+
|
|
438
|
+
const endpoint = new Endpoint({
|
|
439
|
+
route: '/users',
|
|
440
|
+
method: 'POST',
|
|
441
|
+
fn: async (
|
|
442
|
+
ctx: EndpointContext<
|
|
443
|
+
undefined,
|
|
444
|
+
[typeof databaseService],
|
|
445
|
+
Logger,
|
|
446
|
+
unknown,
|
|
447
|
+
TestAuditAction,
|
|
448
|
+
undefined,
|
|
449
|
+
KyselyAuditStorage<TestDatabase>
|
|
450
|
+
>,
|
|
451
|
+
) => {
|
|
452
|
+
const database = ctx.services.database;
|
|
453
|
+
|
|
454
|
+
// Insert user
|
|
455
|
+
const user = await database
|
|
456
|
+
.insertInto('users')
|
|
457
|
+
.values({ name: 'Success User', email: 'success@example.com' })
|
|
458
|
+
.returningAll()
|
|
459
|
+
.executeTakeFirstOrThrow();
|
|
460
|
+
|
|
461
|
+
// Record audit - auditor is guaranteed to exist
|
|
462
|
+
ctx.auditor.audit('user.created', {
|
|
463
|
+
userId: user.id,
|
|
464
|
+
email: user.email,
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
return { id: user.id, email: user.email };
|
|
468
|
+
},
|
|
469
|
+
input: undefined,
|
|
470
|
+
output: outputSchema,
|
|
471
|
+
services: [databaseService],
|
|
472
|
+
logger: mockLogger,
|
|
473
|
+
timeout: undefined,
|
|
474
|
+
memorySize: undefined,
|
|
475
|
+
status: 201,
|
|
476
|
+
getSession: undefined,
|
|
477
|
+
authorize: undefined,
|
|
478
|
+
description: undefined,
|
|
479
|
+
events: [],
|
|
480
|
+
publisherService: undefined,
|
|
481
|
+
auditorStorageService: auditStorageService,
|
|
482
|
+
audits: [],
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
const adaptor = new HonoEndpoint(endpoint);
|
|
486
|
+
const app = new Hono();
|
|
487
|
+
HonoEndpoint.applyEventMiddleware(app, serviceDiscovery);
|
|
488
|
+
adaptor.addRoute(serviceDiscovery, app);
|
|
489
|
+
|
|
490
|
+
const response = await app.request('/users', {
|
|
491
|
+
method: 'POST',
|
|
492
|
+
body: JSON.stringify({}),
|
|
493
|
+
headers: { 'Content-Type': 'application/json' },
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
expect(response.status).toBe(201);
|
|
497
|
+
|
|
498
|
+
// Verify user was created
|
|
499
|
+
const usersInDb = await db.selectFrom('users').selectAll().execute();
|
|
500
|
+
expect(usersInDb).toHaveLength(1);
|
|
501
|
+
expect(usersInDb[0].email).toBe('success@example.com');
|
|
502
|
+
|
|
503
|
+
// Verify audit was written
|
|
504
|
+
const auditsInDb = await db.selectFrom('auditLogs').selectAll().execute();
|
|
505
|
+
expect(auditsInDb).toHaveLength(1);
|
|
506
|
+
expect(auditsInDb[0].type).toBe('user.created');
|
|
507
|
+
expect(auditsInDb[0].payload).toEqual({
|
|
508
|
+
userId: usersInDb[0].id,
|
|
509
|
+
email: 'success@example.com',
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it('should handle combined declarative and manual audits', async () => {
|
|
514
|
+
const serviceDiscovery = createServiceDiscovery(mockLogger);
|
|
515
|
+
|
|
516
|
+
const auditStorageService: Service<
|
|
517
|
+
'auditStorage',
|
|
518
|
+
KyselyAuditStorage<TestDatabase>
|
|
519
|
+
> = {
|
|
520
|
+
serviceName: 'auditStorage' as const,
|
|
521
|
+
register: vi.fn().mockResolvedValue(auditStorage),
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
const outputSchema = z.object({ id: z.number(), email: z.string() });
|
|
525
|
+
|
|
526
|
+
// Declarative audit
|
|
527
|
+
const audits: MappedAudit<TestAuditAction, typeof outputSchema>[] = [
|
|
528
|
+
{
|
|
529
|
+
type: 'user.created',
|
|
530
|
+
payload: (response) => ({
|
|
531
|
+
userId: response.id,
|
|
532
|
+
email: response.email,
|
|
533
|
+
}),
|
|
534
|
+
},
|
|
535
|
+
];
|
|
536
|
+
|
|
537
|
+
const endpoint = new Endpoint({
|
|
538
|
+
route: '/users',
|
|
539
|
+
method: 'POST',
|
|
540
|
+
fn: async (
|
|
541
|
+
ctx: EndpointContext<
|
|
542
|
+
undefined,
|
|
543
|
+
[],
|
|
544
|
+
Logger,
|
|
545
|
+
unknown,
|
|
546
|
+
TestAuditAction,
|
|
547
|
+
undefined,
|
|
548
|
+
KyselyAuditStorage<TestDatabase>
|
|
549
|
+
>,
|
|
550
|
+
) => {
|
|
551
|
+
// Manual audit - auditor is guaranteed to exist
|
|
552
|
+
ctx.auditor.audit('user.updated', {
|
|
553
|
+
userId: 100,
|
|
554
|
+
changes: ['verified'],
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
return { id: 100, email: 'combined@example.com' };
|
|
558
|
+
},
|
|
559
|
+
input: undefined,
|
|
560
|
+
output: outputSchema,
|
|
561
|
+
services: [],
|
|
562
|
+
logger: mockLogger,
|
|
563
|
+
timeout: undefined,
|
|
564
|
+
memorySize: undefined,
|
|
565
|
+
status: 201,
|
|
566
|
+
getSession: undefined,
|
|
567
|
+
authorize: undefined,
|
|
568
|
+
description: undefined,
|
|
569
|
+
events: [],
|
|
570
|
+
publisherService: undefined,
|
|
571
|
+
auditorStorageService: auditStorageService,
|
|
572
|
+
audits,
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
const adaptor = new HonoEndpoint(endpoint);
|
|
576
|
+
const app = new Hono();
|
|
577
|
+
HonoEndpoint.applyEventMiddleware(app, serviceDiscovery);
|
|
578
|
+
adaptor.addRoute(serviceDiscovery, app);
|
|
579
|
+
|
|
580
|
+
const response = await app.request('/users', {
|
|
581
|
+
method: 'POST',
|
|
582
|
+
body: JSON.stringify({}),
|
|
583
|
+
headers: { 'Content-Type': 'application/json' },
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
expect(response.status).toBe(201);
|
|
587
|
+
|
|
588
|
+
// Verify both audits were written
|
|
589
|
+
const auditsInDb = await db.selectFrom('auditLogs').selectAll().execute();
|
|
590
|
+
|
|
591
|
+
expect(auditsInDb).toHaveLength(2);
|
|
592
|
+
|
|
593
|
+
const auditTypes = auditsInDb.map((a) => a.type).sort();
|
|
594
|
+
expect(auditTypes).toEqual(['user.created', 'user.updated']);
|
|
595
|
+
});
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
describe('actor extraction with real database', () => {
|
|
599
|
+
it('should include actor information in audit records', async () => {
|
|
600
|
+
const serviceDiscovery = createServiceDiscovery(mockLogger);
|
|
601
|
+
|
|
602
|
+
const auditStorageService: Service<
|
|
603
|
+
'auditStorage',
|
|
604
|
+
KyselyAuditStorage<TestDatabase>
|
|
605
|
+
> = {
|
|
606
|
+
serviceName: 'auditStorage' as const,
|
|
607
|
+
register: vi.fn().mockResolvedValue(auditStorage),
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
const outputSchema = z.object({ id: z.number(), email: z.string() });
|
|
611
|
+
|
|
612
|
+
const audits: MappedAudit<TestAuditAction, typeof outputSchema>[] = [
|
|
613
|
+
{
|
|
614
|
+
type: 'user.created',
|
|
615
|
+
payload: (response) => ({
|
|
616
|
+
userId: response.id,
|
|
617
|
+
email: response.email,
|
|
618
|
+
}),
|
|
619
|
+
},
|
|
620
|
+
];
|
|
621
|
+
|
|
622
|
+
const endpoint = new Endpoint({
|
|
623
|
+
route: '/users',
|
|
624
|
+
method: 'POST',
|
|
625
|
+
fn: async () => {
|
|
626
|
+
return { id: 1, email: 'actor-test@example.com' };
|
|
627
|
+
},
|
|
628
|
+
input: undefined,
|
|
629
|
+
output: outputSchema,
|
|
630
|
+
services: [],
|
|
631
|
+
logger: mockLogger,
|
|
632
|
+
timeout: undefined,
|
|
633
|
+
memorySize: undefined,
|
|
634
|
+
status: 201,
|
|
635
|
+
getSession: undefined,
|
|
636
|
+
authorize: undefined,
|
|
637
|
+
description: undefined,
|
|
638
|
+
events: [],
|
|
639
|
+
publisherService: undefined,
|
|
640
|
+
auditorStorageService: auditStorageService,
|
|
641
|
+
audits,
|
|
642
|
+
actorExtractor: async ({ header }) => {
|
|
643
|
+
const userId = header('x-user-id');
|
|
644
|
+
return {
|
|
645
|
+
id: userId ?? 'anonymous',
|
|
646
|
+
type: userId ? 'user' : 'anonymous',
|
|
647
|
+
};
|
|
648
|
+
},
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
const adaptor = new HonoEndpoint(endpoint);
|
|
652
|
+
const app = new Hono();
|
|
653
|
+
HonoEndpoint.applyEventMiddleware(app, serviceDiscovery);
|
|
654
|
+
adaptor.addRoute(serviceDiscovery, app);
|
|
655
|
+
|
|
656
|
+
const response = await app.request('/users', {
|
|
657
|
+
method: 'POST',
|
|
658
|
+
body: JSON.stringify({}),
|
|
659
|
+
headers: {
|
|
660
|
+
'Content-Type': 'application/json',
|
|
661
|
+
'x-user-id': 'user-123',
|
|
662
|
+
},
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
expect(response.status).toBe(201);
|
|
666
|
+
|
|
667
|
+
// Verify actor was included in audit
|
|
668
|
+
const auditsInDb = await db.selectFrom('auditLogs').selectAll().execute();
|
|
669
|
+
|
|
670
|
+
expect(auditsInDb).toHaveLength(1);
|
|
671
|
+
expect(auditsInDb[0].actorId).toBe('user-123');
|
|
672
|
+
expect(auditsInDb[0].actorType).toBe('user');
|
|
673
|
+
});
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
describe('database service name matching', () => {
|
|
677
|
+
it('should use audit transaction as db when databaseServiceName matches', async () => {
|
|
678
|
+
const serviceDiscovery = createServiceDiscovery(mockLogger);
|
|
679
|
+
|
|
680
|
+
// Create audit storage WITH databaseServiceName
|
|
681
|
+
const auditStorageWithServiceName = new KyselyAuditStorage({
|
|
682
|
+
db,
|
|
683
|
+
tableName: 'auditLogs',
|
|
684
|
+
databaseServiceName: 'database', // Matches the database service
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
const databaseService: Service<'database', Kysely<TestDatabase>> = {
|
|
688
|
+
serviceName: 'database' as const,
|
|
689
|
+
register: vi.fn().mockResolvedValue(db),
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
const auditStorageService: Service<
|
|
693
|
+
'auditStorage',
|
|
694
|
+
KyselyAuditStorage<TestDatabase>
|
|
695
|
+
> = {
|
|
696
|
+
serviceName: 'auditStorage' as const,
|
|
697
|
+
register: vi.fn().mockResolvedValue(auditStorageWithServiceName),
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
const outputSchema = z.object({ id: z.number(), email: z.string() });
|
|
701
|
+
|
|
702
|
+
let receivedDbIsTransaction = false;
|
|
703
|
+
|
|
704
|
+
const endpoint = new Endpoint({
|
|
705
|
+
route: '/users',
|
|
706
|
+
method: 'POST',
|
|
707
|
+
fn: async (
|
|
708
|
+
ctx: EndpointContext<
|
|
709
|
+
undefined,
|
|
710
|
+
[typeof databaseService],
|
|
711
|
+
Logger,
|
|
712
|
+
unknown,
|
|
713
|
+
TestAuditAction,
|
|
714
|
+
Kysely<TestDatabase>,
|
|
715
|
+
KyselyAuditStorage<TestDatabase>
|
|
716
|
+
>,
|
|
717
|
+
) => {
|
|
718
|
+
// Check if db is a transaction (has isTransaction property from Kysely)
|
|
719
|
+
// When databaseServiceName matches, db should be the transaction
|
|
720
|
+
receivedDbIsTransaction = (ctx.db as any)?.isTransaction === true;
|
|
721
|
+
|
|
722
|
+
// Insert user using ctx.db (should be the transaction)
|
|
723
|
+
const user = await ctx.db
|
|
724
|
+
.insertInto('users')
|
|
725
|
+
.values({ name: 'Transaction User', email: 'trx@example.com' })
|
|
726
|
+
.returningAll()
|
|
727
|
+
.executeTakeFirstOrThrow();
|
|
728
|
+
|
|
729
|
+
// Record audit
|
|
730
|
+
ctx.auditor.audit('user.created', {
|
|
731
|
+
userId: user.id,
|
|
732
|
+
email: user.email,
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
return { id: user.id, email: user.email };
|
|
736
|
+
},
|
|
737
|
+
input: undefined,
|
|
738
|
+
output: outputSchema,
|
|
739
|
+
services: [databaseService],
|
|
740
|
+
logger: mockLogger,
|
|
741
|
+
timeout: undefined,
|
|
742
|
+
memorySize: undefined,
|
|
743
|
+
status: 201,
|
|
744
|
+
getSession: undefined,
|
|
745
|
+
authorize: undefined,
|
|
746
|
+
description: undefined,
|
|
747
|
+
events: [],
|
|
748
|
+
publisherService: undefined,
|
|
749
|
+
auditorStorageService: auditStorageService,
|
|
750
|
+
audits: [],
|
|
751
|
+
databaseService,
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
const adaptor = new HonoEndpoint(endpoint);
|
|
755
|
+
const app = new Hono();
|
|
756
|
+
HonoEndpoint.applyEventMiddleware(app, serviceDiscovery);
|
|
757
|
+
adaptor.addRoute(serviceDiscovery, app);
|
|
758
|
+
|
|
759
|
+
const response = await app.request('/users', {
|
|
760
|
+
method: 'POST',
|
|
761
|
+
body: JSON.stringify({}),
|
|
762
|
+
headers: { 'Content-Type': 'application/json' },
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
expect(response.status).toBe(201);
|
|
766
|
+
expect(receivedDbIsTransaction).toBe(true);
|
|
767
|
+
|
|
768
|
+
// Verify both user and audit were committed
|
|
769
|
+
const usersInDb = await db.selectFrom('users').selectAll().execute();
|
|
770
|
+
expect(usersInDb).toHaveLength(1);
|
|
771
|
+
expect(usersInDb[0].email).toBe('trx@example.com');
|
|
772
|
+
|
|
773
|
+
const auditsInDb = await db.selectFrom('auditLogs').selectAll().execute();
|
|
774
|
+
expect(auditsInDb).toHaveLength(1);
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
it('should use raw db when databaseServiceName does not match', async () => {
|
|
778
|
+
const serviceDiscovery = createServiceDiscovery(mockLogger);
|
|
779
|
+
|
|
780
|
+
// Create audit storage with DIFFERENT databaseServiceName
|
|
781
|
+
const auditStorageWithDifferentServiceName = new KyselyAuditStorage({
|
|
782
|
+
db,
|
|
783
|
+
tableName: 'auditLogs',
|
|
784
|
+
databaseServiceName: 'auditDatabase', // Different from 'database'
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
const databaseService: Service<'database', Kysely<TestDatabase>> = {
|
|
788
|
+
serviceName: 'database' as const,
|
|
789
|
+
register: vi.fn().mockResolvedValue(db),
|
|
790
|
+
};
|
|
791
|
+
|
|
792
|
+
const auditStorageService: Service<
|
|
793
|
+
'auditStorage',
|
|
794
|
+
KyselyAuditStorage<TestDatabase>
|
|
795
|
+
> = {
|
|
796
|
+
serviceName: 'auditStorage' as const,
|
|
797
|
+
register: vi
|
|
798
|
+
.fn()
|
|
799
|
+
.mockResolvedValue(auditStorageWithDifferentServiceName),
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
const outputSchema = z.object({ id: z.number(), email: z.string() });
|
|
803
|
+
|
|
804
|
+
let receivedDbIsTransaction = false;
|
|
805
|
+
|
|
806
|
+
const endpoint = new Endpoint({
|
|
807
|
+
route: '/users',
|
|
808
|
+
method: 'POST',
|
|
809
|
+
fn: async (
|
|
810
|
+
ctx: EndpointContext<
|
|
811
|
+
undefined,
|
|
812
|
+
[typeof databaseService],
|
|
813
|
+
Logger,
|
|
814
|
+
unknown,
|
|
815
|
+
TestAuditAction,
|
|
816
|
+
Kysely<TestDatabase>,
|
|
817
|
+
KyselyAuditStorage<TestDatabase>
|
|
818
|
+
>,
|
|
819
|
+
) => {
|
|
820
|
+
// When databaseServiceName doesn't match, db should be raw (not a transaction)
|
|
821
|
+
receivedDbIsTransaction = (ctx.db as any)?.isTransaction === true;
|
|
822
|
+
|
|
823
|
+
// Insert user using ctx.db (should be raw db, not transaction)
|
|
824
|
+
const user = await ctx.db
|
|
825
|
+
.insertInto('users')
|
|
826
|
+
.values({ name: 'Raw DB User', email: 'raw@example.com' })
|
|
827
|
+
.returningAll()
|
|
828
|
+
.executeTakeFirstOrThrow();
|
|
829
|
+
|
|
830
|
+
// Record audit
|
|
831
|
+
ctx.auditor.audit('user.created', {
|
|
832
|
+
userId: user.id,
|
|
833
|
+
email: user.email,
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
return { id: user.id, email: user.email };
|
|
837
|
+
},
|
|
838
|
+
input: undefined,
|
|
839
|
+
output: outputSchema,
|
|
840
|
+
services: [databaseService],
|
|
841
|
+
logger: mockLogger,
|
|
842
|
+
timeout: undefined,
|
|
843
|
+
memorySize: undefined,
|
|
844
|
+
status: 201,
|
|
845
|
+
getSession: undefined,
|
|
846
|
+
authorize: undefined,
|
|
847
|
+
description: undefined,
|
|
848
|
+
events: [],
|
|
849
|
+
publisherService: undefined,
|
|
850
|
+
auditorStorageService: auditStorageService,
|
|
851
|
+
audits: [],
|
|
852
|
+
databaseService,
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
const adaptor = new HonoEndpoint(endpoint);
|
|
856
|
+
const app = new Hono();
|
|
857
|
+
HonoEndpoint.applyEventMiddleware(app, serviceDiscovery);
|
|
858
|
+
adaptor.addRoute(serviceDiscovery, app);
|
|
859
|
+
|
|
860
|
+
const response = await app.request('/users', {
|
|
861
|
+
method: 'POST',
|
|
862
|
+
body: JSON.stringify({}),
|
|
863
|
+
headers: { 'Content-Type': 'application/json' },
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
expect(response.status).toBe(201);
|
|
867
|
+
// db should NOT be a transaction since service names don't match
|
|
868
|
+
expect(receivedDbIsTransaction).toBe(false);
|
|
869
|
+
|
|
870
|
+
// Both should still be committed (but not in the same transaction)
|
|
871
|
+
const usersInDb = await db.selectFrom('users').selectAll().execute();
|
|
872
|
+
expect(usersInDb).toHaveLength(1);
|
|
873
|
+
|
|
874
|
+
const auditsInDb = await db.selectFrom('auditLogs').selectAll().execute();
|
|
875
|
+
expect(auditsInDb).toHaveLength(1);
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
it('should use raw db when databaseServiceName is not set on audit storage', async () => {
|
|
879
|
+
const serviceDiscovery = createServiceDiscovery(mockLogger);
|
|
880
|
+
|
|
881
|
+
// Create audit storage WITHOUT databaseServiceName (uses default auditStorage from beforeAll)
|
|
882
|
+
const databaseService: Service<'database', Kysely<TestDatabase>> = {
|
|
883
|
+
serviceName: 'database' as const,
|
|
884
|
+
register: vi.fn().mockResolvedValue(db),
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
const auditStorageService: Service<
|
|
888
|
+
'auditStorage',
|
|
889
|
+
KyselyAuditStorage<TestDatabase>
|
|
890
|
+
> = {
|
|
891
|
+
serviceName: 'auditStorage' as const,
|
|
892
|
+
register: vi.fn().mockResolvedValue(auditStorage), // No databaseServiceName set
|
|
893
|
+
};
|
|
894
|
+
|
|
895
|
+
const outputSchema = z.object({ id: z.number(), email: z.string() });
|
|
896
|
+
|
|
897
|
+
let receivedDbIsTransaction = false;
|
|
898
|
+
|
|
899
|
+
const endpoint = new Endpoint({
|
|
900
|
+
route: '/users',
|
|
901
|
+
method: 'POST',
|
|
902
|
+
fn: async (
|
|
903
|
+
ctx: EndpointContext<
|
|
904
|
+
undefined,
|
|
905
|
+
[typeof databaseService],
|
|
906
|
+
Logger,
|
|
907
|
+
unknown,
|
|
908
|
+
TestAuditAction,
|
|
909
|
+
Kysely<TestDatabase>,
|
|
910
|
+
KyselyAuditStorage<TestDatabase>
|
|
911
|
+
>,
|
|
912
|
+
) => {
|
|
913
|
+
// When databaseServiceName is not set, db should be raw
|
|
914
|
+
receivedDbIsTransaction = (ctx.db as any)?.isTransaction === true;
|
|
915
|
+
|
|
916
|
+
const user = await ctx.db
|
|
917
|
+
.insertInto('users')
|
|
918
|
+
.values({
|
|
919
|
+
name: 'No ServiceName User',
|
|
920
|
+
email: 'noname@example.com',
|
|
921
|
+
})
|
|
922
|
+
.returningAll()
|
|
923
|
+
.executeTakeFirstOrThrow();
|
|
924
|
+
|
|
925
|
+
ctx.auditor.audit('user.created', {
|
|
926
|
+
userId: user.id,
|
|
927
|
+
email: user.email,
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
return { id: user.id, email: user.email };
|
|
931
|
+
},
|
|
932
|
+
input: undefined,
|
|
933
|
+
output: outputSchema,
|
|
934
|
+
services: [databaseService],
|
|
935
|
+
logger: mockLogger,
|
|
936
|
+
timeout: undefined,
|
|
937
|
+
memorySize: undefined,
|
|
938
|
+
status: 201,
|
|
939
|
+
getSession: undefined,
|
|
940
|
+
authorize: undefined,
|
|
941
|
+
description: undefined,
|
|
942
|
+
events: [],
|
|
943
|
+
publisherService: undefined,
|
|
944
|
+
auditorStorageService: auditStorageService,
|
|
945
|
+
audits: [],
|
|
946
|
+
databaseService,
|
|
947
|
+
});
|
|
948
|
+
|
|
949
|
+
const adaptor = new HonoEndpoint(endpoint);
|
|
950
|
+
const app = new Hono();
|
|
951
|
+
HonoEndpoint.applyEventMiddleware(app, serviceDiscovery);
|
|
952
|
+
adaptor.addRoute(serviceDiscovery, app);
|
|
953
|
+
|
|
954
|
+
const response = await app.request('/users', {
|
|
955
|
+
method: 'POST',
|
|
956
|
+
body: JSON.stringify({}),
|
|
957
|
+
headers: { 'Content-Type': 'application/json' },
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
expect(response.status).toBe(201);
|
|
961
|
+
expect(receivedDbIsTransaction).toBe(false);
|
|
962
|
+
|
|
963
|
+
const usersInDb = await db.selectFrom('users').selectAll().execute();
|
|
964
|
+
expect(usersInDb).toHaveLength(1);
|
|
965
|
+
|
|
966
|
+
const auditsInDb = await db.selectFrom('auditLogs').selectAll().execute();
|
|
967
|
+
expect(auditsInDb).toHaveLength(1);
|
|
968
|
+
});
|
|
969
|
+
|
|
970
|
+
it('should rollback both user insert and audit when handler fails with matching databaseServiceName', async () => {
|
|
971
|
+
const serviceDiscovery = createServiceDiscovery(mockLogger);
|
|
972
|
+
|
|
973
|
+
// Create audit storage WITH databaseServiceName
|
|
974
|
+
const auditStorageWithServiceName = new KyselyAuditStorage({
|
|
975
|
+
db,
|
|
976
|
+
tableName: 'auditLogs',
|
|
977
|
+
databaseServiceName: 'database',
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
const databaseService: Service<'database', Kysely<TestDatabase>> = {
|
|
981
|
+
serviceName: 'database' as const,
|
|
982
|
+
register: vi.fn().mockResolvedValue(db),
|
|
983
|
+
};
|
|
984
|
+
|
|
985
|
+
const auditStorageService: Service<
|
|
986
|
+
'auditStorage',
|
|
987
|
+
KyselyAuditStorage<TestDatabase>
|
|
988
|
+
> = {
|
|
989
|
+
serviceName: 'auditStorage' as const,
|
|
990
|
+
register: vi.fn().mockResolvedValue(auditStorageWithServiceName),
|
|
991
|
+
};
|
|
992
|
+
|
|
993
|
+
const outputSchema = z.object({ id: z.number(), email: z.string() });
|
|
994
|
+
|
|
995
|
+
const endpoint = new Endpoint({
|
|
996
|
+
route: '/users',
|
|
997
|
+
method: 'POST',
|
|
998
|
+
fn: async (
|
|
999
|
+
ctx: EndpointContext<
|
|
1000
|
+
undefined,
|
|
1001
|
+
[typeof databaseService],
|
|
1002
|
+
Logger,
|
|
1003
|
+
unknown,
|
|
1004
|
+
TestAuditAction,
|
|
1005
|
+
Kysely<TestDatabase>,
|
|
1006
|
+
KyselyAuditStorage<TestDatabase>
|
|
1007
|
+
>,
|
|
1008
|
+
) => {
|
|
1009
|
+
// Insert user (should be rolled back)
|
|
1010
|
+
const user = await ctx.db
|
|
1011
|
+
.insertInto('users')
|
|
1012
|
+
.values({ name: 'Rollback User', email: 'rollback@example.com' })
|
|
1013
|
+
.returningAll()
|
|
1014
|
+
.executeTakeFirstOrThrow();
|
|
1015
|
+
|
|
1016
|
+
// Record audit (should also be rolled back)
|
|
1017
|
+
ctx.auditor.audit('user.created', {
|
|
1018
|
+
userId: user.id,
|
|
1019
|
+
email: user.email,
|
|
1020
|
+
});
|
|
1021
|
+
|
|
1022
|
+
// Fail after both operations
|
|
1023
|
+
throw new Error('Simulated failure');
|
|
1024
|
+
},
|
|
1025
|
+
input: undefined,
|
|
1026
|
+
output: outputSchema,
|
|
1027
|
+
services: [databaseService],
|
|
1028
|
+
logger: mockLogger,
|
|
1029
|
+
timeout: undefined,
|
|
1030
|
+
memorySize: undefined,
|
|
1031
|
+
status: 201,
|
|
1032
|
+
getSession: undefined,
|
|
1033
|
+
authorize: undefined,
|
|
1034
|
+
description: undefined,
|
|
1035
|
+
events: [],
|
|
1036
|
+
publisherService: undefined,
|
|
1037
|
+
auditorStorageService: auditStorageService,
|
|
1038
|
+
audits: [],
|
|
1039
|
+
databaseService,
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
const adaptor = new HonoEndpoint(endpoint);
|
|
1043
|
+
const app = new Hono();
|
|
1044
|
+
HonoEndpoint.applyEventMiddleware(app, serviceDiscovery);
|
|
1045
|
+
adaptor.addRoute(serviceDiscovery, app);
|
|
1046
|
+
|
|
1047
|
+
const response = await app.request('/users', {
|
|
1048
|
+
method: 'POST',
|
|
1049
|
+
body: JSON.stringify({}),
|
|
1050
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1051
|
+
});
|
|
1052
|
+
|
|
1053
|
+
expect(response.status).toBe(500);
|
|
1054
|
+
|
|
1055
|
+
// Both should be rolled back since they're in the same transaction
|
|
1056
|
+
const usersInDb = await db.selectFrom('users').selectAll().execute();
|
|
1057
|
+
expect(usersInDb).toHaveLength(0);
|
|
1058
|
+
|
|
1059
|
+
const auditsInDb = await db.selectFrom('auditLogs').selectAll().execute();
|
|
1060
|
+
expect(auditsInDb).toHaveLength(0);
|
|
1061
|
+
});
|
|
1062
|
+
});
|
|
1063
|
+
});
|