From abda1baca71e7b3b9300a5a09803f17ab72ca569 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 16 Feb 2024 13:45:35 +0100 Subject: [PATCH 0001/1237] Add test for model packs in MRVA to test plan --- docs/images/model-pack-results-table.png | Bin 0 -> 10424 bytes docs/test-plan.md | 42 +++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 docs/images/model-pack-results-table.png diff --git a/docs/images/model-pack-results-table.png b/docs/images/model-pack-results-table.png new file mode 100644 index 0000000000000000000000000000000000000000..d2e48102fd7bb0461b04905a5ae1db7ae605e4e6 GIT binary patch literal 10424 zcmeHtWn7g@^e!N^32EsDY4%1+x>b-y2}L%rK|s1>(;z9G0@5l7(jlRAhlGH%Ai3#H zcieY-&hc>W{dPaxFZa*y_rfqUYi7M`&8%6^Gw*9n_4|Z)G(fnVGzlbmqQCsp!pLUE2 zaw(*0jxOrJZk>*P&_U`73^Y8Y&QVhx?H49mYhM10ESRcHd9>`gc`wpbwu_3iyo00H zhas6i+;ne6yLGDL40GgP^vp3H@O@7deu%wFn_~l+LiaK;b!|0CtA6ci`y}kx&$+l6 zktRskP&~#~$+>f*%yRL7*D>gBg4M3Gnqlc^WC{K8;VT2x{`fo`*OmY@}`8`Ykw)d!iqr=R?XdMrOQ|Lm3ZoOFeD`bT;GQh51uGH1Pk5#9Fj zujL)GFC#E73`Wn}fXQ{u^_8EisiAQLWn47$5GyoHpo9)QG{A#~b|Wqr4F~v33OtHg z82_|lTxQ+)r;G+ib(GUlP*w*1>cE`L&F!2m?OlpX+W{%`QL9J#F8XS!;xKz#eiJi$ zQ*(Z}tpiE~O%g5+6m892Oqk%dHg?Y9a4DAGJ;Z@Bsv69~^t+3TwG@lKnkJKiy^}eU z2tR}$!Xk~w#Ka`&WcFNKTk+oC;=q4WES4@V4&q?2o0}WIn;^fvlLc5nOiT<6xdXm) zhY#q%=j?9hVgl#0b7uW(kblNeG z&!q9(M;IE=(AWl)73Chm(KpjUb!o#Vej2w7%oIq=$6Yz+xj|~tCl)g>x?LE!oUyoxi}4y_C9@74jSdVriTA*GE1G0T|Q%rPwr4; z?GGdiul=@C{$6 z&x!fUM1_UENf1Sg*Ir*zoOj~2mzI4@V)?TDDu~huw-?;#6_9wTh_!STDLR8&P}1Nb zF5C&-*&QaTJKgJ-u0Kmw<)-g+ms`h6115abIfwwHlcGKd}yO2wVmp35^VA=EzS32jiOeiXKpD_$nm$6o)wa z#mb8H=xIxzEXF%bJ9Y*=bNeYQc{mQ+(I%4G*Sw2JbLMuRcwUf-m8)muo-?slP!*Qp zWv_pX=oJmLqyG~8Om~u>uIlh|&dqw04Ew-8?UUOvN0`BwHf03UvpA&qOp61q5n=%!ns)@CR zi^Hi_tB>iUHV271-$(LwTDxLdEqcYBm%l+w+d`Yx13_dMJW^`0A@`&iW|EIzQk8dP znEX^4!`*XRBU!eZl6JA1OetTE*@O`v&Pa+JA;Ry~ax)+nQ zo`v?Fjg@Y=qmZ~CGk|UJWV@AUp&{bDh$YY_l0iy5H9Il~1FVqr*!?BqDkH-f>?R zy+Y?pdc0fKPpi4S;@L%U_gvQ)7#J^9n;-GYfMgQY*K6JIEittpwL`(B=ekNORT zznl1MJ@(FB8v_sSt#UPCkx74ggv>0b^fG6K93+Gt%$kPt%?BH|1jHy&J`*~>K>8Bl z^Vb-|fBECIlyx$s7BDJZ?GI}Zab&bMxx< z$lo~+NQoNbrnw9$iz#{T^@$R7(mAWH0Gn1w`=<2qukF+Il1}llr}p*RBRU#EA2WT# zY3LhI`s_`g!aYdjXFiWU;n}D^nwi_Ip1paW!QBjgb#Vln1S~??e|I6GCZ9IIyeo!z zNBIF;%f|WMK(zZRYx!I-Ew(N<a&qCYM zKs>W&u@NEJ<8!q2Q|Ha}y3g6B=)(;u^ZZ858N=*v&4XGRKip5o2+}9rzNMufY%hRi zK=p`C>Z&Kp7IQ6#5?pJ(x)&=$+4~G}2G%?2e}bbWoxkqpjt0Y2;e2wt-x&4M{?=Dy ze+IutM0v!#(Stnecr_7_1hURKdvRK-5k$*e!fRIE zh0pU8+`>JWYrh(6yri(JKNwx9j|Q<{vKJTLod(>2ZZ8(Bxch1NBl-<&Mb`P)`wSp@ zahFh-pSqNuVf|rR{wm7&**!;!#h^H821pvw+q-LG(P7G zj3Gr;liVA?C>2ZTImSOzR|PEswc=-q0?uxJ)99&Z6qxICaD5g!pf~(6=tN>b?`VuQ zBK5TOlPdpvoIgiKmOSQ@&-2luhlLgWc%8sJyhbp665Urr4>vry%%#edK>WGK6{MQ| z@xh!LQcJiqZVA6;>OFB1W|E*jVsF|Nd}h6ml#o)5wtnb-q75-wk|njvZ!6fcw~MPL zUzACcViVGk&j68K_8Hy#@%ir^S>rYpeWqQ+X%NmHl9pfy1e=I~t|Nk)jF3Lcio@jI ze#nnd-^;yBfoU9tUrjfN(u&q9;~eIKC_BnS=wIsBtbHnY-mABwn8g3AC4fkpiqmtq zn*$-Ax}wM2Of?0ImcpnzE(%4R7F9M0>~w3}umxdbX>HtVB@%o=X_}nd>H5hemo#e!IdyL77d{0Ha8HB!sY-^XfjG zqar~N^<4+!N2k%!rz@q$^Wl__?(&3Jo=<|JmE(M>{nT=8e3Y{5fd<$LzLiCW`?3u! zT4VXo~IaD>j)bcCZ`N_>SlMgI=?079cOwFUwvNu zbt|lZBA;MW(Q(GQq~3H|k_W^&PNl|)IAN9yBrNNZgKIxYY>sankUsms9o}GCTH4bD zhj9Wi=8ETS66UTk0kh26#K1c((%Hoe*1*??z=&T2mVh8S&?|5@wK}U#L_g`>s4ZmM zF*diGRE#fCQe(R!hQx4nymD2!ngX64iQt)M9?N*u_EY?t>al;0X6G>30=;u|C4JD& z5vP^pTAaR{gSWT>7^2u1%n2W8oDy^u7;iha&C>g0l^3p-2; ztM++A8qOdoIY;|2P3fT0gij&kJ{fld-l;}N-IM*Oo{qubA=3KJB^N5Y_5FRP%@2}K ztsS$baYOo2vSrKS@R12hKhv+n>JmXfND~ZRGA14jX5-w(T`lA_^xXVhTaRD*TyU2{ z&yYkRG>`UuAcQ{Rw!8yw1q%W0!VOl|ANNCVVz&XJWQ37GxRId|UsRrs+fAR;UP&Yz zq+)N|9+uYC7;lP1Z|}kMRtv^k5_o|tW11Ksg|$J*$##BB2ixAL80hc_JwuRuI8Z+O za{uMw<|v29QMaf=_EM_`&iUOsp{>NysVr!4&=)Uz&K&V${}RW{=n~ILi$<3oy??ih zBZlzYjz)s!@r2E!{_0o>lwY^5%r#5DxMn`goZ>#Ux(Oe=-=L@|K62<=$5V!h*M;?m zla8(Yo$z_BNi92f?wc};k5#u{zz9g5w_@v)uSJSXa@s}Zd_%{fBd(w5e!yaLTX`Tu z>V}Hg+&!BbcH1A1^&-3BW5Y9eB0Vfr)PYaGi^zL#FWUpbQ-F}wUxaCu#zyvboyC-G zbz}#wRb?~>W#S%Ny4$R`Xt9W9@JEn|5b>n~oWzp>qCPGYtIKcfuN4|AD+B+e`O?*I zopsTIPV<-=XVS9;#L-rHdTCqNb~@KVa)sA>@PxbL?EU$k-h<#Mk2{4kz7FTHCGx8o z@qhM8BGX{5odRs`XG~-r4a0b%o>6UVP9r#&GVK`ohM2)zdF6qa5#szXd!KAMe!g{Y zxB{sq>0Y>uO>0A}N3Ff0CPGxU4==+Hx+!1-9|0vQn{Z8(pLCLOs7Gkr>#(-HOe!+d%^+=Z;f{C}oLU+FU-q0qmb+EBE1da}Ius&;%WA5D z!Go(@i=c6stH#(ZQKqq-bHnQq;O!bfQfd0uI(4QV1re=fAR2qa8bfz3PL__}Np1HJ zLY#(HN{>D@@}JgSojWxo{dvnixvX5t%RY2W^Ei%1CS?J>k46bFg5XO3%CtL+%!PrC z@^PlC_&M23dmEQd>_L;{J+F}UIWa>T z1_Xhowml`OM}%6Z-Hiw3saP2Z-_`Ij#puf(Z(ZD<5-*8nM=w;6UES|nO9jB#Ew6mO z_LMw2MbG^QP(kj4&a=FQhs}5B7QXj!za-3mTOL$5M411c%Uz4c-Nxe7q{u1Z>chTH zaq>{g?ClIW4NEOyPzQQ6g{|`C(zfxibADgr(#Y_%J49T>$mycgrUc#O_;_p{dzRW& zMMh(Lb++DKzi|tv;Z5<>;8c}bpG}dKUEnCk-zZ5I3nb*3M>GJx@2-)mO*{0mCz4i_ zpN8;2N-2lgRYU|Fox`>t#PKETyuwQOtddb=Cs(GftB^dQU|+OK?ajZ-h%`nP1`_%@ zuc4rM`I-w{>0aItJa5&pK0T!3TNSy`8BGN1jEYQ^2Q4uN(+%)D9@%iVTSFsf5H^gG zZ~nLm7)6QPw@l?fxb%lolkvhD4bfB9>(DNYSaYWYSC{8MfXtNys4U(o75!F|csJ6rJZnum;|h%FB_Gi?eGBjrkmZyA2KH4$Nk1wqASu9~`7QKR$8WsRR3 zc2}+Jl(0Qp|BuHLQJ#$RFQvXdQyv@OAC;lccGp#V1Oh6#sc}=8udf6ZQb7Ly(V&(B z?Y@(hR&xML00PocM>%pvuWdQ_;e*INMK9qHS z7IpNYfQrMQ98M6p;rN~L)5jio!SK;PvRZj>!7t??5B_bP9mO`Rw5a? zc&h;bIqULT8z8_nzg`(;Hk|MF#+hdT2yc7#>ayCbVKj}|H%$kMLjT)9ZY3cJ6z0}T z8r454C{Qosc`*6YusVEKqHIUrhkgAKBX%(Q)SeJX`<}zVk1GuomKWY7CZjl+tRPTj z@_clQZx?e&AOMhSm|g+-s>u14TO+=pML%!y%y>MkBLW0&Ja>YHQ}P}iPTAkTVSct* z{TnlW69gJxx$kKJ@T%HhCXlu_hFPHnU~ZfzP&m9ig3&GlS$|~2i&NoLlKEA0rdh*h zH@@`yJ=&m722WnqOlc{w>u7c58IV;I03hk85L9&h6Ue#92{Uad@gnzlO6o{Orfe!4 z0Cb5XT_Leqsne9FMAHKCq>pCTw8lJ^_688D+L~^yGfn?Hp^qXBo~9E>?5pSH?fU0$?4u`Z%W6?gd6^bT9i2YLyh zGKBgZpAV4VJB7gj_}B_0h;JW)kBot2pHtNOyVpK5wSbb~g>TDXwt}<4jQ7#cQUC@m z&s2{FD97K%(iPZcm+0qYLE&e=qs+_wk9X~;d;x6YE))o8ekG1NqI{SJ0k0%Ao-Yw$ zr8*?Y;0V2xwv#90PG9wY8) zK-V1_!}YxPQFwt)Oq|P8c3*94c6)dhcs9A%O6m`vWEll+C(*l23k-IsiT)H~AiEP6 zD82%oIh8sbXU&+gZ@g%!MP$&{>l#KkFa^rczZ^Fy(~!A38F;UyX{ogM=BXVhkeInN zCu)OHGLC%RS_O=tSRjnPnzSyV-)g+NG>kQ^%kWsszk4r&5T>~D7*FlXMtN_`?B!v# zA${pbK#Pc-<4&1Nl0!hE3%#9|)e*2jG}1un6yPLa?sYy9f`!Lt>(HyWdzho5{QLd` zWX$S@XW1bPJ)b!|w=d6-IF~~;u4^M)RR#K&HU_{l*~HXWT0Bcw9>ihvf{d9pBw0y6 zFJ6#HNe@p(-W{NOdm;T1k47A6h8PhlnT9V!I!c=xEzwxzME#vN=_?x8A! z+iaG|uYB0h;+9?;>i&L^uE~A(!%2-a_CX(bb+Q3*G1=<4koCnv)a+-5L zh%;&Wkydi;M7ahF6t_Oxx65O_h?2r6VWwh0S|a1A?dKo!*52Nc)IG-`0 zNRmb%f>Jw_k_RJ#-8PG{ps-v;b|UkxqbaYT`|cu;G)YrJ9%M!XV3ZSu6Jn(tTMI=X zH_wobzCv9n=d8F##qWrRP?y zMH-AlH5t--nc4A8VnR%~kLPIGS;M%vjoPm~S^>Sg-xtLMU!X`{QyHKA_l)gTg2*8v z0@`KTWxUhoG#3N>^1uv z+yES+erCDX9R=YZ(_)ws7S%*1*gQ4a9i88^-aXm#_L8muK5Fvb>kypSBC&yBJFjLS)XFX()EoQm!BjUtHI3 zVG~dh+s6gQ4)(KE20=&zFAvs!@F3FF-D^@j z*lDMn8u=a+K-&lK+_&em;T%h816X)sP6t2q?zf{Yt9G8T`^=o*#Fex}be4UY854Hd z^CeO9@I>--<^6ZE0m(39$S+=H?b*6k$@9C#fY4W6f%V?<$0zG*;GfuY_9k2fsDla~D!2eUG?3AzHvQ=ttS2aPDt ziD_iDQ(X0@OWenz&tYJ{IndWHfuyq+0d=l?f4{&S|B{s4vrbgG98Vvxl)_l?GaYRd zlW9kt91kz6X=ZDF*c;CjLtc_$q{AR7wMw%poRhfsHUU{>I~5GvLbRnm6k95H-*%W= z`p8EGhBTc$j&i7IKZ_K_mwvDY^H4j`^c4$}F@ozV5!cx%*SJ5YxZ(xZ`F1xcpSWjs z+)Xb)3O{9Bzzt_Z@pY>DE%){7l~=drwI85$s!>yRkmyFBu(s{ zrwka-SG=N6WGWCidiXu2ci2cOUWZ757#srO2OrvUDm&`lnZ2ug7sxv8pl>c4s@z_o zl;+)FM$vIzx$FgfkYaIi8c3!dn);x6#$z2W$X;AeXQFfii;CzN1D`jB2Uw-fDQjX6 zqLFOhZrftsrVH;Z-JL9c_H{y~^-Tjwc> z3Z8gL|H3c4pGUL>rb0R}Rp1*DmEKn+WSxGV+!5?7B|q)wp4Ohc6; zUh-jcn`>?ivzGBCLJg zdC!8V-5rrmdDa9sG@#s)<&0zTA5}$zQI}jj)_X=lu>j;TEaH}$Zwv&?rJoNF?+GAq z$JoBzG*Pcverr9iQ zx-6?Wo(1~!N^7Y%R0J$lpYoxgov}Kxr9iarOHfJ_X>|0*W6H*3r42WcbcdVdu63iF z&bXq~@~`69kW$DBa~*NV4CQg6rrTP&w=}g=Hg?{za+Z9^=5(%TQSe!ey^k%~)wjB% zVf(ErRfLo^oJDzqi{X+D{vlM&0tWG&Hhf()pz=wuKCYu&QWQ$=bQxznJ~B3z06q&t zL~s!A6ne;49)IT+F|fdjW=!=Gz#DIj1yhLz@_gqf$4@Zx*^fx&ZxP%9p*JPVYTMYY z>AcbVrg;R;*~wVgXHbY6P@PGBo5QU9viKPPMuV$$FP^`q=xV5aLwyCmjWRRY!5uGl8LtK zJ|cA$5a~|IIbZhn=7mgW7CQIUWiIxU>j3N!C_H8;SQXMB!13WicN!Wummz~QxWzUd zzXprhU242#ZQ&z5i-Kl(k4+`JjfD^=QFa+lsRp>(n{Km67XZ-dh z@F$4#I}9m<{JiM`x#oo1Qlo%-5Y;YC(vDz9e`}o}3ew{E#<)l7^$82N2Q0E096R>9 zlZ}bZbBJ4QzI2l23HfiNrPb@!?g{QOWp2vvG3^P=ewTNqn3Y&AI7ceJP$+Zf$PqnE zL~>;ZpQwW*FTbG&O{&NoXHb5jG(-5w+kT3c%Rc{2ZN=c4ArMZ8#Ya_TC49>}_BH|G zKNV?o7&AVZwy5#9m)z=&mx^|ZxSz(MM%+0=&gBAAIdFu%P|wNqz(1hSfbO<@wBCEt zwUD6(c2$KF2<*Ve^~;Q@fh_*e-7Ke}tb3UUkxVP@8yV}>`hNVbg(zfbGPeZvbjPT z&6C-*p4maiVzySX9~LFUryw&am5G^eTuAN1w~5lr5}jn-L(7*c(1BAFF)S1$`_d;v z$Jwc1?^qPeA*Y9ws29rDz1s_hmTi^3@T->bjh1icA6KOibxGstE=~DjCQO>gh2evE z@#5}g1n^Y?I>zIjN11s0z1)9@W)ySdPXM5D0s^Azm6`>>O{}%6*=Ew8>oOot-a%c@ zLL~pawFLN{A{2W65&Ic>9sPF^=dI{?mq&B;7!Ln+&tYkT(LcZ^iDqO3T#C!$6Av!T zVhq-QxIX%_5TJ*c+Vr~eHF6>g3s>k2d{iaNSofdKY?`n97_LbDRR2Ro@S&)PAgxNG z>&kG-$+kCQGR6Pr{qRxmXF|Ria9slxKpc$TWp Date: Mon, 19 Feb 2024 14:10:28 +0100 Subject: [PATCH 0002/1237] Add timeout to downloading databases --- extensions/ql-vscode/package.json | 5 ++ extensions/ql-vscode/src/config.ts | 8 ++ .../src/databases/database-fetcher.ts | 85 ++++++++++++++++--- 3 files changed, 86 insertions(+), 12 deletions(-) diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index c75b72ce215..55005165254 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -376,6 +376,11 @@ "title": "Adding databases", "order": 6, "properties": { + "codeQL.addingDatabases.downloadTimeout": { + "type": "integer", + "default": 10, + "description": "Download timeout in seconds for adding a CodeQL database." + }, "codeQL.addingDatabases.allowHttp": { "type": "boolean", "default": false, diff --git a/extensions/ql-vscode/src/config.ts b/extensions/ql-vscode/src/config.ts index 50937cf15b7..4b4b3a22ba9 100644 --- a/extensions/ql-vscode/src/config.ts +++ b/extensions/ql-vscode/src/config.ts @@ -644,8 +644,16 @@ const DEPRECATED_ALLOW_HTTP_SETTING = new Setting( const ADDING_DATABASES_SETTING = new Setting("addingDatabases", ROOT_SETTING); +const DOWNLOAD_TIMEOUT_SETTING = new Setting( + "downloadTimeout", + ADDING_DATABASES_SETTING, +); const ALLOW_HTTP_SETTING = new Setting("allowHttp", ADDING_DATABASES_SETTING); +export function downloadTimeout(): number { + return DOWNLOAD_TIMEOUT_SETTING.getValue() || 10; +} + export function allowHttp(): boolean { return ( ALLOW_HTTP_SETTING.getValue() || diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index 91ef4f79d3b..8ae74d009d7 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -1,5 +1,5 @@ import type { Response } from "node-fetch"; -import fetch from "node-fetch"; +import fetch, { AbortError } from "node-fetch"; import { zip } from "zip-a-folder"; import type { InputBoxOptions } from "vscode"; import { Uri, window } from "vscode"; @@ -28,11 +28,16 @@ import { } from "../common/github-url-identifier-helper"; import type { Credentials } from "../common/authentication"; import type { AppCommandManager } from "../common/commands"; -import { addDatabaseSourceToWorkspace, allowHttp } from "../config"; +import { + addDatabaseSourceToWorkspace, + allowHttp, + downloadTimeout, +} from "../config"; import { showAndLogInformationMessage } from "../common/logging"; import { AppOctokit } from "../common/octokit"; import { getLanguageDisplayName } from "../common/query-language"; import type { DatabaseOrigin } from "./local-databases/database-origin"; +import { clearTimeout } from "node:timers"; /** * Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file. @@ -478,10 +483,38 @@ async function fetchAndUnzip( step: 1, }); - const response = await checkForFailingResponse( - await fetch(databaseUrl, { headers: requestHeaders }), - "Error downloading database", - ); + const abortController = new AbortController(); + + const timeout = downloadTimeout() * 1000; + + let timeoutId: NodeJS.Timeout; + + // If we don't get any data within the timeout, abort the download + timeoutId = setTimeout(() => { + abortController.abort(); + }, timeout); + + let response: Response; + try { + response = await checkForFailingResponse( + await fetch(databaseUrl, { + headers: requestHeaders, + signal: abortController.signal, + }), + "Error downloading database", + ); + } catch (e) { + clearTimeout(timeoutId); + + if (e instanceof AbortError) { + const thrownError = new AbortError("The request timed out."); + thrownError.stack = e.stack; + throw thrownError; + } + + throw e; + } + const archiveFileStream = createWriteStream(archivePath); const contentLength = response.headers.get("content-length"); @@ -493,12 +526,40 @@ async function fetchAndUnzip( progress, ); - await new Promise((resolve, reject) => - response.body - .pipe(archiveFileStream) - .on("finish", resolve) - .on("error", reject), - ); + // If we receive any data within the timeout, reset the timeout + response.body.on("data", () => { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + abortController.abort(); + }, timeout); + }); + + try { + await new Promise((resolve, reject) => { + response.body + .pipe(archiveFileStream) + .on("finish", resolve) + .on("error", reject); + + // If an error occurs on the body, we also want to reject the promise (e.g. during a timeout error). + response.body.on("error", reject); + }); + } catch (e) { + // Close and remove the file if an error occurs + archiveFileStream.close(() => { + void remove(archivePath); + }); + + if (e instanceof AbortError) { + const thrownError = new AbortError("The download timed out."); + thrownError.stack = e.stack; + throw thrownError; + } + + throw e; + } finally { + clearTimeout(timeoutId); + } await readAndUnzip( Uri.file(archivePath).toString(true), From 1a9c792756f7980f3eccdc27ed36757bd02dfe6c Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 19 Feb 2024 16:11:07 +0100 Subject: [PATCH 0003/1237] Extract timeout and apply to it to CLI downloads --- extensions/ql-vscode/package.json | 5 ++ .../ql-vscode/src/codeql-cli/distribution.ts | 52 ++++++++++++++++--- .../distribution/releases-api-consumer.ts | 22 ++++++-- .../ql-vscode/src/common/fetch-stream.ts | 36 +++++++++++++ extensions/ql-vscode/src/config.ts | 9 ++++ .../src/databases/database-fetcher.ts | 31 ++++------- 6 files changed, 122 insertions(+), 33 deletions(-) create mode 100644 extensions/ql-vscode/src/common/fetch-stream.ts diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 55005165254..392b28410d5 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -178,6 +178,11 @@ "type": "string", "default": "", "markdownDescription": "Path to the CodeQL executable that should be used by the CodeQL extension. The executable is named `codeql` on Linux/Mac and `codeql.exe` on Windows. If empty, the extension will look for a CodeQL executable on your shell PATH, or if CodeQL is not on your PATH, download and manage its own CodeQL executable (note: if you later introduce CodeQL on your PATH, the extension will prefer a CodeQL executable it has downloaded itself)." + }, + "codeQL.cli.downloadTimeout": { + "type": "integer", + "default": 10, + "description": "Download timeout in seconds for downloading the CLI distribution." } } }, diff --git a/extensions/ql-vscode/src/codeql-cli/distribution.ts b/extensions/ql-vscode/src/codeql-cli/distribution.ts index e69fca7212b..223bf5fe50a 100644 --- a/extensions/ql-vscode/src/codeql-cli/distribution.ts +++ b/extensions/ql-vscode/src/codeql-cli/distribution.ts @@ -1,3 +1,4 @@ +import type { WriteStream } from "fs"; import { createWriteStream, mkdtemp, pathExists, remove } from "fs-extra"; import { tmpdir } from "os"; import { delimiter, dirname, join } from "path"; @@ -26,6 +27,8 @@ import { unzipToDirectoryConcurrently } from "../common/unzip-concurrently"; import { reportUnzipProgress } from "../common/vscode/unzip-progress"; import type { Release } from "./distribution/release"; import { ReleasesApiConsumer } from "./distribution/releases-api-consumer"; +import { createTimeoutSignal } from "../common/fetch-stream"; +import { AbortError } from "node-fetch"; /** * distribution.ts @@ -384,15 +387,25 @@ class ExtensionSpecificDistributionManager { ); } - const assetStream = - await this.createReleasesApiConsumer().streamBinaryContentOfAsset( - assets[0], - ); + const { + signal, + onData, + dispose: disposeTimeout, + } = createTimeoutSignal(this.config.downloadTimeout); + const tmpDirectory = await mkdtemp(join(tmpdir(), "vscode-codeql")); + let archiveFile: WriteStream | undefined = undefined; + try { + const assetStream = + await this.createReleasesApiConsumer().streamBinaryContentOfAsset( + assets[0], + signal, + ); + const archivePath = join(tmpDirectory, "distributionDownload.zip"); - const archiveFile = createWriteStream(archivePath); + archiveFile = createWriteStream(archivePath); const contentLength = assetStream.headers.get("content-length"); const totalNumBytes = contentLength @@ -405,12 +418,23 @@ class ExtensionSpecificDistributionManager { progressCallback, ); - await new Promise((resolve, reject) => + assetStream.body.on("data", onData); + + await new Promise((resolve, reject) => { + if (!archiveFile) { + throw new Error("Invariant violation: archiveFile not set"); + } + assetStream.body .pipe(archiveFile) .on("finish", resolve) - .on("error", reject), - ); + .on("error", reject); + + // If an error occurs on the body, we also want to reject the promise (e.g. during a timeout error). + assetStream.body.on("error", reject); + }); + + disposeTimeout(); await this.bumpDistributionFolderIndex(); @@ -427,7 +451,19 @@ class ExtensionSpecificDistributionManager { ) : undefined, ); + } catch (e) { + if (e instanceof AbortError) { + const thrownError = new AbortError("The download timed out."); + thrownError.stack = e.stack; + throw thrownError; + } + + throw e; } finally { + disposeTimeout(); + + archiveFile?.close(); + await remove(tmpDirectory); } } diff --git a/extensions/ql-vscode/src/codeql-cli/distribution/releases-api-consumer.ts b/extensions/ql-vscode/src/codeql-cli/distribution/releases-api-consumer.ts index 28bef77ad19..ff482a7b2c3 100644 --- a/extensions/ql-vscode/src/codeql-cli/distribution/releases-api-consumer.ts +++ b/extensions/ql-vscode/src/codeql-cli/distribution/releases-api-consumer.ts @@ -90,21 +90,28 @@ export class ReleasesApiConsumer { public async streamBinaryContentOfAsset( asset: ReleaseAsset, + signal?: AbortSignal, ): Promise { const apiPath = `/repos/${this.repositoryNwo}/releases/assets/${asset.id}`; - return await this.makeApiCall(apiPath, { - accept: "application/octet-stream", - }); + return await this.makeApiCall( + apiPath, + { + accept: "application/octet-stream", + }, + signal, + ); } protected async makeApiCall( apiPath: string, additionalHeaders: { [key: string]: string } = {}, + signal?: AbortSignal, ): Promise { const response = await this.makeRawRequest( ReleasesApiConsumer.apiBase + apiPath, Object.assign({}, this.defaultHeaders, additionalHeaders), + signal, ); if (!response.ok) { @@ -129,11 +136,13 @@ export class ReleasesApiConsumer { private async makeRawRequest( requestUrl: string, headers: { [key: string]: string }, + signal?: AbortSignal, redirectCount = 0, ): Promise { const response = await fetch(requestUrl, { headers, redirect: "manual", + signal, }); const redirectUrl = response.headers.get("location"); @@ -153,7 +162,12 @@ export class ReleasesApiConsumer { // mechanism is provided. delete headers["authorization"]; } - return await this.makeRawRequest(redirectUrl, headers, redirectCount + 1); + return await this.makeRawRequest( + redirectUrl, + headers, + signal, + redirectCount + 1, + ); } return response; diff --git a/extensions/ql-vscode/src/common/fetch-stream.ts b/extensions/ql-vscode/src/common/fetch-stream.ts new file mode 100644 index 00000000000..b60bda83ada --- /dev/null +++ b/extensions/ql-vscode/src/common/fetch-stream.ts @@ -0,0 +1,36 @@ +import { clearTimeout } from "node:timers"; + +export function createTimeoutSignal(timeoutSeconds: number): { + signal: AbortSignal; + onData: () => void; + dispose: () => void; +} { + const timeout = timeoutSeconds * 1000; + + const abortController = new AbortController(); + + let timeoutId: NodeJS.Timeout; + + // If we don't get any data within the timeout, abort the download + timeoutId = setTimeout(() => { + abortController.abort(); + }, timeout); + + // If we receive any data within the timeout, reset the timeout + const onData = () => { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + abortController.abort(); + }, timeout); + }; + + const dispose = () => { + clearTimeout(timeoutId); + }; + + return { + signal: abortController.signal, + onData, + dispose, + }; +} diff --git a/extensions/ql-vscode/src/config.ts b/extensions/ql-vscode/src/config.ts index 4b4b3a22ba9..71eca110d87 100644 --- a/extensions/ql-vscode/src/config.ts +++ b/extensions/ql-vscode/src/config.ts @@ -93,6 +93,10 @@ const PERSONAL_ACCESS_TOKEN_SETTING = new Setting( "personalAccessToken", DISTRIBUTION_SETTING, ); +const CLI_DOWNLOAD_TIMEOUT_SETTING = new Setting( + "downloadTimeout", + DISTRIBUTION_SETTING, +); const CLI_CHANNEL_SETTING = new Setting("channel", DISTRIBUTION_SETTING); // Query History configuration @@ -118,6 +122,7 @@ export interface DistributionConfig { updateCustomCodeQlPath: (newPath: string | undefined) => Promise; includePrerelease: boolean; personalAccessToken?: string; + downloadTimeout: number; channel: CLIChannel; onDidChangeConfiguration?: Event; } @@ -272,6 +277,10 @@ export class DistributionConfigListener return PERSONAL_ACCESS_TOKEN_SETTING.getValue() || undefined; } + public get downloadTimeout(): number { + return CLI_DOWNLOAD_TIMEOUT_SETTING.getValue() || 10; + } + public async updateCustomCodeQlPath(newPath: string | undefined) { await CUSTOM_CODEQL_PATH_SETTING.updateValue( newPath, diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index 8ae74d009d7..c9ccbdf09ae 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -37,7 +37,7 @@ import { showAndLogInformationMessage } from "../common/logging"; import { AppOctokit } from "../common/octokit"; import { getLanguageDisplayName } from "../common/query-language"; import type { DatabaseOrigin } from "./local-databases/database-origin"; -import { clearTimeout } from "node:timers"; +import { createTimeoutSignal } from "../common/fetch-stream"; /** * Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file. @@ -483,28 +483,23 @@ async function fetchAndUnzip( step: 1, }); - const abortController = new AbortController(); - - const timeout = downloadTimeout() * 1000; - - let timeoutId: NodeJS.Timeout; - - // If we don't get any data within the timeout, abort the download - timeoutId = setTimeout(() => { - abortController.abort(); - }, timeout); + const { + signal, + onData, + dispose: disposeTimeout, + } = createTimeoutSignal(downloadTimeout()); let response: Response; try { response = await checkForFailingResponse( await fetch(databaseUrl, { headers: requestHeaders, - signal: abortController.signal, + signal, }), "Error downloading database", ); } catch (e) { - clearTimeout(timeoutId); + disposeTimeout(); if (e instanceof AbortError) { const thrownError = new AbortError("The request timed out."); @@ -526,13 +521,7 @@ async function fetchAndUnzip( progress, ); - // If we receive any data within the timeout, reset the timeout - response.body.on("data", () => { - clearTimeout(timeoutId); - timeoutId = setTimeout(() => { - abortController.abort(); - }, timeout); - }); + response.body.on("data", onData); try { await new Promise((resolve, reject) => { @@ -558,7 +547,7 @@ async function fetchAndUnzip( throw e; } finally { - clearTimeout(timeoutId); + disposeTimeout(); } await readAndUnzip( From d9ae0c6d17651322ec33871361fa940f1c7cf5b9 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 20 Feb 2024 12:31:47 +0000 Subject: [PATCH 0004/1237] Disable the automodel button if there are no methods that can be modeled --- .../src/view/model-editor/LibraryRow.tsx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx b/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx index cbe288b3923..0c5c8bc4a0f 100644 --- a/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx @@ -14,6 +14,7 @@ import { } from "@vscode/webview-ui-toolkit/react"; import type { ModelEditorViewState } from "../../model-editor/shared/view-state"; import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions"; +import { getCandidates } from "../../model-editor/auto-model"; const LibraryContainer = styled.div` background-color: var(--vscode-peekViewResult-background); @@ -186,6 +187,17 @@ export const LibraryRow = ({ return methods.some((method) => inProgressMethods.has(method.signature)); }, [methods, inProgressMethods]); + const modelWithAIDisabled = useMemo(() => { + return ( + getCandidates( + viewState.mode, + methods, + modeledMethodsMap, + processedByAutoModelMethods, + ).length === 0 + ); + }, [methods, modeledMethodsMap, processedByAutoModelMethods, viewState.mode]); + return ( @@ -205,7 +217,11 @@ export const LibraryRow = ({ {hasUnsavedChanges ? UNSAVED : null} {viewState.showLlmButton && !canStopAutoModeling && ( - +  Model with AI From 409c89653c3a6c099e8ad69f321722f2a6c0f18b Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Tue, 20 Feb 2024 15:05:49 +0100 Subject: [PATCH 0005/1237] Add processed by auto model state to method modeling panel --- .../ql-vscode/src/common/interface-types.ts | 9 ++++- .../method-modeling-view-provider.ts | 17 ++++++++ .../src/model-editor/modeled-method.ts | 2 +- .../view/method-modeling/MethodModeling.tsx | 3 ++ .../method-modeling/MethodModelingView.tsx | 9 +++++ .../MultipleModeledMethodsPanel.tsx | 3 ++ .../__tests__/MethodModeling.spec.tsx | 2 + .../MultipleModeledMethodsPanel.spec.tsx | 39 +++++++++++++++++++ 8 files changed, 82 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/src/common/interface-types.ts b/extensions/ql-vscode/src/common/interface-types.ts index cc7c60f1617..cde35e6b28f 100644 --- a/extensions/ql-vscode/src/common/interface-types.ts +++ b/extensions/ql-vscode/src/common/interface-types.ts @@ -615,6 +615,11 @@ interface SetInProgressMessage { inProgress: boolean; } +interface SetProcessedByAutoModelMessage { + t: "setProcessedByAutoModel"; + processedByAutoModel: boolean; +} + interface RevealMethodMessage { t: "revealMethod"; methodSignature: string; @@ -686,6 +691,7 @@ interface SetSelectedMethodMessage { modeledMethods: ModeledMethod[]; isModified: boolean; isInProgress: boolean; + processedByAutoModel: boolean; } export type ToMethodModelingMessage = @@ -695,4 +701,5 @@ export type ToMethodModelingMessage = | SetMethodModifiedMessage | SetSelectedMethodMessage | SetInModelingModeMessage - | SetInProgressMessage; + | SetInProgressMessage + | SetProcessedByAutoModelMessage; diff --git a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts index 4c5d6fe7a1a..e921b3776d5 100644 --- a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts +++ b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts @@ -82,6 +82,7 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< modeledMethods: selectedMethod.modeledMethods, isModified: selectedMethod.isModified, isInProgress: selectedMethod.isInProgress, + processedByAutoModel: selectedMethod.processedByAutoModel, }); } @@ -200,6 +201,7 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< modeledMethods: e.modeledMethods, isModified: e.isModified, isInProgress: e.isInProgress, + processedByAutoModel: e.processedByAutoModel, }); } }), @@ -248,6 +250,21 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< } }), ); + + this.push( + this.modelingEvents.onProcessedByAutoModelMethodsChanged(async (e) => { + if (this.method && this.databaseItem) { + const dbUri = this.databaseItem.databaseUri.toString(); + if (e.dbUri === dbUri) { + const processedByAutoModel = e.methods.has(this.method.signature); + await this.postMessage({ + t: "setProcessedByAutoModel", + processedByAutoModel, + }); + } + } + }), + ); } private registerToModelConfigEvents(): void { diff --git a/extensions/ql-vscode/src/model-editor/modeled-method.ts b/extensions/ql-vscode/src/model-editor/modeled-method.ts index 142dfc8fcf3..9d6ed15b390 100644 --- a/extensions/ql-vscode/src/model-editor/modeled-method.ts +++ b/extensions/ql-vscode/src/model-editor/modeled-method.ts @@ -114,7 +114,7 @@ export function modeledMethodSupportsProvenance( export function isModelPending( modeledMethod: ModeledMethod | undefined, modelingStatus: ModelingStatus, - processedByAutoModel?: boolean, + processedByAutoModel: boolean, ): boolean { if ( (!modeledMethod || modeledMethod.type === "none") && diff --git a/extensions/ql-vscode/src/view/method-modeling/MethodModeling.tsx b/extensions/ql-vscode/src/view/method-modeling/MethodModeling.tsx index 47872ee597e..1f16239b791 100644 --- a/extensions/ql-vscode/src/view/method-modeling/MethodModeling.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/MethodModeling.tsx @@ -54,6 +54,7 @@ export type MethodModelingProps = { method: Method; modeledMethods: ModeledMethod[]; isModelingInProgress: boolean; + isProcessedByAutoModel: boolean; onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void; }; @@ -63,6 +64,7 @@ export const MethodModeling = ({ modeledMethods, method, isModelingInProgress, + isProcessedByAutoModel, onChange, }: MethodModelingProps): React.JSX.Element => { return ( @@ -81,6 +83,7 @@ export const MethodModeling = ({ method={method} modeledMethods={modeledMethods} isModelingInProgress={isModelingInProgress} + isProcessedByAutoModel={isProcessedByAutoModel} modelingStatus={modelingStatus} onChange={onChange} /> diff --git a/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx b/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx index fd9742c7bca..188a9903004 100644 --- a/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx @@ -33,6 +33,9 @@ export function MethodModelingView({ const [isModelingInProgress, setIsModelingInProgress] = useState(false); + const [isProcessedByAutoModel, setIsProcessedByAutoModel] = + useState(false); + const modelingStatus = useMemo( () => getModelingStatus(modeledMethods, isMethodModified), [modeledMethods, isMethodModified], @@ -62,10 +65,15 @@ export function MethodModelingView({ setMethod(msg.method); setModeledMethods(msg.modeledMethods); setIsMethodModified(msg.isModified); + setIsModelingInProgress(msg.isInProgress); + setIsProcessedByAutoModel(msg.processedByAutoModel); break; case "setInProgress": setIsModelingInProgress(msg.inProgress); break; + case "setProcessedByAutoModel": + setIsProcessedByAutoModel(msg.processedByAutoModel); + break; default: assertNever(msg); } @@ -112,6 +120,7 @@ export function MethodModelingView({ method={method} modeledMethods={modeledMethods} isModelingInProgress={isModelingInProgress} + isProcessedByAutoModel={isProcessedByAutoModel} onChange={onChange} /> ); diff --git a/extensions/ql-vscode/src/view/method-modeling/MultipleModeledMethodsPanel.tsx b/extensions/ql-vscode/src/view/method-modeling/MultipleModeledMethodsPanel.tsx index 8e393e41fe2..de8589686d9 100644 --- a/extensions/ql-vscode/src/view/method-modeling/MultipleModeledMethodsPanel.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/MultipleModeledMethodsPanel.tsx @@ -23,6 +23,7 @@ export type MultipleModeledMethodsPanelProps = { modeledMethods: ModeledMethod[]; modelingStatus: ModelingStatus; isModelingInProgress: boolean; + isProcessedByAutoModel: boolean; onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void; }; @@ -64,6 +65,7 @@ export const MultipleModeledMethodsPanel = ({ modeledMethods, modelingStatus, isModelingInProgress, + isProcessedByAutoModel, onChange, }: MultipleModeledMethodsPanelProps) => { const [selectedIndex, setSelectedIndex] = useState(0); @@ -160,6 +162,7 @@ export const MultipleModeledMethodsPanel = ({ modelPending={isModelPending( modeledMethods[selectedIndex], modelingStatus, + isProcessedByAutoModel, )} isModelingInProgress={isModelingInProgress} onChange={handleChange} diff --git a/extensions/ql-vscode/src/view/method-modeling/__tests__/MethodModeling.spec.tsx b/extensions/ql-vscode/src/view/method-modeling/__tests__/MethodModeling.spec.tsx index 451bacb82ad..5e6ae64c323 100644 --- a/extensions/ql-vscode/src/view/method-modeling/__tests__/MethodModeling.spec.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/__tests__/MethodModeling.spec.tsx @@ -13,6 +13,7 @@ describe(MethodModeling.name, () => { const method = createMethod(); const modeledMethod = createSinkModeledMethod(); const isModelingInProgress = false; + const isProcessedByAutoModel = false; const onChange = jest.fn(); render({ @@ -21,6 +22,7 @@ describe(MethodModeling.name, () => { method, modeledMethods: [modeledMethod], isModelingInProgress, + isProcessedByAutoModel, onChange, }); diff --git a/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx b/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx index 4b568089a91..44ab596c627 100644 --- a/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx @@ -18,6 +18,7 @@ describe(MultipleModeledMethodsPanel.name, () => { const language = QueryLanguage.Java; const method = createMethod(); const isModelingInProgress = false; + const isProcessedByAutoModel = false; const modelingStatus = "unmodeled"; const onChange = jest.fn(); @@ -31,6 +32,7 @@ describe(MultipleModeledMethodsPanel.name, () => { modeledMethods, modelingStatus, isModelingInProgress, + isProcessedByAutoModel, onChange, }); @@ -49,6 +51,7 @@ describe(MultipleModeledMethodsPanel.name, () => { modeledMethods, modelingStatus, isModelingInProgress, + isProcessedByAutoModel, onChange, }); @@ -71,6 +74,7 @@ describe(MultipleModeledMethodsPanel.name, () => { modeledMethods, modelingStatus, isModelingInProgress, + isProcessedByAutoModel, onChange, }); @@ -102,6 +106,7 @@ describe(MultipleModeledMethodsPanel.name, () => { modeledMethods, modelingStatus, isModelingInProgress, + isProcessedByAutoModel, onChange, }); @@ -120,6 +125,7 @@ describe(MultipleModeledMethodsPanel.name, () => { modeledMethods, modelingStatus, isModelingInProgress, + isProcessedByAutoModel, onChange, }); @@ -141,6 +147,7 @@ describe(MultipleModeledMethodsPanel.name, () => { modeledMethods, modelingStatus, isModelingInProgress, + isProcessedByAutoModel, onChange, }); @@ -158,6 +165,7 @@ describe(MultipleModeledMethodsPanel.name, () => { modeledMethods, modelingStatus, isModelingInProgress, + isProcessedByAutoModel, onChange, }); @@ -184,6 +192,7 @@ describe(MultipleModeledMethodsPanel.name, () => { modeledMethods, modelingStatus, isModelingInProgress, + isProcessedByAutoModel, onChange, }); @@ -197,6 +206,7 @@ describe(MultipleModeledMethodsPanel.name, () => { onChange.mock.calls[onChange.mock.calls.length - 1][1] } isModelingInProgress={isModelingInProgress} + isProcessedByAutoModel={isProcessedByAutoModel} modelingStatus={modelingStatus} onChange={onChange} />, @@ -222,6 +232,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -240,6 +251,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -255,6 +267,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -275,6 +288,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -312,6 +326,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -324,6 +339,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method={method} modeledMethods={[modeledMethods[1]]} isModelingInProgress={isModelingInProgress} + isProcessedByAutoModel={isProcessedByAutoModel} modelingStatus={modelingStatus} onChange={onChange} />, @@ -343,6 +359,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -356,6 +373,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -389,6 +407,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -424,6 +443,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -442,6 +462,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -468,6 +489,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -482,6 +504,7 @@ describe(MultipleModeledMethodsPanel.name, () => { onChange.mock.calls[onChange.mock.calls.length - 1][1] } isModelingInProgress={isModelingInProgress} + isProcessedByAutoModel={isProcessedByAutoModel} modelingStatus={modelingStatus} onChange={onChange} />, @@ -503,6 +526,7 @@ describe(MultipleModeledMethodsPanel.name, () => { onChange.mock.calls[onChange.mock.calls.length - 1][1] } isModelingInProgress={isModelingInProgress} + isProcessedByAutoModel={isProcessedByAutoModel} modelingStatus={modelingStatus} onChange={onChange} />, @@ -522,6 +546,7 @@ describe(MultipleModeledMethodsPanel.name, () => { onChange.mock.calls[onChange.mock.calls.length - 1][1] } isModelingInProgress={isModelingInProgress} + isProcessedByAutoModel={isProcessedByAutoModel} modelingStatus={modelingStatus} onChange={onChange} />, @@ -539,6 +564,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -555,6 +581,7 @@ describe(MultipleModeledMethodsPanel.name, () => { onChange.mock.calls[onChange.mock.calls.length - 1][1] } isModelingInProgress={isModelingInProgress} + isProcessedByAutoModel={isProcessedByAutoModel} modelingStatus={modelingStatus} onChange={onChange} />, @@ -589,6 +616,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -680,6 +708,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -692,6 +721,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method={method} modeledMethods={modeledMethods.slice(0, 2)} isModelingInProgress={isModelingInProgress} + isProcessedByAutoModel={isProcessedByAutoModel} modelingStatus={modelingStatus} onChange={onChange} />, @@ -706,6 +736,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -720,6 +751,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method={method} modeledMethods={modeledMethods.slice(0, 2)} isModelingInProgress={isModelingInProgress} + isProcessedByAutoModel={isProcessedByAutoModel} modelingStatus={modelingStatus} onChange={onChange} />, @@ -748,6 +780,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -763,6 +796,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -781,6 +815,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -800,6 +835,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -818,6 +854,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method={method} modeledMethods={modeledMethods.slice(0, 1)} isModelingInProgress={isModelingInProgress} + isProcessedByAutoModel={isProcessedByAutoModel} modelingStatus={modelingStatus} onChange={onChange} />, @@ -857,6 +894,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); @@ -870,6 +908,7 @@ describe(MultipleModeledMethodsPanel.name, () => { method, modeledMethods, isModelingInProgress, + isProcessedByAutoModel, modelingStatus, onChange, }); From 8f3ab61422f09d141429431981bcdced1416ff7a Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 20 Feb 2024 14:24:13 +0000 Subject: [PATCH 0006/1237] Perform filtering before sorting when determining auto-model candidates --- .../ql-vscode/src/model-editor/auto-model.ts | 34 ++++++++----------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/auto-model.ts b/extensions/ql-vscode/src/model-editor/auto-model.ts index 42020db132a..13a67954bb8 100644 --- a/extensions/ql-vscode/src/model-editor/auto-model.ts +++ b/extensions/ql-vscode/src/model-editor/auto-model.ts @@ -24,39 +24,33 @@ export function getCandidates( modeledMethodsBySignature: Record, processedByAutoModelMethods: Set, ): MethodSignature[] { - // Filter out any methods already processed by auto-model - methods = methods.filter( - (m) => !processedByAutoModelMethods.has(m.signature), - ); - - // Sort the same way as the UI so we send the first ones listed in the UI first - const grouped = groupMethods(methods, mode); - const sortedGroupNames = sortGroupNames(grouped); - const sortedMethods = sortedGroupNames.flatMap((name) => - sortMethods(grouped[name]), - ); - - const candidates: MethodSignature[] = []; + const candidateMethods = methods.filter((method) => { + // Filter out any methods already processed by auto-model + if (processedByAutoModelMethods.has(method.signature)) { + return false; + } - for (const method of sortedMethods) { const modeledMethods: ModeledMethod[] = [ ...(modeledMethodsBySignature[method.signature] ?? []), ]; // Anything that is modeled is not a candidate if (modeledMethods.some((m) => m.type !== "none")) { - continue; + return false; } // A method that is supported is modeled outside of the model file, so it is not a candidate. if (method.supported) { - continue; + return false; } - // The rest are candidates - candidates.push(method); - } - return candidates; + return true; + }); + + // Sort the same way as the UI so we send the first ones listed in the UI first + const grouped = groupMethods(candidateMethods, mode); + const sortedGroupNames = sortGroupNames(grouped); + return sortedGroupNames.flatMap((name) => sortMethods(grouped[name])); } /** From 14dbb65f50cf67b5929543aede73977cca6add25 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 20 Feb 2024 14:38:30 +0000 Subject: [PATCH 0007/1237] Change sort order to place AI predictions at the start --- .../ql-vscode/src/model-editor/auto-model.ts | 6 +- .../methods-usage-data-provider.ts | 24 ++++- .../methods-usage/methods-usage-panel.ts | 3 + .../src/model-editor/shared/sorting.ts | 88 ++++++++++++++----- .../model-editor/ModeledMethodDataGrid.tsx | 15 +++- .../methods-usage-data-provider.test.ts | 19 ++++ .../methods-usage/methods-usage-panel.test.ts | 5 ++ 7 files changed, 133 insertions(+), 27 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/auto-model.ts b/extensions/ql-vscode/src/model-editor/auto-model.ts index 13a67954bb8..98e5f191e5b 100644 --- a/extensions/ql-vscode/src/model-editor/auto-model.ts +++ b/extensions/ql-vscode/src/model-editor/auto-model.ts @@ -50,7 +50,11 @@ export function getCandidates( // Sort the same way as the UI so we send the first ones listed in the UI first const grouped = groupMethods(candidateMethods, mode); const sortedGroupNames = sortGroupNames(grouped); - return sortedGroupNames.flatMap((name) => sortMethods(grouped[name])); + return sortedGroupNames.flatMap((name) => + // We can safely pass empty sets for `modifiedSignatures` and `processedByAutoModelMethods` + // because we've filtered out all methods that are already modeled or have already been processed by auto-model. + sortMethods(grouped[name], modeledMethodsBySignature, new Set(), new Set()), + ); } /** diff --git a/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-data-provider.ts b/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-data-provider.ts index bfa077ebb0d..23d2e2726b8 100644 --- a/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-data-provider.ts +++ b/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-data-provider.ts @@ -63,6 +63,7 @@ export class MethodsUsageDataProvider mode: Mode, modeledMethods: Readonly>, modifiedMethodSignatures: ReadonlySet, + processedByAutoModelMethods: ReadonlySet, ): Promise { if ( this.methods !== methods || @@ -74,7 +75,13 @@ export class MethodsUsageDataProvider ) { this.methods = methods; this.sortedTreeItems = createTreeItems( - sortMethodsInGroups(methods, mode), + sortMethodsInGroups( + methods, + modeledMethods, + mode, + modifiedMethodSignatures, + processedByAutoModelMethods, + ), ); this.databaseItem = databaseItem; this.sourceLocationPrefix = @@ -246,7 +253,13 @@ function urlValueResolvablesAreEqual( return false; } -function sortMethodsInGroups(methods: readonly Method[], mode: Mode): Method[] { +function sortMethodsInGroups( + methods: readonly Method[], + modeledMethods: Readonly>, + mode: Mode, + modifiedMethodSignatures: ReadonlySet, + processedByAutoModelMethods: ReadonlySet, +): Method[] { const grouped = groupMethods(methods, mode); const sortedGroupNames = sortGroupNames(grouped); @@ -254,7 +267,12 @@ function sortMethodsInGroups(methods: readonly Method[], mode: Mode): Method[] { return sortedGroupNames.flatMap((groupName) => { const group = grouped[groupName]; - return sortMethods(group); + return sortMethods( + group, + modeledMethods, + modifiedMethodSignatures, + processedByAutoModelMethods, + ); }); } diff --git a/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-panel.ts b/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-panel.ts index 7d8eed02be3..1180939abff 100644 --- a/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-panel.ts +++ b/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-panel.ts @@ -39,6 +39,7 @@ export class MethodsUsagePanel extends DisposableObject { mode: Mode, modeledMethods: Readonly>, modifiedMethodSignatures: ReadonlySet, + processedByAutoModelMethods: ReadonlySet, ): Promise { await this.dataProvider.setState( methods, @@ -47,6 +48,7 @@ export class MethodsUsagePanel extends DisposableObject { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); const numOfApis = hideModeledMethods ? methods.filter((api) => !api.supported).length @@ -120,6 +122,7 @@ export class MethodsUsagePanel extends DisposableObject { activeState.mode, activeState.modeledMethods, activeState.modifiedMethodSignatures, + activeState.processedByAutoModelMethods, ); } } diff --git a/extensions/ql-vscode/src/model-editor/shared/sorting.ts b/extensions/ql-vscode/src/model-editor/shared/sorting.ts index 1ef7685a81f..52a2ccd2611 100644 --- a/extensions/ql-vscode/src/model-editor/shared/sorting.ts +++ b/extensions/ql-vscode/src/model-editor/shared/sorting.ts @@ -1,4 +1,5 @@ import type { Method } from "../method"; +import type { ModeledMethod } from "../modeled-method"; import { Mode } from "./mode"; import { calculateModeledPercentage } from "./modeled-percentage"; @@ -27,12 +28,76 @@ export function sortGroupNames( ); } -export function sortMethods(methods: readonly Method[]): Method[] { +/** + * Primarily sorts methods into the following order: + * - Unsaved positive AutoModel predictions + * - Negative AutoModel predictions + * - Unsaved manual models + * - Umodeled + * - Modeled and saved (AutoModel and manual) + * + * Secondary sort order is by number of usages descending, then by method signature ascending. + */ +export function sortMethods( + methods: readonly Method[], + modeledMethodsMap: Record, + modifiedSignatures: ReadonlySet, + processedByAutoModelMethods: ReadonlySet, +): Method[] { const sortedMethods = [...methods]; - sortedMethods.sort((a, b) => compareMethod(a, b)); + sortedMethods.sort((a, b) => { + const methodAPrimarySortOrdinal = getMethodPrimarySortOrdinal( + !!modeledMethodsMap[a.signature]?.length, + modifiedSignatures.has(a.signature), + processedByAutoModelMethods.has(a.signature), + ); + const methodBPrimarySortOrdinal = getMethodPrimarySortOrdinal( + !!modeledMethodsMap[b.signature]?.length, + modifiedSignatures.has(b.signature), + processedByAutoModelMethods.has(b.signature), + ); + if (methodAPrimarySortOrdinal !== methodBPrimarySortOrdinal) { + return methodAPrimarySortOrdinal - methodBPrimarySortOrdinal; + } + + // Then sort by number of usages descending + const usageDifference = b.usages.length - a.usages.length; + if (usageDifference !== 0) { + return usageDifference; + } + + // Then sort by method signature ascending + return a.signature.localeCompare(b.signature); + }); return sortedMethods; } +/** + * Assigns numbers to the following classes of methods: + * - Unsaved positive AutoModel predictions => 0 + * - Negative AutoModel predictions => 1 + * - Unsaved manual models => 2 + * - Umodeled => 3 + * - Modeled and saved (AutoModel and manual) => 4 + */ +function getMethodPrimarySortOrdinal( + isModeled: boolean, + isModified: boolean, + isProcessedByAutoModel: boolean, +): number { + if (isModeled && isModified && isProcessedByAutoModel) { + return 0; + } else if (!isModeled && isProcessedByAutoModel) { + return 1; + } else if (isModeled && isModified) { + return 2; + } else if (!isModeled) { + return 3; + } else { + return 4; + } +} + function compareGroups( a: readonly Method[], aName: string, @@ -69,22 +134,3 @@ function compareGroups( // Then sort by number of usages descending return numberOfUsagesB - numberOfUsagesA; } - -function compareMethod(a: Method, b: Method): number { - // Sort first by supported, putting unmodeled methods first. - if (a.supported && !b.supported) { - return 1; - } - if (!a.supported && b.supported) { - return -1; - } - - // Then sort by number of usages descending - const usageDifference = b.usages.length - a.usages.length; - if (usageDifference !== 0) { - return usageDifference; - } - - // Then sort by method signature ascending - return a.signature.localeCompare(b.signature); -} diff --git a/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx b/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx index f320edf3369..dbe992691b1 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx @@ -48,7 +48,12 @@ export const ModeledMethodDataGrid = ({ ] = useMemo(() => { const methodsWithModelability = []; let numHiddenMethods = 0; - for (const method of sortMethods(methods)) { + for (const method of sortMethods( + methods, + modeledMethodsMap, + modifiedSignatures, + processedByAutoModelMethods, + )) { const modeledMethods = modeledMethodsMap[method.signature] ?? []; const methodIsUnsaved = modifiedSignatures.has(method.signature); const methodCanBeModeled = canMethodBeModeled( @@ -64,7 +69,13 @@ export const ModeledMethodDataGrid = ({ } } return [methodsWithModelability, numHiddenMethods]; - }, [hideModeledMethods, methods, modeledMethodsMap, modifiedSignatures]); + }, [ + hideModeledMethods, + methods, + modeledMethodsMap, + modifiedSignatures, + processedByAutoModelMethods, + ]); const someMethodsAreVisible = methodsWithModelability.length > 0; diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-data-provider.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-data-provider.test.ts index ffe4e7c5184..0bf5ecd9c75 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-data-provider.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-data-provider.test.ts @@ -27,6 +27,7 @@ describe("MethodsUsageDataProvider", () => { const methods: Method[] = []; const modeledMethods: Record = {}; const modifiedMethodSignatures: Set = new Set(); + const processedByAutoModelMethods: Set = new Set(); const dbItem = mockedObject({ getSourceLocationPrefix: () => "test", }); @@ -39,6 +40,7 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); const onDidChangeTreeDataListener = jest.fn(); @@ -51,6 +53,7 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); expect(onDidChangeTreeDataListener).not.toHaveBeenCalled(); @@ -66,6 +69,7 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); const onDidChangeTreeDataListener = jest.fn(); @@ -78,6 +82,7 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1); @@ -95,6 +100,7 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); const onDidChangeTreeDataListener = jest.fn(); @@ -107,6 +113,7 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1); @@ -120,6 +127,7 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); const onDidChangeTreeDataListener = jest.fn(); @@ -132,6 +140,7 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1); @@ -147,6 +156,7 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); const onDidChangeTreeDataListener = jest.fn(); @@ -159,6 +169,7 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods2, modifiedMethodSignatures, + processedByAutoModelMethods, ); expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1); @@ -174,6 +185,7 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); const onDidChangeTreeDataListener = jest.fn(); @@ -186,6 +198,7 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures2, + processedByAutoModelMethods, ); expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1); @@ -204,6 +217,7 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); const onDidChangeTreeDataListener = jest.fn(); @@ -216,6 +230,7 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1); @@ -255,6 +270,7 @@ describe("MethodsUsageDataProvider", () => { createSinkModeledMethod(), ]; const modifiedMethodSignatures: Set = new Set(); + const processedByAutoModelMethods: Set = new Set(); const dbItem = mockedObject({ getSourceLocationPrefix: () => "test", @@ -291,6 +307,7 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); expect(dataProvider.getChildren().length).toEqual(4); }); @@ -304,6 +321,7 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); expect(dataProvider.getChildren().length).toEqual(3); }); @@ -400,6 +418,7 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); expect( dataProvider diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-panel.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-panel.test.ts index 0d290ec29ca..5069922ac87 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-panel.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-panel.test.ts @@ -28,6 +28,7 @@ describe("MethodsUsagePanel", () => { const methods: Method[] = [createMethod()]; const modeledMethods: Record = {}; const modifiedMethodSignatures: Set = new Set(); + const processedByAutoModelMethods: Set = new Set(); it("should update the tree view with the correct batch number", async () => { const mockTreeView = { @@ -50,6 +51,7 @@ describe("MethodsUsagePanel", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); expect(mockTreeView.badge?.value).toBe(1); @@ -65,6 +67,7 @@ describe("MethodsUsagePanel", () => { const mode = Mode.Application; const modeledMethods: Record = {}; const modifiedMethodSignatures: Set = new Set(); + const processedByAutoModelMethods: Set = new Set(); const usage = createUsage(); beforeEach(() => { @@ -95,6 +98,7 @@ describe("MethodsUsagePanel", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); await panel.revealItem(method.signature, usage); @@ -122,6 +126,7 @@ describe("MethodsUsagePanel", () => { mode, modeledMethods, modifiedMethodSignatures, + processedByAutoModelMethods, ); await panel.revealItem(method.signature, usage); From 70a64886d6f6dff00386eb255db4bbcf12f43c79 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 20 Feb 2024 15:19:01 +0000 Subject: [PATCH 0008/1237] Rename + expand existing helper for shuffling lists --- .../query-history/history-tree-data-provider.test.ts | 7 ++----- .../query-history/query-history-manager.test.ts | 7 ++----- .../ql-vscode/test/vscode-tests/utils/list-helpers.ts | 3 +++ .../test/vscode-tests/utils/query-history-helpers.ts | 5 ----- 4 files changed, 7 insertions(+), 15 deletions(-) create mode 100644 extensions/ql-vscode/test/vscode-tests/utils/list-helpers.ts delete mode 100644 extensions/ql-vscode/test/vscode-tests/utils/query-history-helpers.ts diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/history-tree-data-provider.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/history-tree-data-provider.test.ts index ec3fd0e6d70..7026e6c6684 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/history-tree-data-provider.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/history-tree-data-provider.test.ts @@ -17,7 +17,7 @@ import { createMockLocalQueryInfo, createMockQueryWithResults, } from "../../../factories/query-history/local-query-history-item"; -import { shuffleHistoryItems } from "../../utils/query-history-helpers"; +import { shuffle } from "../../utils/list-helpers"; import { createMockVariantAnalysisHistoryItem } from "../../../factories/query-history/variant-analysis-history-item"; import type { VariantAnalysisHistoryItem } from "../../../../src/query-history/variant-analysis-history-item"; import { QueryStatus } from "../../../../src/query-history/query-status"; @@ -121,10 +121,7 @@ describe("HistoryTreeDataProvider", () => { variantAnalysisStatus: VariantAnalysisStatus.InProgress, }), ]; - allHistory = shuffleHistoryItems([ - ...localQueryHistory, - ...variantAnalysisHistory, - ]); + allHistory = shuffle([...localQueryHistory, ...variantAnalysisHistory]); labelProvider = new HistoryItemLabelProvider({ format: "", diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts index 69e5d3fae13..b5176590822 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts @@ -19,7 +19,7 @@ import { createMockLocalQueryInfo, createMockQueryWithResults, } from "../../../factories/query-history/local-query-history-item"; -import { shuffleHistoryItems } from "../../utils/query-history-helpers"; +import { shuffle } from "../../utils/list-helpers"; import { createMockVariantAnalysisHistoryItem } from "../../../factories/query-history/variant-analysis-history-item"; import type { VariantAnalysisHistoryItem } from "../../../../src/query-history/variant-analysis-history-item"; import { QueryStatus } from "../../../../src/query-history/query-status"; @@ -138,10 +138,7 @@ describe("QueryHistoryManager", () => { variantAnalysisStatus: VariantAnalysisStatus.InProgress, }), ]; - allHistory = shuffleHistoryItems([ - ...localQueryHistory, - ...variantAnalysisHistory, - ]); + allHistory = shuffle([...localQueryHistory, ...variantAnalysisHistory]); }); afterEach(async () => { diff --git a/extensions/ql-vscode/test/vscode-tests/utils/list-helpers.ts b/extensions/ql-vscode/test/vscode-tests/utils/list-helpers.ts new file mode 100644 index 00000000000..a0186d2bee7 --- /dev/null +++ b/extensions/ql-vscode/test/vscode-tests/utils/list-helpers.ts @@ -0,0 +1,3 @@ +export function shuffle(xs: T[]): T[] { + return xs.sort(() => Math.random() - 0.5); +} diff --git a/extensions/ql-vscode/test/vscode-tests/utils/query-history-helpers.ts b/extensions/ql-vscode/test/vscode-tests/utils/query-history-helpers.ts deleted file mode 100644 index d4ec43dc189..00000000000 --- a/extensions/ql-vscode/test/vscode-tests/utils/query-history-helpers.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { QueryHistoryInfo } from "../../../src/query-history/query-history-info"; - -export function shuffleHistoryItems(history: QueryHistoryInfo[]) { - return history.sort(() => Math.random() - 0.5); -} From 95fd0154144c2ec0b34592f3027edfbc19a2ee24 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 20 Feb 2024 16:07:03 +0000 Subject: [PATCH 0009/1237] Add unit tests of method sorting --- .../model-editor/shared/sorting.test.ts | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 extensions/ql-vscode/test/unit-tests/model-editor/shared/sorting.test.ts diff --git a/extensions/ql-vscode/test/unit-tests/model-editor/shared/sorting.test.ts b/extensions/ql-vscode/test/unit-tests/model-editor/shared/sorting.test.ts new file mode 100644 index 00000000000..83b7a09c83b --- /dev/null +++ b/extensions/ql-vscode/test/unit-tests/model-editor/shared/sorting.test.ts @@ -0,0 +1,136 @@ +import type { Method } from "../../../../src/model-editor/method"; +import type { ModeledMethod } from "../../../../src/model-editor/modeled-method"; +import { sortMethods } from "../../../../src/model-editor/shared/sorting"; +import { + createMethod, + createUsage, +} from "../../../factories/model-editor/method-factories"; +import { createSinkModeledMethod } from "../../../factories/model-editor/modeled-method-factories"; +import { shuffle } from "../../../vscode-tests/utils/list-helpers"; + +describe("sortMethods", () => { + it("uses primary sort order", () => { + const unsavedPositiveAutoModelPrediction = createMethod({ + signature: "org.sql2o.Sql2o#open1()", + }); + const negativeAutoModelPrediction = createMethod({ + signature: "org.sql2o.Sql2o#open2()", + }); + const unsavedManualModel = createMethod({ + signature: "org.sql2o.Sql2o#open3()", + }); + const unmodeledMethod = createMethod({ + signature: "org.sql2o.Sql2o#open4()", + }); + const savedAutoModelPrediction = createMethod({ + signature: "org.sql2o.Sql2o#open5()", + }); + const savedManualModel = createMethod({ + signature: "org.sql2o.Sql2o#open6()", + }); + + const methods: Method[] = shuffle([ + unsavedPositiveAutoModelPrediction, + negativeAutoModelPrediction, + unsavedManualModel, + unmodeledMethod, + savedAutoModelPrediction, + savedManualModel, + ]); + + const modeledMethodsMap: Record = {}; + modeledMethodsMap[unsavedPositiveAutoModelPrediction.signature] = [ + createSinkModeledMethod(), + ]; + modeledMethodsMap[unsavedManualModel.signature] = [ + createSinkModeledMethod(), + ]; + modeledMethodsMap[savedAutoModelPrediction.signature] = [ + createSinkModeledMethod(), + ]; + modeledMethodsMap[savedManualModel.signature] = [createSinkModeledMethod()]; + + const modifiedSignatures: Set = new Set([ + unsavedPositiveAutoModelPrediction.signature, + unsavedManualModel.signature, + ]); + + const processedByAutoModelMethods: Set = new Set([ + unsavedPositiveAutoModelPrediction.signature, + negativeAutoModelPrediction.signature, + savedAutoModelPrediction.signature, + ]); + + expect( + sortMethods( + methods, + modeledMethodsMap, + modifiedSignatures, + processedByAutoModelMethods, + ), + ).toEqual([ + unsavedPositiveAutoModelPrediction, + negativeAutoModelPrediction, + unsavedManualModel, + unmodeledMethod, + savedAutoModelPrediction, + savedManualModel, + ]); + }); + + it("uses secondary sort order based on usages and signature", () => { + const negativeAutoModelPrediction = createMethod({ + signature: "org.sql2o.Sql2o#negative()", + usages: [], + }); + + const unmodeledMethodWithTwoUsages = createMethod({ + signature: "org.sql2o.Sql2o#two()", + usages: [createUsage(), createUsage()], + }); + const unmodeledMethodWithOneUsage = createMethod({ + signature: "org.sql2o.Sql2o#one()", + usages: [createUsage()], + }); + + const unmodeledMethodWithEarlierSignature = createMethod({ + signature: "org.sql2o.Sql2o#aaa()", + usages: [], + }); + const unmodeledMethodWithLaterSignature = createMethod({ + signature: "org.sql2o.Sql2o#bbb()", + usages: [], + }); + + const methods: Method[] = shuffle([ + negativeAutoModelPrediction, + unmodeledMethodWithTwoUsages, + unmodeledMethodWithOneUsage, + unmodeledMethodWithEarlierSignature, + unmodeledMethodWithLaterSignature, + ]); + + const modeledMethodsMap: Record = {}; + + const modifiedSignatures: Set = new Set([]); + + const processedByAutoModelMethods: Set = new Set([ + negativeAutoModelPrediction.signature, + ]); + + expect( + sortMethods( + methods, + modeledMethodsMap, + modifiedSignatures, + processedByAutoModelMethods, + ), + ).toEqual([ + negativeAutoModelPrediction, + unmodeledMethodWithTwoUsages, + unmodeledMethodWithOneUsage, + unmodeledMethodWithEarlierSignature, + unmodeledMethodWithLaterSignature, + ]); + }); +}); From 7a68fcd05dc54e4366f5c53823a13a4901795b98 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 21 Feb 2024 10:40:38 +0000 Subject: [PATCH 0010/1237] Add comment explaining sort order more --- extensions/ql-vscode/src/model-editor/shared/sorting.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/ql-vscode/src/model-editor/shared/sorting.ts b/extensions/ql-vscode/src/model-editor/shared/sorting.ts index 52a2ccd2611..f7ba951d67d 100644 --- a/extensions/ql-vscode/src/model-editor/shared/sorting.ts +++ b/extensions/ql-vscode/src/model-editor/shared/sorting.ts @@ -46,6 +46,7 @@ export function sortMethods( ): Method[] { const sortedMethods = [...methods]; sortedMethods.sort((a, b) => { + // First sort by the type of method const methodAPrimarySortOrdinal = getMethodPrimarySortOrdinal( !!modeledMethodsMap[a.signature]?.length, modifiedSignatures.has(a.signature), From 3ca4c7623e571294d16c651f4b838f9b4dece690 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 21 Feb 2024 11:29:17 +0000 Subject: [PATCH 0011/1237] Update sort order for supported methods and unsaved manual models --- .../src/model-editor/shared/sorting.ts | 44 +++++++++++-------- .../model-editor/shared/sorting.test.ts | 17 +++++++ 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/shared/sorting.ts b/extensions/ql-vscode/src/model-editor/shared/sorting.ts index f7ba951d67d..c09c05c1023 100644 --- a/extensions/ql-vscode/src/model-editor/shared/sorting.ts +++ b/extensions/ql-vscode/src/model-editor/shared/sorting.ts @@ -1,3 +1,4 @@ +import { canMethodBeModeled } from "../method"; import type { Method } from "../method"; import type { ModeledMethod } from "../modeled-method"; import { Mode } from "./mode"; @@ -32,9 +33,9 @@ export function sortGroupNames( * Primarily sorts methods into the following order: * - Unsaved positive AutoModel predictions * - Negative AutoModel predictions - * - Unsaved manual models - * - Umodeled - * - Modeled and saved (AutoModel and manual) + * - Unsaved manual models + unmodeled methods + * - Saved models from this model pack (AutoModel and manual) + * - Methods not modelable in this model pack * * Secondary sort order is by number of usages descending, then by method signature ascending. */ @@ -48,12 +49,14 @@ export function sortMethods( sortedMethods.sort((a, b) => { // First sort by the type of method const methodAPrimarySortOrdinal = getMethodPrimarySortOrdinal( - !!modeledMethodsMap[a.signature]?.length, + a, + modeledMethodsMap[a.signature] ?? [], modifiedSignatures.has(a.signature), processedByAutoModelMethods.has(a.signature), ); const methodBPrimarySortOrdinal = getMethodPrimarySortOrdinal( - !!modeledMethodsMap[b.signature]?.length, + b, + modeledMethodsMap[b.signature] ?? [], modifiedSignatures.has(b.signature), processedByAutoModelMethods.has(b.signature), ); @@ -77,23 +80,28 @@ export function sortMethods( * Assigns numbers to the following classes of methods: * - Unsaved positive AutoModel predictions => 0 * - Negative AutoModel predictions => 1 - * - Unsaved manual models => 2 - * - Umodeled => 3 - * - Modeled and saved (AutoModel and manual) => 4 + * - Unsaved manual models + unmodeled methods => 2 + * - Saved models from this model pack (AutoModel and manual) => 3 + * - Methods not modelable in this model pack => 4 */ function getMethodPrimarySortOrdinal( - isModeled: boolean, - isModified: boolean, + method: Method, + modeledMethods: readonly ModeledMethod[], + isUnsaved: boolean, isProcessedByAutoModel: boolean, ): number { - if (isModeled && isModified && isProcessedByAutoModel) { - return 0; - } else if (!isModeled && isProcessedByAutoModel) { - return 1; - } else if (isModeled && isModified) { - return 2; - } else if (!isModeled) { - return 3; + const canBeModeled = canMethodBeModeled(method, modeledMethods, isUnsaved); + const isModeled = modeledMethods.length > 0; + if (canBeModeled) { + if (isModeled && isUnsaved && isProcessedByAutoModel) { + return 0; + } else if (!isModeled && isProcessedByAutoModel) { + return 1; + } else if ((isModeled && isUnsaved) || !isModeled) { + return 2; + } else { + return 3; + } } else { return 4; } diff --git a/extensions/ql-vscode/test/unit-tests/model-editor/shared/sorting.test.ts b/extensions/ql-vscode/test/unit-tests/model-editor/shared/sorting.test.ts index 83b7a09c83b..950900cc2b5 100644 --- a/extensions/ql-vscode/test/unit-tests/model-editor/shared/sorting.test.ts +++ b/extensions/ql-vscode/test/unit-tests/model-editor/shared/sorting.test.ts @@ -12,21 +12,31 @@ describe("sortMethods", () => { it("uses primary sort order", () => { const unsavedPositiveAutoModelPrediction = createMethod({ signature: "org.sql2o.Sql2o#open1()", + supported: false, }); const negativeAutoModelPrediction = createMethod({ signature: "org.sql2o.Sql2o#open2()", + supported: false, }); const unsavedManualModel = createMethod({ signature: "org.sql2o.Sql2o#open3()", + supported: false, }); const unmodeledMethod = createMethod({ signature: "org.sql2o.Sql2o#open4()", + supported: false, }); const savedAutoModelPrediction = createMethod({ signature: "org.sql2o.Sql2o#open5()", + supported: false, }); const savedManualModel = createMethod({ signature: "org.sql2o.Sql2o#open6()", + supported: false, + }); + const supportedMethod = createMethod({ + signature: "org.sql2o.Sql2o#open7()", + supported: true, }); const methods: Method[] = shuffle([ @@ -36,6 +46,7 @@ describe("sortMethods", () => { unmodeledMethod, savedAutoModelPrediction, savedManualModel, + supportedMethod, ]); const modeledMethodsMap: Record = {}; @@ -75,30 +86,36 @@ describe("sortMethods", () => { unmodeledMethod, savedAutoModelPrediction, savedManualModel, + supportedMethod, ]); }); it("uses secondary sort order based on usages and signature", () => { const negativeAutoModelPrediction = createMethod({ signature: "org.sql2o.Sql2o#negative()", + supported: false, usages: [], }); const unmodeledMethodWithTwoUsages = createMethod({ signature: "org.sql2o.Sql2o#two()", + supported: false, usages: [createUsage(), createUsage()], }); const unmodeledMethodWithOneUsage = createMethod({ signature: "org.sql2o.Sql2o#one()", + supported: false, usages: [createUsage()], }); const unmodeledMethodWithEarlierSignature = createMethod({ signature: "org.sql2o.Sql2o#aaa()", + supported: false, usages: [], }); const unmodeledMethodWithLaterSignature = createMethod({ signature: "org.sql2o.Sql2o#bbb()", + supported: false, usages: [], }); From 759f8d476887c0c9ae3adfe1785dd7ee66da0227 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 21 Feb 2024 11:39:54 +0000 Subject: [PATCH 0012/1237] Give test methods signatures that'll be easier to identify --- .../model-editor/shared/sorting.test.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/extensions/ql-vscode/test/unit-tests/model-editor/shared/sorting.test.ts b/extensions/ql-vscode/test/unit-tests/model-editor/shared/sorting.test.ts index 950900cc2b5..f7f8c3a5a3a 100644 --- a/extensions/ql-vscode/test/unit-tests/model-editor/shared/sorting.test.ts +++ b/extensions/ql-vscode/test/unit-tests/model-editor/shared/sorting.test.ts @@ -11,31 +11,31 @@ import { shuffle } from "../../../vscode-tests/utils/list-helpers"; describe("sortMethods", () => { it("uses primary sort order", () => { const unsavedPositiveAutoModelPrediction = createMethod({ - signature: "org.sql2o.Sql2o#open1()", + signature: "org.sql2o.Sql2o#unsavedPositiveAutoModelPrediction()", supported: false, }); const negativeAutoModelPrediction = createMethod({ - signature: "org.sql2o.Sql2o#open2()", + signature: "org.sql2o.Sql2o#negativeAutoModelPrediction()", supported: false, }); const unsavedManualModel = createMethod({ - signature: "org.sql2o.Sql2o#open3()", + signature: "org.sql2o.Sql2o#unsavedManualModel()", supported: false, }); const unmodeledMethod = createMethod({ - signature: "org.sql2o.Sql2o#open4()", + signature: "org.sql2o.Sql2o#unmodeledMethod()", supported: false, }); const savedAutoModelPrediction = createMethod({ - signature: "org.sql2o.Sql2o#open5()", + signature: "org.sql2o.Sql2o#savedAutoModelPrediction()", supported: false, }); const savedManualModel = createMethod({ - signature: "org.sql2o.Sql2o#open6()", + signature: "org.sql2o.Sql2o#savedManualModel()", supported: false, }); const supportedMethod = createMethod({ - signature: "org.sql2o.Sql2o#open7()", + signature: "org.sql2o.Sql2o#supportedMethod()", supported: true, }); @@ -92,29 +92,29 @@ describe("sortMethods", () => { it("uses secondary sort order based on usages and signature", () => { const negativeAutoModelPrediction = createMethod({ - signature: "org.sql2o.Sql2o#negative()", + signature: "org.sql2o.Sql2o#negativeAutoModelPrediction()", supported: false, usages: [], }); const unmodeledMethodWithTwoUsages = createMethod({ - signature: "org.sql2o.Sql2o#two()", + signature: "org.sql2o.Sql2o#unmodeledMethodWithTwoUsages()", supported: false, usages: [createUsage(), createUsage()], }); const unmodeledMethodWithOneUsage = createMethod({ - signature: "org.sql2o.Sql2o#one()", + signature: "org.sql2o.Sql2o#unmodeledMethodWithOneUsage()", supported: false, usages: [createUsage()], }); const unmodeledMethodWithEarlierSignature = createMethod({ - signature: "org.sql2o.Sql2o#aaa()", + signature: "org.sql2o.Sql2o#aaa_unmodeledMethodWithEarlierSignature()", supported: false, usages: [], }); const unmodeledMethodWithLaterSignature = createMethod({ - signature: "org.sql2o.Sql2o#bbb()", + signature: "org.sql2o.Sql2o#bbb_unmodeledMethodWithLaterSignature()", supported: false, usages: [], }); From b59437e7dc3e194dbdaf01dd3625efdbb7194d76 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 21 Feb 2024 11:49:13 +0000 Subject: [PATCH 0013/1237] Cover in unit test that unmodeled and unsaved manual models are sorted together --- .../unit-tests/model-editor/shared/sorting.test.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/extensions/ql-vscode/test/unit-tests/model-editor/shared/sorting.test.ts b/extensions/ql-vscode/test/unit-tests/model-editor/shared/sorting.test.ts index f7f8c3a5a3a..47320e13114 100644 --- a/extensions/ql-vscode/test/unit-tests/model-editor/shared/sorting.test.ts +++ b/extensions/ql-vscode/test/unit-tests/model-editor/shared/sorting.test.ts @@ -22,8 +22,12 @@ describe("sortMethods", () => { signature: "org.sql2o.Sql2o#unsavedManualModel()", supported: false, }); - const unmodeledMethod = createMethod({ - signature: "org.sql2o.Sql2o#unmodeledMethod()", + const unmodeledMethodWithEarlierSignature = createMethod({ + signature: "org.sql2o.Sql2o#aaa_unmodeledMethodWithEarlierSignature()", + supported: false, + }); + const unmodeledMethodWithLaterSignature = createMethod({ + signature: "org.sql2o.Sql2o#zzz_unmodeledMethodWithLaterSignature()", supported: false, }); const savedAutoModelPrediction = createMethod({ @@ -43,7 +47,8 @@ describe("sortMethods", () => { unsavedPositiveAutoModelPrediction, negativeAutoModelPrediction, unsavedManualModel, - unmodeledMethod, + unmodeledMethodWithEarlierSignature, + unmodeledMethodWithLaterSignature, savedAutoModelPrediction, savedManualModel, supportedMethod, @@ -82,8 +87,9 @@ describe("sortMethods", () => { ).toEqual([ unsavedPositiveAutoModelPrediction, negativeAutoModelPrediction, + unmodeledMethodWithEarlierSignature, unsavedManualModel, - unmodeledMethod, + unmodeledMethodWithLaterSignature, savedAutoModelPrediction, savedManualModel, supportedMethod, From 2fdafaf616ad5553d226d89f82d99ac9fd4c046c Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 21 Feb 2024 12:29:43 +0000 Subject: [PATCH 0014/1237] Move getCandidates to the shared directory --- .../ql-vscode/src/model-editor/auto-model.ts | 53 -------- .../src/model-editor/auto-modeler.ts | 3 +- .../shared/auto-model-candidates.ts | 55 ++++++++ .../src/view/model-editor/LibraryRow.tsx | 2 +- .../model-editor/auto-model.test.ts | 119 ----------------- .../shared/auto-model-candidates.test.ts | 120 ++++++++++++++++++ 6 files changed, 178 insertions(+), 174 deletions(-) create mode 100644 extensions/ql-vscode/src/model-editor/shared/auto-model-candidates.ts create mode 100644 extensions/ql-vscode/test/unit-tests/model-editor/shared/auto-model-candidates.test.ts diff --git a/extensions/ql-vscode/src/model-editor/auto-model.ts b/extensions/ql-vscode/src/model-editor/auto-model.ts index 42020db132a..e0099fae518 100644 --- a/extensions/ql-vscode/src/model-editor/auto-model.ts +++ b/extensions/ql-vscode/src/model-editor/auto-model.ts @@ -5,59 +5,6 @@ import type { AutoModelQueriesResult } from "./auto-model-codeml-queries"; import { assertNever } from "../common/helpers-pure"; import type { Log } from "sarif"; import { gzipEncode } from "../common/zlib"; -import type { Method, MethodSignature } from "./method"; -import type { ModeledMethod } from "./modeled-method"; -import { groupMethods, sortGroupNames, sortMethods } from "./shared/sorting"; - -/** - * Return the candidates that the model should be run on. This includes limiting the number of - * candidates to the candidate limit and filtering out anything that is already modeled and respecting - * the order in the UI. - * @param mode Whether it is application or framework mode. - * @param methods all methods. - * @param modeledMethodsBySignature the currently modeled methods. - * @returns list of modeled methods that are candidates for modeling. - */ -export function getCandidates( - mode: Mode, - methods: readonly Method[], - modeledMethodsBySignature: Record, - processedByAutoModelMethods: Set, -): MethodSignature[] { - // Filter out any methods already processed by auto-model - methods = methods.filter( - (m) => !processedByAutoModelMethods.has(m.signature), - ); - - // Sort the same way as the UI so we send the first ones listed in the UI first - const grouped = groupMethods(methods, mode); - const sortedGroupNames = sortGroupNames(grouped); - const sortedMethods = sortedGroupNames.flatMap((name) => - sortMethods(grouped[name]), - ); - - const candidates: MethodSignature[] = []; - - for (const method of sortedMethods) { - const modeledMethods: ModeledMethod[] = [ - ...(modeledMethodsBySignature[method.signature] ?? []), - ]; - - // Anything that is modeled is not a candidate - if (modeledMethods.some((m) => m.type !== "none")) { - continue; - } - - // A method that is supported is modeled outside of the model file, so it is not a candidate. - if (method.supported) { - continue; - } - - // The rest are candidates - candidates.push(method); - } - return candidates; -} /** * Encode a SARIF log to the format expected by the server: JSON, GZIP-compressed, base64-encoded diff --git a/extensions/ql-vscode/src/model-editor/auto-modeler.ts b/extensions/ql-vscode/src/model-editor/auto-modeler.ts index 96695f669e7..e3f62ba3be8 100644 --- a/extensions/ql-vscode/src/model-editor/auto-modeler.ts +++ b/extensions/ql-vscode/src/model-editor/auto-modeler.ts @@ -3,7 +3,8 @@ import type { ModeledMethod } from "./modeled-method"; import { load as loadYaml } from "js-yaml"; import type { ProgressCallback } from "../common/vscode/progress"; import { withProgress } from "../common/vscode/progress"; -import { createAutoModelRequest, getCandidates } from "./auto-model"; +import { createAutoModelRequest } from "./auto-model"; +import { getCandidates } from "./shared/auto-model-candidates"; import { runAutoModelQueries } from "./auto-model-codeml-queries"; import { loadDataExtensionYaml } from "./yaml"; import type { ModelRequest, ModelResponse } from "./auto-model-api"; diff --git a/extensions/ql-vscode/src/model-editor/shared/auto-model-candidates.ts b/extensions/ql-vscode/src/model-editor/shared/auto-model-candidates.ts new file mode 100644 index 00000000000..bfc6e5a8280 --- /dev/null +++ b/extensions/ql-vscode/src/model-editor/shared/auto-model-candidates.ts @@ -0,0 +1,55 @@ +import type { Method, MethodSignature } from "../method"; +import type { ModeledMethod } from "../modeled-method"; +import type { Mode } from "./mode"; +import { groupMethods, sortGroupNames, sortMethods } from "./sorting"; + +/** + * Return the candidates that the model should be run on. This includes limiting the number of + * candidates to the candidate limit and filtering out anything that is already modeled and respecting + * the order in the UI. + * @param mode Whether it is application or framework mode. + * @param methods all methods. + * @param modeledMethodsBySignature the currently modeled methods. + * @returns list of modeled methods that are candidates for modeling. + */ + +export function getCandidates( + mode: Mode, + methods: readonly Method[], + modeledMethodsBySignature: Record, + processedByAutoModelMethods: Set, +): MethodSignature[] { + // Filter out any methods already processed by auto-model + methods = methods.filter( + (m) => !processedByAutoModelMethods.has(m.signature), + ); + + // Sort the same way as the UI so we send the first ones listed in the UI first + const grouped = groupMethods(methods, mode); + const sortedGroupNames = sortGroupNames(grouped); + const sortedMethods = sortedGroupNames.flatMap((name) => + sortMethods(grouped[name]), + ); + + const candidates: MethodSignature[] = []; + + for (const method of sortedMethods) { + const modeledMethods: ModeledMethod[] = [ + ...(modeledMethodsBySignature[method.signature] ?? []), + ]; + + // Anything that is modeled is not a candidate + if (modeledMethods.some((m) => m.type !== "none")) { + continue; + } + + // A method that is supported is modeled outside of the model file, so it is not a candidate. + if (method.supported) { + continue; + } + + // The rest are candidates + candidates.push(method); + } + return candidates; +} diff --git a/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx b/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx index 0c5c8bc4a0f..40fe4359bd1 100644 --- a/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx @@ -14,7 +14,7 @@ import { } from "@vscode/webview-ui-toolkit/react"; import type { ModelEditorViewState } from "../../model-editor/shared/view-state"; import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions"; -import { getCandidates } from "../../model-editor/auto-model"; +import { getCandidates } from "../../model-editor/shared/auto-model-candidates"; const LibraryContainer = styled.div` background-color: var(--vscode-peekViewResult-background); diff --git a/extensions/ql-vscode/test/unit-tests/model-editor/auto-model.test.ts b/extensions/ql-vscode/test/unit-tests/model-editor/auto-model.test.ts index 4693f349ab2..50e67fffdc8 100644 --- a/extensions/ql-vscode/test/unit-tests/model-editor/auto-model.test.ts +++ b/extensions/ql-vscode/test/unit-tests/model-editor/auto-model.test.ts @@ -1,16 +1,12 @@ import { createAutoModelRequest, encodeSarif, - getCandidates, } from "../../../src/model-editor/auto-model"; import { Mode } from "../../../src/model-editor/shared/mode"; import { AutomodelMode } from "../../../src/model-editor/auto-model-api"; import type { AutoModelQueriesResult } from "../../../src/model-editor/auto-model-codeml-queries"; import type { Log } from "sarif"; import { gzipDecode } from "../../../src/common/zlib"; -import type { Method } from "../../../src/model-editor/method"; -import { EndpointType } from "../../../src/model-editor/method"; -import type { ModeledMethod } from "../../../src/model-editor/modeled-method"; describe("createAutoModelRequest", () => { const createSarifLog = (queryId: string): Log => { @@ -84,118 +80,3 @@ describe("createAutoModelRequest", () => { expect(parsed).toEqual(result.candidates); }); }); - -describe("getCandidates", () => { - it("doesn't return methods that are already modelled", () => { - const methods: Method[] = [ - { - library: "my.jar", - signature: "org.my.A#x()", - endpointType: EndpointType.Method, - packageName: "org.my", - typeName: "A", - methodName: "x", - methodParameters: "()", - supported: false, - supportedType: "none", - usages: [], - }, - ]; - const modeledMethods: Record = { - "org.my.A#x()": [ - { - type: "neutral", - kind: "sink", - provenance: "manual", - signature: "org.my.A#x()", - endpointType: EndpointType.Method, - packageName: "org.my", - typeName: "A", - methodName: "x", - methodParameters: "()", - }, - ], - }; - const candidates = getCandidates( - Mode.Application, - methods, - modeledMethods, - new Set(), - ); - expect(candidates.length).toEqual(0); - }); - - it("doesn't return methods that are supported from other sources", () => { - const methods: Method[] = [ - { - library: "my.jar", - signature: "org.my.A#x()", - endpointType: EndpointType.Method, - packageName: "org.my", - typeName: "A", - methodName: "x", - methodParameters: "()", - supported: true, - supportedType: "none", - usages: [], - }, - ]; - const modeledMethods = {}; - const candidates = getCandidates( - Mode.Application, - methods, - modeledMethods, - new Set(), - ); - expect(candidates.length).toEqual(0); - }); - - it("doesn't return methods that are already processed by auto model", () => { - const methods: Method[] = [ - { - library: "my.jar", - signature: "org.my.A#x()", - endpointType: EndpointType.Method, - packageName: "org.my", - typeName: "A", - methodName: "x", - methodParameters: "()", - supported: false, - supportedType: "none", - usages: [], - }, - ]; - const modeledMethods = {}; - const candidates = getCandidates( - Mode.Application, - methods, - modeledMethods, - new Set(["org.my.A#x()"]), - ); - expect(candidates.length).toEqual(0); - }); - - it("returns methods that are neither modeled nor supported from other sources", () => { - const methods: Method[] = []; - methods.push({ - library: "my.jar", - signature: "org.my.A#x()", - endpointType: EndpointType.Method, - packageName: "org.my", - typeName: "A", - methodName: "x", - methodParameters: "()", - supported: false, - supportedType: "none", - usages: [], - }); - const modeledMethods = {}; - const candidates = getCandidates( - Mode.Application, - methods, - modeledMethods, - new Set(), - ); - expect(candidates.length).toEqual(1); - }); -}); diff --git a/extensions/ql-vscode/test/unit-tests/model-editor/shared/auto-model-candidates.test.ts b/extensions/ql-vscode/test/unit-tests/model-editor/shared/auto-model-candidates.test.ts new file mode 100644 index 00000000000..c89e604c53f --- /dev/null +++ b/extensions/ql-vscode/test/unit-tests/model-editor/shared/auto-model-candidates.test.ts @@ -0,0 +1,120 @@ +import type { Method } from "../../../../src/model-editor/method"; +import { EndpointType } from "../../../../src/model-editor/method"; +import type { ModeledMethod } from "../../../../src/model-editor/modeled-method"; +import { getCandidates } from "../../../../src/model-editor/shared/auto-model-candidates"; +import { Mode } from "../../../../src/model-editor/shared/mode"; + +describe("getCandidates", () => { + it("doesn't return methods that are already modelled", () => { + const methods: Method[] = [ + { + library: "my.jar", + signature: "org.my.A#x()", + endpointType: EndpointType.Method, + packageName: "org.my", + typeName: "A", + methodName: "x", + methodParameters: "()", + supported: false, + supportedType: "none", + usages: [], + }, + ]; + const modeledMethods: Record = { + "org.my.A#x()": [ + { + type: "neutral", + kind: "sink", + provenance: "manual", + signature: "org.my.A#x()", + endpointType: EndpointType.Method, + packageName: "org.my", + typeName: "A", + methodName: "x", + methodParameters: "()", + }, + ], + }; + const candidates = getCandidates( + Mode.Application, + methods, + modeledMethods, + new Set(), + ); + expect(candidates.length).toEqual(0); + }); + + it("doesn't return methods that are supported from other sources", () => { + const methods: Method[] = [ + { + library: "my.jar", + signature: "org.my.A#x()", + endpointType: EndpointType.Method, + packageName: "org.my", + typeName: "A", + methodName: "x", + methodParameters: "()", + supported: true, + supportedType: "none", + usages: [], + }, + ]; + const modeledMethods = {}; + const candidates = getCandidates( + Mode.Application, + methods, + modeledMethods, + new Set(), + ); + expect(candidates.length).toEqual(0); + }); + + it("doesn't return methods that are already processed by auto model", () => { + const methods: Method[] = [ + { + library: "my.jar", + signature: "org.my.A#x()", + endpointType: EndpointType.Method, + packageName: "org.my", + typeName: "A", + methodName: "x", + methodParameters: "()", + supported: false, + supportedType: "none", + usages: [], + }, + ]; + const modeledMethods = {}; + const candidates = getCandidates( + Mode.Application, + methods, + modeledMethods, + new Set(["org.my.A#x()"]), + ); + expect(candidates.length).toEqual(0); + }); + + it("returns methods that are neither modeled nor supported from other sources", () => { + const methods: Method[] = []; + methods.push({ + library: "my.jar", + signature: "org.my.A#x()", + endpointType: EndpointType.Method, + packageName: "org.my", + typeName: "A", + methodName: "x", + methodParameters: "()", + supported: false, + supportedType: "none", + usages: [], + }); + const modeledMethods = {}; + const candidates = getCandidates( + Mode.Application, + methods, + modeledMethods, + new Set(), + ); + expect(candidates.length).toEqual(1); + }); +}); From 2c6eb3c8328ed66d4d45718a0d1d1e164b1edf51 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 21 Feb 2024 16:24:11 +0100 Subject: [PATCH 0015/1237] Fix incorrect modelPending prop --- .../view/method-modeling/MultipleModeledMethodsPanel.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/view/method-modeling/MultipleModeledMethodsPanel.tsx b/extensions/ql-vscode/src/view/method-modeling/MultipleModeledMethodsPanel.tsx index de8589686d9..705c4313b86 100644 --- a/extensions/ql-vscode/src/view/method-modeling/MultipleModeledMethodsPanel.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/MultipleModeledMethodsPanel.tsx @@ -172,7 +172,11 @@ export const MultipleModeledMethodsPanel = ({ language={language} method={method} modeledMethod={undefined} - modelPending={false} + modelPending={isModelPending( + modeledMethods[selectedIndex], + modelingStatus, + isProcessedByAutoModel, + )} isModelingInProgress={isModelingInProgress} onChange={handleChange} /> From 873ccaa7fde820fbd7566d5aaca84dd0c0ae2f48 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 22 Feb 2024 12:08:10 +0100 Subject: [PATCH 0016/1237] Update Ruby model generation query constraints --- .../ql-vscode/src/model-editor/languages/models-as-data.ts | 2 +- .../ql-vscode/src/model-editor/languages/ruby/index.ts | 7 ++++--- .../ql-vscode/src/model-editor/languages/static/index.ts | 4 ++-- extensions/ql-vscode/src/model-editor/model-editor-view.ts | 2 +- .../no-workspace/model-editor/generate.test.ts | 3 ++- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts b/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts index 97fc39391ae..eb497128e37 100644 --- a/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts +++ b/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts @@ -33,7 +33,7 @@ export type ModelsAsDataLanguagePredicate = { }; type ModelsAsDataLanguageModelGeneration = { - queryConstraints: QueryConstraints; + queryConstraints: (mode: Mode) => QueryConstraints; filterQueries?: (queryPath: string) => boolean; parseResults: ( // The path to the query that generated the results. diff --git a/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts b/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts index ba4134ce51a..ea19f3905bd 100644 --- a/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts +++ b/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts @@ -172,9 +172,10 @@ export const ruby: ModelsAsDataLanguage = { }, }, modelGeneration: { - queryConstraints: { - "query path": "queries/modeling/GenerateModel.ql", - }, + queryConstraints: (mode) => ({ + kind: "table", + "tags contain all": ["modeleditor", "generate-model", modeTag(mode)], + }), parseResults: parseGenerateModelResults, }, accessPathSuggestions: { diff --git a/extensions/ql-vscode/src/model-editor/languages/static/index.ts b/extensions/ql-vscode/src/model-editor/languages/static/index.ts index 6c38f394d86..2875627348d 100644 --- a/extensions/ql-vscode/src/model-editor/languages/static/index.ts +++ b/extensions/ql-vscode/src/model-editor/languages/static/index.ts @@ -141,9 +141,9 @@ export const staticLanguage: ModelsAsDataLanguage = { }, }, modelGeneration: { - queryConstraints: { + queryConstraints: () => ({ "tags contain": ["modelgenerator"], - }, + }), filterQueries: filterFlowModelQueries, parseResults: parseFlowModelResults, }, diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 393e0967321..0456c375137 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -619,7 +619,7 @@ export class ModelEditorView extends AbstractWebview< try { await runGenerateQueries({ - queryConstraints: modelGeneration.queryConstraints, + queryConstraints: modelGeneration.queryConstraints(mode), filterQueries: modelGeneration.filterQueries, parseResults: (queryPath, results) => modelGeneration.parseResults( diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/generate.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/generate.test.ts index 8259916c30a..ba79d4d2983 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/generate.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/generate.test.ts @@ -13,6 +13,7 @@ import { runGenerateQueries } from "../../../../src/model-editor/generate"; import { ruby } from "../../../../src/model-editor/languages/ruby"; import type { ModeledMethod } from "../../../../src/model-editor/modeled-method"; import { EndpointType } from "../../../../src/model-editor/method"; +import { Mode } from "../../../../src/model-editor/shared/mode"; describe("runGenerateQueries", () => { const modelsAsDataLanguage = ruby; @@ -125,7 +126,7 @@ describe("runGenerateQueries", () => { }; await runGenerateQueries({ - queryConstraints: modelGeneration.queryConstraints, + queryConstraints: modelGeneration.queryConstraints(Mode.Framework), filterQueries: modelGeneration.filterQueries, parseResults: (queryPath, results) => modelGeneration.parseResults( From 6161c7d3d46b20523dacfcfa9a392ce40f498c37 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 22 Feb 2024 11:54:32 +0000 Subject: [PATCH 0017/1237] Bump CLI version from v2.16.2 to v2.16.3 for integration tests --- extensions/ql-vscode/supported_cli_versions.json | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/ql-vscode/supported_cli_versions.json b/extensions/ql-vscode/supported_cli_versions.json index 98bcc5ba93c..75a66c6d038 100644 --- a/extensions/ql-vscode/supported_cli_versions.json +++ b/extensions/ql-vscode/supported_cli_versions.json @@ -1,4 +1,5 @@ [ + "v2.16.3", "v2.16.2", "v2.15.5", "v2.14.6", From 9437cd094bf5468cd89eb42553b29287f3400fcb Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 22 Feb 2024 13:25:02 +0100 Subject: [PATCH 0018/1237] Update CHANGELOG --- extensions/ql-vscode/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 61798e510ee..15cf3b35754 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -3,6 +3,7 @@ ## [UNRELEASED] - Remove support for CodeQL CLI versions older than 2.13.5. [#3371](https://github.com/github/vscode-codeql/pull/3371) +- Add a timeout to downloading databases and the CodeQL CLI. These can be changed using the `codeQL.addingDatabases.downloadTimeout` and `codeQL.cli.downloadTimeout` settings respectively. [#3373](https://github.com/github/vscode-codeql/pull/3373) ## 1.12.2 - 14 February 2024 From 91e59323f375dc625e67b6207a011b1a6d16af50 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 22 Feb 2024 12:30:47 +0000 Subject: [PATCH 0019/1237] Delete setModifiedMethods as it is unused --- extensions/ql-vscode/src/model-editor/modeling-store.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/modeling-store.ts b/extensions/ql-vscode/src/model-editor/modeling-store.ts index 11317d158c2..3499e7dd0a0 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-store.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-store.ts @@ -250,15 +250,6 @@ export class ModelingStore extends DisposableObject { }); } - public setModifiedMethods( - dbItem: DatabaseItem, - methodSignatures: Set, - ) { - this.changeModifiedMethods(dbItem, (state) => { - state.modifiedMethodSignatures = new Set(methodSignatures); - }); - } - public addModifiedMethods( dbItem: DatabaseItem, methodSignatures: Iterable, From 59118f63aa72210aa750627eb9e0555fb75100e4 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 22 Feb 2024 12:17:33 +0000 Subject: [PATCH 0020/1237] Change addModeledMethods and updateModeledMethods to also set methods as modified --- .../method-modeling-view-provider.ts | 5 +---- .../src/model-editor/model-editor-view.ts | 9 ++++----- .../ql-vscode/src/model-editor/modeling-store.ts | 14 ++++++++++---- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts index e921b3776d5..7a4b9905220 100644 --- a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts +++ b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts @@ -124,10 +124,7 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< this.databaseItem, msg.methodSignature, msg.modeledMethods, - ); - this.modelingStore.addModifiedMethod( - this.databaseItem, - msg.methodSignature, + true, ); break; } diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 393e0967321..838cf945b8a 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -889,11 +889,10 @@ export class ModelEditorView extends AbstractWebview< } private addModeledMethods(modeledMethods: Record) { - this.modelingStore.addModeledMethods(this.databaseItem, modeledMethods); - - this.modelingStore.addModifiedMethods( + this.modelingStore.addModeledMethods( this.databaseItem, - new Set(Object.keys(modeledMethods)), + modeledMethods, + true, ); } @@ -916,8 +915,8 @@ export class ModelEditorView extends AbstractWebview< this.databaseItem, signature, methods, + true, ); - this.modelingStore.addModifiedMethod(this.databaseItem, signature); } private startModelEvaluation() { diff --git a/extensions/ql-vscode/src/model-editor/modeling-store.ts b/extensions/ql-vscode/src/model-editor/modeling-store.ts index 3499e7dd0a0..ec7f2608e85 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-store.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-store.ts @@ -214,6 +214,7 @@ export class ModelingStore extends DisposableObject { public addModeledMethods( dbItem: DatabaseItem, methods: Record, + setModified: boolean, ) { this.changeModeledMethods(dbItem, (state) => { const newModeledMethods = { @@ -227,6 +228,10 @@ export class ModelingStore extends DisposableObject { }; state.modeledMethods = newModeledMethods; }); + + if (setModified) { + this.addModifiedMethods(dbItem, new Set(Object.keys(methods))); + } } public setModeledMethods( @@ -242,12 +247,17 @@ export class ModelingStore extends DisposableObject { dbItem: DatabaseItem, signature: string, modeledMethods: ModeledMethod[], + setModified: boolean, ) { this.changeModeledMethods(dbItem, (state) => { const newModeledMethods = { ...state.modeledMethods }; newModeledMethods[signature] = modeledMethods; state.modeledMethods = newModeledMethods; }); + + if (setModified) { + this.addModifiedMethods(dbItem, [signature]); + } } public addModifiedMethods( @@ -263,10 +273,6 @@ export class ModelingStore extends DisposableObject { }); } - public addModifiedMethod(dbItem: DatabaseItem, methodSignature: string) { - this.addModifiedMethods(dbItem, [methodSignature]); - } - public removeModifiedMethods( dbItem: DatabaseItem, methodSignatures: string[], From 63de6cece5332111f3d1c3c6bc8ecbc91d5afabd Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 22 Feb 2024 12:36:06 +0000 Subject: [PATCH 0021/1237] Allow changeModeledMethods to also call fireModifiedMethodsChangedEvent --- .../src/model-editor/modeling-store.ts | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/modeling-store.ts b/extensions/ql-vscode/src/model-editor/modeling-store.ts index ec7f2608e85..49dffde8f46 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-store.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-store.ts @@ -216,7 +216,7 @@ export class ModelingStore extends DisposableObject { methods: Record, setModified: boolean, ) { - this.changeModeledMethods(dbItem, (state) => { + this.changeModeledMethods(dbItem, setModified, (state) => { const newModeledMethods = { ...methods, // Keep all methods that are already modeled in some form in the state @@ -227,18 +227,22 @@ export class ModelingStore extends DisposableObject { ), }; state.modeledMethods = newModeledMethods; - }); - if (setModified) { - this.addModifiedMethods(dbItem, new Set(Object.keys(methods))); - } + if (setModified) { + const newModifiedMethods = new Set([ + ...state.modifiedMethodSignatures, + ...new Set(Object.keys(methods)), + ]); + state.modifiedMethodSignatures = newModifiedMethods; + } + }); } public setModeledMethods( dbItem: DatabaseItem, methods: Record, ) { - this.changeModeledMethods(dbItem, (state) => { + this.changeModeledMethods(dbItem, false, (state) => { state.modeledMethods = { ...methods }; }); } @@ -249,27 +253,18 @@ export class ModelingStore extends DisposableObject { modeledMethods: ModeledMethod[], setModified: boolean, ) { - this.changeModeledMethods(dbItem, (state) => { + this.changeModeledMethods(dbItem, setModified, (state) => { const newModeledMethods = { ...state.modeledMethods }; newModeledMethods[signature] = modeledMethods; state.modeledMethods = newModeledMethods; - }); - if (setModified) { - this.addModifiedMethods(dbItem, [signature]); - } - } - - public addModifiedMethods( - dbItem: DatabaseItem, - methodSignatures: Iterable, - ) { - this.changeModifiedMethods(dbItem, (state) => { - const newModifiedMethods = new Set([ - ...state.modifiedMethodSignatures, - ...methodSignatures, - ]); - state.modifiedMethodSignatures = newModifiedMethods; + if (setModified) { + const newModifiedMethods = new Set([ + ...state.modifiedMethodSignatures, + signature, + ]); + state.modifiedMethodSignatures = newModifiedMethods; + } }); } @@ -422,6 +417,7 @@ export class ModelingStore extends DisposableObject { private changeModeledMethods( dbItem: DatabaseItem, + modifiedMethodsChanged: boolean, updateState: (state: InternalDbModelingState) => void, ) { const state = this.getState(dbItem); @@ -433,6 +429,14 @@ export class ModelingStore extends DisposableObject { dbItem.databaseUri.toString(), dbItem.databaseUri.toString() === this.activeDb, ); + + if (modifiedMethodsChanged) { + this.modelingEvents.fireModifiedMethodsChangedEvent( + state.modifiedMethodSignatures, + dbItem.databaseUri.toString(), + dbItem.databaseUri.toString() === this.activeDb, + ); + } } private changeInProgressMethods( From 2b14639c809796a32d9cd97e1dd5250e7004a0f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 06:21:58 -0800 Subject: [PATCH 0022/1237] Bump chokidar from 3.5.3 to 3.6.0 in /extensions/ql-vscode (#3389) Bumps [chokidar](https://github.com/paulmillr/chokidar) from 3.5.3 to 3.6.0. - [Release notes](https://github.com/paulmillr/chokidar/releases) - [Commits](https://github.com/paulmillr/chokidar/compare/3.5.3...3.6.0) --- updated-dependencies: - dependency-name: chokidar dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/ql-vscode/package-lock.json | 17 +++++++---------- extensions/ql-vscode/package.json | 2 +- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index e144accece3..4a1125dcc62 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -19,7 +19,7 @@ "@vscode/webview-ui-toolkit": "^1.0.1", "ajv": "^8.11.0", "child-process-promise": "^2.2.1", - "chokidar": "^3.5.3", + "chokidar": "^3.6.0", "d3": "^7.6.1", "d3-graphviz": "^5.0.2", "fs-extra": "^11.1.1", @@ -12740,15 +12740,9 @@ } }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -12761,6 +12755,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 392b28410d5..a91aac7a4a3 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1947,7 +1947,7 @@ "@vscode/webview-ui-toolkit": "^1.0.1", "ajv": "^8.11.0", "child-process-promise": "^2.2.1", - "chokidar": "^3.5.3", + "chokidar": "^3.6.0", "d3": "^7.6.1", "d3-graphviz": "^5.0.2", "fs-extra": "^11.1.1", From 4b64e98e9f72cc7bf8c6374f88aecc90f4448eb9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 06:22:18 -0800 Subject: [PATCH 0023/1237] Bump @testing-library/react in /extensions/ql-vscode (#3390) Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 14.1.2 to 14.2.1. - [Release notes](https://github.com/testing-library/react-testing-library/releases) - [Changelog](https://github.com/testing-library/react-testing-library/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/react-testing-library/compare/v14.1.2...v14.2.1) --- updated-dependencies: - dependency-name: "@testing-library/react" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/ql-vscode/package-lock.json | 8 ++++---- extensions/ql-vscode/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 4a1125dcc62..81fdcc8963d 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -67,7 +67,7 @@ "@storybook/theming": "^7.6.12", "@testing-library/dom": "^9.3.4", "@testing-library/jest-dom": "^6.4.2", - "@testing-library/react": "^14.0.0", + "@testing-library/react": "^14.2.1", "@testing-library/user-event": "^14.5.2", "@types/child-process-promise": "^2.2.1", "@types/d3": "^7.4.0", @@ -8827,9 +8827,9 @@ } }, "node_modules/@testing-library/react": { - "version": "14.1.2", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.1.2.tgz", - "integrity": "sha512-z4p7DVBTPjKM5qDZ0t5ZjzkpSNb+fZy1u6bzO7kk8oeGagpPCAtgh4cx1syrfp7a+QWkM021jGqjJaxJJnXAZg==", + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.2.1.tgz", + "integrity": "sha512-sGdjws32ai5TLerhvzThYFbpnF9XtL65Cjf+gB0Dhr29BGqK+mAeN7SURSdu+eqgET4ANcWoC7FQpkaiGvBr+A==", "dev": true, "dependencies": { "@babel/runtime": "^7.12.5", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index a91aac7a4a3..b0512a2e848 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1995,7 +1995,7 @@ "@storybook/theming": "^7.6.12", "@testing-library/dom": "^9.3.4", "@testing-library/jest-dom": "^6.4.2", - "@testing-library/react": "^14.0.0", + "@testing-library/react": "^14.2.1", "@testing-library/user-event": "^14.5.2", "@types/child-process-promise": "^2.2.1", "@types/d3": "^7.4.0", From 5c305f96de4826c6f67a0edc364d28df0acfcee2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 06:22:45 -0800 Subject: [PATCH 0024/1237] Bump @faker-js/faker from 8.3.1 to 8.4.1 in /extensions/ql-vscode (#3391) Bumps [@faker-js/faker](https://github.com/faker-js/faker) from 8.3.1 to 8.4.1. - [Release notes](https://github.com/faker-js/faker/releases) - [Changelog](https://github.com/faker-js/faker/blob/next/CHANGELOG.md) - [Commits](https://github.com/faker-js/faker/compare/v8.3.1...v8.4.1) --- updated-dependencies: - dependency-name: "@faker-js/faker" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/ql-vscode/package-lock.json | 8 ++++---- extensions/ql-vscode/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 81fdcc8963d..fbee681c06f 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -50,7 +50,7 @@ "@babel/preset-env": "^7.23.9", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.4", - "@faker-js/faker": "^8.0.2", + "@faker-js/faker": "^8.4.1", "@github/markdownlint-github": "^0.6.0", "@octokit/plugin-throttling": "^8.0.0", "@playwright/test": "^1.40.1", @@ -2945,9 +2945,9 @@ } }, "node_modules/@faker-js/faker": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.3.1.tgz", - "integrity": "sha512-FdgpFxY6V6rLZE9mmIBb9hM0xpfvQOSNOLnzolzKwsE1DH+gC7lEKV1p1IbR0lAYyvYd5a4u3qWJzowUkw1bIw==", + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.4.1.tgz", + "integrity": "sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==", "dev": true, "funding": [ { diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index b0512a2e848..6cfa9174136 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1978,7 +1978,7 @@ "@babel/preset-env": "^7.23.9", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.4", - "@faker-js/faker": "^8.0.2", + "@faker-js/faker": "^8.4.1", "@github/markdownlint-github": "^0.6.0", "@octokit/plugin-throttling": "^8.0.0", "@playwright/test": "^1.40.1", From 98491fb1dc7cd957a87623a26e001d9eff35445c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 06:23:05 -0800 Subject: [PATCH 0025/1237] Bump @typescript-eslint/parser in /extensions/ql-vscode (#3392) Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 6.16.0 to 6.21.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.21.0/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/parser" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/ql-vscode/package-lock.json | 50 +++++++++++++------------- extensions/ql-vscode/package.json | 2 +- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index fbee681c06f..5357df18ca8 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -94,7 +94,7 @@ "@types/vscode": "^1.82.0", "@types/yauzl": "^2.10.3", "@typescript-eslint/eslint-plugin": "^6.19.0", - "@typescript-eslint/parser": "^6.16.0", + "@typescript-eslint/parser": "^6.21.0", "@vscode/test-electron": "^2.2.0", "@vscode/vsce": "^2.19.0", "ansi-colors": "^4.1.1", @@ -10135,15 +10135,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.16.0.tgz", - "integrity": "sha512-H2GM3eUo12HpKZU9njig3DF5zJ58ja6ahj1GoHEHOgQvYxzoFJJEvC1MQ7T2l9Ha+69ZSOn7RTxOdpC/y3ikMw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.16.0", - "@typescript-eslint/types": "6.16.0", - "@typescript-eslint/typescript-estree": "6.16.0", - "@typescript-eslint/visitor-keys": "6.16.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "engines": { @@ -10163,13 +10163,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.16.0.tgz", - "integrity": "sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.16.0", - "@typescript-eslint/visitor-keys": "6.16.0" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -10289,9 +10289,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.16.0.tgz", - "integrity": "sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -10302,13 +10302,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.16.0.tgz", - "integrity": "sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.16.0", - "@typescript-eslint/visitor-keys": "6.16.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -10478,12 +10478,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.16.0.tgz", - "integrity": "sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.16.0", + "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 6cfa9174136..6a96a927091 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -2022,7 +2022,7 @@ "@types/vscode": "^1.82.0", "@types/yauzl": "^2.10.3", "@typescript-eslint/eslint-plugin": "^6.19.0", - "@typescript-eslint/parser": "^6.16.0", + "@typescript-eslint/parser": "^6.21.0", "@vscode/test-electron": "^2.2.0", "@vscode/vsce": "^2.19.0", "ansi-colors": "^4.1.1", From 07d9c4efc432cc5cdef5b928a18f7b7d2c4b090c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 06:23:26 -0800 Subject: [PATCH 0026/1237] Bump @storybook/components from 7.6.7 to 7.6.17 in /extensions/ql-vscode (#3393) Bumps [@storybook/components](https://github.com/storybookjs/storybook/tree/HEAD/code/ui/components) from 7.6.7 to 7.6.17. - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v7.6.17/code/ui/components) --- updated-dependencies: - dependency-name: "@storybook/components" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/ql-vscode/package-lock.json | 78 ++++++++++---------------- extensions/ql-vscode/package.json | 2 +- 2 files changed, 30 insertions(+), 50 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 5357df18ca8..69daa843735 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -59,7 +59,7 @@ "@storybook/addon-essentials": "^7.1.0", "@storybook/addon-interactions": "^7.1.0", "@storybook/addon-links": "^7.1.0", - "@storybook/components": "^7.6.7", + "@storybook/components": "^7.6.17", "@storybook/csf": "^0.1.1", "@storybook/manager-api": "^7.6.7", "@storybook/react": "^7.1.0", @@ -6580,18 +6580,18 @@ } }, "node_modules/@storybook/components": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.6.7.tgz", - "integrity": "sha512-1HN4p+MCI4Tx9VGZayZyqbW7SB7mXQLnS5fUbTE1gXaMYHpzFvcrRNROeV1LZPClJX6qx1jgE5ngZojhxGuxMA==", + "version": "7.6.17", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.6.17.tgz", + "integrity": "sha512-lbh7GynMidA+CZcJnstVku6Nhs+YkqjYaZ+mKPugvlVhGVWv0DaaeQFVuZ8cJtUGJ/5FFU4Y+n+gylYUHkGBMA==", "dev": true, "dependencies": { "@radix-ui/react-select": "^1.2.2", "@radix-ui/react-toolbar": "^1.0.4", - "@storybook/client-logger": "7.6.7", + "@storybook/client-logger": "7.6.17", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/theming": "7.6.7", - "@storybook/types": "7.6.7", + "@storybook/theming": "7.6.17", + "@storybook/types": "7.6.17", "memoizerific": "^1.11.3", "use-resize-observer": "^9.1.0", "util-deprecate": "^1.0.2" @@ -6606,13 +6606,13 @@ } }, "node_modules/@storybook/components/node_modules/@storybook/channels": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.7.tgz", - "integrity": "sha512-u1hURhfQHHtZyRIDUENRCp+CRRm7IQfcjQaoWI06XCevQPuhVEtFUfXHjG+J74aA/JuuTLFUtqwNm1zGqbXTAQ==", + "version": "7.6.17", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.17.tgz", + "integrity": "sha512-GFG40pzaSxk1hUr/J/TMqW5AFDDPUSu+HkeE/oqSWJbOodBOLJzHN6CReJS6y1DjYSZLNFt1jftPWZZInG/XUA==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.7", - "@storybook/core-events": "7.6.7", + "@storybook/client-logger": "7.6.17", + "@storybook/core-events": "7.6.17", "@storybook/global": "^5.0.0", "qs": "^6.10.0", "telejson": "^7.2.0", @@ -6624,9 +6624,9 @@ } }, "node_modules/@storybook/components/node_modules/@storybook/client-logger": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.7.tgz", - "integrity": "sha512-A16zpWgsa0gSdXMR9P3bWVdC9u/1B1oG4H7Z1+JhNzgnL3CdyOYO0qFSiAtNBso4nOjIAJVb6/AoBzdRhmSVQg==", + "version": "7.6.17", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.17.tgz", + "integrity": "sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0" @@ -6637,9 +6637,9 @@ } }, "node_modules/@storybook/components/node_modules/@storybook/core-events": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.7.tgz", - "integrity": "sha512-KZ5d03c47pnr5/kY26pJtWq7WpmCPXLbgyjJZDSc+TTY153BdZksvlBXRHtqM1yj2UM6QsSyIuiJaADJNAbP2w==", + "version": "7.6.17", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.17.tgz", + "integrity": "sha512-AriWMCm/k1cxlv10f+jZ1wavThTRpLaN3kY019kHWbYT9XgaSuLU67G7GPr3cGnJ6HuA6uhbzu8qtqVCd6OfXA==", "dev": true, "dependencies": { "ts-dedent": "^2.0.0" @@ -6649,33 +6649,13 @@ "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/components/node_modules/@storybook/theming": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.7.tgz", - "integrity": "sha512-+42rfC4rZtWVAXJ7JBUQKnQ6vWBXJVHZ9HtNUWzQLPR9sJSMmHnnSMV6y5tizGgZqmBnAIkuoYk+Tt6NfwUmSA==", - "dev": true, - "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.7", - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/@storybook/components/node_modules/@storybook/types": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.7.tgz", - "integrity": "sha512-VcGwrI4AkBENxkoAUJ+Z7SyMK73hpoY0TTtw2J7tc05/xdiXhkQTX15Qa12IBWIkoXCyNrtaU+q7KR8Tjzi+uw==", + "version": "7.6.17", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.17.tgz", + "integrity": "sha512-GRY0xEJQ0PrL7DY2qCNUdIfUOE0Gsue6N+GBJw9ku1IUDFLJRDOF+4Dx2BvYcVCPI5XPqdWKlEyZdMdKjiQN7Q==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.7", + "@storybook/channels": "7.6.17", "@types/babel__core": "^7.0.0", "@types/express": "^4.7.0", "file-system-cache": "2.3.0" @@ -8397,13 +8377,13 @@ } }, "node_modules/@storybook/theming": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.12.tgz", - "integrity": "sha512-P4zoMKlSYbNrWJjQROuz+DZSDEpdf3TUvk203EqBRdElqw2EMHcqZ8+0HGPFfVHpqEj05+B9Mr6R/Z/BURj0lw==", + "version": "7.6.17", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.17.tgz", + "integrity": "sha512-ZbaBt3KAbmBtfjNqgMY7wPMBshhSJlhodyMNQypv+95xLD/R+Az6aBYbpVAOygLaUQaQk4ar7H/Ww6lFIoiFbA==", "dev": true, "dependencies": { "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.12", + "@storybook/client-logger": "7.6.17", "@storybook/global": "^5.0.0", "memoizerific": "^1.11.3" }, @@ -8417,9 +8397,9 @@ } }, "node_modules/@storybook/theming/node_modules/@storybook/client-logger": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.12.tgz", - "integrity": "sha512-hiRv6dXsOttMPqm9SxEuFoAtDe9rs7TUS8XcO5rmJ9BgfwBJsYlHzAxXkazxmvlyZtKL7gMx6m8OYbCdZgUqtA==", + "version": "7.6.17", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.17.tgz", + "integrity": "sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0" diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 6a96a927091..3499c3a2060 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1987,7 +1987,7 @@ "@storybook/addon-essentials": "^7.1.0", "@storybook/addon-interactions": "^7.1.0", "@storybook/addon-links": "^7.1.0", - "@storybook/components": "^7.6.7", + "@storybook/components": "^7.6.17", "@storybook/csf": "^0.1.1", "@storybook/manager-api": "^7.6.7", "@storybook/react": "^7.1.0", From e408c0ac95a50627e368d9fc87063cec5ec25a40 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 22 Feb 2024 16:43:56 +0100 Subject: [PATCH 0027/1237] Auto-generate type models for Ruby --- .../model-editor/languages/models-as-data.ts | 36 +++++--- .../src/model-editor/languages/ruby/index.ts | 22 +++++ .../src/model-editor/model-editor-view.ts | 85 +++++++++++++++++-- 3 files changed, 125 insertions(+), 18 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts b/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts index eb497128e37..c4f6c6a57b4 100644 --- a/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts +++ b/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts @@ -32,20 +32,34 @@ export type ModelsAsDataLanguagePredicate = { readModeledMethod: ReadModeledMethod; }; +type ParseGenerationResults = ( + // The path to the query that generated the results. + queryPath: string, + // The results of the query. + bqrs: DecodedBqrs, + // The language-specific predicate that was used to generate the results. This is passed to allow + // sharing of code between different languages. + modelsAsDataLanguage: ModelsAsDataLanguage, + // The logger to use for logging. + logger: BaseLogger, +) => ModeledMethod[]; + type ModelsAsDataLanguageModelGeneration = { queryConstraints: (mode: Mode) => QueryConstraints; filterQueries?: (queryPath: string) => boolean; - parseResults: ( - // The path to the query that generated the results. - queryPath: string, - // The results of the query. - bqrs: DecodedBqrs, - // The language-specific predicate that was used to generate the results. This is passed to allow - // sharing of code between different languages. - modelsAsDataLanguage: ModelsAsDataLanguage, - // The logger to use for logging. - logger: BaseLogger, - ) => ModeledMethod[]; + parseResults: ParseGenerationResults; + /** + * If autoRun is not undefined, the query will be run automatically when the user starts the + * model editor. + * + * This only applies to framework mode. Application mode will never run the query automatically. + */ + autoRun?: { + /** + * If defined, will use a custom parsing function when the query is run automatically. + */ + parseResults?: ParseGenerationResults; + }; }; type ModelsAsDataLanguageAccessPathSuggestions = { diff --git a/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts b/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts index ea19f3905bd..bd6c9dc11bc 100644 --- a/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts +++ b/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts @@ -177,6 +177,28 @@ export const ruby: ModelsAsDataLanguage = { "tags contain all": ["modeleditor", "generate-model", modeTag(mode)], }), parseResults: parseGenerateModelResults, + autoRun: { + parseResults: (queryPath, bqrs, modelsAsDataLanguage, logger) => { + // Only type models are generated automatically + const typePredicate = modelsAsDataLanguage.predicates.type; + if (!typePredicate) { + throw new Error("Type predicate not found"); + } + + const filteredBqrs = Object.fromEntries( + Object.entries(bqrs).filter( + ([key]) => key === typePredicate.extensiblePredicate, + ), + ); + + return parseGenerateModelResults( + queryPath, + filteredBqrs, + modelsAsDataLanguage, + logger, + ); + }, + }, }, accessPathSuggestions: { queryConstraints: (mode) => ({ diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 0456c375137..98fc83b740b 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -64,6 +64,9 @@ export class ModelEditorView extends AbstractWebview< > { private readonly autoModeler: AutoModeler; private readonly languageDefinition: ModelsAsDataLanguage; + // Cancellation token source that can be used for passing into long-running operations. Should only + // be cancelled when the view is closed + private readonly cancellationTokenSource = new CancellationTokenSource(); public constructor( protected readonly app: App, @@ -83,6 +86,12 @@ export class ModelEditorView extends AbstractWebview< ) { super(app); + this.push({ + dispose: () => { + this.cancellationTokenSource.cancel(); + }, + }); + this.modelingStore.initializeStateForDb(databaseItem, initialMode); this.registerToModelingEvents(); this.registerToModelConfigEvents(); @@ -367,6 +376,8 @@ export class ModelEditorView extends AbstractWebview< this.setViewState(), withProgress((progress, token) => this.loadMethods(progress, token), { cancellable: true, + }).then(async () => { + await this.generateModeledMethodsOnStartup(); }), this.loadExistingModeledMethods(), // Only load access path suggestions if the feature is enabled @@ -471,7 +482,7 @@ export class ModelEditorView extends AbstractWebview< try { if (!token) { - token = new CancellationTokenSource().token; + token = this.cancellationTokenSource.token; } const queryResult = await runModelEditorQueries(mode, { cliServer: this.cliServer, @@ -522,8 +533,6 @@ export class ModelEditorView extends AbstractWebview< protected async loadAccessPathSuggestions( progress: ProgressCallback, ): Promise { - const tokenSource = new CancellationTokenSource(); - const mode = this.modelingStore.getMode(this.databaseItem); const modelsAsDataLanguage = getModelsAsDataLanguage(this.language); @@ -546,7 +555,7 @@ export class ModelEditorView extends AbstractWebview< queryStorageDir: this.queryStorageDir, databaseItem: this.databaseItem, progress, - token: tokenSource.token, + token: this.cancellationTokenSource.token, logger: this.app.logger, }); @@ -577,8 +586,6 @@ export class ModelEditorView extends AbstractWebview< protected async generateModeledMethods(): Promise { await withProgress( async (progress) => { - const tokenSource = new CancellationTokenSource(); - const mode = this.modelingStore.getMode(this.databaseItem); const modelsAsDataLanguage = getModelsAsDataLanguage(this.language); @@ -636,7 +643,7 @@ export class ModelEditorView extends AbstractWebview< queryStorageDir: this.queryStorageDir, databaseItem: addedDatabase ?? this.databaseItem, progress, - token: tokenSource.token, + token: this.cancellationTokenSource.token, }); } catch (e: unknown) { void showAndLogExceptionWithTelemetry( @@ -652,6 +659,70 @@ export class ModelEditorView extends AbstractWebview< ); } + protected async generateModeledMethodsOnStartup(): Promise { + const mode = this.modelingStore.getMode(this.databaseItem); + if (mode !== Mode.Framework) { + return; + } + + const modelsAsDataLanguage = getModelsAsDataLanguage(this.language); + const modelGeneration = modelsAsDataLanguage.modelGeneration; + const autoRun = modelGeneration?.autoRun; + + if (modelGeneration === undefined || autoRun === undefined) { + return; + } + + await withProgress( + async (progress) => { + progress({ + step: 0, + maxStep: 4000, + message: "Generating models", + }); + + const parseResults = + autoRun.parseResults ?? modelGeneration.parseResults; + + try { + await runGenerateQueries({ + queryConstraints: modelGeneration.queryConstraints(mode), + filterQueries: modelGeneration.filterQueries, + parseResults: (queryPath, results) => + parseResults( + queryPath, + results, + modelsAsDataLanguage, + this.app.logger, + ), + onResults: async (modeledMethods) => { + this.addModeledMethodsFromArray(modeledMethods); + }, + cliServer: this.cliServer, + queryRunner: this.queryRunner, + queryStorageDir: this.queryStorageDir, + databaseItem: this.databaseItem, + progress, + token: this.cancellationTokenSource.token, + }); + } catch (e: unknown) { + void showAndLogExceptionWithTelemetry( + this.app.logger, + this.app.telemetry, + redactableError( + asError(e), + )`Failed to auto-run generating models: ${getErrorMessage(e)}`, + ); + } + }, + { + cancellable: false, + location: ProgressLocation.Window, + title: "Generating models", + }, + ); + } + private async generateModeledMethodsFromLlm( packageName: string, methodSignatures: string[], From 8832655c2fde06ef1ec1440c41a0ef2986d15624 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 22 Feb 2024 12:46:07 +0000 Subject: [PATCH 0028/1237] Add modifiedMethodSignatures to ModeledMethodsChangedEvent --- .../method-modeling-view-provider.ts | 7 +++++++ .../src/model-editor/model-editor-view.ts | 7 +++++++ .../src/model-editor/modeling-events.ts | 3 +++ .../ql-vscode/src/model-editor/modeling-store.ts | 16 ++++------------ 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts index 7a4b9905220..7e70231df1f 100644 --- a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts +++ b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts @@ -169,6 +169,13 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< modeledMethods, }); } + + if (e.modifiedMethodSignatures !== undefined) { + await this.postMessage({ + t: "setMethodModified", + isModified: e.modifiedMethodSignatures.has(this.method.signature), + }); + } } }), ); diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 838cf945b8a..833d0d3ef02 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -824,6 +824,13 @@ export class ModelEditorView extends AbstractWebview< t: "setModeledMethods", methods: event.modeledMethods, }); + + if (event.modifiedMethodSignatures !== undefined) { + await this.postMessage({ + t: "setModifiedMethods", + methodSignatures: [...event.modifiedMethodSignatures], + }); + } } }), ); diff --git a/extensions/ql-vscode/src/model-editor/modeling-events.ts b/extensions/ql-vscode/src/model-editor/modeling-events.ts index 8b99f726a41..7853b7dc68a 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-events.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-events.ts @@ -25,6 +25,7 @@ interface ModeChangedEvent { interface ModeledMethodsChangedEvent { readonly modeledMethods: Readonly>; + readonly modifiedMethodSignatures: ReadonlySet; readonly dbUri: string; readonly isActiveDb: boolean; } @@ -211,11 +212,13 @@ export class ModelingEvents extends DisposableObject { public fireModeledMethodsChangedEvent( modeledMethods: Record, + modifiedMethodSignatures: ReadonlySet, dbUri: string, isActiveDb: boolean, ) { this.onModeledMethodsChangedEventEmitter.fire({ modeledMethods, + modifiedMethodSignatures, dbUri, isActiveDb, }); diff --git a/extensions/ql-vscode/src/model-editor/modeling-store.ts b/extensions/ql-vscode/src/model-editor/modeling-store.ts index 49dffde8f46..c6eb96b7978 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-store.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-store.ts @@ -216,7 +216,7 @@ export class ModelingStore extends DisposableObject { methods: Record, setModified: boolean, ) { - this.changeModeledMethods(dbItem, setModified, (state) => { + this.changeModeledMethods(dbItem, (state) => { const newModeledMethods = { ...methods, // Keep all methods that are already modeled in some form in the state @@ -242,7 +242,7 @@ export class ModelingStore extends DisposableObject { dbItem: DatabaseItem, methods: Record, ) { - this.changeModeledMethods(dbItem, false, (state) => { + this.changeModeledMethods(dbItem, (state) => { state.modeledMethods = { ...methods }; }); } @@ -253,7 +253,7 @@ export class ModelingStore extends DisposableObject { modeledMethods: ModeledMethod[], setModified: boolean, ) { - this.changeModeledMethods(dbItem, setModified, (state) => { + this.changeModeledMethods(dbItem, (state) => { const newModeledMethods = { ...state.modeledMethods }; newModeledMethods[signature] = modeledMethods; state.modeledMethods = newModeledMethods; @@ -417,7 +417,6 @@ export class ModelingStore extends DisposableObject { private changeModeledMethods( dbItem: DatabaseItem, - modifiedMethodsChanged: boolean, updateState: (state: InternalDbModelingState) => void, ) { const state = this.getState(dbItem); @@ -426,17 +425,10 @@ export class ModelingStore extends DisposableObject { this.modelingEvents.fireModeledMethodsChangedEvent( state.modeledMethods, + state.modifiedMethodSignatures, dbItem.databaseUri.toString(), dbItem.databaseUri.toString() === this.activeDb, ); - - if (modifiedMethodsChanged) { - this.modelingEvents.fireModifiedMethodsChangedEvent( - state.modifiedMethodSignatures, - dbItem.databaseUri.toString(), - dbItem.databaseUri.toString() === this.activeDb, - ); - } } private changeInProgressMethods( From 5ce09e6ccc8ea8514035447bd5d617b36ed867e0 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 22 Feb 2024 12:51:09 +0000 Subject: [PATCH 0029/1237] Include modifiedMethodSignatures in SetModeledMethodsMessage --- extensions/ql-vscode/src/common/interface-types.ts | 1 + .../ql-vscode/src/model-editor/model-editor-view.ts | 8 +------- .../ql-vscode/src/view/model-editor/ModelEditor.tsx | 1 + 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/extensions/ql-vscode/src/common/interface-types.ts b/extensions/ql-vscode/src/common/interface-types.ts index f104f50b727..c19291ffbb3 100644 --- a/extensions/ql-vscode/src/common/interface-types.ts +++ b/extensions/ql-vscode/src/common/interface-types.ts @@ -531,6 +531,7 @@ interface SetMethodsMessage { interface SetModeledMethodsMessage { t: "setModeledMethods"; methods: Record; + modifiedMethodSignatures: string[]; } interface SetModifiedMethodsMessage { diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 833d0d3ef02..e895c5660b6 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -823,14 +823,8 @@ export class ModelEditorView extends AbstractWebview< await this.postMessage({ t: "setModeledMethods", methods: event.modeledMethods, + modifiedMethodSignatures: [...event.modifiedMethodSignatures], }); - - if (event.modifiedMethodSignatures !== undefined) { - await this.postMessage({ - t: "setModifiedMethods", - methodSignatures: [...event.modifiedMethodSignatures], - }); - } } }), ); diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx index 004bc1fc695..8f419d1dfbc 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx @@ -196,6 +196,7 @@ export function ModelEditor({ break; case "setModeledMethods": setModeledMethods(msg.methods); + setModifiedSignatures(new Set(msg.modifiedMethodSignatures)); break; case "setModifiedMethods": setModifiedSignatures(new Set(msg.methodSignatures)); From 948c1e2eb7f17cb7180051a6ac004a94fa51b2d8 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Thu, 22 Feb 2024 16:28:32 +0000 Subject: [PATCH 0030/1237] Add model evaluation run to store and view (#3385) --- .../ql-vscode/src/common/interface-types.ts | 9 ++- .../src/model-editor/model-editor-view.ts | 26 +++++-- .../src/model-editor/model-evaluation-run.ts | 4 + .../src/model-editor/model-evaluator.ts | 73 +++++++++++++++++++ .../src/model-editor/modeling-events.ts | 24 ++++++ .../src/model-editor/modeling-store.ts | 27 +++++++ .../shared/model-evaluation-run-state.ts | 19 +++++ .../src/view/model-editor/ModelEditor.tsx | 19 +++-- .../model-editor/modelingEventsMock.ts | 3 + 9 files changed, 188 insertions(+), 16 deletions(-) create mode 100644 extensions/ql-vscode/src/model-editor/model-evaluation-run.ts create mode 100644 extensions/ql-vscode/src/model-editor/model-evaluator.ts create mode 100644 extensions/ql-vscode/src/model-editor/shared/model-evaluation-run-state.ts diff --git a/extensions/ql-vscode/src/common/interface-types.ts b/extensions/ql-vscode/src/common/interface-types.ts index f104f50b727..87bfc8fd976 100644 --- a/extensions/ql-vscode/src/common/interface-types.ts +++ b/extensions/ql-vscode/src/common/interface-types.ts @@ -25,6 +25,7 @@ import type { UrlValueResolvable, } from "./raw-result-types"; import type { AccessPathSuggestionOptions } from "../model-editor/suggestions"; +import type { ModelEvaluationRunState } from "../model-editor/shared/model-evaluation-run-state"; /** * This module contains types and code that are shared between @@ -638,6 +639,11 @@ interface SetAccessPathSuggestionsMessage { accessPathSuggestions: AccessPathSuggestionOptions; } +interface SetModelEvaluationRunMessage { + t: "setModelEvaluationRun"; + run: ModelEvaluationRunState | undefined; +} + export type ToModelEditorMessage = | SetExtensionPackStateMessage | SetMethodsMessage @@ -646,7 +652,8 @@ export type ToModelEditorMessage = | SetInProgressMethodsMessage | SetProcessedByAutoModelMethodsMessage | RevealMethodMessage - | SetAccessPathSuggestionsMessage; + | SetAccessPathSuggestionsMessage + | SetModelEvaluationRunMessage; export type FromModelEditorMessage = | CommonFromViewMessages diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 0456c375137..647eb2d3828 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -57,12 +57,15 @@ import { LSPErrorCodes } from "vscode-languageclient"; import type { AccessPathSuggestionOptions } from "./suggestions"; import { runSuggestionsQuery } from "./suggestion-queries"; import { parseAccessPathSuggestionRowsToOptions } from "./suggestions-bqrs"; +import { ModelEvaluator } from "./model-evaluator"; +import type { ModelEvaluationRunState } from "./shared/model-evaluation-run-state"; export class ModelEditorView extends AbstractWebview< ToModelEditorMessage, FromModelEditorMessage > { private readonly autoModeler: AutoModeler; + private readonly modelEvaluator: ModelEvaluator; private readonly languageDefinition: ModelsAsDataLanguage; public constructor( @@ -101,6 +104,14 @@ export class ModelEditorView extends AbstractWebview< }, ); this.languageDefinition = getModelsAsDataLanguage(language); + + this.modelEvaluator = new ModelEvaluator( + modelingStore, + modelingEvents, + databaseItem, + this.updateModelEvaluationRun.bind(this), + ); + this.push(this.modelEvaluator); } public async openView() { @@ -338,10 +349,10 @@ export class ModelEditorView extends AbstractWebview< break; } case "startModelEvaluation": - this.startModelEvaluation(); + await this.modelEvaluator.startEvaluation(); break; case "stopModelEvaluation": - this.stopModelEvaluation(); + await this.modelEvaluator.stopEvaluation(); break; case "telemetry": telemetryListener?.sendUIInteraction(msg.action); @@ -920,11 +931,10 @@ export class ModelEditorView extends AbstractWebview< this.modelingStore.addModifiedMethod(this.databaseItem, signature); } - private startModelEvaluation() { - // Do nothing for now. This will be fleshed out in the near future. - } - - private stopModelEvaluation() { - // Do nothing for now. This will be fleshed out in the near future. + private async updateModelEvaluationRun(run: ModelEvaluationRunState) { + await this.postMessage({ + t: "setModelEvaluationRun", + run, + }); } } diff --git a/extensions/ql-vscode/src/model-editor/model-evaluation-run.ts b/extensions/ql-vscode/src/model-editor/model-evaluation-run.ts new file mode 100644 index 00000000000..b944e086153 --- /dev/null +++ b/extensions/ql-vscode/src/model-editor/model-evaluation-run.ts @@ -0,0 +1,4 @@ +export interface ModelEvaluationRun { + isPreparing: boolean; + variantAnalysisId: number | undefined; +} diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts new file mode 100644 index 00000000000..d3920cdb238 --- /dev/null +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -0,0 +1,73 @@ +import type { ModelingStore } from "./modeling-store"; +import type { ModelingEvents } from "./modeling-events"; +import type { DatabaseItem } from "../databases/local-databases"; +import type { ModelEvaluationRun } from "./model-evaluation-run"; +import { DisposableObject } from "../common/disposable-object"; +import { sleep } from "../common/time"; +import type { ModelEvaluationRunState } from "./shared/model-evaluation-run-state"; + +export class ModelEvaluator extends DisposableObject { + public constructor( + private readonly modelingStore: ModelingStore, + private readonly modelingEvents: ModelingEvents, + private readonly dbItem: DatabaseItem, + private readonly updateView: ( + run: ModelEvaluationRunState, + ) => Promise, + ) { + super(); + + this.registerToModelingEvents(); + } + + public async startEvaluation() { + // Update store with evaluation run status + const evaluationRun: ModelEvaluationRun = { + isPreparing: true, + variantAnalysisId: undefined, + }; + this.modelingStore.updateModelEvaluationRun(this.dbItem, evaluationRun); + + // For now, just wait 5 seconds and then update the store. + // In the future, this will be replaced with the actual evaluation process. + void sleep(5000).then(() => { + const completedEvaluationRun: ModelEvaluationRun = { + isPreparing: false, + variantAnalysisId: undefined, + }; + this.modelingStore.updateModelEvaluationRun( + this.dbItem, + completedEvaluationRun, + ); + }); + } + + public async stopEvaluation() { + // For now just update the store. + // This will be fleshed out in the near future. + const evaluationRun: ModelEvaluationRun = { + isPreparing: false, + variantAnalysisId: undefined, + }; + this.modelingStore.updateModelEvaluationRun(this.dbItem, evaluationRun); + } + + private registerToModelingEvents() { + this.push( + this.modelingEvents.onModelEvaluationRunChanged(async (event) => { + if ( + event.evaluationRun && + event.dbUri === this.dbItem.databaseUri.toString() + ) { + const run: ModelEvaluationRunState = { + isPreparing: event.evaluationRun.isPreparing, + + // TODO: Get variant analysis from id. + variantAnalysis: undefined, + }; + await this.updateView(run); + } + }), + ); + } +} diff --git a/extensions/ql-vscode/src/model-editor/modeling-events.ts b/extensions/ql-vscode/src/model-editor/modeling-events.ts index 8b99f726a41..e1ccb448694 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-events.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-events.ts @@ -3,6 +3,7 @@ import { DisposableObject } from "../common/disposable-object"; import type { AppEvent, AppEventEmitter } from "../common/events"; import type { DatabaseItem } from "../databases/local-databases"; import type { Method, Usage } from "./method"; +import type { ModelEvaluationRun } from "./model-evaluation-run"; import type { ModeledMethod } from "./modeled-method"; import type { Mode } from "./shared/mode"; @@ -55,6 +56,11 @@ interface ProcessedByAutoModelMethodsChangedEvent { readonly methods: ReadonlySet; } +interface ModelEvaluationRunChangedEvent { + readonly dbUri: string; + readonly evaluationRun: ModelEvaluationRun | undefined; +} + interface RevealInModelEditorEvent { dbUri: string; method: Method; @@ -76,6 +82,7 @@ export class ModelingEvents extends DisposableObject { public readonly onSelectedMethodChanged: AppEvent; public readonly onInProgressMethodsChanged: AppEvent; public readonly onProcessedByAutoModelMethodsChanged: AppEvent; + public readonly onModelEvaluationRunChanged: AppEvent; public readonly onRevealInModelEditor: AppEvent; public readonly onFocusModelEditor: AppEvent; @@ -90,6 +97,7 @@ export class ModelingEvents extends DisposableObject { private readonly onSelectedMethodChangedEventEmitter: AppEventEmitter; private readonly onInProgressMethodsChangedEventEmitter: AppEventEmitter; private readonly onProcessedByAutoModelMethodsChangedEventEmitter: AppEventEmitter; + private readonly onModelEvaluationRunChangedEventEmitter: AppEventEmitter; private readonly onRevealInModelEditorEventEmitter: AppEventEmitter; private readonly onFocusModelEditorEventEmitter: AppEventEmitter; @@ -155,6 +163,12 @@ export class ModelingEvents extends DisposableObject { this.onProcessedByAutoModelMethodsChanged = this.onProcessedByAutoModelMethodsChangedEventEmitter.event; + this.onModelEvaluationRunChangedEventEmitter = this.push( + app.createEventEmitter(), + ); + this.onModelEvaluationRunChanged = + this.onModelEvaluationRunChangedEventEmitter.event; + this.onRevealInModelEditorEventEmitter = this.push( app.createEventEmitter(), ); @@ -273,6 +287,16 @@ export class ModelingEvents extends DisposableObject { }); } + public fireModelEvaluationRunChangedEvent( + dbUri: string, + evaluationRun: ModelEvaluationRun | undefined, + ) { + this.onModelEvaluationRunChangedEventEmitter.fire({ + dbUri, + evaluationRun, + }); + } + public fireRevealInModelEditorEvent(dbUri: string, method: Method) { this.onRevealInModelEditorEventEmitter.fire({ dbUri, diff --git a/extensions/ql-vscode/src/model-editor/modeling-store.ts b/extensions/ql-vscode/src/model-editor/modeling-store.ts index 11317d158c2..a9fda24f698 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-store.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-store.ts @@ -1,6 +1,7 @@ import { DisposableObject } from "../common/disposable-object"; import type { DatabaseItem } from "../databases/local-databases"; import type { Method, Usage } from "./method"; +import type { ModelEvaluationRun } from "./model-evaluation-run"; import type { ModeledMethod } from "./modeled-method"; import type { ModelingEvents } from "./modeling-events"; import { INITIAL_HIDE_MODELED_METHODS_VALUE } from "./shared/hide-modeled-methods"; @@ -17,6 +18,7 @@ interface InternalDbModelingState { processedByAutoModelMethods: Set; selectedMethod: Method | undefined; selectedUsage: Usage | undefined; + modelEvaluationRun: ModelEvaluationRun | undefined; } interface DbModelingState { @@ -30,6 +32,7 @@ interface DbModelingState { readonly processedByAutoModelMethods: ReadonlySet; readonly selectedMethod: Method | undefined; readonly selectedUsage: Usage | undefined; + readonly modelEvaluationRun: ModelEvaluationRun | undefined; } interface SelectedMethodDetails { @@ -66,6 +69,7 @@ export class ModelingStore extends DisposableObject { selectedMethod: undefined, selectedUsage: undefined, inProgressMethods: new Set(), + modelEvaluationRun: undefined, }); this.modelingEvents.fireDbOpenedEvent(databaseItem); @@ -372,6 +376,15 @@ export class ModelingStore extends DisposableObject { }); } + public updateModelEvaluationRun( + dbItem: DatabaseItem, + evaluationRun: ModelEvaluationRun, + ) { + this.changeModelEvaluationRun(dbItem, (state) => { + state.modelEvaluationRun = evaluationRun; + }); + } + public getSelectedMethodDetails(): SelectedMethodDetails | undefined { const dbState = this.getInternalStateForActiveDb(); if (!dbState) { @@ -465,4 +478,18 @@ export class ModelingStore extends DisposableObject { state.processedByAutoModelMethods, ); } + + private changeModelEvaluationRun( + dbItem: DatabaseItem, + updateState: (state: InternalDbModelingState) => void, + ) { + const state = this.getState(dbItem); + + updateState(state); + + this.modelingEvents.fireModelEvaluationRunChangedEvent( + dbItem.databaseUri.toString(), + state.modelEvaluationRun, + ); + } } diff --git a/extensions/ql-vscode/src/model-editor/shared/model-evaluation-run-state.ts b/extensions/ql-vscode/src/model-editor/shared/model-evaluation-run-state.ts new file mode 100644 index 00000000000..52087c1a6df --- /dev/null +++ b/extensions/ql-vscode/src/model-editor/shared/model-evaluation-run-state.ts @@ -0,0 +1,19 @@ +import { VariantAnalysisStatus } from "../../variant-analysis/shared/variant-analysis"; +import type { VariantAnalysis } from "../../variant-analysis/shared/variant-analysis"; + +export interface ModelEvaluationRunState { + isPreparing: boolean; + variantAnalysis: VariantAnalysis | undefined; +} + +export function modelEvaluationRunIsRunning( + run: ModelEvaluationRunState, +): boolean { + return ( + run.isPreparing || + !!( + run.variantAnalysis && + run.variantAnalysis.status === VariantAnalysisStatus.InProgress + ) + ); +} diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx index 004bc1fc695..64ad0b6a3dc 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx @@ -20,6 +20,8 @@ import { Mode } from "../../model-editor/shared/mode"; import { getLanguageDisplayName } from "../../common/query-language"; import { INITIAL_HIDE_MODELED_METHODS_VALUE } from "../../model-editor/shared/hide-modeled-methods"; import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions"; +import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state"; +import { modelEvaluationRunIsRunning } from "../../model-editor/shared/model-evaluation-run-state"; const LoadingContainer = styled.div` text-align: center; @@ -87,20 +89,20 @@ const ModelEvaluation = ({ modifiedSignatures, onStartEvaluation, onStopEvaluation, - evaluationInProgress, + evaluationRun, }: { viewState: ModelEditorViewState; modeledMethods: Record; modifiedSignatures: Set; onStartEvaluation: () => void; onStopEvaluation: () => void; - evaluationInProgress: boolean; + evaluationRun: ModelEvaluationRunState | undefined; }) => { if (!viewState.showEvaluationUi) { return null; } - if (!evaluationInProgress) { + if (!evaluationRun || !modelEvaluationRunIsRunning(evaluationRun)) { const customModelsExist = Object.values(modeledMethods).some( (methods) => methods.filter((m) => m.type !== "none").length > 0, ); @@ -166,7 +168,9 @@ export function ModelEditor({ string | null >(null); - const [evaluationInProgress, setEvaluationInProgress] = useState(false); + const [evaluationRun, setEvaluationRun] = useState< + ModelEvaluationRunState | undefined + >(undefined); useEffect(() => { vscode.postMessage({ @@ -214,6 +218,9 @@ export function ModelEditor({ case "setAccessPathSuggestions": setAccessPathSuggestions(msg.accessPathSuggestions); break; + case "setModelEvaluationRun": + setEvaluationRun(msg.run); + break; default: assertNever(msg); } @@ -309,14 +316,12 @@ export function ModelEditor({ ); const onStartEvaluation = useCallback(() => { - setEvaluationInProgress(true); vscode.postMessage({ t: "startModelEvaluation", }); }, []); const onStopEvaluation = useCallback(() => { - setEvaluationInProgress(false); vscode.postMessage({ t: "stopModelEvaluation", }); @@ -447,7 +452,7 @@ export function ModelEditor({ modifiedSignatures={modifiedSignatures} onStartEvaluation={onStartEvaluation} onStopEvaluation={onStopEvaluation} - evaluationInProgress={evaluationInProgress} + evaluationRun={evaluationRun} /> diff --git a/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts b/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts index 3444f946ccc..763d9720be9 100644 --- a/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts +++ b/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts @@ -13,6 +13,7 @@ export function createMockModelingEvents({ onProcessedByAutoModelMethodsChanged = jest.fn(), onRevealInModelEditor = jest.fn(), onFocusModelEditor = jest.fn(), + onModelEvaluationRunChanged = jest.fn(), }: { onActiveDbChanged?: ModelingEvents["onActiveDbChanged"]; onDbClosed?: ModelingEvents["onDbClosed"]; @@ -25,6 +26,7 @@ export function createMockModelingEvents({ onProcessedByAutoModelMethodsChanged?: ModelingEvents["onProcessedByAutoModelMethodsChanged"]; onRevealInModelEditor?: ModelingEvents["onRevealInModelEditor"]; onFocusModelEditor?: ModelingEvents["onFocusModelEditor"]; + onModelEvaluationRunChanged?: ModelingEvents["onModelEvaluationRunChanged"]; } = {}): ModelingEvents { return mockedObject({ onActiveDbChanged, @@ -38,5 +40,6 @@ export function createMockModelingEvents({ onProcessedByAutoModelMethodsChanged, onRevealInModelEditor, onFocusModelEditor, + onModelEvaluationRunChanged, }); } From ff685f0233825125824fe83cb6f425c0236af8d6 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 22 Feb 2024 17:07:09 +0000 Subject: [PATCH 0031/1237] Move all sorting to the modeling store, and then all views preserve the sorting --- .../methods-usage-data-provider.ts | 33 ++----------- .../methods-usage/methods-usage-panel.ts | 3 -- .../src/model-editor/model-editor-view.ts | 2 + .../src/model-editor/modeling-store.ts | 46 +++++++++++++++---- .../shared/auto-model-candidates.ts | 9 +--- .../src/model-editor/shared/sorting.ts | 4 ++ .../model-editor/ModeledMethodDataGrid.tsx | 16 +------ .../methods-usage-data-provider.test.ts | 19 -------- .../methods-usage/methods-usage-panel.test.ts | 5 -- 9 files changed, 50 insertions(+), 87 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-data-provider.ts b/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-data-provider.ts index 23d2e2726b8..78c72d6bb6f 100644 --- a/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-data-provider.ts +++ b/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-data-provider.ts @@ -16,7 +16,7 @@ import { INITIAL_HIDE_MODELED_METHODS_VALUE } from "../shared/hide-modeled-metho import { getModelingStatus } from "../shared/modeling-status"; import { assertNever } from "../../common/helpers-pure"; import type { ModeledMethod } from "../modeled-method"; -import { groupMethods, sortGroupNames, sortMethods } from "../shared/sorting"; +import { groupMethods, sortGroupNames } from "../shared/sorting"; import type { Mode } from "../shared/mode"; import { INITIAL_MODE } from "../shared/mode"; import type { UrlValueResolvable } from "../../common/raw-result-types"; @@ -63,7 +63,6 @@ export class MethodsUsageDataProvider mode: Mode, modeledMethods: Readonly>, modifiedMethodSignatures: ReadonlySet, - processedByAutoModelMethods: ReadonlySet, ): Promise { if ( this.methods !== methods || @@ -75,13 +74,7 @@ export class MethodsUsageDataProvider ) { this.methods = methods; this.sortedTreeItems = createTreeItems( - sortMethodsInGroups( - methods, - modeledMethods, - mode, - modifiedMethodSignatures, - processedByAutoModelMethods, - ), + sortMethodsInGroups(methods, mode), ); this.databaseItem = databaseItem; this.sourceLocationPrefix = @@ -253,27 +246,9 @@ function urlValueResolvablesAreEqual( return false; } -function sortMethodsInGroups( - methods: readonly Method[], - modeledMethods: Readonly>, - mode: Mode, - modifiedMethodSignatures: ReadonlySet, - processedByAutoModelMethods: ReadonlySet, -): Method[] { +function sortMethodsInGroups(methods: readonly Method[], mode: Mode): Method[] { const grouped = groupMethods(methods, mode); - - const sortedGroupNames = sortGroupNames(grouped); - - return sortedGroupNames.flatMap((groupName) => { - const group = grouped[groupName]; - - return sortMethods( - group, - modeledMethods, - modifiedMethodSignatures, - processedByAutoModelMethods, - ); - }); + return sortGroupNames(grouped).flatMap((groupName) => grouped[groupName]); } function createTreeItems(methods: readonly Method[]): MethodTreeViewItem[] { diff --git a/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-panel.ts b/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-panel.ts index 1180939abff..7d8eed02be3 100644 --- a/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-panel.ts +++ b/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-panel.ts @@ -39,7 +39,6 @@ export class MethodsUsagePanel extends DisposableObject { mode: Mode, modeledMethods: Readonly>, modifiedMethodSignatures: ReadonlySet, - processedByAutoModelMethods: ReadonlySet, ): Promise { await this.dataProvider.setState( methods, @@ -48,7 +47,6 @@ export class MethodsUsagePanel extends DisposableObject { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); const numOfApis = hideModeledMethods ? methods.filter((api) => !api.supported).length @@ -122,7 +120,6 @@ export class MethodsUsagePanel extends DisposableObject { activeState.mode, activeState.modeledMethods, activeState.modifiedMethodSignatures, - activeState.processedByAutoModelMethods, ); } } diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 647eb2d3828..757e58bdb05 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -273,6 +273,8 @@ export class ModelEditorView extends AbstractWebview< Object.keys(modeledMethods), ); + this.modelingStore.updateMethodSorting(this.databaseItem); + void telemetryListener?.sendUIInteraction( "model-editor-save-modeled-methods", ); diff --git a/extensions/ql-vscode/src/model-editor/modeling-store.ts b/extensions/ql-vscode/src/model-editor/modeling-store.ts index a9fda24f698..7267415ff49 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-store.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-store.ts @@ -6,6 +6,7 @@ import type { ModeledMethod } from "./modeled-method"; import type { ModelingEvents } from "./modeling-events"; import { INITIAL_HIDE_MODELED_METHODS_VALUE } from "./shared/hide-modeled-methods"; import type { Mode } from "./shared/mode"; +import { sortMethods } from "./shared/sorting"; interface InternalDbModelingState { databaseItem: DatabaseItem; @@ -155,17 +156,25 @@ export class ModelingStore extends DisposableObject { } public setMethods(dbItem: DatabaseItem, methods: Method[]) { - const dbState = this.getState(dbItem); - const dbUri = dbItem.databaseUri.toString(); - - dbState.methods = [...methods]; + this.changeMethods(dbItem, (state) => { + state.methods = sortMethods( + methods, + state.modeledMethods, + state.modifiedMethodSignatures, + state.processedByAutoModelMethods, + ); + }); + } - this.modelingEvents.fireMethodsChangedEvent( - methods, - dbUri, - dbItem, - dbUri === this.activeDb, - ); + public updateMethodSorting(dbItem: DatabaseItem) { + this.changeMethods(dbItem, (state) => { + state.methods = sortMethods( + state.methods, + state.modeledMethods, + state.modifiedMethodSignatures, + state.processedByAutoModelMethods, + ); + }); } public setHideModeledMethods( @@ -374,6 +383,7 @@ export class ModelingStore extends DisposableObject { ...processedByAutoModelMethods, ]); }); + this.updateMethodSorting(dbItem); } public updateModelEvaluationRun( @@ -421,6 +431,22 @@ export class ModelingStore extends DisposableObject { return this.state.get(databaseItem.databaseUri.toString())!; } + private changeMethods( + dbItem: DatabaseItem, + updateState: (state: InternalDbModelingState) => void, + ) { + const state = this.getState(dbItem); + + updateState(state); + + this.modelingEvents.fireMethodsChangedEvent( + state.methods, + dbItem.databaseUri.toString(), + dbItem, + dbItem.databaseUri.toString() === this.activeDb, + ); + } + private changeModifiedMethods( dbItem: DatabaseItem, updateState: (state: InternalDbModelingState) => void, diff --git a/extensions/ql-vscode/src/model-editor/shared/auto-model-candidates.ts b/extensions/ql-vscode/src/model-editor/shared/auto-model-candidates.ts index 7653f4c2c0a..879be09505d 100644 --- a/extensions/ql-vscode/src/model-editor/shared/auto-model-candidates.ts +++ b/extensions/ql-vscode/src/model-editor/shared/auto-model-candidates.ts @@ -1,7 +1,7 @@ import type { Method, MethodSignature } from "../method"; import type { ModeledMethod } from "../modeled-method"; import type { Mode } from "./mode"; -import { groupMethods, sortGroupNames, sortMethods } from "./sorting"; +import { groupMethods, sortGroupNames } from "./sorting"; /** * Return the candidates that the model should be run on. This includes limiting the number of @@ -44,10 +44,5 @@ export function getCandidates( // Sort the same way as the UI so we send the first ones listed in the UI first const grouped = groupMethods(candidateMethods, mode); - const sortedGroupNames = sortGroupNames(grouped); - return sortedGroupNames.flatMap((name) => - // We can safely pass empty sets for `modifiedSignatures` and `processedByAutoModelMethods` - // because we've filtered out all methods that are already modeled or have already been processed by auto-model. - sortMethods(grouped[name], modeledMethodsBySignature, new Set(), new Set()), - ); + return sortGroupNames(grouped).flatMap((name) => grouped[name]); } diff --git a/extensions/ql-vscode/src/model-editor/shared/sorting.ts b/extensions/ql-vscode/src/model-editor/shared/sorting.ts index c09c05c1023..f09304b1c6a 100644 --- a/extensions/ql-vscode/src/model-editor/shared/sorting.ts +++ b/extensions/ql-vscode/src/model-editor/shared/sorting.ts @@ -4,6 +4,10 @@ import type { ModeledMethod } from "../modeled-method"; import { Mode } from "./mode"; import { calculateModeledPercentage } from "./modeled-percentage"; +/** + * Groups methods by library or package name. + * Does not change the order of methods within a group. + */ export function groupMethods( methods: readonly Method[], mode: Mode, diff --git a/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx b/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx index dbe992691b1..172f3736b6b 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx @@ -3,7 +3,6 @@ import type { Method } from "../../model-editor/method"; import { canMethodBeModeled } from "../../model-editor/method"; import type { ModeledMethod } from "../../model-editor/modeled-method"; import { useMemo } from "react"; -import { sortMethods } from "../../model-editor/shared/sorting"; import { HiddenMethodsRow } from "./HiddenMethodsRow"; import type { ModelEditorViewState } from "../../model-editor/shared/view-state"; import { ScreenReaderOnly } from "../common/ScreenReaderOnly"; @@ -48,12 +47,7 @@ export const ModeledMethodDataGrid = ({ ] = useMemo(() => { const methodsWithModelability = []; let numHiddenMethods = 0; - for (const method of sortMethods( - methods, - modeledMethodsMap, - modifiedSignatures, - processedByAutoModelMethods, - )) { + for (const method of methods) { const modeledMethods = modeledMethodsMap[method.signature] ?? []; const methodIsUnsaved = modifiedSignatures.has(method.signature); const methodCanBeModeled = canMethodBeModeled( @@ -69,13 +63,7 @@ export const ModeledMethodDataGrid = ({ } } return [methodsWithModelability, numHiddenMethods]; - }, [ - hideModeledMethods, - methods, - modeledMethodsMap, - modifiedSignatures, - processedByAutoModelMethods, - ]); + }, [hideModeledMethods, methods, modeledMethodsMap, modifiedSignatures]); const someMethodsAreVisible = methodsWithModelability.length > 0; diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-data-provider.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-data-provider.test.ts index 0bf5ecd9c75..ffe4e7c5184 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-data-provider.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-data-provider.test.ts @@ -27,7 +27,6 @@ describe("MethodsUsageDataProvider", () => { const methods: Method[] = []; const modeledMethods: Record = {}; const modifiedMethodSignatures: Set = new Set(); - const processedByAutoModelMethods: Set = new Set(); const dbItem = mockedObject({ getSourceLocationPrefix: () => "test", }); @@ -40,7 +39,6 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); const onDidChangeTreeDataListener = jest.fn(); @@ -53,7 +51,6 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); expect(onDidChangeTreeDataListener).not.toHaveBeenCalled(); @@ -69,7 +66,6 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); const onDidChangeTreeDataListener = jest.fn(); @@ -82,7 +78,6 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1); @@ -100,7 +95,6 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); const onDidChangeTreeDataListener = jest.fn(); @@ -113,7 +107,6 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1); @@ -127,7 +120,6 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); const onDidChangeTreeDataListener = jest.fn(); @@ -140,7 +132,6 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1); @@ -156,7 +147,6 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); const onDidChangeTreeDataListener = jest.fn(); @@ -169,7 +159,6 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods2, modifiedMethodSignatures, - processedByAutoModelMethods, ); expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1); @@ -185,7 +174,6 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); const onDidChangeTreeDataListener = jest.fn(); @@ -198,7 +186,6 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures2, - processedByAutoModelMethods, ); expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1); @@ -217,7 +204,6 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); const onDidChangeTreeDataListener = jest.fn(); @@ -230,7 +216,6 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); expect(onDidChangeTreeDataListener).toHaveBeenCalledTimes(1); @@ -270,7 +255,6 @@ describe("MethodsUsageDataProvider", () => { createSinkModeledMethod(), ]; const modifiedMethodSignatures: Set = new Set(); - const processedByAutoModelMethods: Set = new Set(); const dbItem = mockedObject({ getSourceLocationPrefix: () => "test", @@ -307,7 +291,6 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); expect(dataProvider.getChildren().length).toEqual(4); }); @@ -321,7 +304,6 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); expect(dataProvider.getChildren().length).toEqual(3); }); @@ -418,7 +400,6 @@ describe("MethodsUsageDataProvider", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); expect( dataProvider diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-panel.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-panel.test.ts index 5069922ac87..0d290ec29ca 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-panel.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-panel.test.ts @@ -28,7 +28,6 @@ describe("MethodsUsagePanel", () => { const methods: Method[] = [createMethod()]; const modeledMethods: Record = {}; const modifiedMethodSignatures: Set = new Set(); - const processedByAutoModelMethods: Set = new Set(); it("should update the tree view with the correct batch number", async () => { const mockTreeView = { @@ -51,7 +50,6 @@ describe("MethodsUsagePanel", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); expect(mockTreeView.badge?.value).toBe(1); @@ -67,7 +65,6 @@ describe("MethodsUsagePanel", () => { const mode = Mode.Application; const modeledMethods: Record = {}; const modifiedMethodSignatures: Set = new Set(); - const processedByAutoModelMethods: Set = new Set(); const usage = createUsage(); beforeEach(() => { @@ -98,7 +95,6 @@ describe("MethodsUsagePanel", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); await panel.revealItem(method.signature, usage); @@ -126,7 +122,6 @@ describe("MethodsUsagePanel", () => { mode, modeledMethods, modifiedMethodSignatures, - processedByAutoModelMethods, ); await panel.revealItem(method.signature, usage); From f57d4418c7ce8e2eee56544096689280cbd7aff3 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 23 Feb 2024 10:31:28 +0100 Subject: [PATCH 0032/1237] Hide type models for Ruby in non-canary mode --- .../model-editor/languages/models-as-data.ts | 12 ++++++ .../src/model-editor/languages/ruby/index.ts | 2 + .../method-modeling-view-provider.ts | 2 + .../src/model-editor/model-editor-view.ts | 2 + .../src/model-editor/shared/view-state.ts | 2 + .../MethodModelingInputs.stories.tsx | 1 + .../view/method-modeling/MethodModeling.tsx | 3 ++ .../method-modeling/MethodModelingInputs.tsx | 4 +- .../method-modeling/MethodModelingView.tsx | 1 + .../MultipleModeledMethodsPanel.tsx | 4 ++ .../__tests__/MethodModeling.spec.tsx | 1 + .../__tests__/MethodModelingInputs.spec.tsx | 6 +++ .../MultipleModeledMethodsPanel.spec.tsx | 39 +++++++++++++++++++ .../src/view/model-editor/MethodRow.tsx | 38 ++++++++++++++++-- .../view/model-editor/ModelTypeDropdown.tsx | 8 +++- .../__tests__/ModelTypeDropdown.spec.tsx | 25 +++++++++++- .../test/factories/model-editor/view-state.ts | 1 + 17 files changed, 144 insertions(+), 7 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts b/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts index c4f6c6a57b4..662c6b29f30 100644 --- a/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts +++ b/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts @@ -19,6 +19,10 @@ import type { AccessPathSuggestionRow } from "../suggestions"; type GenerateMethodDefinition = (method: T) => DataTuple[]; type ReadModeledMethod = (row: DataTuple[]) => ModeledMethod; +type IsHiddenContext = { + method: MethodDefinition; + isCanary: boolean; +}; export type ModelsAsDataLanguagePredicate = { extensiblePredicate: string; @@ -30,6 +34,14 @@ export type ModelsAsDataLanguagePredicate = { supportedEndpointTypes?: EndpointType[]; generateMethodDefinition: GenerateMethodDefinition; readModeledMethod: ReadModeledMethod; + + /** + * Controls whether this predicate is hidden for a certain method. This only applies to the UI. + * If not specified, the predicate is visible for all methods. + * + * @param method The method to check if the predicate is hidden for. + */ + isHidden?: (context: IsHiddenContext) => boolean; }; type ParseGenerationResults = ( diff --git a/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts b/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts index bd6c9dc11bc..23b6e2f1aa8 100644 --- a/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts +++ b/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts @@ -169,6 +169,8 @@ export const ruby: ModelsAsDataLanguage = { methodParameters: "", }; }, + // Hide for all non-canary users + isHidden: ({ isCanary }) => !isCanary, }, }, modelGeneration: { diff --git a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts index e921b3776d5..8ae34293183 100644 --- a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts +++ b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts @@ -11,6 +11,7 @@ import type { ModelingStore } from "../modeling-store"; import { AbstractWebviewViewProvider } from "../../common/vscode/abstract-webview-view-provider"; import { assertNever } from "../../common/helpers-pure"; import type { ModelConfigListener } from "../../config"; +import { isCanary } from "../../config"; import type { DatabaseItem } from "../../databases/local-databases"; import type { ModelingEvents } from "../modeling-events"; import type { QueryLanguage } from "../../common/query-language"; @@ -46,6 +47,7 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< t: "setMethodModelingPanelViewState", viewState: { language: this.language, + isCanary: isCanary(), }, }); } diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 664ecb72886..a2991697a1c 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -40,6 +40,7 @@ import type { Method } from "./method"; import type { ModeledMethod } from "./modeled-method"; import type { ExtensionPack } from "./shared/extension-pack"; import type { ModelConfigListener } from "../config"; +import { isCanary } from "../config"; import { Mode } from "./shared/mode"; import { loadModeledMethods, saveModeledMethods } from "./modeled-method-fs"; import { pickExtensionPack } from "./extension-pack-picker"; @@ -450,6 +451,7 @@ export class ModelEditorView extends AbstractWebview< mode: this.modelingStore.getMode(this.databaseItem), showModeSwitchButton, sourceArchiveAvailable, + isCanary: isCanary(), }, }); } diff --git a/extensions/ql-vscode/src/model-editor/shared/view-state.ts b/extensions/ql-vscode/src/model-editor/shared/view-state.ts index 111019e3013..c9978e27047 100644 --- a/extensions/ql-vscode/src/model-editor/shared/view-state.ts +++ b/extensions/ql-vscode/src/model-editor/shared/view-state.ts @@ -11,8 +11,10 @@ export interface ModelEditorViewState { mode: Mode; showModeSwitchButton: boolean; sourceArchiveAvailable: boolean; + isCanary: boolean; } export interface MethodModelingPanelViewState { language: QueryLanguage | undefined; + isCanary: boolean; } diff --git a/extensions/ql-vscode/src/stories/method-modeling/MethodModelingInputs.stories.tsx b/extensions/ql-vscode/src/stories/method-modeling/MethodModelingInputs.stories.tsx index a40fe8bff66..eea86a94b87 100644 --- a/extensions/ql-vscode/src/stories/method-modeling/MethodModelingInputs.stories.tsx +++ b/extensions/ql-vscode/src/stories/method-modeling/MethodModelingInputs.stories.tsx @@ -34,6 +34,7 @@ const Template: StoryFn = (args) => { language={QueryLanguage.Java} modeledMethod={m} onChange={onChange} + isCanary={true} /> ); }; diff --git a/extensions/ql-vscode/src/view/method-modeling/MethodModeling.tsx b/extensions/ql-vscode/src/view/method-modeling/MethodModeling.tsx index 1f16239b791..1dc0fe969cd 100644 --- a/extensions/ql-vscode/src/view/method-modeling/MethodModeling.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/MethodModeling.tsx @@ -50,6 +50,7 @@ const UnsavedTag = ({ modelingStatus }: { modelingStatus: ModelingStatus }) => ( export type MethodModelingProps = { language: QueryLanguage; + isCanary: boolean; modelingStatus: ModelingStatus; method: Method; modeledMethods: ModeledMethod[]; @@ -60,6 +61,7 @@ export type MethodModelingProps = { export const MethodModeling = ({ language, + isCanary, modelingStatus, modeledMethods, method, @@ -80,6 +82,7 @@ export const MethodModeling = ({ ) : ( - + )} diff --git a/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx b/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx index 188a9903004..8d9caaba124 100644 --- a/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx @@ -116,6 +116,7 @@ export function MethodModelingView({ return ( 0 ? ( { render({ language: QueryLanguage.Java, + isCanary: false, modelingStatus: "saved", method, modeledMethods: [modeledMethod], diff --git a/extensions/ql-vscode/src/view/method-modeling/__tests__/MethodModelingInputs.spec.tsx b/extensions/ql-vscode/src/view/method-modeling/__tests__/MethodModelingInputs.spec.tsx index 2083b3c4123..2a002d3f447 100644 --- a/extensions/ql-vscode/src/view/method-modeling/__tests__/MethodModelingInputs.spec.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/__tests__/MethodModelingInputs.spec.tsx @@ -19,6 +19,7 @@ describe(MethodModelingInputs.name, () => { const modeledMethod = createSinkModeledMethod(); const modelPending = false; const isModelingInProgress = false; + const isCanary = false; const onChange = jest.fn(); it("renders the method modeling inputs", () => { @@ -28,6 +29,7 @@ describe(MethodModelingInputs.name, () => { modeledMethod, modelPending, isModelingInProgress, + isCanary, onChange, }); @@ -55,6 +57,7 @@ describe(MethodModelingInputs.name, () => { modeledMethod, modelPending, isModelingInProgress, + isCanary, onChange, }); @@ -78,6 +81,7 @@ describe(MethodModelingInputs.name, () => { modeledMethod, modelPending, isModelingInProgress, + isCanary, onChange, }); @@ -93,6 +97,7 @@ describe(MethodModelingInputs.name, () => { modeledMethod={updatedModeledMethod} modelPending={modelPending} isModelingInProgress={isModelingInProgress} + isCanary={isCanary} onChange={onChange} />, ); @@ -123,6 +128,7 @@ describe(MethodModelingInputs.name, () => { modeledMethod, modelPending, isModelingInProgress: true, + isCanary, onChange, }); diff --git a/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx b/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx index 44ab596c627..e5d254d7af0 100644 --- a/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx @@ -16,6 +16,7 @@ describe(MultipleModeledMethodsPanel.name, () => { reactRender(); const language = QueryLanguage.Java; + const isCanary = false; const method = createMethod(); const isModelingInProgress = false; const isProcessedByAutoModel = false; @@ -28,6 +29,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("renders the method modeling inputs once", () => { render({ language, + isCanary, method, modeledMethods, modelingStatus, @@ -47,6 +49,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("disables all pagination", () => { render({ language, + isCanary, method, modeledMethods, modelingStatus, @@ -70,6 +73,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("cannot add or delete modeling", () => { render({ language, + isCanary, method, modeledMethods, modelingStatus, @@ -102,6 +106,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("renders the method modeling inputs once", () => { render({ language, + isCanary, method, modeledMethods, modelingStatus, @@ -121,6 +126,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("disables all pagination", () => { render({ language, + isCanary, method, modeledMethods, modelingStatus, @@ -143,6 +149,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("cannot delete modeling", () => { render({ language, + isCanary, method, modeledMethods, modelingStatus, @@ -161,6 +168,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("can add modeling", async () => { render({ language, + isCanary, method, modeledMethods, modelingStatus, @@ -188,6 +196,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("changes selection to the newly added modeling", async () => { const { rerender } = render({ language, + isCanary, method, modeledMethods, modelingStatus, @@ -201,6 +210,7 @@ describe(MultipleModeledMethodsPanel.name, () => { rerender( { it("renders the method modeling inputs once", () => { render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -248,6 +259,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("renders the pagination", () => { render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -264,6 +276,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("disables the correct pagination", async () => { render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -285,6 +298,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("can use the pagination", async () => { render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -323,6 +337,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("correctly updates selected pagination index when the number of models decreases", async () => { const { rerender } = render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -336,6 +351,7 @@ describe(MultipleModeledMethodsPanel.name, () => { rerender( { it("does not show errors", () => { render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -370,6 +387,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("can update the first modeling", async () => { render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -404,6 +422,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("can update the second modeling", async () => { render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -440,6 +459,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("can delete modeling", async () => { render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -459,6 +479,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("can add modeling", async () => { render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -486,6 +507,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("shows an error when adding a neutral modeling", async () => { const { rerender } = render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -499,6 +521,7 @@ describe(MultipleModeledMethodsPanel.name, () => { rerender( { rerender( { rerender( { it("changes selection to the newly added modeling", async () => { const { rerender } = render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -576,6 +602,7 @@ describe(MultipleModeledMethodsPanel.name, () => { rerender( { it("can use the pagination", async () => { render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -705,6 +733,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("preserves selection when a modeling other than the selected modeling is removed", async () => { const { rerender } = render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -718,6 +747,7 @@ describe(MultipleModeledMethodsPanel.name, () => { rerender( { it("reduces selection when the selected modeling is removed", async () => { const { rerender } = render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -748,6 +779,7 @@ describe(MultipleModeledMethodsPanel.name, () => { rerender( { it("can add modeling", () => { render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -793,6 +826,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("can delete first modeling", async () => { render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -812,6 +846,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("can delete second modeling", async () => { render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -832,6 +867,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("can add modeling after deleting second modeling", async () => { const { rerender } = render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -851,6 +887,7 @@ describe(MultipleModeledMethodsPanel.name, () => { rerender( { it("shows errors", () => { render({ language, + isCanary, method, modeledMethods, isModelingInProgress, @@ -905,6 +943,7 @@ describe(MultipleModeledMethodsPanel.name, () => { it("shows the correct error message", async () => { render({ language, + isCanary, method, modeledMethods, isModelingInProgress, diff --git a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx index 37c7558bc34..4788e47fa40 100644 --- a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx @@ -37,6 +37,7 @@ import { createEmptyModeledMethod } from "../../model-editor/modeled-method-empt import type { AccessPathOption } from "../../model-editor/suggestions"; import { ModelInputSuggestBox } from "./ModelInputSuggestBox"; import { ModelOutputSuggestBox } from "./ModelOutputSuggestBox"; +import { getModelsAsDataLanguage } from "../../model-editor/languages"; const ApiOrMethodRow = styled.div` min-height: calc(var(--input-height) * 1px); @@ -192,9 +193,37 @@ const ModelableMethodRow = forwardRef( [method], ); - const modelingStatus = getModelingStatus(modeledMethods, methodIsUnsaved); + // Only show modeled methods that are non-hidden. These are also the ones that are + // used for determining the modeling status. + const shownModeledMethods = useMemo(() => { + const modelsAsDataLanguage = getModelsAsDataLanguage(viewState.language); + + return modeledMethodsToDisplay( + modeledMethods.filter((modeledMethod) => { + if (modeledMethod.type === "none") { + return true; + } + + const predicate = modelsAsDataLanguage.predicates[modeledMethod.type]; + if (!predicate) { + return true; + } + + return !predicate.isHidden?.({ + method, + isCanary: viewState.isCanary, + }); + }), + method, + ); + }, [method, modeledMethods, viewState]); + + const modelingStatus = getModelingStatus( + shownModeledMethods, + methodIsUnsaved, + ); - const addModelButtonDisabled = !canAddNewModeledMethod(modeledMethods); + const addModelButtonDisabled = !canAddNewModeledMethod(shownModeledMethods); return ( ( }} > @@ -257,7 +286,7 @@ const ModelableMethodRow = forwardRef( )} {!props.modelingInProgress && ( <> - {modeledMethods.map((modeledMethod, index) => { + {shownModeledMethods.map((modeledMethod, index) => { const modelPending = isModelPending( modeledMethod, modelingStatus, @@ -269,6 +298,7 @@ const ModelableMethodRow = forwardRef( ) => { diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/ModelTypeDropdown.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelTypeDropdown.spec.tsx index 6f6f13d6801..8f682aafbf2 100644 --- a/extensions/ql-vscode/src/view/model-editor/__tests__/ModelTypeDropdown.spec.tsx +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelTypeDropdown.spec.tsx @@ -23,6 +23,7 @@ describe(ModelTypeDropdown.name, () => { modelPending={false} onChange={onChange} method={method} + isCanary={false} />, ); @@ -34,7 +35,7 @@ describe(ModelTypeDropdown.name, () => { ); }); - it("allows changing the type to 'Type' for Ruby", async () => { + it("allows changing the type to 'Type' for Ruby in canary mode", async () => { const method = createMethod(); const modeledMethod = createNoneModeledMethod(); @@ -45,6 +46,7 @@ describe(ModelTypeDropdown.name, () => { modelPending={false} onChange={onChange} method={method} + isCanary={true} />, ); @@ -56,6 +58,26 @@ describe(ModelTypeDropdown.name, () => { ); }); + it("does not allow changing the type to 'Type' for Ruby in non-canary mode", async () => { + const method = createMethod(); + const modeledMethod = createNoneModeledMethod(); + + render( + , + ); + + expect( + screen.queryByRole("option", { name: "Type" }), + ).not.toBeInTheDocument(); + }); + it("does not allow changing the type to 'Type' for Java", async () => { const method = createMethod(); const modeledMethod = createNoneModeledMethod(); @@ -67,6 +89,7 @@ describe(ModelTypeDropdown.name, () => { modelPending={false} onChange={onChange} method={method} + isCanary={false} />, ); diff --git a/extensions/ql-vscode/test/factories/model-editor/view-state.ts b/extensions/ql-vscode/test/factories/model-editor/view-state.ts index 06a40da5a04..39f6b93f765 100644 --- a/extensions/ql-vscode/test/factories/model-editor/view-state.ts +++ b/extensions/ql-vscode/test/factories/model-editor/view-state.ts @@ -15,6 +15,7 @@ export function createMockModelEditorViewState( showModeSwitchButton: true, extensionPack: createMockExtensionPack(), sourceArchiveAvailable: true, + isCanary: false, ...data, }; } From f4a866b04b2021813bd91c24c9305caf640bfaf6 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Fri, 23 Feb 2024 11:17:25 +0000 Subject: [PATCH 0033/1237] Add logic to trigger model evaluation run (#3397) --- extensions/ql-vscode/src/extension.ts | 1 + .../src/model-editor/model-editor-module.ts | 5 + .../src/model-editor/model-editor-view.ts | 7 + .../src/model-editor/model-evaluator.ts | 120 ++++++++++++++---- .../src/model-editor/modeling-store.ts | 2 +- .../variant-analysis-manager.ts | 6 +- .../model-editor/model-editor-view.test.ts | 3 + 7 files changed, 117 insertions(+), 27 deletions(-) diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 8e468013930..53a3b12dfd4 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -977,6 +977,7 @@ async function activateWithInstalledDistribution( const modelEditorModule = await ModelEditorModule.initialize( app, dbm, + variantAnalysisManager, cliServer, qs, tmpDir.name, diff --git a/extensions/ql-vscode/src/model-editor/model-editor-module.ts b/extensions/ql-vscode/src/model-editor/model-editor-module.ts index 6acd88d7ff9..7551835d1e4 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-module.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-module.ts @@ -31,6 +31,7 @@ import { getModelsAsDataLanguage } from "./languages"; import { INITIAL_MODE } from "./shared/mode"; import { isSupportedLanguage } from "./supported-languages"; import { DefaultNotifier, checkConsistency } from "./consistency-check"; +import type { VariantAnalysisManager } from "../variant-analysis/variant-analysis-manager"; export class ModelEditorModule extends DisposableObject { private readonly queryStorageDir: string; @@ -43,6 +44,7 @@ export class ModelEditorModule extends DisposableObject { private constructor( private readonly app: App, private readonly databaseManager: DatabaseManager, + private readonly variantAnalysisManager: VariantAnalysisManager, private readonly cliServer: CodeQLCliServer, private readonly queryRunner: QueryRunner, baseQueryStorageDir: string, @@ -65,6 +67,7 @@ export class ModelEditorModule extends DisposableObject { public static async initialize( app: App, databaseManager: DatabaseManager, + variantAnalysisManager: VariantAnalysisManager, cliServer: CodeQLCliServer, queryRunner: QueryRunner, queryStorageDir: string, @@ -72,6 +75,7 @@ export class ModelEditorModule extends DisposableObject { const modelEditorModule = new ModelEditorModule( app, databaseManager, + variantAnalysisManager, cliServer, queryRunner, queryStorageDir, @@ -240,6 +244,7 @@ export class ModelEditorModule extends DisposableObject { this.modelingEvents, this.modelConfig, this.databaseManager, + this.variantAnalysisManager, this.cliServer, this.queryRunner, this.queryStorageDir, diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 664ecb72886..31ca255f65b 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -59,6 +59,7 @@ import { runSuggestionsQuery } from "./suggestion-queries"; import { parseAccessPathSuggestionRowsToOptions } from "./suggestions-bqrs"; import { ModelEvaluator } from "./model-evaluator"; import type { ModelEvaluationRunState } from "./shared/model-evaluation-run-state"; +import type { VariantAnalysisManager } from "../variant-analysis/variant-analysis-manager"; export class ModelEditorView extends AbstractWebview< ToModelEditorMessage, @@ -77,6 +78,7 @@ export class ModelEditorView extends AbstractWebview< private readonly modelingEvents: ModelingEvents, private readonly modelConfig: ModelConfigListener, private readonly databaseManager: DatabaseManager, + private readonly variantAnalysisManager: VariantAnalysisManager, private readonly cliServer: CodeQLCliServer, private readonly queryRunner: QueryRunner, private readonly queryStorageDir: string, @@ -115,9 +117,13 @@ export class ModelEditorView extends AbstractWebview< this.languageDefinition = getModelsAsDataLanguage(language); this.modelEvaluator = new ModelEvaluator( + this.app.logger, + this.cliServer, modelingStore, modelingEvents, + this.variantAnalysisManager, databaseItem, + language, this.updateModelEvaluationRun.bind(this), ); this.push(this.modelEvaluator); @@ -801,6 +807,7 @@ export class ModelEditorView extends AbstractWebview< this.modelingEvents, this.modelConfig, this.databaseManager, + this.variantAnalysisManager, this.cliServer, this.queryRunner, this.queryStorageDir, diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts index d3920cdb238..5e1e9dc5592 100644 --- a/extensions/ql-vscode/src/model-editor/model-evaluator.ts +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -3,14 +3,24 @@ import type { ModelingEvents } from "./modeling-events"; import type { DatabaseItem } from "../databases/local-databases"; import type { ModelEvaluationRun } from "./model-evaluation-run"; import { DisposableObject } from "../common/disposable-object"; -import { sleep } from "../common/time"; import type { ModelEvaluationRunState } from "./shared/model-evaluation-run-state"; +import type { BaseLogger } from "../common/logging"; +import type { CodeQLCliServer } from "../codeql-cli/cli"; +import type { VariantAnalysisManager } from "../variant-analysis/variant-analysis-manager"; +import type { QueryLanguage } from "../common/query-language"; +import { resolveCodeScanningQueryPack } from "../variant-analysis/code-scanning-pack"; +import { withProgress } from "../common/vscode/progress"; +import type { VariantAnalysis } from "../variant-analysis/shared/variant-analysis"; export class ModelEvaluator extends DisposableObject { public constructor( + private readonly logger: BaseLogger, + private readonly cliServer: CodeQLCliServer, private readonly modelingStore: ModelingStore, private readonly modelingEvents: ModelingEvents, + private readonly variantAnalysisManager: VariantAnalysisManager, private readonly dbItem: DatabaseItem, + private readonly language: QueryLanguage, private readonly updateView: ( run: ModelEvaluationRunState, ) => Promise, @@ -28,18 +38,48 @@ export class ModelEvaluator extends DisposableObject { }; this.modelingStore.updateModelEvaluationRun(this.dbItem, evaluationRun); - // For now, just wait 5 seconds and then update the store. - // In the future, this will be replaced with the actual evaluation process. - void sleep(5000).then(() => { - const completedEvaluationRun: ModelEvaluationRun = { - isPreparing: false, - variantAnalysisId: undefined, - }; - this.modelingStore.updateModelEvaluationRun( - this.dbItem, - completedEvaluationRun, - ); - }); + // Build pack + const qlPack = await resolveCodeScanningQueryPack( + this.logger, + this.cliServer, + this.language, + ); + + if (!qlPack) { + this.modelingStore.updateModelEvaluationRun(this.dbItem, undefined); + throw new Error("Unable to trigger evaluation run"); + } + + // Submit variant analysis and monitor progress + return withProgress( + async (progress, token) => { + let variantAnalysisId: number | undefined = undefined; + try { + variantAnalysisId = + await this.variantAnalysisManager.runVariantAnalysis( + qlPack, + progress, + token, + ); + } catch (e) { + this.modelingStore.updateModelEvaluationRun(this.dbItem, undefined); + throw e; + } + + if (variantAnalysisId) { + this.monitorVariantAnalysis(variantAnalysisId); + } else { + this.modelingStore.updateModelEvaluationRun(this.dbItem, undefined); + throw new Error( + "Unable to trigger variant analysis for evaluation run", + ); + } + }, + { + title: "Run Variant Analysis", + cancellable: true, + }, + ); } public async stopEvaluation() { @@ -55,19 +95,51 @@ export class ModelEvaluator extends DisposableObject { private registerToModelingEvents() { this.push( this.modelingEvents.onModelEvaluationRunChanged(async (event) => { - if ( - event.evaluationRun && - event.dbUri === this.dbItem.databaseUri.toString() - ) { - const run: ModelEvaluationRunState = { - isPreparing: event.evaluationRun.isPreparing, - - // TODO: Get variant analysis from id. - variantAnalysis: undefined, - }; - await this.updateView(run); + if (event.dbUri === this.dbItem.databaseUri.toString()) { + if (!event.evaluationRun) { + await this.updateView({ + isPreparing: false, + variantAnalysis: undefined, + }); + } else { + const variantAnalysis = await this.getVariantAnalysisForRun( + event.evaluationRun, + ); + const run: ModelEvaluationRunState = { + isPreparing: event.evaluationRun.isPreparing, + variantAnalysis, + }; + await this.updateView(run); + } } }), ); } + + private async getVariantAnalysisForRun( + evaluationRun: ModelEvaluationRun, + ): Promise { + if (evaluationRun.variantAnalysisId) { + return await this.variantAnalysisManager.getVariantAnalysis( + evaluationRun.variantAnalysisId, + ); + } + return undefined; + } + + private monitorVariantAnalysis(variantAnalysisId: number) { + this.push( + this.variantAnalysisManager.onVariantAnalysisStatusUpdated( + async (variantAnalysis) => { + // Make sure it's the variant analysis we're interested in + if (variantAnalysisId === variantAnalysis.id) { + await this.updateView({ + isPreparing: false, + variantAnalysis, + }); + } + }, + ), + ); + } } diff --git a/extensions/ql-vscode/src/model-editor/modeling-store.ts b/extensions/ql-vscode/src/model-editor/modeling-store.ts index a9fda24f698..364db48a9e2 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-store.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-store.ts @@ -378,7 +378,7 @@ export class ModelingStore extends DisposableObject { public updateModelEvaluationRun( dbItem: DatabaseItem, - evaluationRun: ModelEvaluationRun, + evaluationRun: ModelEvaluationRun | undefined, ) { this.changeModelEvaluationRun(dbItem, (state) => { state.modelEvaluationRun = evaluationRun; diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 3fc650f9f00..7e59ae17ba6 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -298,7 +298,7 @@ export class VariantAnalysisManager qlPackDetails: QlPackDetails, progress: ProgressCallback, token: CancellationToken, - ): Promise { + ): Promise { await saveBeforeStart(); progress({ @@ -379,7 +379,7 @@ export class VariantAnalysisManager } catch (e: unknown) { // If the error is handled by the handleRequestError function, we don't need to throw if (e instanceof RequestError && handleRequestError(e, this.app.logger)) { - return; + return undefined; } throw e; @@ -405,6 +405,8 @@ export class VariantAnalysisManager "codeQL.monitorNewVariantAnalysis", processedVariantAnalysis, ); + + return processedVariantAnalysis.id; } public async rehydrateVariantAnalysis(variantAnalysis: VariantAnalysis) { diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/model-editor-view.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/model-editor-view.test.ts index ee2cf8bb1fb..e410c5ad156 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/model-editor-view.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/model-editor-view.test.ts @@ -12,6 +12,7 @@ import { createMockModelingStore } from "../../../__mocks__/model-editor/modelin import type { ModelConfigListener } from "../../../../src/config"; import { createMockModelingEvents } from "../../../__mocks__/model-editor/modelingEventsMock"; import { QueryLanguage } from "../../../../src/common/query-language"; +import type { VariantAnalysisManager } from "../../../../src/variant-analysis/variant-analysis-manager"; describe("ModelEditorView", () => { const app = createMockApp({}); @@ -21,6 +22,7 @@ describe("ModelEditorView", () => { onDidChangeConfiguration: jest.fn(), }); const databaseManager = mockEmptyDatabaseManager(); + const variantAnalysisManager = mockedObject({}); const cliServer = mockedObject({}); const queryRunner = mockedObject({}); const queryStorageDir = "/a/b/c/d"; @@ -48,6 +50,7 @@ describe("ModelEditorView", () => { modelingEvents, modelConfig, databaseManager, + variantAnalysisManager, cliServer, queryRunner, queryStorageDir, From 5468aef458a13fd1bd47b7fa657fc1858dac3c57 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 23 Feb 2024 14:38:10 +0100 Subject: [PATCH 0034/1237] Extract saving of model extension YAML file --- .../model-extension-file.schema.json | 53 +++++++------ .../src/model-editor/model-extension-file.ts | 2 +- extensions/ql-vscode/src/model-editor/yaml.ts | 78 ++++++++++++------- 3 files changed, 77 insertions(+), 56 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/model-extension-file.schema.json b/extensions/ql-vscode/src/model-editor/model-extension-file.schema.json index 5a19a1222a3..a44b8d9de06 100644 --- a/extensions/ql-vscode/src/model-editor/model-extension-file.schema.json +++ b/extensions/ql-vscode/src/model-editor/model-extension-file.schema.json @@ -8,36 +8,39 @@ "extensions": { "type": "array", "items": { - "type": "object", - "properties": { - "addsTo": { - "type": "object", - "properties": { - "pack": { - "type": "string" - }, - "extensible": { - "type": "string" - } - }, - "required": ["pack", "extensible"] - }, - "data": { - "type": "array", - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/DataTuple" - } - } - } - }, - "required": ["addsTo", "data"] + "$ref": "#/definitions/ModelExtension" } } }, "required": ["extensions"] }, + "ModelExtension": { + "type": "object", + "properties": { + "addsTo": { + "type": "object", + "properties": { + "pack": { + "type": "string" + }, + "extensible": { + "type": "string" + } + }, + "required": ["pack", "extensible"] + }, + "data": { + "type": "array", + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/DataTuple" + } + } + } + }, + "required": ["addsTo", "data"] + }, "DataTuple": { "type": ["boolean", "number", "string"] } diff --git a/extensions/ql-vscode/src/model-editor/model-extension-file.ts b/extensions/ql-vscode/src/model-editor/model-extension-file.ts index 7a838483f1b..ec560034982 100644 --- a/extensions/ql-vscode/src/model-editor/model-extension-file.ts +++ b/extensions/ql-vscode/src/model-editor/model-extension-file.ts @@ -7,7 +7,7 @@ export type DataTuple = boolean | number | string; type DataRow = DataTuple[]; -type ModelExtension = { +export type ModelExtension = { addsTo: ExtensibleReference; data: DataRow[]; }; diff --git a/extensions/ql-vscode/src/model-editor/yaml.ts b/extensions/ql-vscode/src/model-editor/yaml.ts index b165e039de5..10c1c9b87e4 100644 --- a/extensions/ql-vscode/src/model-editor/yaml.ts +++ b/extensions/ql-vscode/src/model-editor/yaml.ts @@ -16,7 +16,10 @@ import type { import { getModelsAsDataLanguage } from "./languages"; import { Mode } from "./shared/mode"; import { assertNever } from "../common/helpers-pure"; -import type { ModelExtensionFile } from "./model-extension-file"; +import type { + ModelExtension, + ModelExtensionFile, +} from "./model-extension-file"; import type { QueryLanguage } from "../common/query-language"; import modelExtensionFileSchema from "./model-extension-file.schema.json"; @@ -24,38 +27,22 @@ import modelExtensionFileSchema from "./model-extension-file.schema.json"; const ajv = new Ajv({ allErrors: true, allowUnionTypes: true }); const modelExtensionFileSchemaValidate = ajv.compile(modelExtensionFileSchema); -function createDataProperty( - methods: readonly T[], - definition: ModelsAsDataLanguagePredicate, -) { - if (methods.length === 0) { - return " []"; - } - - return `\n${methods - .map( - (method) => - ` - ${JSON.stringify( - definition.generateMethodDefinition(method), - )}`, - ) - .join("\n")}`; -} - function createExtensions( language: QueryLanguage, methods: readonly T[], definition: ModelsAsDataLanguagePredicate | undefined, -) { +): ModelExtension | undefined { if (!definition) { - return ""; + return undefined; } - return ` - addsTo: - pack: codeql/${language}-all - extensible: ${definition.extensiblePredicate} - data:${createDataProperty(methods, definition)} -`; + return { + addsTo: { + pack: `codeql/${language}-all`, + extensible: definition.extensiblePredicate, + }, + data: methods.map((method) => definition.generateMethodDefinition(method)), + }; } export function createDataExtensionYaml( @@ -99,7 +86,7 @@ export function createDataExtensionYaml( } const extensions = Object.keys(methodsByType) - .map((typeKey) => { + .map((typeKey): ModelExtension | undefined => { const type = typeKey as keyof ModelsAsDataLanguagePredicates; switch (type) { @@ -137,10 +124,11 @@ export function createDataExtensionYaml( assertNever(type); } }) - .filter((extensions) => extensions !== ""); + .filter( + (extension): extension is ModelExtension => extension !== undefined, + ); - return `extensions: -${extensions.join("\n")}`; + return modelExtensionFileToYaml({ extensions }); } export function createDataExtensionYamls( @@ -341,6 +329,36 @@ function validateModelExtensionFile(data: unknown): data is ModelExtensionFile { return true; } +/** + * Creates a string for the data extension YAML file from the + * structure of the data extension file. This should be used + * instead of creating a JSON string directly or dumping the + * YAML directly to ensure that the file is formatted correctly. + * + * @param data The data extension file + */ +function modelExtensionFileToYaml(data: ModelExtensionFile) { + const extensions = data.extensions + .map((extension) => { + const data = + extension.data.length === 0 + ? " []" + : `\n${extension.data + .map((row) => ` - ${JSON.stringify(row)}`) + .join("\n")}`; + + return ` - addsTo: + pack: ${extension.addsTo.pack} + extensible: ${extension.addsTo.extensible} + data:${data} +`; + }) + .filter((extensions) => extensions !== ""); + + return `extensions: +${extensions.join("\n")}`; +} + export function loadDataExtensionYaml( data: unknown, language: QueryLanguage, From 22024462fb3aaa71b3f51a26cb1dab857a63a994 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 23 Feb 2024 15:37:58 +0100 Subject: [PATCH 0035/1237] Generate separate file for generated type models in Ruby --- .../ql-vscode/src/model-editor/generate.ts | 12 +-- .../model-editor/languages/models-as-data.ts | 41 +++++++--- .../model-editor/languages/ruby/generate.ts | 16 +++- .../src/model-editor/languages/ruby/index.ts | 49 +++++++----- .../src/model-editor/model-editor-view.ts | 74 ++++++++++++++----- .../src/model-editor/modeled-method-fs.ts | 8 ++ extensions/ql-vscode/src/model-editor/yaml.ts | 2 +- .../languages/ruby/generate.test.ts | 5 ++ .../model-editor/generate.test.ts | 22 ++++-- 9 files changed, 161 insertions(+), 68 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/generate.ts b/extensions/ql-vscode/src/model-editor/generate.ts index 8f1147e65cb..9f4b20c13da 100644 --- a/extensions/ql-vscode/src/model-editor/generate.ts +++ b/extensions/ql-vscode/src/model-editor/generate.ts @@ -5,19 +5,15 @@ import type { QueryRunner } from "../query-server"; import type { CodeQLCliServer } from "../codeql-cli/cli"; import type { ProgressCallback } from "../common/vscode/progress"; import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders"; -import type { ModeledMethod } from "./modeled-method"; import { runQuery } from "../local-queries/run-query"; import type { QueryConstraints } from "../local-queries"; import { resolveQueries } from "../local-queries"; import type { DecodedBqrs } from "../common/bqrs-cli-types"; + type GenerateQueriesOptions = { queryConstraints: QueryConstraints; filterQueries?: (queryPath: string) => boolean; - parseResults: ( - queryPath: string, - results: DecodedBqrs, - ) => ModeledMethod[] | Promise; - onResults: (results: ModeledMethod[]) => void | Promise; + onResults: (queryPath: string, results: DecodedBqrs) => void | Promise; cliServer: CodeQLCliServer; queryRunner: QueryRunner; @@ -28,7 +24,7 @@ type GenerateQueriesOptions = { }; export async function runGenerateQueries(options: GenerateQueriesOptions) { - const { queryConstraints, filterQueries, parseResults, onResults } = options; + const { queryConstraints, filterQueries, onResults } = options; options.progress({ message: "Resolving queries", @@ -55,7 +51,7 @@ export async function runGenerateQueries(options: GenerateQueriesOptions) { const bqrs = await runSingleGenerateQuery(queryPath, i, maxStep, options); if (bqrs) { - await onResults(await parseResults(queryPath, bqrs)); + await onResults(queryPath, bqrs); } } } diff --git a/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts b/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts index c4f6c6a57b4..0ea8ecea18c 100644 --- a/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts +++ b/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts @@ -7,7 +7,7 @@ import type { SummaryModeledMethod, TypeModeledMethod, } from "../modeled-method"; -import type { DataTuple } from "../model-extension-file"; +import type { DataTuple, ModelExtension } from "../model-extension-file"; import type { Mode } from "../shared/mode"; import type { QueryConstraints } from "../../local-queries/query-constraints"; import type { @@ -32,6 +32,11 @@ export type ModelsAsDataLanguagePredicate = { readModeledMethod: ReadModeledMethod; }; +export type GenerationContext = { + mode: Mode; + isCanary: boolean; +}; + type ParseGenerationResults = ( // The path to the query that generated the results. queryPath: string, @@ -42,24 +47,37 @@ type ParseGenerationResults = ( modelsAsDataLanguage: ModelsAsDataLanguage, // The logger to use for logging. logger: BaseLogger, + // Context about this invocation of the generation. + context: GenerationContext, ) => ModeledMethod[]; type ModelsAsDataLanguageModelGeneration = { queryConstraints: (mode: Mode) => QueryConstraints; filterQueries?: (queryPath: string) => boolean; parseResults: ParseGenerationResults; +}; + +type ParseResultsToYaml = ( + // The path to the query that generated the results. + queryPath: string, + // The results of the query. + bqrs: DecodedBqrs, + // The language-specific predicate that was used to generate the results. This is passed to allow + // sharing of code between different languages. + modelsAsDataLanguage: ModelsAsDataLanguage, + // The logger to use for logging. + logger: BaseLogger, +) => ModelExtension[]; + +type ModelsAsDataLanguageAutoModelGeneration = { + queryConstraints: (mode: Mode) => QueryConstraints; + filterQueries?: (queryPath: string) => boolean; + parseResultsToYaml: ParseResultsToYaml; /** - * If autoRun is not undefined, the query will be run automatically when the user starts the - * model editor. - * - * This only applies to framework mode. Application mode will never run the query automatically. + * By default, auto model generation is enabled for all modes. This function can be used to + * override that behavior. */ - autoRun?: { - /** - * If defined, will use a custom parsing function when the query is run automatically. - */ - parseResults?: ParseGenerationResults; - }; + enabled?: (context: GenerationContext) => boolean; }; type ModelsAsDataLanguageAccessPathSuggestions = { @@ -109,6 +127,7 @@ export type ModelsAsDataLanguage = { ) => EndpointType | undefined; predicates: ModelsAsDataLanguagePredicates; modelGeneration?: ModelsAsDataLanguageModelGeneration; + autoModelGeneration?: ModelsAsDataLanguageAutoModelGeneration; accessPathSuggestions?: ModelsAsDataLanguageAccessPathSuggestions; /** * Returns the list of valid arguments that can be selected for the given method. diff --git a/extensions/ql-vscode/src/model-editor/languages/ruby/generate.ts b/extensions/ql-vscode/src/model-editor/languages/ruby/generate.ts index 64a10f0ee53..3d6e3ae0c04 100644 --- a/extensions/ql-vscode/src/model-editor/languages/ruby/generate.ts +++ b/extensions/ql-vscode/src/model-editor/languages/ruby/generate.ts @@ -1,6 +1,9 @@ import type { BaseLogger } from "../../../common/logging"; import type { DecodedBqrs } from "../../../common/bqrs-cli-types"; -import type { ModelsAsDataLanguage } from "../models-as-data"; +import type { + GenerationContext, + ModelsAsDataLanguage, +} from "../models-as-data"; import type { ModeledMethod } from "../../modeled-method"; import type { DataTuple } from "../../model-extension-file"; @@ -9,10 +12,21 @@ export function parseGenerateModelResults( bqrs: DecodedBqrs, modelsAsDataLanguage: ModelsAsDataLanguage, logger: BaseLogger, + { isCanary }: GenerationContext, ): ModeledMethod[] { const modeledMethods: ModeledMethod[] = []; for (const resultSetName in bqrs) { + if ( + resultSetName === + modelsAsDataLanguage.predicates.type?.extensiblePredicate && + !isCanary + ) { + // Don't load generated type results in non-canary mode. These are already automatically + // generated on start-up. + continue; + } + const definition = Object.values(modelsAsDataLanguage.predicates).find( (definition) => definition.extensiblePredicate === resultSetName, ); diff --git a/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts b/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts index bd6c9dc11bc..aa694cac4c3 100644 --- a/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts +++ b/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts @@ -177,28 +177,39 @@ export const ruby: ModelsAsDataLanguage = { "tags contain all": ["modeleditor", "generate-model", modeTag(mode)], }), parseResults: parseGenerateModelResults, - autoRun: { - parseResults: (queryPath, bqrs, modelsAsDataLanguage, logger) => { - // Only type models are generated automatically - const typePredicate = modelsAsDataLanguage.predicates.type; - if (!typePredicate) { - throw new Error("Type predicate not found"); - } + }, + autoModelGeneration: { + queryConstraints: (mode) => ({ + kind: "table", + "tags contain all": ["modeleditor", "generate-model", modeTag(mode)], + }), + parseResultsToYaml: (_queryPath, bqrs, modelsAsDataLanguage) => { + const typePredicate = modelsAsDataLanguage.predicates.type; + if (!typePredicate) { + throw new Error("Type predicate not found"); + } - const filteredBqrs = Object.fromEntries( - Object.entries(bqrs).filter( - ([key]) => key === typePredicate.extensiblePredicate, - ), - ); + const typeTuples = bqrs[typePredicate.extensiblePredicate]; + if (!typeTuples) { + return []; + } - return parseGenerateModelResults( - queryPath, - filteredBqrs, - modelsAsDataLanguage, - logger, - ); - }, + return [ + { + addsTo: { + pack: "codeql/ruby-all", + extensible: typePredicate.extensiblePredicate, + }, + data: typeTuples.tuples.filter((tuple): tuple is string[] => { + return ( + tuple.filter((x) => typeof x === "string").length === tuple.length + ); + }), + }, + ]; }, + // Only enabled for framework mode in non-canary + enabled: ({ mode, isCanary }) => mode === Mode.Framework && !isCanary, }, accessPathSuggestions: { queryConstraints: (mode) => ({ diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 31ca255f65b..3b2959ecf86 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -40,8 +40,13 @@ import type { Method } from "./method"; import type { ModeledMethod } from "./modeled-method"; import type { ExtensionPack } from "./shared/extension-pack"; import type { ModelConfigListener } from "../config"; +import { isCanary } from "../config"; import { Mode } from "./shared/mode"; -import { loadModeledMethods, saveModeledMethods } from "./modeled-method-fs"; +import { + GENERATED_MODELS_SUFFIX, + loadModeledMethods, + saveModeledMethods, +} from "./modeled-method-fs"; import { pickExtensionPack } from "./extension-pack-picker"; import type { QueryLanguage } from "../common/query-language"; import { getLanguageDisplayName } from "../common/query-language"; @@ -60,6 +65,10 @@ import { parseAccessPathSuggestionRowsToOptions } from "./suggestions-bqrs"; import { ModelEvaluator } from "./model-evaluator"; import type { ModelEvaluationRunState } from "./shared/model-evaluation-run-state"; import type { VariantAnalysisManager } from "../variant-analysis/variant-analysis-manager"; +import type { ModelExtensionFile } from "./model-extension-file"; +import { modelExtensionFileToYaml } from "./yaml"; +import { outputFile } from "fs-extra"; +import { join } from "path"; export class ModelEditorView extends AbstractWebview< ToModelEditorMessage, @@ -645,14 +654,18 @@ export class ModelEditorView extends AbstractWebview< await runGenerateQueries({ queryConstraints: modelGeneration.queryConstraints(mode), filterQueries: modelGeneration.filterQueries, - parseResults: (queryPath, results) => - modelGeneration.parseResults( + onResults: async (queryPath, results) => { + const modeledMethods = modelGeneration.parseResults( queryPath, results, modelsAsDataLanguage, this.app.logger, - ), - onResults: async (modeledMethods) => { + { + mode, + isCanary: isCanary(), + }, + ); + this.addModeledMethodsFromArray(modeledMethods); }, cliServer: this.cliServer, @@ -678,15 +691,17 @@ export class ModelEditorView extends AbstractWebview< protected async generateModeledMethodsOnStartup(): Promise { const mode = this.modelingStore.getMode(this.databaseItem); - if (mode !== Mode.Framework) { + const modelsAsDataLanguage = getModelsAsDataLanguage(this.language); + const autoModelGeneration = modelsAsDataLanguage.autoModelGeneration; + + if (autoModelGeneration === undefined) { return; } - const modelsAsDataLanguage = getModelsAsDataLanguage(this.language); - const modelGeneration = modelsAsDataLanguage.modelGeneration; - const autoRun = modelGeneration?.autoRun; - - if (modelGeneration === undefined || autoRun === undefined) { + if ( + autoModelGeneration.enabled && + !autoModelGeneration.enabled({ mode, isCanary: isCanary() }) + ) { return; } @@ -698,22 +713,23 @@ export class ModelEditorView extends AbstractWebview< message: "Generating models", }); - const parseResults = - autoRun.parseResults ?? modelGeneration.parseResults; + const extensionFile: ModelExtensionFile = { + extensions: [], + }; try { await runGenerateQueries({ - queryConstraints: modelGeneration.queryConstraints(mode), - filterQueries: modelGeneration.filterQueries, - parseResults: (queryPath, results) => - parseResults( + queryConstraints: autoModelGeneration.queryConstraints(mode), + filterQueries: autoModelGeneration.filterQueries, + onResults: (queryPath, results) => { + const extensions = autoModelGeneration.parseResultsToYaml( queryPath, results, modelsAsDataLanguage, this.app.logger, - ), - onResults: async (modeledMethods) => { - this.addModeledMethodsFromArray(modeledMethods); + ); + + extensionFile.extensions.push(...extensions); }, cliServer: this.cliServer, queryRunner: this.queryRunner, @@ -730,7 +746,25 @@ export class ModelEditorView extends AbstractWebview< asError(e), )`Failed to auto-run generating models: ${getErrorMessage(e)}`, ); + return; } + + progress({ + step: 4000, + maxStep: 4000, + message: "Saving generated models", + }); + + const fileContents = `# This file was automatically generated based from ${this.databaseItem.name}. Manual changes will not persist.\n\n${modelExtensionFileToYaml(extensionFile)}`; + const filePath = join( + this.extensionPack.path, + "models", + `${this.language}${GENERATED_MODELS_SUFFIX}`, + ); + + await outputFile(filePath, fileContents); + + void this.app.logger.log(`Saved generated model file to ${filePath}`); }, { cancellable: false, diff --git a/extensions/ql-vscode/src/model-editor/modeled-method-fs.ts b/extensions/ql-vscode/src/model-editor/modeled-method-fs.ts index c7f40cd1a44..f56aed39fff 100644 --- a/extensions/ql-vscode/src/model-editor/modeled-method-fs.ts +++ b/extensions/ql-vscode/src/model-editor/modeled-method-fs.ts @@ -12,6 +12,9 @@ import { load as loadYaml } from "js-yaml"; import type { CodeQLCliServer } from "../codeql-cli/cli"; import { pathsEqual } from "../common/files"; import type { QueryLanguage } from "../common/query-language"; +import { isCanary } from "../config"; + +export const GENERATED_MODELS_SUFFIX = ".model.generated.yml"; export async function saveModeledMethods( extensionPack: ExtensionPack, @@ -118,6 +121,11 @@ export async function listModelFiles( for (const [path, extensions] of Object.entries(result.data)) { if (pathsEqual(path, extensionPackPath)) { for (const extension of extensions) { + // We only load generated models in canary mode + if (!isCanary() && extension.file.endsWith(GENERATED_MODELS_SUFFIX)) { + continue; + } + modelFiles.add(relative(extensionPackPath, extension.file)); } } diff --git a/extensions/ql-vscode/src/model-editor/yaml.ts b/extensions/ql-vscode/src/model-editor/yaml.ts index 10c1c9b87e4..a31df81cd88 100644 --- a/extensions/ql-vscode/src/model-editor/yaml.ts +++ b/extensions/ql-vscode/src/model-editor/yaml.ts @@ -337,7 +337,7 @@ function validateModelExtensionFile(data: unknown): data is ModelExtensionFile { * * @param data The data extension file */ -function modelExtensionFileToYaml(data: ModelExtensionFile) { +export function modelExtensionFileToYaml(data: ModelExtensionFile) { const extensions = data.extensions .map((extension) => { const data = diff --git a/extensions/ql-vscode/test/unit-tests/model-editor/languages/ruby/generate.test.ts b/extensions/ql-vscode/test/unit-tests/model-editor/languages/ruby/generate.test.ts index fb7737bfc25..c280da68b47 100644 --- a/extensions/ql-vscode/test/unit-tests/model-editor/languages/ruby/generate.test.ts +++ b/extensions/ql-vscode/test/unit-tests/model-editor/languages/ruby/generate.test.ts @@ -4,6 +4,7 @@ import { ruby } from "../../../../../src/model-editor/languages/ruby"; import { createMockLogger } from "../../../../__mocks__/loggerMock"; import type { ModeledMethod } from "../../../../../src/model-editor/modeled-method"; import { EndpointType } from "../../../../../src/model-editor/method"; +import { Mode } from "../../../../../src/model-editor/shared/mode"; describe("parseGenerateModelResults", () => { it("should return the results", async () => { @@ -76,6 +77,10 @@ describe("parseGenerateModelResults", () => { bqrs, ruby, createMockLogger(), + { + isCanary: true, + mode: Mode.Framework, + }, ); expect(result).toEqual([ { diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/generate.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/generate.test.ts index ba79d4d2983..4ac7a594514 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/generate.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/generate.test.ts @@ -128,14 +128,20 @@ describe("runGenerateQueries", () => { await runGenerateQueries({ queryConstraints: modelGeneration.queryConstraints(Mode.Framework), filterQueries: modelGeneration.filterQueries, - parseResults: (queryPath, results) => - modelGeneration.parseResults( - queryPath, - results, - modelsAsDataLanguage, - createMockLogger(), - ), - onResults, + onResults: (queryPath, results) => { + onResults( + modelGeneration.parseResults( + queryPath, + results, + modelsAsDataLanguage, + createMockLogger(), + { + isCanary: true, + mode: Mode.Framework, + }, + ), + ); + }, ...options, }); expect(onResults).toHaveBeenCalledWith([ From 48718ca2e68169d286be9fa469a3fc5a5ca00639 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 23 Feb 2024 15:47:40 +0100 Subject: [PATCH 0036/1237] Reduce repetition in `MultipleModeledMethodsPanel.spec.tsx` --- .../MultipleModeledMethodsPanel.spec.tsx | 391 ++++-------------- 1 file changed, 72 insertions(+), 319 deletions(-) diff --git a/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx b/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx index 44ab596c627..a5893df2a8a 100644 --- a/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx @@ -10,31 +10,43 @@ import { MultipleModeledMethodsPanel } from "../MultipleModeledMethodsPanel"; import { userEvent } from "@testing-library/user-event"; import type { ModeledMethod } from "../../../model-editor/modeled-method"; import { QueryLanguage } from "../../../common/query-language"; +import type { ModelingStatus } from "../../../model-editor/shared/modeling-status"; describe(MultipleModeledMethodsPanel.name, () => { - const render = (props: MultipleModeledMethodsPanelProps) => - reactRender(); - const language = QueryLanguage.Java; const method = createMethod(); const isModelingInProgress = false; const isProcessedByAutoModel = false; - const modelingStatus = "unmodeled"; + const modelingStatus: ModelingStatus = "unmodeled"; const onChange = jest.fn(); + const baseProps = { + language, + method, + modelingStatus, + isModelingInProgress, + isProcessedByAutoModel, + onChange, + }; + + const createRender = + (modeledMethods: ModeledMethod[]) => + (props: Partial = {}) => + reactRender( + , + ); + describe("with no modeled methods", () => { const modeledMethods: ModeledMethod[] = []; + const render = createRender(modeledMethods); + it("renders the method modeling inputs once", () => { - render({ - language, - method, - modeledMethods, - modelingStatus, - isModelingInProgress, - isProcessedByAutoModel, - onChange, - }); + render(); expect(screen.getAllByRole("combobox")).toHaveLength(4); expect( @@ -45,15 +57,7 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("disables all pagination", () => { - render({ - language, - method, - modeledMethods, - modelingStatus, - isModelingInProgress, - isProcessedByAutoModel, - onChange, - }); + render(); expect( screen @@ -68,15 +72,7 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("cannot add or delete modeling", () => { - render({ - language, - method, - modeledMethods, - modelingStatus, - isModelingInProgress, - isProcessedByAutoModel, - onChange, - }); + render(); expect( screen @@ -99,16 +95,10 @@ describe(MultipleModeledMethodsPanel.name, () => { }), ]; + const render = createRender(modeledMethods); + it("renders the method modeling inputs once", () => { - render({ - language, - method, - modeledMethods, - modelingStatus, - isModelingInProgress, - isProcessedByAutoModel, - onChange, - }); + render(); expect(screen.getAllByRole("combobox")).toHaveLength(4); expect( @@ -119,15 +109,7 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("disables all pagination", () => { - render({ - language, - method, - modeledMethods, - modelingStatus, - isModelingInProgress, - isProcessedByAutoModel, - onChange, - }); + render(); expect( screen @@ -141,15 +123,7 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("cannot delete modeling", () => { - render({ - language, - method, - modeledMethods, - modelingStatus, - isModelingInProgress, - isProcessedByAutoModel, - onChange, - }); + render(); expect( screen @@ -159,15 +133,7 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("can add modeling", async () => { - render({ - language, - method, - modeledMethods, - modelingStatus, - isModelingInProgress, - isProcessedByAutoModel, - onChange, - }); + render(); await userEvent.click(screen.getByLabelText("Add modeling")); @@ -186,29 +152,16 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("changes selection to the newly added modeling", async () => { - const { rerender } = render({ - language, - method, - modeledMethods, - modelingStatus, - isModelingInProgress, - isProcessedByAutoModel, - onChange, - }); + const { rerender } = render(); await userEvent.click(screen.getByLabelText("Add modeling")); rerender( , ); @@ -226,16 +179,10 @@ describe(MultipleModeledMethodsPanel.name, () => { }), ]; + const render = createRender(modeledMethods); + it("renders the method modeling inputs once", () => { - render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + render(); expect(screen.getAllByRole("combobox")).toHaveLength(4); expect( @@ -246,15 +193,7 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("renders the pagination", () => { - render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + render(); expect(screen.getByLabelText("Previous modeling")).toBeInTheDocument(); expect(screen.getByLabelText("Next modeling")).toBeInTheDocument(); @@ -262,15 +201,7 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("disables the correct pagination", async () => { - render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + render(); expect( screen @@ -283,15 +214,7 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("can use the pagination", async () => { - render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + render(); await userEvent.click(screen.getByLabelText("Next modeling")); @@ -321,27 +244,14 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("correctly updates selected pagination index when the number of models decreases", async () => { - const { rerender } = render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + const { rerender } = render(); await userEvent.click(screen.getByLabelText("Next modeling")); rerender( , ); @@ -354,29 +264,13 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("does not show errors", () => { - render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + render(); expect(screen.queryByRole("alert")).not.toBeInTheDocument(); }); it("can update the first modeling", async () => { - render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + render(); const modelTypeDropdown = screen.getByRole("combobox", { name: "Model type", @@ -402,15 +296,7 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("can update the second modeling", async () => { - render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + render(); await userEvent.click(screen.getByLabelText("Next modeling")); @@ -438,15 +324,7 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("can delete modeling", async () => { - render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + render(); await userEvent.click(screen.getByLabelText("Delete modeling")); @@ -457,15 +335,7 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("can add modeling", async () => { - render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + render(); await userEvent.click(screen.getByLabelText("Add modeling")); @@ -484,29 +354,16 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("shows an error when adding a neutral modeling", async () => { - const { rerender } = render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + const { rerender } = render(); await userEvent.click(screen.getByLabelText("Add modeling")); rerender( , ); @@ -520,15 +377,10 @@ describe(MultipleModeledMethodsPanel.name, () => { rerender( , ); @@ -540,15 +392,10 @@ describe(MultipleModeledMethodsPanel.name, () => { rerender( , ); @@ -559,15 +406,7 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("changes selection to the newly added modeling", async () => { - const { rerender } = render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + const { rerender } = render(); expect(screen.getByText("1/2")).toBeInTheDocument(); @@ -575,15 +414,10 @@ describe(MultipleModeledMethodsPanel.name, () => { rerender( , ); @@ -610,16 +444,10 @@ describe(MultipleModeledMethodsPanel.name, () => { }), ]; + const render = createRender(modeledMethods); + it("can use the pagination", async () => { - render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + render(); expect( screen @@ -703,27 +531,14 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("preserves selection when a modeling other than the selected modeling is removed", async () => { - const { rerender } = render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + const { rerender } = render(); expect(screen.getByText("1/3")).toBeInTheDocument(); rerender( , ); @@ -731,15 +546,7 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("reduces selection when the selected modeling is removed", async () => { - const { rerender } = render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + const { rerender } = render(); await userEvent.click(screen.getByLabelText("Next modeling")); await userEvent.click(screen.getByLabelText("Next modeling")); @@ -747,13 +554,8 @@ describe(MultipleModeledMethodsPanel.name, () => { rerender( , ); @@ -774,16 +576,10 @@ describe(MultipleModeledMethodsPanel.name, () => { }), ]; + const render = createRender(modeledMethods); + it("can add modeling", () => { - render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + render(); expect( screen.getByLabelText("Add modeling").getElementsByTagName("input")[0], @@ -791,15 +587,7 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("can delete first modeling", async () => { - render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + render(); await userEvent.click(screen.getByLabelText("Delete modeling")); @@ -810,15 +598,7 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("can delete second modeling", async () => { - render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + render(); await userEvent.click(screen.getByLabelText("Next modeling")); await userEvent.click(screen.getByLabelText("Delete modeling")); @@ -830,15 +610,7 @@ describe(MultipleModeledMethodsPanel.name, () => { }); it("can add modeling after deleting second modeling", async () => { - const { rerender } = render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + const { rerender } = render(); await userEvent.click(screen.getByLabelText("Next modeling")); await userEvent.click(screen.getByLabelText("Delete modeling")); @@ -850,13 +622,8 @@ describe(MultipleModeledMethodsPanel.name, () => { rerender( , ); @@ -888,30 +655,16 @@ describe(MultipleModeledMethodsPanel.name, () => { }), ]; + const render = createRender(modeledMethods); + it("shows errors", () => { - render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + render(); expect(screen.getByRole("alert")).toBeInTheDocument(); }); it("shows the correct error message", async () => { - render({ - language, - method, - modeledMethods, - isModelingInProgress, - isProcessedByAutoModel, - modelingStatus, - onChange, - }); + render(); expect( screen.getByText("Error: Duplicated classification"), From ca21ed18d02ec09161cfde2407ae4b018cb1bfb8 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Sun, 25 Feb 2024 10:58:55 -0800 Subject: [PATCH 0037/1237] Import testproj databases into workspace storage If a folder that ends with `.testproj` is encountered, assume it is a database created by a codeql test. When the user wants to import this database, copy it into workspace storage. The database can be re-imported, which first removes the old version before importing it again. --- extensions/ql-vscode/package-lock.json | 141 +++++++++++++++++- extensions/ql-vscode/package.json | 18 ++- extensions/ql-vscode/src/common/commands.ts | 1 + .../src/databases/database-fetcher.ts | 58 +++++-- .../src/databases/local-databases-ui.ts | 55 ++++++- .../local-databases/database-manager.ts | 17 +++ .../local-databases/database-origin.ts | 8 +- .../databases/database-fetcher.test.ts | 26 +++- .../cli-integration/jest.setup.ts | 16 +- .../test/vscode-tests/global.helper.ts | 10 +- 10 files changed, 322 insertions(+), 28 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 69daa843735..7eee552c114 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -140,7 +140,8 @@ "ts-loader": "^9.4.2", "ts-node": "^10.7.0", "ts-unused-exports": "^10.0.0", - "typescript": "^5.0.2" + "typescript": "^5.0.2", + "unzipper": "^0.10.14" }, "engines": { "node": "^18.17.1", @@ -12242,6 +12243,19 @@ "node": "*" } }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dev": true, + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -12295,6 +12309,12 @@ "node": ">= 6" } }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "dev": true + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -12520,6 +12540,24 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "dev": true, + "engines": { + "node": ">=0.2.0" + } + }, "node_modules/bundle-name": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", @@ -12643,6 +12681,18 @@ "node": ">=4" } }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dev": true, + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -15003,6 +15053,15 @@ "node": ">=12" } }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, "node_modules/duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -17818,6 +17877,53 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/fstream/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fstream/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -23787,6 +23893,12 @@ "node": ">= 8" } }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "dev": true + }, "node_modules/listr2": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.0.0.tgz", @@ -30690,6 +30802,15 @@ "node": ">=12" } }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -31632,6 +31753,24 @@ "node": ">=8" } }, + "node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, "node_modules/upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 3499c3a2060..249f15f3b37 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -738,6 +738,10 @@ "command": "codeQL.setCurrentDatabase", "title": "CodeQL: Set Current Database" }, + { + "command": "codeQL.importTestDatabase", + "title": "CodeQL: (Re-)Import Test Database" + }, { "command": "codeQL.getCurrentDatabase", "title": "CodeQL: Get Current Database" @@ -1322,7 +1326,12 @@ { "command": "codeQL.setCurrentDatabase", "group": "9_qlCommands", - "when": "resourceScheme == codeql-zip-archive || explorerResourceIsFolder || resourceExtname == .zip" + "when": "resourceExtname != .testproj && (resourceScheme == codeql-zip-archive || explorerResourceIsFolder || resourceExtname == .zipz)" + }, + { + "command": "codeQL.importTestDatabase", + "group": "9_qlCommands", + "when": "explorerResourceIsFolder && resourceExtname == .testproj" }, { "command": "codeQL.viewAstContextExplorer", @@ -1476,6 +1485,10 @@ "command": "codeQL.setCurrentDatabase", "when": "false" }, + { + "command": "codeQL.importTestDatabase", + "when": "false" + }, { "command": "codeQL.getCurrentDatabase", "when": "false" @@ -2068,7 +2081,8 @@ "ts-loader": "^9.4.2", "ts-node": "^10.7.0", "ts-unused-exports": "^10.0.0", - "typescript": "^5.0.2" + "typescript": "^5.0.2", + "unzipper": "^0.10.14" }, "lint-staged": { "./**/*.{json,css,scss}": [ diff --git a/extensions/ql-vscode/src/common/commands.ts b/extensions/ql-vscode/src/common/commands.ts index b1463a84853..ded87aea391 100644 --- a/extensions/ql-vscode/src/common/commands.ts +++ b/extensions/ql-vscode/src/common/commands.ts @@ -220,6 +220,7 @@ export type LocalDatabasesCommands = { // Explorer context menu "codeQL.setCurrentDatabase": (uri: Uri) => Promise; + "codeQL.importTestDatabase": (uri: Uri) => Promise; // Database panel view title commands "codeQLDatabases.chooseDatabaseFolder": () => Promise; diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index c9ccbdf09ae..70c6b1509e6 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -12,6 +12,7 @@ import { remove, stat, readdir, + copy, } from "fs-extra"; import { basename, join } from "path"; import type { Octokit } from "@octokit/rest"; @@ -61,7 +62,7 @@ export async function promptImportInternetDatabase( validateUrl(databaseUrl); - const item = await databaseArchiveFetcher( + const item = await fetchDatabaseToWorkspaceStorage( databaseUrl, {}, databaseManager, @@ -254,7 +255,7 @@ export async function downloadGitHubDatabaseFromUrl( * We only need the actual token string. */ const octokitToken = ((await octokit.auth()) as { token: string })?.token; - return await databaseArchiveFetcher( + return await fetchDatabaseToWorkspaceStorage( databaseUrl, { Accept: "application/zip", @@ -278,14 +279,15 @@ export async function downloadGitHubDatabaseFromUrl( } /** - * Imports a database from a local archive. + * Imports a database from a local archive or a test database that is in a folder + * ending with `.testproj`. * - * @param databaseUrl the file url of the archive to import + * @param databaseUrl the file url of the archive or directory to import * @param databaseManager the DatabaseManager * @param storagePath where to store the unzipped database. * @param cli the CodeQL CLI server */ -export async function importArchiveDatabase( +export async function importLocalDatabase( commandManager: AppCommandManager, databaseUrl: string, databaseManager: DatabaseManager, @@ -294,16 +296,18 @@ export async function importArchiveDatabase( cli: CodeQLCliServer, ): Promise { try { - const item = await databaseArchiveFetcher( + const origin: DatabaseOrigin = { + type: databaseUrl.endsWith(".testproj") ? "testproj" : "archive", + // TODO validate that archive origins can use a file path, not a URI + path: Uri.parse(databaseUrl).fsPath, + }; + const item = await fetchDatabaseToWorkspaceStorage( databaseUrl, {}, databaseManager, storagePath, undefined, - { - type: "archive", - path: databaseUrl, - }, + origin, progress, cli, ); @@ -328,10 +332,10 @@ export async function importArchiveDatabase( } /** - * Fetches an archive database. The database might be on the internet + * Fetches a database into workspace storage. The database might be on the internet * or in the local filesystem. * - * @param databaseUrl URL from which to grab the database + * @param databaseUrl URL from which to grab the database. This could be a local archive file, a local directory, or a remote URL. * @param requestHeaders Headers to send with the request * @param databaseManager the DatabaseManager * @param storagePath where to store the unzipped database. @@ -342,7 +346,7 @@ export async function importArchiveDatabase( * @param makeSelected make the new database selected in the databases panel (default: true) * @param addSourceArchiveFolder whether to add a workspace folder containing the source archive to the workspace */ -async function databaseArchiveFetcher( +async function fetchDatabaseToWorkspaceStorage( databaseUrl: string, requestHeaders: { [key: string]: string }, databaseManager: DatabaseManager, @@ -366,7 +370,11 @@ async function databaseArchiveFetcher( const unzipPath = await getStorageFolder(storagePath, databaseUrl); if (isFile(databaseUrl)) { - await readAndUnzip(databaseUrl, unzipPath, cli, progress); + if (origin.type == "testproj") { + await copyDatabase(origin.path, unzipPath, progress); + } else { + await readAndUnzip(databaseUrl, unzipPath, cli, progress); + } } else { await fetchAndUnzip(databaseUrl, requestHeaders, unzipPath, cli, progress); } @@ -416,6 +424,8 @@ async function getStorageFolder(storagePath: string, urlStr: string) { let lastName = basename(url.path).substring(0, 250); if (lastName.endsWith(".zip")) { lastName = lastName.substring(0, lastName.length - 4); + } else if (lastName.endsWith(".testproj")) { + lastName = lastName.substring(0, lastName.length - 9); } const realpath = await fs_realpath(storagePath); @@ -446,6 +456,26 @@ function validateUrl(databaseUrl: string) { } } +/** + * Copies a database folder from the file system into the workspace storage. + * @param scrDir the original location of the database + * @param destDir the location to copy the database to. This should be a folder in the workspace storage. + * @param progress callback to send progress messages to + */ +async function copyDatabase( + scrDir: string, + destDir: string, + progress?: ProgressCallback, +) { + progress?.({ + maxStep: 10, + step: 9, + message: `Copying database ${basename(destDir)} into the workspace`, + }); + await ensureDir(destDir); + await copy(scrDir, destDir); +} + async function readAndUnzip( zipUrl: string, unzipPath: string, diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 70f4c6eefcf..8cf57b6ce6b 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -43,7 +43,7 @@ import { showAndLogErrorMessage, } from "../common/logging"; import { - importArchiveDatabase, + importLocalDatabase, promptImportGithubDatabase, promptImportInternetDatabase, } from "./database-fetcher"; @@ -282,6 +282,7 @@ export class DatabaseUI extends DisposableObject { this.handleChooseDatabaseInternet.bind(this), "codeQL.chooseDatabaseGithub": this.handleChooseDatabaseGithub.bind(this), "codeQL.setCurrentDatabase": this.handleSetCurrentDatabase.bind(this), + "codeQL.importTestDatabase": this.handleImportTestDatabase.bind(this), "codeQL.setDefaultTourDatabase": this.handleSetDefaultTourDatabase.bind(this), "codeQL.upgradeCurrentDatabase": @@ -716,7 +717,7 @@ export class DatabaseUI extends DisposableObject { try { // Assume user has selected an archive if the file has a .zip extension if (uri.path.endsWith(".zip")) { - await importArchiveDatabase( + await importLocalDatabase( this.app.commands, uri.toString(true), this.databaseManager, @@ -744,6 +745,54 @@ export class DatabaseUI extends DisposableObject { ); } + private async handleImportTestDatabase(uri: Uri): Promise { + return withProgress( + async (progress) => { + try { + // Assume user has selected an archive if the file has a .zip extension + if (!uri.path.endsWith(".testproj")) { + throw new Error( + "Please select a valid test database to import. Test databases end with `.testproj`.", + ); + } + + // Check if the database is already in the workspace. If + // so, delete it first before importing the new one. + const existingItem = this.databaseManager.findTestDatabase(uri); + if (existingItem !== undefined) { + progress({ + maxStep: 9, + step: 1, + message: `Removing existing test database ${basename( + uri.fsPath, + )}`, + }); + await this.databaseManager.removeDatabaseItem(existingItem); + } + + await importLocalDatabase( + this.app.commands, + uri.toString(true), + this.databaseManager, + this.storagePath, + progress, + this.queryServer.cliServer, + ); + } catch (e) { + // rethrow and let this be handled by default error handling. + throw new Error( + `Could not set database to ${basename( + uri.fsPath, + )}. Reason: ${getErrorMessage(e)}`, + ); + } + }, + { + title: "(Re-)importing test database from directory", + }, + ); + } + private async handleRemoveDatabase( databaseItems: DatabaseItem[], ): Promise { @@ -963,7 +1012,7 @@ export class DatabaseUI extends DisposableObject { } else { // we are selecting a database archive. Must unzip into a workspace-controlled area // before importing. - return await importArchiveDatabase( + return await importLocalDatabase( this.app.commands, uri.toString(true), this.databaseManager, diff --git a/extensions/ql-vscode/src/databases/local-databases/database-manager.ts b/extensions/ql-vscode/src/databases/local-databases/database-manager.ts index c78a91b9194..f1234b2f74a 100644 --- a/extensions/ql-vscode/src/databases/local-databases/database-manager.ts +++ b/extensions/ql-vscode/src/databases/local-databases/database-manager.ts @@ -159,6 +159,23 @@ export class DatabaseManager extends DisposableObject { ); } + /** + * Finds a test database that was originally imported from `uri`. + * A test database is creeated by the `codeql test run` command + * and ends with `.testproj`. + * @param uri The original location of the database + * @returns The first database item found that matches the uri + */ + public findTestDatabase(uri: vscode.Uri): DatabaseItem | undefined { + const originPath = uri.fsPath; + for (const item of this._databaseItems) { + if (item.origin?.type === "testproj" && item.origin.path === originPath) { + return item + } + } + return undefined; + } + /** * Adds a {@link DatabaseItem} to the list of open databases, if that database is not already on * the list. diff --git a/extensions/ql-vscode/src/databases/local-databases/database-origin.ts b/extensions/ql-vscode/src/databases/local-databases/database-origin.ts index 7c9aba02d52..e9541b8b5e2 100644 --- a/extensions/ql-vscode/src/databases/local-databases/database-origin.ts +++ b/extensions/ql-vscode/src/databases/local-databases/database-origin.ts @@ -24,9 +24,15 @@ interface DatabaseOriginDebugger { type: "debugger"; } +export interface DatabaseOriginTestProj { + type: "testproj"; + path: string; +} + export type DatabaseOrigin = | DatabaseOriginFolder | DatabaseOriginArchive | DatabaseOriginGitHub | DatabaseOriginInternet - | DatabaseOriginDebugger; + | DatabaseOriginDebugger + | DatabaseOriginTestProj; diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/databases/database-fetcher.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/databases/database-fetcher.test.ts index ed80c1f037b..1f8f7a5c8f6 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/databases/database-fetcher.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/databases/database-fetcher.test.ts @@ -4,7 +4,7 @@ import { Uri, window } from "vscode"; import type { CodeQLCliServer } from "../../../../src/codeql-cli/cli"; import type { DatabaseManager } from "../../../../src/databases/local-databases"; import { - importArchiveDatabase, + importLocalDatabase, promptImportInternetDatabase, } from "../../../../src/databases/database-fetcher"; import { @@ -13,6 +13,7 @@ import { DB_URL, getActivatedExtension, storagePath, + testprojLoc, } from "../../global.helper"; import { createMockCommandManager } from "../../../__mocks__/commandsMock"; import { remove } from "fs-extra"; @@ -46,10 +47,10 @@ describe("database-fetcher", () => { await remove(storagePath); }); - describe("importArchiveDatabase", () => { - it("should add a database from a folder", async () => { + describe("importLocalDatabase", () => { + it("should add a database from an archive", async () => { const uri = Uri.file(dbLoc); - let dbItem = await importArchiveDatabase( + let dbItem = await importLocalDatabase( createMockCommandManager(), uri.toString(true), databaseManager, @@ -64,6 +65,23 @@ describe("database-fetcher", () => { expect(dbItem.name).toBe("db"); expect(dbItem.databaseUri.fsPath).toBe(join(storagePath, "db", "db")); }); + + it("should import a testproj database", async () => { + let dbItem = await importLocalDatabase( + createMockCommandManager(), + Uri.file(testprojLoc).toString(true), + databaseManager, + storagePath, + progressCallback, + cli, + ); + expect(dbItem).toBe(databaseManager.currentDatabaseItem); + expect(dbItem).toBe(databaseManager.databaseItems[0]); + expect(dbItem).toBeDefined(); + dbItem = dbItem!; + expect(dbItem.name).toBe("db"); + expect(dbItem.databaseUri.fsPath).toBe(join(storagePath, "db", "db")); + }); }); describe("promptImportInternetDatabase", () => { diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts index 4fcbfe775cf..69c2fd804cd 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts @@ -7,8 +7,10 @@ import { } from "../jest.activated-extension.setup"; import { createWriteStream, existsSync, mkdirpSync } from "fs-extra"; import { dirname } from "path"; -import { DB_URL, dbLoc } from "../global.helper"; +import { DB_URL, dbLoc, testprojLoc } from "../global.helper"; import fetch from "node-fetch"; +import { createReadStream, renameSync } from "fs"; +import { Extract } from "unzipper"; beforeAll(async () => { // ensure the test database is downloaded @@ -28,6 +30,18 @@ beforeAll(async () => { }); }); }); + + // unzip the database from dbLoc to testprojLoc + if (!existsSync(testprojLoc)) { + console.log(`Unzipping test database to ${testprojLoc}`); + const dbDir = dirname(testprojLoc); + mkdirpSync(dbDir); + console.log(`Unzipping test database to ${testprojLoc}`); + createReadStream(dbLoc) + .pipe(Extract({ path: dirname(dbDir) })) + .on("close", () => console.log("Unzip completed.")); + } + renameSync(dbLoc, testprojLoc); } await beforeAllAction(); diff --git a/extensions/ql-vscode/test/vscode-tests/global.helper.ts b/extensions/ql-vscode/test/vscode-tests/global.helper.ts index 36be9878915..1e104d9c54a 100644 --- a/extensions/ql-vscode/test/vscode-tests/global.helper.ts +++ b/extensions/ql-vscode/test/vscode-tests/global.helper.ts @@ -7,7 +7,7 @@ import type { } from "../../src/databases/local-databases"; import type { CodeQLCliServer } from "../../src/codeql-cli/cli"; import type { CodeQLExtensionInterface } from "../../src/extension"; -import { importArchiveDatabase } from "../../src/databases/database-fetcher"; +import { importLocalDatabase } from "../../src/databases/database-fetcher"; import { createMockCommandManager } from "../__mocks__/commandsMock"; // This file contains helpers shared between tests that work with an activated extension. @@ -21,6 +21,12 @@ export const dbLoc = join( realpathSync(join(__dirname, "../../../")), "build/tests/db.zip", ); + +export const testprojLoc = join( + realpathSync(join(__dirname, "../../../")), + "build/tests/db.zip", +); + // eslint-disable-next-line import/no-mutable-exports export let storagePath: string; @@ -34,7 +40,7 @@ export async function ensureTestDatabase( // Add a database, but make sure the database manager is empty first await cleanDatabases(databaseManager); const uri = Uri.file(dbLoc); - const maybeDbItem = await importArchiveDatabase( + const maybeDbItem = await importLocalDatabase( createMockCommandManager(), uri.toString(true), databaseManager, From 7591c65db2f65ad3c04e296f81fa03aa4a90817e Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 26 Feb 2024 11:19:23 +0100 Subject: [PATCH 0038/1237] Add `showTypeModels` setting --- extensions/ql-vscode/src/config.ts | 6 +++++ .../model-editor/languages/models-as-data.ts | 26 ++++++++++++++++++- .../src/model-editor/languages/ruby/index.ts | 3 +-- .../method-modeling-view-provider.ts | 4 +-- .../src/model-editor/model-editor-view.ts | 5 ++-- .../src/model-editor/shared/view-state.ts | 5 ++-- .../MethodModelingInputs.stories.tsx | 3 ++- .../view/method-modeling/MethodModeling.tsx | 7 ++--- .../method-modeling/MethodModelingInputs.tsx | 7 ++--- .../method-modeling/MethodModelingView.tsx | 3 ++- .../MultipleModeledMethodsPanel.tsx | 9 ++++--- .../__tests__/MethodModeling.spec.tsx | 3 ++- .../__tests__/MethodModelingInputs.spec.tsx | 13 +++++----- .../MultipleModeledMethodsPanel.spec.tsx | 5 ++-- .../src/view/model-editor/MethodRow.tsx | 4 +-- .../view/model-editor/ModelTypeDropdown.tsx | 16 ++++++++---- .../__tests__/ModelTypeDropdown.spec.tsx | 12 ++++++--- .../test/factories/model-editor/view-state.ts | 3 ++- 18 files changed, 91 insertions(+), 43 deletions(-) diff --git a/extensions/ql-vscode/src/config.ts b/extensions/ql-vscode/src/config.ts index b3a22e52a75..03a79fbba8a 100644 --- a/extensions/ql-vscode/src/config.ts +++ b/extensions/ql-vscode/src/config.ts @@ -724,6 +724,7 @@ export async function setAutogenerateQlPacks(choice: AutogenerateQLPacks) { const MODEL_SETTING = new Setting("model", ROOT_SETTING); const FLOW_GENERATION = new Setting("flowGeneration", MODEL_SETTING); const LLM_GENERATION = new Setting("llmGeneration", MODEL_SETTING); +const SHOW_TYPE_MODELS = new Setting("showTypeModels", MODEL_SETTING); const LLM_GENERATION_BATCH_SIZE = new Setting( "llmGenerationBatchSize", MODEL_SETTING, @@ -743,6 +744,7 @@ const ENABLE_ACCESS_PATH_SUGGESTIONS = new Setting( export interface ModelConfig { flowGeneration: boolean; llmGeneration: boolean; + showTypeModels: boolean; getExtensionsDirectory(languageId: string): string | undefined; enablePython: boolean; enableAccessPathSuggestions: boolean; @@ -761,6 +763,10 @@ export class ModelConfigListener extends ConfigListener implements ModelConfig { return !!LLM_GENERATION.getValue(); } + public get showTypeModels(): boolean { + return !!SHOW_TYPE_MODELS.getValue(); + } + /** * Limits the number of candidates we send to the model in each request to avoid long requests. * Note that the model may return fewer than this number of candidates. diff --git a/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts b/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts index 662c6b29f30..035b0e6b21c 100644 --- a/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts +++ b/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts @@ -17,11 +17,35 @@ import type { import type { BaseLogger } from "../../common/logging"; import type { AccessPathSuggestionRow } from "../suggestions"; +// This is a subset of the model config that doesn't import the vscode module. +// It only includes settings that are actually used. +export type ModelConfig = { + showTypeModels: boolean; +}; + +/** + * This function creates a new model config object from the given model config object. + * The new model config object is a deep copy of the given model config object. + * + * @param modelConfig The model config object to create a new model config object from. + * In most cases, this is a `ModelConfigListener`. + */ +export function createModelConfig(modelConfig: ModelConfig): ModelConfig { + return { + showTypeModels: modelConfig.showTypeModels, + }; +} + +export const defaultModelConfig: ModelConfig = { + showTypeModels: false, +}; + type GenerateMethodDefinition = (method: T) => DataTuple[]; type ReadModeledMethod = (row: DataTuple[]) => ModeledMethod; + type IsHiddenContext = { method: MethodDefinition; - isCanary: boolean; + config: ModelConfig; }; export type ModelsAsDataLanguagePredicate = { diff --git a/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts b/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts index 23b6e2f1aa8..cbe719ca923 100644 --- a/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts +++ b/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts @@ -169,8 +169,7 @@ export const ruby: ModelsAsDataLanguage = { methodParameters: "", }; }, - // Hide for all non-canary users - isHidden: ({ isCanary }) => !isCanary, + isHidden: ({ config }) => !config.showTypeModels, }, }, modelGeneration: { diff --git a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts index 8ae34293183..803fa40ce6c 100644 --- a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts +++ b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts @@ -11,11 +11,11 @@ import type { ModelingStore } from "../modeling-store"; import { AbstractWebviewViewProvider } from "../../common/vscode/abstract-webview-view-provider"; import { assertNever } from "../../common/helpers-pure"; import type { ModelConfigListener } from "../../config"; -import { isCanary } from "../../config"; import type { DatabaseItem } from "../../databases/local-databases"; import type { ModelingEvents } from "../modeling-events"; import type { QueryLanguage } from "../../common/query-language"; import { tryGetQueryLanguage } from "../../common/query-language"; +import { createModelConfig } from "../languages"; export class MethodModelingViewProvider extends AbstractWebviewViewProvider< ToMethodModelingMessage, @@ -47,7 +47,7 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< t: "setMethodModelingPanelViewState", viewState: { language: this.language, - isCanary: isCanary(), + modelConfig: createModelConfig(this.modelConfig), }, }); } diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 1166c87bfeb..391238b9a20 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -40,7 +40,6 @@ import type { Method } from "./method"; import type { ModeledMethod } from "./modeled-method"; import type { ExtensionPack } from "./shared/extension-pack"; import type { ModelConfigListener } from "../config"; -import { isCanary } from "../config"; import { Mode } from "./shared/mode"; import { loadModeledMethods, saveModeledMethods } from "./modeled-method-fs"; import { pickExtensionPack } from "./extension-pack-picker"; @@ -51,7 +50,7 @@ import { telemetryListener } from "../common/vscode/telemetry"; import type { ModelingStore } from "./modeling-store"; import type { ModelingEvents } from "./modeling-events"; import type { ModelsAsDataLanguage } from "./languages"; -import { getModelsAsDataLanguage } from "./languages"; +import { createModelConfig, getModelsAsDataLanguage } from "./languages"; import { runGenerateQueries } from "./generate"; import { ResponseError } from "vscode-jsonrpc"; import { LSPErrorCodes } from "vscode-languageclient"; @@ -457,7 +456,7 @@ export class ModelEditorView extends AbstractWebview< mode: this.modelingStore.getMode(this.databaseItem), showModeSwitchButton, sourceArchiveAvailable, - isCanary: isCanary(), + modelConfig: createModelConfig(this.modelConfig), }, }); } diff --git a/extensions/ql-vscode/src/model-editor/shared/view-state.ts b/extensions/ql-vscode/src/model-editor/shared/view-state.ts index c9978e27047..62c4bcf3858 100644 --- a/extensions/ql-vscode/src/model-editor/shared/view-state.ts +++ b/extensions/ql-vscode/src/model-editor/shared/view-state.ts @@ -1,6 +1,7 @@ import type { ExtensionPack } from "./extension-pack"; import type { Mode } from "./mode"; import type { QueryLanguage } from "../../common/query-language"; +import type { ModelConfig } from "../languages"; export interface ModelEditorViewState { extensionPack: ExtensionPack; @@ -11,10 +12,10 @@ export interface ModelEditorViewState { mode: Mode; showModeSwitchButton: boolean; sourceArchiveAvailable: boolean; - isCanary: boolean; + modelConfig: ModelConfig; } export interface MethodModelingPanelViewState { language: QueryLanguage | undefined; - isCanary: boolean; + modelConfig: ModelConfig; } diff --git a/extensions/ql-vscode/src/stories/method-modeling/MethodModelingInputs.stories.tsx b/extensions/ql-vscode/src/stories/method-modeling/MethodModelingInputs.stories.tsx index eea86a94b87..8cea272943c 100644 --- a/extensions/ql-vscode/src/stories/method-modeling/MethodModelingInputs.stories.tsx +++ b/extensions/ql-vscode/src/stories/method-modeling/MethodModelingInputs.stories.tsx @@ -6,6 +6,7 @@ import { createSinkModeledMethod } from "../../../test/factories/model-editor/mo import { useState } from "react"; import type { ModeledMethod } from "../../model-editor/modeled-method"; import { QueryLanguage } from "../../common/query-language"; +import { defaultModelConfig } from "../../model-editor/languages"; export default { title: "Method Modeling/Method Modeling Inputs", @@ -34,7 +35,7 @@ const Template: StoryFn = (args) => { language={QueryLanguage.Java} modeledMethod={m} onChange={onChange} - isCanary={true} + modelConfig={defaultModelConfig} /> ); }; diff --git a/extensions/ql-vscode/src/view/method-modeling/MethodModeling.tsx b/extensions/ql-vscode/src/view/method-modeling/MethodModeling.tsx index 1dc0fe969cd..fcb3ed333c1 100644 --- a/extensions/ql-vscode/src/view/method-modeling/MethodModeling.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/MethodModeling.tsx @@ -8,6 +8,7 @@ import { VSCodeTag } from "@vscode/webview-ui-toolkit/react"; import { ReviewInEditorButton } from "./ReviewInEditorButton"; import { MultipleModeledMethodsPanel } from "./MultipleModeledMethodsPanel"; import type { QueryLanguage } from "../../common/query-language"; +import type { ModelConfig } from "../../model-editor/languages"; const Container = styled.div` padding-top: 0.5rem; @@ -50,7 +51,7 @@ const UnsavedTag = ({ modelingStatus }: { modelingStatus: ModelingStatus }) => ( export type MethodModelingProps = { language: QueryLanguage; - isCanary: boolean; + modelConfig: ModelConfig; modelingStatus: ModelingStatus; method: Method; modeledMethods: ModeledMethod[]; @@ -61,7 +62,7 @@ export type MethodModelingProps = { export const MethodModeling = ({ language, - isCanary, + modelConfig, modelingStatus, modeledMethods, method, @@ -82,7 +83,7 @@ export const MethodModeling = ({ ) : ( - + )} diff --git a/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx b/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx index 8d9caaba124..69e76240c41 100644 --- a/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx @@ -11,6 +11,7 @@ import { NotInModelingMode } from "./NotInModelingMode"; import { NoMethodSelected } from "./NoMethodSelected"; import type { MethodModelingPanelViewState } from "../../model-editor/shared/view-state"; import { MethodAlreadyModeled } from "./MethodAlreadyModeled"; +import { defaultModelConfig } from "../../model-editor/languages"; type Props = { initialViewState?: MethodModelingPanelViewState; @@ -116,7 +117,7 @@ export function MethodModelingView({ return ( 0 ? ( { const render = (props: MethodModelingProps) => @@ -18,7 +19,7 @@ describe(MethodModeling.name, () => { render({ language: QueryLanguage.Java, - isCanary: false, + modelConfig: defaultModelConfig, modelingStatus: "saved", method, modeledMethods: [modeledMethod], diff --git a/extensions/ql-vscode/src/view/method-modeling/__tests__/MethodModelingInputs.spec.tsx b/extensions/ql-vscode/src/view/method-modeling/__tests__/MethodModelingInputs.spec.tsx index 2a002d3f447..e5dc9bb5bde 100644 --- a/extensions/ql-vscode/src/view/method-modeling/__tests__/MethodModelingInputs.spec.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/__tests__/MethodModelingInputs.spec.tsx @@ -9,6 +9,7 @@ import { } from "../../../../test/factories/model-editor/modeled-method-factories"; import { QueryLanguage } from "../../../common/query-language"; import { createEmptyModeledMethod } from "../../../model-editor/modeled-method-empty"; +import { defaultModelConfig } from "../../../model-editor/languages"; describe(MethodModelingInputs.name, () => { const render = (props: MethodModelingInputsProps) => @@ -19,7 +20,7 @@ describe(MethodModelingInputs.name, () => { const modeledMethod = createSinkModeledMethod(); const modelPending = false; const isModelingInProgress = false; - const isCanary = false; + const modelConfig = defaultModelConfig; const onChange = jest.fn(); it("renders the method modeling inputs", () => { @@ -29,7 +30,7 @@ describe(MethodModelingInputs.name, () => { modeledMethod, modelPending, isModelingInProgress, - isCanary, + modelConfig, onChange, }); @@ -57,7 +58,7 @@ describe(MethodModelingInputs.name, () => { modeledMethod, modelPending, isModelingInProgress, - isCanary, + modelConfig, onChange, }); @@ -81,7 +82,7 @@ describe(MethodModelingInputs.name, () => { modeledMethod, modelPending, isModelingInProgress, - isCanary, + modelConfig, onChange, }); @@ -97,7 +98,7 @@ describe(MethodModelingInputs.name, () => { modeledMethod={updatedModeledMethod} modelPending={modelPending} isModelingInProgress={isModelingInProgress} - isCanary={isCanary} + modelConfig={modelConfig} onChange={onChange} />, ); @@ -128,7 +129,7 @@ describe(MethodModelingInputs.name, () => { modeledMethod, modelPending, isModelingInProgress: true, - isCanary, + modelConfig, onChange, }); diff --git a/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx b/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx index adf12c2013e..87c220c7c05 100644 --- a/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/__tests__/MultipleModeledMethodsPanel.spec.tsx @@ -11,6 +11,7 @@ import { userEvent } from "@testing-library/user-event"; import type { ModeledMethod } from "../../../model-editor/modeled-method"; import { QueryLanguage } from "../../../common/query-language"; import type { ModelingStatus } from "../../../model-editor/shared/modeling-status"; +import { defaultModelConfig } from "../../../model-editor/languages"; describe(MultipleModeledMethodsPanel.name, () => { const language = QueryLanguage.Java; @@ -19,14 +20,14 @@ describe(MultipleModeledMethodsPanel.name, () => { const isProcessedByAutoModel = false; const modelingStatus: ModelingStatus = "unmodeled"; const onChange = jest.fn(); - const isCanary = false; + const modelConfig = defaultModelConfig; const baseProps = { language, method, modelingStatus, isModelingInProgress, - isCanary, + modelConfig, isProcessedByAutoModel, onChange, }; diff --git a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx index 4788e47fa40..0f317ef815e 100644 --- a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx @@ -211,7 +211,7 @@ const ModelableMethodRow = forwardRef( return !predicate.isHidden?.({ method, - isCanary: viewState.isCanary, + config: viewState.modelConfig, }); }), method, @@ -298,7 +298,7 @@ const ModelableMethodRow = forwardRef( ) => { diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/ModelTypeDropdown.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelTypeDropdown.spec.tsx index 8f682aafbf2..50002c476a4 100644 --- a/extensions/ql-vscode/src/view/model-editor/__tests__/ModelTypeDropdown.spec.tsx +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelTypeDropdown.spec.tsx @@ -4,6 +4,7 @@ import { createNoneModeledMethod } from "../../../../test/factories/model-editor import { QueryLanguage } from "../../../common/query-language"; import { ModelTypeDropdown } from "../ModelTypeDropdown"; import { createMethod } from "../../../../test/factories/model-editor/method-factories"; +import { defaultModelConfig } from "../../../model-editor/languages"; describe(ModelTypeDropdown.name, () => { const onChange = jest.fn(); @@ -23,7 +24,7 @@ describe(ModelTypeDropdown.name, () => { modelPending={false} onChange={onChange} method={method} - isCanary={false} + modelConfig={defaultModelConfig} />, ); @@ -46,7 +47,10 @@ describe(ModelTypeDropdown.name, () => { modelPending={false} onChange={onChange} method={method} - isCanary={true} + modelConfig={{ + ...defaultModelConfig, + showTypeModels: true, + }} />, ); @@ -69,7 +73,7 @@ describe(ModelTypeDropdown.name, () => { modelPending={false} onChange={onChange} method={method} - isCanary={false} + modelConfig={defaultModelConfig} />, ); @@ -89,7 +93,7 @@ describe(ModelTypeDropdown.name, () => { modelPending={false} onChange={onChange} method={method} - isCanary={false} + modelConfig={defaultModelConfig} />, ); diff --git a/extensions/ql-vscode/test/factories/model-editor/view-state.ts b/extensions/ql-vscode/test/factories/model-editor/view-state.ts index 39f6b93f765..f7d338a19cd 100644 --- a/extensions/ql-vscode/test/factories/model-editor/view-state.ts +++ b/extensions/ql-vscode/test/factories/model-editor/view-state.ts @@ -2,6 +2,7 @@ import type { ModelEditorViewState } from "../../../src/model-editor/shared/view import { Mode } from "../../../src/model-editor/shared/mode"; import { createMockExtensionPack } from "./extension-pack"; import { QueryLanguage } from "../../../src/common/query-language"; +import { defaultModelConfig } from "../../../src/model-editor/languages"; export function createMockModelEditorViewState( data: Partial = {}, @@ -15,7 +16,7 @@ export function createMockModelEditorViewState( showModeSwitchButton: true, extensionPack: createMockExtensionPack(), sourceArchiveAvailable: true, - isCanary: false, + modelConfig: defaultModelConfig, ...data, }; } From a1c84ac6899d3608bc5d0bc68498c9e88a139de7 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 26 Feb 2024 10:32:46 +0000 Subject: [PATCH 0039/1237] Reorder expected test data --- .../methods-usage/methods-usage-data-provider.test.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-data-provider.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-data-provider.test.ts index ffe4e7c5184..b049aa672fa 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-data-provider.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-data-provider.test.ts @@ -392,7 +392,7 @@ describe("MethodsUsageDataProvider", () => { }), ]; - it("should sort methods", async () => { + it("should not change sort methods of methods", async () => { await dataProvider.setState( methods, dbItem, @@ -407,11 +407,7 @@ describe("MethodsUsageDataProvider", () => { .map( (item) => (item as MethodsUsageTreeViewItem).method.signature, ), - ).toEqual(["b.a.C.d()", "b.a.C.b()", "b.a.C.a()", "a.b.C.d()"]); - // reasoning for sort order: - // b.a.C.d() has more usages than b.a.C.b() - // b.a.C.b() is supported, b.a.C.a() is not - // b.a.C.a() is in a more unsupported library, a.b.C.d() is in a more supported library + ).toEqual(["b.a.C.a()", "b.a.C.b()", "b.a.C.d()", "a.b.C.d()"]); }); }); }); From 9a5654648d37c69306e5c689dbbdb2f8f67e4afd Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 26 Feb 2024 10:35:51 +0000 Subject: [PATCH 0040/1237] Rename to createGroups --- .../methods-usage/methods-usage-data-provider.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-data-provider.ts b/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-data-provider.ts index 78c72d6bb6f..fe3c3a86732 100644 --- a/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-data-provider.ts +++ b/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-data-provider.ts @@ -73,9 +73,7 @@ export class MethodsUsageDataProvider this.modifiedMethodSignatures !== modifiedMethodSignatures ) { this.methods = methods; - this.sortedTreeItems = createTreeItems( - sortMethodsInGroups(methods, mode), - ); + this.sortedTreeItems = createTreeItems(createGroups(methods, mode)); this.databaseItem = databaseItem; this.sourceLocationPrefix = await this.databaseItem.getSourceLocationPrefix(this.cliServer); @@ -246,7 +244,7 @@ function urlValueResolvablesAreEqual( return false; } -function sortMethodsInGroups(methods: readonly Method[], mode: Mode): Method[] { +function createGroups(methods: readonly Method[], mode: Mode): Method[] { const grouped = groupMethods(methods, mode); return sortGroupNames(grouped).flatMap((groupName) => grouped[groupName]); } From 713ca9f78570edaebbc38222700b5bee294409cb Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 26 Feb 2024 11:45:23 +0100 Subject: [PATCH 0041/1237] Fix test names --- .../view/model-editor/__tests__/ModelTypeDropdown.spec.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/ModelTypeDropdown.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelTypeDropdown.spec.tsx index 50002c476a4..0ddd84ef3dd 100644 --- a/extensions/ql-vscode/src/view/model-editor/__tests__/ModelTypeDropdown.spec.tsx +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelTypeDropdown.spec.tsx @@ -36,7 +36,7 @@ describe(ModelTypeDropdown.name, () => { ); }); - it("allows changing the type to 'Type' for Ruby in canary mode", async () => { + it("allows changing the type to 'Type' for Ruby when type models are shown", async () => { const method = createMethod(); const modeledMethod = createNoneModeledMethod(); @@ -62,7 +62,7 @@ describe(ModelTypeDropdown.name, () => { ); }); - it("does not allow changing the type to 'Type' for Ruby in non-canary mode", async () => { + it("does not allow changing the type to 'Type' for Ruby when type models are not shown", async () => { const method = createMethod(); const modeledMethod = createNoneModeledMethod(); From 7dceded98c46a576ff13978cb2d67adfae4043b4 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 26 Feb 2024 12:11:16 +0100 Subject: [PATCH 0042/1237] Use `showTypeModels` setting instead of canary mode --- .../model-editor/languages/models-as-data.ts | 2 +- .../model-editor/languages/ruby/generate.ts | 8 +-- .../src/model-editor/languages/ruby/index.ts | 5 +- .../src/model-editor/model-editor-view.ts | 7 ++- .../src/model-editor/modeled-method-fs.ts | 21 +++++-- .../languages/ruby/generate.test.ts | 6 +- .../model-editor/modeled-method-fs.test.ts | 60 ++++++++++++++++++- .../model-editor/generate.test.ts | 6 +- 8 files changed, 96 insertions(+), 19 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts b/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts index dfdf02cb94e..050b23f0a12 100644 --- a/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts +++ b/extensions/ql-vscode/src/model-editor/languages/models-as-data.ts @@ -70,7 +70,7 @@ export type ModelsAsDataLanguagePredicate = { export type GenerationContext = { mode: Mode; - isCanary: boolean; + config: ModelConfig; }; type ParseGenerationResults = ( diff --git a/extensions/ql-vscode/src/model-editor/languages/ruby/generate.ts b/extensions/ql-vscode/src/model-editor/languages/ruby/generate.ts index 3d6e3ae0c04..23e3957a303 100644 --- a/extensions/ql-vscode/src/model-editor/languages/ruby/generate.ts +++ b/extensions/ql-vscode/src/model-editor/languages/ruby/generate.ts @@ -12,7 +12,7 @@ export function parseGenerateModelResults( bqrs: DecodedBqrs, modelsAsDataLanguage: ModelsAsDataLanguage, logger: BaseLogger, - { isCanary }: GenerationContext, + { config }: GenerationContext, ): ModeledMethod[] { const modeledMethods: ModeledMethod[] = []; @@ -20,10 +20,10 @@ export function parseGenerateModelResults( if ( resultSetName === modelsAsDataLanguage.predicates.type?.extensiblePredicate && - !isCanary + !config.showTypeModels ) { - // Don't load generated type results in non-canary mode. These are already automatically - // generated on start-up. + // Don't load generated type results when type models are hidden. These are already + // automatically generated on start-up. continue; } diff --git a/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts b/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts index cb5bc170e36..f995b034f24 100644 --- a/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts +++ b/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts @@ -209,8 +209,9 @@ export const ruby: ModelsAsDataLanguage = { }, ]; }, - // Only enabled for framework mode in non-canary - enabled: ({ mode, isCanary }) => mode === Mode.Framework && !isCanary, + // Only enabled for framework mode when type models are hidden + enabled: ({ mode, config }) => + mode === Mode.Framework && !config.showTypeModels, }, accessPathSuggestions: { queryConstraints: (mode) => ({ diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 38806f045b0..aa5b51100c9 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -40,7 +40,6 @@ import type { Method } from "./method"; import type { ModeledMethod } from "./modeled-method"; import type { ExtensionPack } from "./shared/extension-pack"; import type { ModelConfigListener } from "../config"; -import { isCanary } from "../config"; import { Mode } from "./shared/mode"; import { GENERATED_MODELS_SUFFIX, @@ -273,6 +272,7 @@ export class ModelEditorView extends AbstractWebview< modeledMethods, mode, this.cliServer, + this.modelConfig, this.app.logger, ); @@ -490,6 +490,7 @@ export class ModelEditorView extends AbstractWebview< this.extensionPack, this.language, this.cliServer, + this.modelConfig, this.app.logger, ); this.modelingStore.setModeledMethods(this.databaseItem, modeledMethods); @@ -663,7 +664,7 @@ export class ModelEditorView extends AbstractWebview< this.app.logger, { mode, - isCanary: isCanary(), + config: this.modelConfig, }, ); @@ -701,7 +702,7 @@ export class ModelEditorView extends AbstractWebview< if ( autoModelGeneration.enabled && - !autoModelGeneration.enabled({ mode, isCanary: isCanary() }) + !autoModelGeneration.enabled({ mode, config: this.modelConfig }) ) { return; } diff --git a/extensions/ql-vscode/src/model-editor/modeled-method-fs.ts b/extensions/ql-vscode/src/model-editor/modeled-method-fs.ts index f56aed39fff..4b9661a0e56 100644 --- a/extensions/ql-vscode/src/model-editor/modeled-method-fs.ts +++ b/extensions/ql-vscode/src/model-editor/modeled-method-fs.ts @@ -12,7 +12,7 @@ import { load as loadYaml } from "js-yaml"; import type { CodeQLCliServer } from "../codeql-cli/cli"; import { pathsEqual } from "../common/files"; import type { QueryLanguage } from "../common/query-language"; -import { isCanary } from "../config"; +import type { ModelConfig } from "./languages"; export const GENERATED_MODELS_SUFFIX = ".model.generated.yml"; @@ -23,12 +23,14 @@ export async function saveModeledMethods( modeledMethods: Readonly>, mode: Mode, cliServer: CodeQLCliServer, + modelConfig: ModelConfig, logger: NotificationLogger, ): Promise { const existingModeledMethods = await loadModeledMethodFiles( extensionPack, language, cliServer, + modelConfig, logger, ); @@ -51,9 +53,14 @@ async function loadModeledMethodFiles( extensionPack: ExtensionPack, language: QueryLanguage, cliServer: CodeQLCliServer, + modelConfig: ModelConfig, logger: NotificationLogger, ): Promise>> { - const modelFiles = await listModelFiles(extensionPack.path, cliServer); + const modelFiles = await listModelFiles( + extensionPack.path, + cliServer, + modelConfig, + ); const modeledMethodsByFile: Record< string, @@ -85,6 +92,7 @@ export async function loadModeledMethods( extensionPack: ExtensionPack, language: QueryLanguage, cliServer: CodeQLCliServer, + modelConfig: ModelConfig, logger: NotificationLogger, ): Promise> { const existingModeledMethods: Record = {}; @@ -93,6 +101,7 @@ export async function loadModeledMethods( extensionPack, language, cliServer, + modelConfig, logger, ); for (const modeledMethods of Object.values(modeledMethodsByFile)) { @@ -111,6 +120,7 @@ export async function loadModeledMethods( export async function listModelFiles( extensionPackPath: string, cliServer: CodeQLCliServer, + modelConfig: ModelConfig, ): Promise> { const result = await cliServer.resolveExtensions( extensionPackPath, @@ -121,8 +131,11 @@ export async function listModelFiles( for (const [path, extensions] of Object.entries(result.data)) { if (pathsEqual(path, extensionPackPath)) { for (const extension of extensions) { - // We only load generated models in canary mode - if (!isCanary() && extension.file.endsWith(GENERATED_MODELS_SUFFIX)) { + // We only load generated models when type models are shown + if ( + !modelConfig.showTypeModels && + extension.file.endsWith(GENERATED_MODELS_SUFFIX) + ) { continue; } diff --git a/extensions/ql-vscode/test/unit-tests/model-editor/languages/ruby/generate.test.ts b/extensions/ql-vscode/test/unit-tests/model-editor/languages/ruby/generate.test.ts index c280da68b47..26f905fbbb1 100644 --- a/extensions/ql-vscode/test/unit-tests/model-editor/languages/ruby/generate.test.ts +++ b/extensions/ql-vscode/test/unit-tests/model-editor/languages/ruby/generate.test.ts @@ -5,6 +5,7 @@ import { createMockLogger } from "../../../../__mocks__/loggerMock"; import type { ModeledMethod } from "../../../../../src/model-editor/modeled-method"; import { EndpointType } from "../../../../../src/model-editor/method"; import { Mode } from "../../../../../src/model-editor/shared/mode"; +import { defaultModelConfig } from "../../../../../src/model-editor/languages"; describe("parseGenerateModelResults", () => { it("should return the results", async () => { @@ -78,8 +79,11 @@ describe("parseGenerateModelResults", () => { ruby, createMockLogger(), { - isCanary: true, mode: Mode.Framework, + config: { + ...defaultModelConfig, + showTypeModels: true, + }, }, ); expect(result).toEqual([ diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/model-editor/modeled-method-fs.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/model-editor/modeled-method-fs.test.ts index 90f723448c6..8bc14bf6337 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/model-editor/modeled-method-fs.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/model-editor/modeled-method-fs.test.ts @@ -13,6 +13,7 @@ import { homedir, tmpdir } from "os"; import { mkdir, rm } from "fs-extra"; import { nanoid } from "nanoid"; import { QueryLanguage } from "../../../../src/common/query-language"; +import { defaultModelConfig } from "../../../../src/model-editor/languages"; const dummyExtensionPackContents = ` name: dummy/pack @@ -135,7 +136,11 @@ describe("modeled-method-fs", () => { it("should return the empty set when the extension pack is empty", async () => { const extensionPackPath = writeExtensionPackFiles("extension-pack", []); - const modelFiles = await listModelFiles(extensionPackPath, cli); + const modelFiles = await listModelFiles( + extensionPackPath, + cli, + defaultModelConfig, + ); expect(modelFiles).toEqual(new Set()); }); @@ -145,7 +150,31 @@ describe("modeled-method-fs", () => { "library2.model.yml", ]); - const modelFiles = await listModelFiles(extensionPackPath, cli); + const modelFiles = await listModelFiles( + extensionPackPath, + cli, + defaultModelConfig, + ); + expect(modelFiles).toEqual( + new Set([ + join("models", "library1.model.yml"), + join("models", "library2.model.yml"), + ]), + ); + }); + + it("should ignore generated type models when type models are hidden", async () => { + const extensionPackPath = writeExtensionPackFiles("extension-pack", [ + "library1.model.yml", + "library2.model.yml", + "library.model.generated.yml", + ]); + + const modelFiles = await listModelFiles( + extensionPackPath, + cli, + defaultModelConfig, + ); expect(modelFiles).toEqual( new Set([ join("models", "library1.model.yml"), @@ -154,13 +183,37 @@ describe("modeled-method-fs", () => { ); }); + it("should include generated type models when type models are shown", async () => { + const extensionPackPath = writeExtensionPackFiles("extension-pack", [ + "library1.model.yml", + "library2.model.yml", + "library.model.generated.yml", + ]); + + const modelFiles = await listModelFiles(extensionPackPath, cli, { + ...defaultModelConfig, + showTypeModels: true, + }); + expect(modelFiles).toEqual( + new Set([ + join("models", "library.model.generated.yml"), + join("models", "library1.model.yml"), + join("models", "library2.model.yml"), + ]), + ); + }); + it("should ignore model files from other extension packs", async () => { const extensionPackPath = writeExtensionPackFiles("extension-pack", [ "library1.model.yml", ]); writeExtensionPackFiles("another-extension-pack", ["library2.model.yml"]); - const modelFiles = await listModelFiles(extensionPackPath, cli); + const modelFiles = await listModelFiles( + extensionPackPath, + cli, + defaultModelConfig, + ); expect(modelFiles).toEqual( new Set([join("models", "library1.model.yml")]), ); @@ -177,6 +230,7 @@ describe("modeled-method-fs", () => { makeExtensionPack(extensionPackPath), QueryLanguage.Java, cli, + defaultModelConfig, extLogger, ); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/generate.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/generate.test.ts index 4ac7a594514..a43b341f3bc 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/generate.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/generate.test.ts @@ -14,6 +14,7 @@ import { ruby } from "../../../../src/model-editor/languages/ruby"; import type { ModeledMethod } from "../../../../src/model-editor/modeled-method"; import { EndpointType } from "../../../../src/model-editor/method"; import { Mode } from "../../../../src/model-editor/shared/mode"; +import { defaultModelConfig } from "../../../../src/model-editor/languages"; describe("runGenerateQueries", () => { const modelsAsDataLanguage = ruby; @@ -136,8 +137,11 @@ describe("runGenerateQueries", () => { modelsAsDataLanguage, createMockLogger(), { - isCanary: true, mode: Mode.Framework, + config: { + ...defaultModelConfig, + showTypeModels: true, + }, }, ), ); From 1567d83463016ad38c9a6393d625ca8b8081f21d Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 26 Feb 2024 11:18:31 +0000 Subject: [PATCH 0043/1237] modifiedMethodSignatures is always defined --- .../method-modeling/method-modeling-view-provider.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts index a72c071a904..1777202314a 100644 --- a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts +++ b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts @@ -172,12 +172,10 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< }); } - if (e.modifiedMethodSignatures !== undefined) { - await this.postMessage({ - t: "setMethodModified", - isModified: e.modifiedMethodSignatures.has(this.method.signature), - }); - } + await this.postMessage({ + t: "setMethodModified", + isModified: e.modifiedMethodSignatures.has(this.method.signature), + }); } }), ); From 7c77b39d30e7e55b74d3addf6203267aaad7d073 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 26 Feb 2024 11:07:41 +0000 Subject: [PATCH 0044/1237] Rename to ModeledAndModifiedMethodsChanged --- .../ql-vscode/src/common/interface-types.ts | 6 +++--- .../method-modeling-view-provider.ts | 2 +- .../src/model-editor/model-editor-view.ts | 4 ++-- .../src/model-editor/modeling-events.ts | 18 +++++++++--------- .../src/model-editor/modeling-store.ts | 10 +++++----- .../src/view/model-editor/ModelEditor.tsx | 2 +- .../model-editor/modelingEventsMock.ts | 7 ++++--- 7 files changed, 25 insertions(+), 24 deletions(-) diff --git a/extensions/ql-vscode/src/common/interface-types.ts b/extensions/ql-vscode/src/common/interface-types.ts index 159d33b3c95..25c54208e05 100644 --- a/extensions/ql-vscode/src/common/interface-types.ts +++ b/extensions/ql-vscode/src/common/interface-types.ts @@ -529,8 +529,8 @@ interface SetMethodsMessage { methods: Method[]; } -interface SetModeledMethodsMessage { - t: "setModeledMethods"; +interface SetModeledAndModifiedMethodsMessage { + t: "setModeledAndModifiedMethods"; methods: Record; modifiedMethodSignatures: string[]; } @@ -648,7 +648,7 @@ interface SetModelEvaluationRunMessage { export type ToModelEditorMessage = | SetExtensionPackStateMessage | SetMethodsMessage - | SetModeledMethodsMessage + | SetModeledAndModifiedMethodsMessage | SetModifiedMethodsMessage | SetInProgressMethodsMessage | SetProcessedByAutoModelMethodsMessage diff --git a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts index 1777202314a..039ff7f86cc 100644 --- a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts +++ b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts @@ -161,7 +161,7 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< private registerToModelingEvents(): void { this.push( - this.modelingEvents.onModeledMethodsChanged(async (e) => { + this.modelingEvents.onModeledAndModifiedMethodsChanged(async (e) => { if (this.webviewView && e.isActiveDb && this.method) { const modeledMethods = e.modeledMethods[this.method.signature]; if (modeledMethods) { diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 92ad7a50071..d90659a658f 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -910,10 +910,10 @@ export class ModelEditorView extends AbstractWebview< ); this.push( - this.modelingEvents.onModeledMethodsChanged(async (event) => { + this.modelingEvents.onModeledAndModifiedMethodsChanged(async (event) => { if (event.dbUri === this.databaseItem.databaseUri.toString()) { await this.postMessage({ - t: "setModeledMethods", + t: "setModeledAndModifiedMethods", methods: event.modeledMethods, modifiedMethodSignatures: [...event.modifiedMethodSignatures], }); diff --git a/extensions/ql-vscode/src/model-editor/modeling-events.ts b/extensions/ql-vscode/src/model-editor/modeling-events.ts index 01e2ccffd8d..4c37fa1ffcd 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-events.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-events.ts @@ -24,7 +24,7 @@ interface ModeChangedEvent { readonly isActiveDb: boolean; } -interface ModeledMethodsChangedEvent { +interface ModeledAndModifiedMethodsChangedEvent { readonly modeledMethods: Readonly>; readonly modifiedMethodSignatures: ReadonlySet; readonly dbUri: string; @@ -78,7 +78,7 @@ export class ModelingEvents extends DisposableObject { public readonly onMethodsChanged: AppEvent; public readonly onHideModeledMethodsChanged: AppEvent; public readonly onModeChanged: AppEvent; - public readonly onModeledMethodsChanged: AppEvent; + public readonly onModeledAndModifiedMethodsChanged: AppEvent; public readonly onModifiedMethodsChanged: AppEvent; public readonly onSelectedMethodChanged: AppEvent; public readonly onInProgressMethodsChanged: AppEvent; @@ -93,7 +93,7 @@ export class ModelingEvents extends DisposableObject { private readonly onMethodsChangedEventEmitter: AppEventEmitter; private readonly onHideModeledMethodsChangedEventEmitter: AppEventEmitter; private readonly onModeChangedEventEmitter: AppEventEmitter; - private readonly onModeledMethodsChangedEventEmitter: AppEventEmitter; + private readonly onModeledAndModifiedMethodsChangedEventEmitter: AppEventEmitter; private readonly onModifiedMethodsChangedEventEmitter: AppEventEmitter; private readonly onSelectedMethodChangedEventEmitter: AppEventEmitter; private readonly onInProgressMethodsChangedEventEmitter: AppEventEmitter; @@ -134,11 +134,11 @@ export class ModelingEvents extends DisposableObject { ); this.onModeChanged = this.onModeChangedEventEmitter.event; - this.onModeledMethodsChangedEventEmitter = this.push( - app.createEventEmitter(), + this.onModeledAndModifiedMethodsChangedEventEmitter = this.push( + app.createEventEmitter(), ); - this.onModeledMethodsChanged = - this.onModeledMethodsChangedEventEmitter.event; + this.onModeledAndModifiedMethodsChanged = + this.onModeledAndModifiedMethodsChangedEventEmitter.event; this.onModifiedMethodsChangedEventEmitter = this.push( app.createEventEmitter(), @@ -224,13 +224,13 @@ export class ModelingEvents extends DisposableObject { }); } - public fireModeledMethodsChangedEvent( + public fireModeledAndModifiedMethodsChangedEvent( modeledMethods: Record, modifiedMethodSignatures: ReadonlySet, dbUri: string, isActiveDb: boolean, ) { - this.onModeledMethodsChangedEventEmitter.fire({ + this.onModeledAndModifiedMethodsChangedEventEmitter.fire({ modeledMethods, modifiedMethodSignatures, dbUri, diff --git a/extensions/ql-vscode/src/model-editor/modeling-store.ts b/extensions/ql-vscode/src/model-editor/modeling-store.ts index 60bc63a8479..8969b71b91b 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-store.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-store.ts @@ -229,7 +229,7 @@ export class ModelingStore extends DisposableObject { methods: Record, setModified: boolean, ) { - this.changeModeledMethods(dbItem, (state) => { + this.changeModeledAndModifiedMethods(dbItem, (state) => { const newModeledMethods = { ...methods, // Keep all methods that are already modeled in some form in the state @@ -255,7 +255,7 @@ export class ModelingStore extends DisposableObject { dbItem: DatabaseItem, methods: Record, ) { - this.changeModeledMethods(dbItem, (state) => { + this.changeModeledAndModifiedMethods(dbItem, (state) => { state.modeledMethods = { ...methods }; }); } @@ -266,7 +266,7 @@ export class ModelingStore extends DisposableObject { modeledMethods: ModeledMethod[], setModified: boolean, ) { - this.changeModeledMethods(dbItem, (state) => { + this.changeModeledAndModifiedMethods(dbItem, (state) => { const newModeledMethods = { ...state.modeledMethods }; newModeledMethods[signature] = modeledMethods; state.modeledMethods = newModeledMethods; @@ -454,7 +454,7 @@ export class ModelingStore extends DisposableObject { ); } - private changeModeledMethods( + private changeModeledAndModifiedMethods( dbItem: DatabaseItem, updateState: (state: InternalDbModelingState) => void, ) { @@ -462,7 +462,7 @@ export class ModelingStore extends DisposableObject { updateState(state); - this.modelingEvents.fireModeledMethodsChangedEvent( + this.modelingEvents.fireModeledAndModifiedMethodsChangedEvent( state.modeledMethods, state.modifiedMethodSignatures, dbItem.databaseUri.toString(), diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx index 40940dc4638..980c0f61d33 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx @@ -198,7 +198,7 @@ export function ModelEditor({ case "setMethods": setMethods(msg.methods); break; - case "setModeledMethods": + case "setModeledAndModifiedMethods": setModeledMethods(msg.methods); setModifiedSignatures(new Set(msg.modifiedMethodSignatures)); break; diff --git a/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts b/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts index 763d9720be9..6f3d6784804 100644 --- a/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts +++ b/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts @@ -7,7 +7,8 @@ export function createMockModelingEvents({ onMethodsChanged = jest.fn(), onHideModeledMethodsChanged = jest.fn(), onModeChanged = jest.fn(), - onModeledMethodsChanged = jest.fn(), + onModeledAndModifiedMethodsChanged: + onModeledAndModifiedMethodsChanged = jest.fn(), onModifiedMethodsChanged = jest.fn(), onInProgressMethodsChanged = jest.fn(), onProcessedByAutoModelMethodsChanged = jest.fn(), @@ -20,7 +21,7 @@ export function createMockModelingEvents({ onMethodsChanged?: ModelingEvents["onMethodsChanged"]; onHideModeledMethodsChanged?: ModelingEvents["onHideModeledMethodsChanged"]; onModeChanged?: ModelingEvents["onModeChanged"]; - onModeledMethodsChanged?: ModelingEvents["onModeledMethodsChanged"]; + onModeledAndModifiedMethodsChanged?: ModelingEvents["onModeledAndModifiedMethodsChanged"]; onModifiedMethodsChanged?: ModelingEvents["onModifiedMethodsChanged"]; onInProgressMethodsChanged?: ModelingEvents["onInProgressMethodsChanged"]; onProcessedByAutoModelMethodsChanged?: ModelingEvents["onProcessedByAutoModelMethodsChanged"]; @@ -34,7 +35,7 @@ export function createMockModelingEvents({ onMethodsChanged, onHideModeledMethodsChanged, onModeChanged, - onModeledMethodsChanged, + onModeledAndModifiedMethodsChanged, onModifiedMethodsChanged, onInProgressMethodsChanged, onProcessedByAutoModelMethodsChanged, From 7fac0405b3b1444cf9b698fd115b4c0f5279d01f Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 26 Feb 2024 11:21:59 +0000 Subject: [PATCH 0045/1237] Remove dedicated modified methods event --- .../method-modeling-view-provider.ts | 12 --------- .../methods-usage/methods-usage-panel.ts | 2 +- .../src/model-editor/model-editor-view.ts | 11 -------- .../src/model-editor/modeling-events.ts | 26 ------------------- .../src/model-editor/modeling-store.ts | 17 +----------- .../model-editor/modelingEventsMock.ts | 3 --- 6 files changed, 2 insertions(+), 69 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts index 039ff7f86cc..493c420b3a1 100644 --- a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts +++ b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts @@ -180,18 +180,6 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< }), ); - this.push( - this.modelingEvents.onModifiedMethodsChanged(async (e) => { - if (this.webviewView && e.isActiveDb && this.method) { - const isModified = e.modifiedMethods.has(this.method.signature); - await this.postMessage({ - t: "setMethodModified", - isModified, - }); - } - }), - ); - this.push( this.modelingEvents.onSelectedMethodChanged(async (e) => { if (this.webviewView) { diff --git a/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-panel.ts b/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-panel.ts index 7d8eed02be3..ad6b93db16c 100644 --- a/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-panel.ts +++ b/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-panel.ts @@ -102,7 +102,7 @@ export class MethodsUsagePanel extends DisposableObject { ); this.push( - this.modelingEvents.onModifiedMethodsChanged(async (event) => { + this.modelingEvents.onModeledAndModifiedMethodsChanged(async (event) => { if (event.isActiveDb) { await this.handleStateChangeEvent(); } diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index d90659a658f..cf2c3ff513f 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -921,17 +921,6 @@ export class ModelEditorView extends AbstractWebview< }), ); - this.push( - this.modelingEvents.onModifiedMethodsChanged(async (event) => { - if (event.dbUri === this.databaseItem.databaseUri.toString()) { - await this.postMessage({ - t: "setModifiedMethods", - methodSignatures: [...event.modifiedMethods], - }); - } - }), - ); - this.push( this.modelingEvents.onInProgressMethodsChanged(async (event) => { if (event.dbUri === this.databaseItem.databaseUri.toString()) { diff --git a/extensions/ql-vscode/src/model-editor/modeling-events.ts b/extensions/ql-vscode/src/model-editor/modeling-events.ts index 4c37fa1ffcd..867e85d3677 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-events.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-events.ts @@ -31,12 +31,6 @@ interface ModeledAndModifiedMethodsChangedEvent { readonly isActiveDb: boolean; } -interface ModifiedMethodsChangedEvent { - readonly modifiedMethods: ReadonlySet; - readonly dbUri: string; - readonly isActiveDb: boolean; -} - interface SelectedMethodChangedEvent { readonly databaseItem: DatabaseItem; readonly method: Method; @@ -79,7 +73,6 @@ export class ModelingEvents extends DisposableObject { public readonly onHideModeledMethodsChanged: AppEvent; public readonly onModeChanged: AppEvent; public readonly onModeledAndModifiedMethodsChanged: AppEvent; - public readonly onModifiedMethodsChanged: AppEvent; public readonly onSelectedMethodChanged: AppEvent; public readonly onInProgressMethodsChanged: AppEvent; public readonly onProcessedByAutoModelMethodsChanged: AppEvent; @@ -94,7 +87,6 @@ export class ModelingEvents extends DisposableObject { private readonly onHideModeledMethodsChangedEventEmitter: AppEventEmitter; private readonly onModeChangedEventEmitter: AppEventEmitter; private readonly onModeledAndModifiedMethodsChangedEventEmitter: AppEventEmitter; - private readonly onModifiedMethodsChangedEventEmitter: AppEventEmitter; private readonly onSelectedMethodChangedEventEmitter: AppEventEmitter; private readonly onInProgressMethodsChangedEventEmitter: AppEventEmitter; private readonly onProcessedByAutoModelMethodsChangedEventEmitter: AppEventEmitter; @@ -140,12 +132,6 @@ export class ModelingEvents extends DisposableObject { this.onModeledAndModifiedMethodsChanged = this.onModeledAndModifiedMethodsChangedEventEmitter.event; - this.onModifiedMethodsChangedEventEmitter = this.push( - app.createEventEmitter(), - ); - this.onModifiedMethodsChanged = - this.onModifiedMethodsChangedEventEmitter.event; - this.onSelectedMethodChangedEventEmitter = this.push( app.createEventEmitter(), ); @@ -238,18 +224,6 @@ export class ModelingEvents extends DisposableObject { }); } - public fireModifiedMethodsChangedEvent( - modifiedMethods: ReadonlySet, - dbUri: string, - isActiveDb: boolean, - ) { - this.onModifiedMethodsChangedEventEmitter.fire({ - modifiedMethods, - dbUri, - isActiveDb, - }); - } - public fireSelectedMethodChangedEvent( databaseItem: DatabaseItem, method: Method, diff --git a/extensions/ql-vscode/src/model-editor/modeling-store.ts b/extensions/ql-vscode/src/model-editor/modeling-store.ts index 8969b71b91b..1d170891919 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-store.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-store.ts @@ -285,7 +285,7 @@ export class ModelingStore extends DisposableObject { dbItem: DatabaseItem, methodSignatures: string[], ) { - this.changeModifiedMethods(dbItem, (state) => { + this.changeModeledAndModifiedMethods(dbItem, (state) => { const newModifiedMethods = Array.from( state.modifiedMethodSignatures, ).filter((s) => !methodSignatures.includes(s)); @@ -439,21 +439,6 @@ export class ModelingStore extends DisposableObject { ); } - private changeModifiedMethods( - dbItem: DatabaseItem, - updateState: (state: InternalDbModelingState) => void, - ) { - const state = this.getState(dbItem); - - updateState(state); - - this.modelingEvents.fireModifiedMethodsChangedEvent( - state.modifiedMethodSignatures, - dbItem.databaseUri.toString(), - dbItem.databaseUri.toString() === this.activeDb, - ); - } - private changeModeledAndModifiedMethods( dbItem: DatabaseItem, updateState: (state: InternalDbModelingState) => void, diff --git a/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts b/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts index 6f3d6784804..4bbba65de35 100644 --- a/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts +++ b/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts @@ -9,7 +9,6 @@ export function createMockModelingEvents({ onModeChanged = jest.fn(), onModeledAndModifiedMethodsChanged: onModeledAndModifiedMethodsChanged = jest.fn(), - onModifiedMethodsChanged = jest.fn(), onInProgressMethodsChanged = jest.fn(), onProcessedByAutoModelMethodsChanged = jest.fn(), onRevealInModelEditor = jest.fn(), @@ -22,7 +21,6 @@ export function createMockModelingEvents({ onHideModeledMethodsChanged?: ModelingEvents["onHideModeledMethodsChanged"]; onModeChanged?: ModelingEvents["onModeChanged"]; onModeledAndModifiedMethodsChanged?: ModelingEvents["onModeledAndModifiedMethodsChanged"]; - onModifiedMethodsChanged?: ModelingEvents["onModifiedMethodsChanged"]; onInProgressMethodsChanged?: ModelingEvents["onInProgressMethodsChanged"]; onProcessedByAutoModelMethodsChanged?: ModelingEvents["onProcessedByAutoModelMethodsChanged"]; onRevealInModelEditor?: ModelingEvents["onRevealInModelEditor"]; @@ -36,7 +34,6 @@ export function createMockModelingEvents({ onHideModeledMethodsChanged, onModeChanged, onModeledAndModifiedMethodsChanged, - onModifiedMethodsChanged, onInProgressMethodsChanged, onProcessedByAutoModelMethodsChanged, onRevealInModelEditor, From a74e36314cf0f766379cd6c4748e72007c3cf760 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 26 Feb 2024 13:11:22 +0000 Subject: [PATCH 0046/1237] Update extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts Co-authored-by: Koen Vlaswinkel --- .../test/__mocks__/model-editor/modelingEventsMock.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts b/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts index 4bbba65de35..396be26796f 100644 --- a/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts +++ b/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts @@ -7,8 +7,7 @@ export function createMockModelingEvents({ onMethodsChanged = jest.fn(), onHideModeledMethodsChanged = jest.fn(), onModeChanged = jest.fn(), - onModeledAndModifiedMethodsChanged: - onModeledAndModifiedMethodsChanged = jest.fn(), + onModeledAndModifiedMethodsChanged = jest.fn(), onInProgressMethodsChanged = jest.fn(), onProcessedByAutoModelMethodsChanged = jest.fn(), onRevealInModelEditor = jest.fn(), From a416bcf54447562b58ae1c050107af00da617af2 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Tue, 27 Feb 2024 14:07:12 +0000 Subject: [PATCH 0047/1237] Rename variable in variant analysis manager (#3408) --- .../src/variant-analysis/variant-analysis-manager.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 7e59ae17ba6..7265152fdc2 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -385,28 +385,28 @@ export class VariantAnalysisManager throw e; } - const processedVariantAnalysis = mapVariantAnalysis( + const mappedVariantAnalysis = mapVariantAnalysis( variantAnalysisSubmission, variantAnalysisResponse, ); - await this.onVariantAnalysisSubmitted(processedVariantAnalysis); + await this.onVariantAnalysisSubmitted(mappedVariantAnalysis); void showAndLogInformationMessage( this.app.logger, - `Variant analysis ${processedVariantAnalysis.query.name} submitted for processing`, + `Variant analysis ${mappedVariantAnalysis.query.name} submitted for processing`, ); void this.app.commands.execute( "codeQL.openVariantAnalysisView", - processedVariantAnalysis.id, + mappedVariantAnalysis.id, ); void this.app.commands.execute( "codeQL.monitorNewVariantAnalysis", - processedVariantAnalysis, + mappedVariantAnalysis, ); - return processedVariantAnalysis.id; + return mappedVariantAnalysis.id; } public async rehydrateVariantAnalysis(variantAnalysis: VariantAnalysis) { From 5f047386c9ef852f25427a19da451bf01539628c Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Tue, 27 Feb 2024 14:31:43 +0000 Subject: [PATCH 0048/1237] Rename getVariantAnalysis to tryGetVariantAnalysis (#3409) --- extensions/ql-vscode/src/model-editor/model-evaluator.ts | 2 +- extensions/ql-vscode/src/variant-analysis/export-results.ts | 2 +- .../variant-analysis/variant-analysis-content-provider.ts | 2 +- .../src/variant-analysis/variant-analysis-manager.ts | 6 +++--- .../src/variant-analysis/variant-analysis-view-manager.ts | 2 +- .../ql-vscode/src/variant-analysis/variant-analysis-view.ts | 4 ++-- .../variant-analysis/variant-analysis-manager.test.ts | 4 +++- 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts index 5e1e9dc5592..51c32ce0527 100644 --- a/extensions/ql-vscode/src/model-editor/model-evaluator.ts +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -120,7 +120,7 @@ export class ModelEvaluator extends DisposableObject { evaluationRun: ModelEvaluationRun, ): Promise { if (evaluationRun.variantAnalysisId) { - return await this.variantAnalysisManager.getVariantAnalysis( + return await this.variantAnalysisManager.tryGetVariantAnalysis( evaluationRun.variantAnalysisId, ); } diff --git a/extensions/ql-vscode/src/variant-analysis/export-results.ts b/extensions/ql-vscode/src/variant-analysis/export-results.ts index af90e2fb06a..6c0569465aa 100644 --- a/extensions/ql-vscode/src/variant-analysis/export-results.ts +++ b/extensions/ql-vscode/src/variant-analysis/export-results.ts @@ -42,7 +42,7 @@ export async function exportVariantAnalysisResults( await withProgress( async (progress: ProgressCallback, token: CancellationToken) => { const variantAnalysis = - await variantAnalysisManager.getVariantAnalysis(variantAnalysisId); + await variantAnalysisManager.tryGetVariantAnalysis(variantAnalysisId); if (!variantAnalysis) { void extLogger.log( `Could not find variant analysis with id ${variantAnalysisId}`, diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-content-provider.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-content-provider.ts index 766aa1ab16d..50ec16daebe 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-content-provider.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-content-provider.ts @@ -22,7 +22,7 @@ export const createVariantAnalysisContentProvider = ( const variantAnalysisId = parseInt(variantAnalysisIdString); const variantAnalysis = - await variantAnalysisManager.getVariantAnalysis(variantAnalysisId); + await variantAnalysisManager.tryGetVariantAnalysis(variantAnalysisId); if (!variantAnalysis) { void showAndLogWarningMessage( extLogger, diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 7265152fdc2..7dc7eda4851 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -484,7 +484,7 @@ export class VariantAnalysisManager } public async openQueryText(variantAnalysisId: number): Promise { - const variantAnalysis = await this.getVariantAnalysis(variantAnalysisId); + const variantAnalysis = await this.tryGetVariantAnalysis(variantAnalysisId); if (!variantAnalysis) { void showAndLogWarningMessage( this.app.logger, @@ -515,7 +515,7 @@ export class VariantAnalysisManager } public async openQueryFile(variantAnalysisId: number): Promise { - const variantAnalysis = await this.getVariantAnalysis(variantAnalysisId); + const variantAnalysis = await this.tryGetVariantAnalysis(variantAnalysisId); if (!variantAnalysis) { void showAndLogWarningMessage( @@ -557,7 +557,7 @@ export class VariantAnalysisManager return this.views.get(variantAnalysisId); } - public async getVariantAnalysis( + public async tryGetVariantAnalysis( variantAnalysisId: number, ): Promise { return this.variantAnalyses.get(variantAnalysisId); diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts index a498146dd25..a6f35caebbc 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts @@ -19,7 +19,7 @@ export interface VariantAnalysisViewManager< unregisterView(view: T): void; getView(variantAnalysisId: number): T | undefined; - getVariantAnalysis( + tryGetVariantAnalysis( variantAnalysisId: number, ): Promise; getRepoStates( diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts index 65f5151a675..2442b2e87bb 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts @@ -96,7 +96,7 @@ export class VariantAnalysisView } protected async getPanelConfig(): Promise { - const variantAnalysis = await this.manager.getVariantAnalysis( + const variantAnalysis = await this.manager.tryGetVariantAnalysis( this.variantAnalysisId, ); @@ -178,7 +178,7 @@ export class VariantAnalysisView void this.app.logger.log("Variant analysis view loaded"); - const variantAnalysis = await this.manager.getVariantAnalysis( + const variantAnalysis = await this.manager.tryGetVariantAnalysis( this.variantAnalysisId, ); diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts index e90e3e6e95e..1ca034b4270 100644 --- a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts @@ -109,7 +109,9 @@ describe("Variant Analysis Manager", () => { await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); expect( - await variantAnalysisManager.getVariantAnalysis(variantAnalysis.id), + await variantAnalysisManager.tryGetVariantAnalysis( + variantAnalysis.id, + ), ).toEqual(variantAnalysis); }); From 040c7fc726ea4b3c838dd77d715bd1b943357693 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Tue, 27 Feb 2024 16:32:24 +0000 Subject: [PATCH 0049/1237] Add support for 'canceling' status for variant analysis (#3405) --- extensions/ql-vscode/CHANGELOG.md | 1 + .../src/query-history/query-status.ts | 2 ++ ...-history-variant-analysis-domain-mapper.ts | 5 +++ .../VariantAnalysisActions.stories.tsx | 6 ++++ .../shared/variant-analysis.ts | 1 + .../variant-analysis-manager.ts | 36 +++++++++++++++++-- .../variant-analysis-mapper.ts | 11 +++++- .../variant-analysis-monitor.ts | 9 +++++ .../VariantAnalysisActions.tsx | 5 +++ .../variant-analysis/VariantAnalysisStats.tsx | 4 +++ .../VariantAnalysisStatusStats.tsx | 3 +- .../__tests__/VariantAnalysisActions.spec.tsx | 18 ++++++++++ .../__tests__/VariantAnalysisStats.spec.tsx | 6 ++++ .../variant-analysis-manager.test.ts | 24 ++++++++++++- .../variant-analysis-monitor.test.ts | 27 +++++++++++++- 15 files changed, 152 insertions(+), 6 deletions(-) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 15cf3b35754..8d324491d6c 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -2,6 +2,7 @@ ## [UNRELEASED] +- Update variant analysis view to show when cancelation is in progress. [#3405](https://github.com/github/vscode-codeql/pull/3405) - Remove support for CodeQL CLI versions older than 2.13.5. [#3371](https://github.com/github/vscode-codeql/pull/3371) - Add a timeout to downloading databases and the CodeQL CLI. These can be changed using the `codeQL.addingDatabases.downloadTimeout` and `codeQL.cli.downloadTimeout` settings respectively. [#3373](https://github.com/github/vscode-codeql/pull/3373) diff --git a/extensions/ql-vscode/src/query-history/query-status.ts b/extensions/ql-vscode/src/query-history/query-status.ts index c68fb9fd7b0..732b1d5f945 100644 --- a/extensions/ql-vscode/src/query-history/query-status.ts +++ b/extensions/ql-vscode/src/query-history/query-status.ts @@ -17,6 +17,8 @@ export function variantAnalysisStatusToQueryStatus( return QueryStatus.Failed; case VariantAnalysisStatus.InProgress: return QueryStatus.InProgress; + case VariantAnalysisStatus.Canceling: + return QueryStatus.InProgress; case VariantAnalysisStatus.Canceled: return QueryStatus.Completed; default: diff --git a/extensions/ql-vscode/src/query-history/store/query-history-variant-analysis-domain-mapper.ts b/extensions/ql-vscode/src/query-history/store/query-history-variant-analysis-domain-mapper.ts index 329a3404755..6ca7c63132e 100644 --- a/extensions/ql-vscode/src/query-history/store/query-history-variant-analysis-domain-mapper.ts +++ b/extensions/ql-vscode/src/query-history/store/query-history-variant-analysis-domain-mapper.ts @@ -195,6 +195,11 @@ function mapVariantAnalysisStatusToDto( return VariantAnalysisStatusDto.Succeeded; case VariantAnalysisStatus.Failed: return VariantAnalysisStatusDto.Failed; + case VariantAnalysisStatus.Canceling: + // The canceling state shouldn't be persisted. We can just + // assume that the analysis is still in progress, since the + // canceling state is very short-lived. + return VariantAnalysisStatusDto.InProgress; case VariantAnalysisStatus.Canceled: return VariantAnalysisStatusDto.Canceled; default: diff --git a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisActions.stories.tsx b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisActions.stories.tsx index 1e4eec19f78..daeb84e54b6 100644 --- a/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisActions.stories.tsx +++ b/extensions/ql-vscode/src/stories/variant-analysis/VariantAnalysisActions.stories.tsx @@ -70,3 +70,9 @@ Failed.args = { ...InProgress.args, variantAnalysisStatus: VariantAnalysisStatus.Failed, }; + +export const Canceling = Template.bind({}); +Canceling.args = { + ...InProgress.args, + variantAnalysisStatus: VariantAnalysisStatus.Canceling, +}; diff --git a/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts b/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts index 0da27a50afd..3a698d24cc7 100644 --- a/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts +++ b/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts @@ -39,6 +39,7 @@ export enum VariantAnalysisStatus { InProgress = "inProgress", Succeeded = "succeeded", Failed = "failed", + Canceling = "canceling", Canceled = "canceled", } diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 7dc7eda4851..82533e90135 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -34,6 +34,7 @@ import { isVariantAnalysisComplete, parseVariantAnalysisQueryLanguage, VariantAnalysisScannedRepositoryDownloadStatus, + VariantAnalysisStatus, } from "./shared/variant-analysis"; import { getErrorMessage } from "../common/helpers-pure"; import { VariantAnalysisView } from "./variant-analysis-view"; @@ -145,6 +146,7 @@ export class VariantAnalysisManager new VariantAnalysisMonitor( app, this.shouldCancelMonitorVariantAnalysis.bind(this), + this.getVariantAnalysisStatus.bind(this), ), ); this.variantAnalysisMonitor.onVariantAnalysisChange( @@ -604,6 +606,19 @@ export class VariantAnalysisManager return !this.variantAnalyses.has(variantAnalysisId); } + private getVariantAnalysisStatus( + variantAnalysisId: number, + ): VariantAnalysisStatus { + const variantAnalysis = this.variantAnalyses.get(variantAnalysisId); + if (!variantAnalysis) { + throw new Error( + `No variant analysis found with id: ${variantAnalysisId}.`, + ); + } + + return variantAnalysis.status; + } + public async onVariantAnalysisUpdated( variantAnalysis: VariantAnalysis | undefined, ): Promise { @@ -611,7 +626,11 @@ export class VariantAnalysisManager return; } - if (!this.variantAnalyses.has(variantAnalysis.id)) { + const originalVariantAnalysis = this.variantAnalyses.get( + variantAnalysis.id, + ); + + if (!originalVariantAnalysis) { return; } @@ -844,11 +863,24 @@ export class VariantAnalysisManager ); } + await this.onVariantAnalysisUpdated({ + ...variantAnalysis, + status: VariantAnalysisStatus.Canceling, + }); + void showAndLogInformationMessage( this.app.logger, "Cancelling variant analysis. This may take a while.", ); - await cancelVariantAnalysis(this.app.credentials, variantAnalysis); + try { + await cancelVariantAnalysis(this.app.credentials, variantAnalysis); + } catch (e) { + await this.onVariantAnalysisUpdated({ + ...variantAnalysis, + status: VariantAnalysisStatus.InProgress, + }); + throw e; + } } public async openVariantAnalysisLogs(variantAnalysisId: number) { diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts index 22044fed2c8..7fee8186167 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts @@ -40,6 +40,7 @@ export function mapVariantAnalysis( databases: submission.databases, executionStartTime: submission.startTime, }, + undefined, response, ); } @@ -49,6 +50,7 @@ export function mapUpdatedVariantAnalysis( VariantAnalysis, "language" | "query" | "queries" | "databases" | "executionStartTime" >, + currentStatus: VariantAnalysisStatus | undefined, response: ApiVariantAnalysis, ): VariantAnalysis { let scannedRepos: VariantAnalysisScannedRepository[] = []; @@ -66,6 +68,13 @@ export function mapUpdatedVariantAnalysis( ); } + // Maintain the canceling status if we are still canceling. + const status = + currentStatus === VariantAnalysisStatus.Canceling && + response.status === "in_progress" + ? VariantAnalysisStatus.Canceling + : mapApiStatus(response.status); + const variantAnalysis: VariantAnalysis = { id: response.id, controllerRepo: { @@ -80,7 +89,7 @@ export function mapUpdatedVariantAnalysis( executionStartTime: previousVariantAnalysis.executionStartTime, createdAt: response.created_at, updatedAt: response.updated_at, - status: mapApiStatus(response.status), + status, completedAt: response.completed_at, actionsWorkflowRunId: response.actions_workflow_run_id, scannedRepos, diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts index 9cfb7100ec6..a91b948cd7f 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts @@ -5,6 +5,7 @@ import { RequestError } from "@octokit/request-error"; import type { VariantAnalysis, VariantAnalysisScannedRepository, + VariantAnalysisStatus, } from "./shared/variant-analysis"; import { isFinalVariantAnalysisStatus, @@ -36,6 +37,9 @@ export class VariantAnalysisMonitor extends DisposableObject { private readonly shouldCancelMonitor: ( variantAnalysisId: number, ) => Promise, + private readonly getVariantAnalysisStatus: ( + variantAnalysisId: number, + ) => VariantAnalysisStatus, ) { super(); } @@ -119,8 +123,13 @@ export class VariantAnalysisMonitor extends DisposableObject { continue; } + // Get the current status of the variant analysis as known by the rest + // of the app, because it may have been changed by the user and this code + // may not be aware of it yet. + const currentStatus = this.getVariantAnalysisStatus(variantAnalysis.id); variantAnalysis = mapUpdatedVariantAnalysis( variantAnalysis, + currentStatus, variantAnalysisSummary, ); diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisActions.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisActions.tsx index d324e32158c..ed1b473c66a 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisActions.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisActions.tsx @@ -103,6 +103,11 @@ export const VariantAnalysisActions = ({ Stop query )} + {variantAnalysisStatus === VariantAnalysisStatus.Canceling && ( + + )} ); }; diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStats.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStats.tsx index eea7dd41c71..49020c138f7 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStats.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStats.tsx @@ -48,6 +48,10 @@ export const VariantAnalysisStats = ({ return "Failed"; } + if (variantAnalysisStatus === VariantAnalysisStatus.Canceling) { + return "Canceling"; + } + if (variantAnalysisStatus === VariantAnalysisStatus.Canceled) { return "Stopped"; } diff --git a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStatusStats.tsx b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStatusStats.tsx index 0ee714ada0d..3969fd7951e 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStatusStats.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/VariantAnalysisStatusStats.tsx @@ -28,7 +28,8 @@ export const VariantAnalysisStatusStats = ({ }: VariantAnalysisStatusStatsProps) => { return ( - {variantAnalysisStatus === VariantAnalysisStatus.InProgress ? ( + {variantAnalysisStatus === VariantAnalysisStatus.InProgress || + variantAnalysisStatus === VariantAnalysisStatus.Canceling ? (
diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisActions.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisActions.spec.tsx index 9f23c3eec80..de19458646e 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisActions.spec.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisActions.spec.tsx @@ -45,6 +45,24 @@ describe(VariantAnalysisActions.name, () => { expect(onStopQueryClick).toHaveBeenCalledTimes(1); }); + it("renders the stopping query disabled button when canceling", async () => { + render({ + variantAnalysisStatus: VariantAnalysisStatus.Canceling, + }); + + const button = screen.getByText("Stopping query"); + expect(button).toBeInTheDocument(); + expect(button.getElementsByTagName("input")[0]).toBeDisabled(); + }); + + it("does not render a stop query button when canceling", async () => { + render({ + variantAnalysisStatus: VariantAnalysisStatus.Canceling, + }); + + expect(screen.queryByText("Stop query")).not.toBeInTheDocument(); + }); + it("renders 3 buttons when in progress with results", async () => { const { container } = render({ variantAnalysisStatus: VariantAnalysisStatus.InProgress, diff --git a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStats.spec.tsx b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStats.spec.tsx index fd2bce56b99..e3a9f3a4abf 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStats.spec.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/__tests__/VariantAnalysisStats.spec.tsx @@ -160,6 +160,12 @@ describe(VariantAnalysisStats.name, () => { expect(screen.getByText("Failed")).toBeInTheDocument(); }); + it("renders 'Stopping' text when the variant analysis status is canceling", () => { + render({ variantAnalysisStatus: VariantAnalysisStatus.Canceling }); + + expect(screen.getByText("Canceling")).toBeInTheDocument(); + }); + it("renders 'Stopped' text when the variant analysis status is canceled", () => { render({ variantAnalysisStatus: VariantAnalysisStatus.Canceled }); diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts index 1ca034b4270..24b9567ba9f 100644 --- a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts @@ -609,7 +609,7 @@ describe("Variant Analysis Manager", () => { } }); - it("should return cancel if valid", async () => { + it("should make API request to cancel if valid", async () => { await variantAnalysisManager.cancelVariantAnalysis(variantAnalysis.id); expect(mockCancelVariantAnalysis).toHaveBeenCalledWith( @@ -617,6 +617,28 @@ describe("Variant Analysis Manager", () => { variantAnalysis, ); }); + + it("should set the status to canceling", async () => { + await variantAnalysisManager.cancelVariantAnalysis(variantAnalysis.id); + + const updatedAnalysis = + await variantAnalysisManager.tryGetVariantAnalysis(variantAnalysis.id); + expect(updatedAnalysis?.status).toBe(VariantAnalysisStatus.Canceling); + }); + + it("should set the status back to in progress if canceling fails", async () => { + mockCancelVariantAnalysis.mockRejectedValueOnce( + new Error("Error when cancelling"), + ); + + await expect( + variantAnalysisManager.cancelVariantAnalysis(variantAnalysis.id), + ).rejects.toThrow("Error when cancelling"); + + const updatedAnalysis = + await variantAnalysisManager.tryGetVariantAnalysis(variantAnalysis.id); + expect(updatedAnalysis?.status).toBe(VariantAnalysisStatus.InProgress); + }); }); describe("copyRepoListToClipboard", () => { diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts index ac7e3957e6a..b24bdb12f7e 100644 --- a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts @@ -32,6 +32,7 @@ describe("Variant Analysis Monitor", () => { >; let variantAnalysisMonitor: VariantAnalysisMonitor; let shouldCancelMonitor: jest.Mock, [number]>; + let mockGetVariantAnalysisStatus: jest.Mock; let variantAnalysis: VariantAnalysis; const onVariantAnalysisChangeSpy = jest.fn(); @@ -43,6 +44,7 @@ describe("Variant Analysis Monitor", () => { variantAnalysis = createMockVariantAnalysis({}); shouldCancelMonitor = jest.fn(); + mockGetVariantAnalysisStatus = jest.fn(); logger = createMockLogger(); @@ -54,6 +56,7 @@ describe("Variant Analysis Monitor", () => { logger, }), shouldCancelMonitor, + mockGetVariantAnalysisStatus, ); variantAnalysisMonitor.onVariantAnalysisChange(onVariantAnalysisChangeSpy); @@ -128,7 +131,11 @@ describe("Variant Analysis Monitor", () => { index + 1, "codeQL.autoDownloadVariantAnalysisResult", mapScannedRepository(succeededRepo), - mapUpdatedVariantAnalysis(variantAnalysis, mockApiResponse), + mapUpdatedVariantAnalysis( + variantAnalysis, + variantAnalysis.status, + mockApiResponse, + ), ); }); }); @@ -336,6 +343,24 @@ describe("Variant Analysis Monitor", () => { ); }); }); + + describe("cancelation", () => { + it("should maintain canceling status", async () => { + mockGetVariantAnalysisStatus.mockReturnValueOnce( + VariantAnalysisStatus.Canceling, + ); + mockApiResponse = createMockApiResponse("in_progress"); + mockGetVariantAnalysis.mockResolvedValue(mockApiResponse); + + await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis); + + expect(onVariantAnalysisChangeSpy).toHaveBeenCalledWith( + expect.objectContaining({ + status: VariantAnalysisStatus.Canceling, + }), + ); + }); + }); }); function limitNumberOfAttemptsToMonitor() { From e86127672a2e6786c97bc3ea9797347644e7bcc0 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Tue, 27 Feb 2024 16:57:53 +0000 Subject: [PATCH 0050/1237] Drop async from some functions that are not async (#3410) --- .../src/model-editor/model-evaluator.ts | 2 +- .../src/variant-analysis/export-results.ts | 4 ++-- .../variant-analysis-content-provider.ts | 2 +- .../variant-analysis-manager.ts | 12 ++++++------ .../variant-analysis-view-manager.ts | 6 ++---- .../variant-analysis/variant-analysis-view.ts | 6 +++--- .../variant-analysis-manager.test.ts | 18 +++++++++--------- 7 files changed, 24 insertions(+), 26 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts index 51c32ce0527..9ad202def1d 100644 --- a/extensions/ql-vscode/src/model-editor/model-evaluator.ts +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -120,7 +120,7 @@ export class ModelEvaluator extends DisposableObject { evaluationRun: ModelEvaluationRun, ): Promise { if (evaluationRun.variantAnalysisId) { - return await this.variantAnalysisManager.tryGetVariantAnalysis( + return this.variantAnalysisManager.tryGetVariantAnalysis( evaluationRun.variantAnalysisId, ); } diff --git a/extensions/ql-vscode/src/variant-analysis/export-results.ts b/extensions/ql-vscode/src/variant-analysis/export-results.ts index 6c0569465aa..17d1997a47d 100644 --- a/extensions/ql-vscode/src/variant-analysis/export-results.ts +++ b/extensions/ql-vscode/src/variant-analysis/export-results.ts @@ -42,7 +42,7 @@ export async function exportVariantAnalysisResults( await withProgress( async (progress: ProgressCallback, token: CancellationToken) => { const variantAnalysis = - await variantAnalysisManager.tryGetVariantAnalysis(variantAnalysisId); + variantAnalysisManager.tryGetVariantAnalysis(variantAnalysisId); if (!variantAnalysis) { void extLogger.log( `Could not find variant analysis with id ${variantAnalysisId}`, @@ -57,7 +57,7 @@ export async function exportVariantAnalysisResults( } const repoStates = - await variantAnalysisManager.getRepoStates(variantAnalysisId); + variantAnalysisManager.getRepoStates(variantAnalysisId); void extLogger.log( `Exporting variant analysis results for variant analysis with id ${variantAnalysis.id}`, diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-content-provider.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-content-provider.ts index 50ec16daebe..255a419e656 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-content-provider.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-content-provider.ts @@ -22,7 +22,7 @@ export const createVariantAnalysisContentProvider = ( const variantAnalysisId = parseInt(variantAnalysisIdString); const variantAnalysis = - await variantAnalysisManager.tryGetVariantAnalysis(variantAnalysisId); + variantAnalysisManager.tryGetVariantAnalysis(variantAnalysisId); if (!variantAnalysis) { void showAndLogWarningMessage( extLogger, diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 82533e90135..2e97b92e434 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -486,7 +486,7 @@ export class VariantAnalysisManager } public async openQueryText(variantAnalysisId: number): Promise { - const variantAnalysis = await this.tryGetVariantAnalysis(variantAnalysisId); + const variantAnalysis = this.tryGetVariantAnalysis(variantAnalysisId); if (!variantAnalysis) { void showAndLogWarningMessage( this.app.logger, @@ -517,7 +517,7 @@ export class VariantAnalysisManager } public async openQueryFile(variantAnalysisId: number): Promise { - const variantAnalysis = await this.tryGetVariantAnalysis(variantAnalysisId); + const variantAnalysis = this.tryGetVariantAnalysis(variantAnalysisId); if (!variantAnalysis) { void showAndLogWarningMessage( @@ -559,15 +559,15 @@ export class VariantAnalysisManager return this.views.get(variantAnalysisId); } - public async tryGetVariantAnalysis( + public tryGetVariantAnalysis( variantAnalysisId: number, - ): Promise { + ): VariantAnalysis | undefined { return this.variantAnalyses.get(variantAnalysisId); } - public async getRepoStates( + public getRepoStates( variantAnalysisId: number, - ): Promise { + ): VariantAnalysisScannedRepositoryState[] { return Object.values(this.repoStates.get(variantAnalysisId) ?? {}); } diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts index a6f35caebbc..cc531c558e7 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view-manager.ts @@ -19,12 +19,10 @@ export interface VariantAnalysisViewManager< unregisterView(view: T): void; getView(variantAnalysisId: number): T | undefined; - tryGetVariantAnalysis( - variantAnalysisId: number, - ): Promise; + tryGetVariantAnalysis(variantAnalysisId: number): VariantAnalysis | undefined; getRepoStates( variantAnalysisId: number, - ): Promise; + ): VariantAnalysisScannedRepositoryState[]; openQueryFile(variantAnalysisId: number): Promise; openQueryText(variantAnalysisId: number): Promise; cancelVariantAnalysis(variantAnalysisId: number): Promise; diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts index 2442b2e87bb..f8a87e47b87 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-view.ts @@ -96,7 +96,7 @@ export class VariantAnalysisView } protected async getPanelConfig(): Promise { - const variantAnalysis = await this.manager.tryGetVariantAnalysis( + const variantAnalysis = this.manager.tryGetVariantAnalysis( this.variantAnalysisId, ); @@ -178,7 +178,7 @@ export class VariantAnalysisView void this.app.logger.log("Variant analysis view loaded"); - const variantAnalysis = await this.manager.tryGetVariantAnalysis( + const variantAnalysis = this.manager.tryGetVariantAnalysis( this.variantAnalysisId, ); @@ -206,7 +206,7 @@ export class VariantAnalysisView filterSortState, }); - const repoStates = await this.manager.getRepoStates(this.variantAnalysisId); + const repoStates = this.manager.getRepoStates(this.variantAnalysisId); if (repoStates.length === 0) { return; } diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts index 24b9567ba9f..debe0af8d7e 100644 --- a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts @@ -109,9 +109,7 @@ describe("Variant Analysis Manager", () => { await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); expect( - await variantAnalysisManager.tryGetVariantAnalysis( - variantAnalysis.id, - ), + variantAnalysisManager.tryGetVariantAnalysis(variantAnalysis.id), ).toEqual(variantAnalysis); }); @@ -119,7 +117,7 @@ describe("Variant Analysis Manager", () => { await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); expect( - await variantAnalysisManager.getRepoStates(variantAnalysis.id), + variantAnalysisManager.getRepoStates(variantAnalysis.id), ).toEqual([]); }); @@ -147,7 +145,7 @@ describe("Variant Analysis Manager", () => { await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); expect( - await variantAnalysisManager.getRepoStates(variantAnalysis.id), + variantAnalysisManager.getRepoStates(variantAnalysis.id), ).toEqual( expect.arrayContaining([ { @@ -621,8 +619,9 @@ describe("Variant Analysis Manager", () => { it("should set the status to canceling", async () => { await variantAnalysisManager.cancelVariantAnalysis(variantAnalysis.id); - const updatedAnalysis = - await variantAnalysisManager.tryGetVariantAnalysis(variantAnalysis.id); + const updatedAnalysis = variantAnalysisManager.tryGetVariantAnalysis( + variantAnalysis.id, + ); expect(updatedAnalysis?.status).toBe(VariantAnalysisStatus.Canceling); }); @@ -635,8 +634,9 @@ describe("Variant Analysis Manager", () => { variantAnalysisManager.cancelVariantAnalysis(variantAnalysis.id), ).rejects.toThrow("Error when cancelling"); - const updatedAnalysis = - await variantAnalysisManager.tryGetVariantAnalysis(variantAnalysis.id); + const updatedAnalysis = variantAnalysisManager.tryGetVariantAnalysis( + variantAnalysis.id, + ); expect(updatedAnalysis?.status).toBe(VariantAnalysisStatus.InProgress); }); }); From 0599adccc0bedb162a9e0927461e9e58a549863c Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 28 Feb 2024 09:58:33 +0100 Subject: [PATCH 0051/1237] Fix generated text Co-authored-by: Shati Patel <42641846+shati-patel@users.noreply.github.com> --- extensions/ql-vscode/src/model-editor/model-editor-view.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index aa5b51100c9..03c2d95aa8e 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -757,7 +757,7 @@ export class ModelEditorView extends AbstractWebview< message: "Saving generated models", }); - const fileContents = `# This file was automatically generated based from ${this.databaseItem.name}. Manual changes will not persist.\n\n${modelExtensionFileToYaml(extensionFile)}`; + const fileContents = `# This file was automatically generated from ${this.databaseItem.name}. Manual changes will not persist.\n\n${modelExtensionFileToYaml(extensionFile)}`; const filePath = join( this.extensionPack.path, "models", From ab3615351108d43f01aa9f823963d9a7bcd0813c Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Wed, 28 Feb 2024 09:12:26 +0000 Subject: [PATCH 0052/1237] Minor renames in variant analysis monitor tests (#3411) * Rename mockGetVariantAnalysis -> mockGetVariantAnalysisFromApi * Rename mockEecuteCommand -> mockExecuteCommand --- .../variant-analysis-monitor.test.ts | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts index b24bdb12f7e..1b90eb397d4 100644 --- a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts @@ -27,7 +27,7 @@ import { createMockLogger } from "../../../__mocks__/loggerMock"; jest.setTimeout(60_000); describe("Variant Analysis Monitor", () => { - let mockGetVariantAnalysis: jest.SpiedFunction< + let mockGetVariantAnalysisFromApi: jest.SpiedFunction< typeof ghApiClient.getVariantAnalysis >; let variantAnalysisMonitor: VariantAnalysisMonitor; @@ -36,7 +36,7 @@ describe("Variant Analysis Monitor", () => { let variantAnalysis: VariantAnalysis; const onVariantAnalysisChangeSpy = jest.fn(); - const mockEecuteCommand = jest.fn(); + const mockExecuteCommand = jest.fn(); let logger: NotificationLogger; @@ -51,7 +51,7 @@ describe("Variant Analysis Monitor", () => { variantAnalysisMonitor = new VariantAnalysisMonitor( createMockApp({ commands: createMockCommandManager({ - executeCommand: mockEecuteCommand, + executeCommand: mockExecuteCommand, }), logger, }), @@ -60,7 +60,7 @@ describe("Variant Analysis Monitor", () => { ); variantAnalysisMonitor.onVariantAnalysisChange(onVariantAnalysisChangeSpy); - mockGetVariantAnalysis = jest + mockGetVariantAnalysisFromApi = jest .spyOn(ghApiClient, "getVariantAnalysis") .mockRejectedValue(new Error("Not mocked")); @@ -80,13 +80,13 @@ describe("Variant Analysis Monitor", () => { beforeEach(async () => { mockFailedApiResponse = createFailedMockApiResponse(); - mockGetVariantAnalysis.mockResolvedValue(mockFailedApiResponse); + mockGetVariantAnalysisFromApi.mockResolvedValue(mockFailedApiResponse); }); it("should mark as failed and stop monitoring", async () => { await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis); - expect(mockGetVariantAnalysis).toHaveBeenCalledTimes(1); + expect(mockGetVariantAnalysisFromApi).toHaveBeenCalledTimes(1); expect(onVariantAnalysisChangeSpy).toHaveBeenCalledWith( expect.objectContaining({ @@ -115,7 +115,7 @@ describe("Variant Analysis Monitor", () => { "succeeded", ]); mockApiResponse = createMockApiResponse("succeeded", scannedRepos); - mockGetVariantAnalysis.mockResolvedValue(mockApiResponse); + mockGetVariantAnalysisFromApi.mockResolvedValue(mockApiResponse); }); it("should trigger a download extension command for each repo", async () => { @@ -124,10 +124,10 @@ describe("Variant Analysis Monitor", () => { ); await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis); - expect(mockEecuteCommand).toHaveBeenCalledTimes(succeededRepos.length); + expect(mockExecuteCommand).toHaveBeenCalledTimes(succeededRepos.length); succeededRepos.forEach((succeededRepo, index) => { - expect(mockEecuteCommand).toHaveBeenNthCalledWith( + expect(mockExecuteCommand).toHaveBeenNthCalledWith( index + 1, "codeQL.autoDownloadVariantAnalysisResult", mapScannedRepository(succeededRepo), @@ -147,13 +147,13 @@ describe("Variant Analysis Monitor", () => { beforeEach(async () => { scannedRepos = createMockScannedRepos(["pending", "in_progress"]); mockApiResponse = createMockApiResponse("in_progress", scannedRepos); - mockGetVariantAnalysis.mockResolvedValue(mockApiResponse); + mockGetVariantAnalysisFromApi.mockResolvedValue(mockApiResponse); }); it("should succeed and not download any repos via a command", async () => { await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis); - expect(mockEecuteCommand).not.toHaveBeenCalled(); + expect(mockExecuteCommand).not.toHaveBeenCalled(); }); }); @@ -170,7 +170,7 @@ describe("Variant Analysis Monitor", () => { "pending", ]); mockApiResponse = createMockApiResponse("in_progress", scannedRepos); - mockGetVariantAnalysis.mockResolvedValueOnce(mockApiResponse); + mockGetVariantAnalysisFromApi.mockResolvedValueOnce(mockApiResponse); let nextApiResponse = { ...mockApiResponse, @@ -178,7 +178,7 @@ describe("Variant Analysis Monitor", () => { }; nextApiResponse.scanned_repositories[0].analysis_status = "succeeded"; nextApiResponse.scanned_repositories[1].analysis_status = "succeeded"; - mockGetVariantAnalysis.mockResolvedValueOnce(nextApiResponse); + mockGetVariantAnalysisFromApi.mockResolvedValueOnce(nextApiResponse); nextApiResponse = { ...mockApiResponse, @@ -188,7 +188,7 @@ describe("Variant Analysis Monitor", () => { }; nextApiResponse.scanned_repositories[2].analysis_status = "succeeded"; nextApiResponse.scanned_repositories[5].analysis_status = "succeeded"; - mockGetVariantAnalysis.mockResolvedValueOnce(nextApiResponse); + mockGetVariantAnalysisFromApi.mockResolvedValueOnce(nextApiResponse); nextApiResponse = { ...mockApiResponse, @@ -198,14 +198,14 @@ describe("Variant Analysis Monitor", () => { }; nextApiResponse.scanned_repositories[3].analysis_status = "succeeded"; nextApiResponse.scanned_repositories[4].analysis_status = "failed"; - mockGetVariantAnalysis.mockResolvedValueOnce(nextApiResponse); + mockGetVariantAnalysisFromApi.mockResolvedValueOnce(nextApiResponse); }); it("should trigger a download extension command for each repo", async () => { await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis); - expect(mockGetVariantAnalysis).toHaveBeenCalledTimes(4); - expect(mockEecuteCommand).toHaveBeenCalledTimes(5); + expect(mockGetVariantAnalysisFromApi).toHaveBeenCalledTimes(4); + expect(mockExecuteCommand).toHaveBeenCalledTimes(5); }); }); @@ -222,15 +222,15 @@ describe("Variant Analysis Monitor", () => { "pending", ]); mockApiResponse = createMockApiResponse("in_progress", scannedRepos); - mockGetVariantAnalysis.mockResolvedValueOnce(mockApiResponse); + mockGetVariantAnalysisFromApi.mockResolvedValueOnce(mockApiResponse); - mockGetVariantAnalysis.mockRejectedValueOnce( + mockGetVariantAnalysisFromApi.mockRejectedValueOnce( new Error("No internet connection"), ); - mockGetVariantAnalysis.mockRejectedValueOnce( + mockGetVariantAnalysisFromApi.mockRejectedValueOnce( new Error("No internet connection"), ); - mockGetVariantAnalysis.mockRejectedValueOnce( + mockGetVariantAnalysisFromApi.mockRejectedValueOnce( new Error("My different error"), ); @@ -241,15 +241,15 @@ describe("Variant Analysis Monitor", () => { nextApiResponse.scanned_repositories[0].analysis_status = "succeeded"; nextApiResponse.scanned_repositories[1].analysis_status = "succeeded"; - mockGetVariantAnalysis.mockResolvedValueOnce(nextApiResponse); + mockGetVariantAnalysisFromApi.mockResolvedValueOnce(nextApiResponse); - mockGetVariantAnalysis.mockRejectedValueOnce( + mockGetVariantAnalysisFromApi.mockRejectedValueOnce( new Error("My different error"), ); - mockGetVariantAnalysis.mockRejectedValueOnce( + mockGetVariantAnalysisFromApi.mockRejectedValueOnce( new Error("My different error"), ); - mockGetVariantAnalysis.mockRejectedValueOnce( + mockGetVariantAnalysisFromApi.mockRejectedValueOnce( new Error("Another different error"), ); @@ -262,7 +262,7 @@ describe("Variant Analysis Monitor", () => { nextApiResponse.scanned_repositories[4].analysis_status = "failed"; nextApiResponse.scanned_repositories[5].analysis_status = "succeeded"; nextApiResponse.status = "succeeded"; - mockGetVariantAnalysis.mockResolvedValueOnce(nextApiResponse); + mockGetVariantAnalysisFromApi.mockResolvedValueOnce(nextApiResponse); }); it("should only trigger the warning once per error", async () => { @@ -292,13 +292,13 @@ describe("Variant Analysis Monitor", () => { beforeEach(async () => { scannedRepos = []; mockApiResponse = createMockApiResponse("succeeded", scannedRepos); - mockGetVariantAnalysis.mockResolvedValue(mockApiResponse); + mockGetVariantAnalysisFromApi.mockResolvedValue(mockApiResponse); }); it("should not try to download any repos", async () => { await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis); - expect(mockEecuteCommand).not.toHaveBeenCalled(); + expect(mockExecuteCommand).not.toHaveBeenCalled(); }); }); @@ -313,9 +313,9 @@ describe("Variant Analysis Monitor", () => { "pending", ]); mockApiResponse = createMockApiResponse("in_progress", scannedRepos); - mockGetVariantAnalysis.mockResolvedValueOnce(mockApiResponse); + mockGetVariantAnalysisFromApi.mockResolvedValueOnce(mockApiResponse); - mockGetVariantAnalysis.mockRejectedValueOnce( + mockGetVariantAnalysisFromApi.mockRejectedValueOnce( new RequestError("Not Found", 404, { request: { method: "GET", @@ -336,7 +336,7 @@ describe("Variant Analysis Monitor", () => { it("should stop requesting the variant analysis", async () => { await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis); - expect(mockGetVariantAnalysis).toHaveBeenCalledTimes(2); + expect(mockGetVariantAnalysisFromApi).toHaveBeenCalledTimes(2); expect(logger.showWarningMessage).toHaveBeenCalledTimes(1); expect(logger.showWarningMessage).toHaveBeenCalledWith( expect.stringMatching(/not found/i), @@ -350,7 +350,7 @@ describe("Variant Analysis Monitor", () => { VariantAnalysisStatus.Canceling, ); mockApiResponse = createMockApiResponse("in_progress"); - mockGetVariantAnalysis.mockResolvedValue(mockApiResponse); + mockGetVariantAnalysisFromApi.mockResolvedValue(mockApiResponse); await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis); From d14c7b4114e3eb7308eba7ef04940e765f701f20 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 28 Feb 2024 10:47:29 +0100 Subject: [PATCH 0053/1237] Show test results for tests with warnings --- extensions/ql-vscode/src/codeql-cli/cli.ts | 14 ++++-- .../src/query-testing/test-manager.ts | 48 ++++++++++++------- 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index fd93fd79ae9..7c0b75ae706 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -156,9 +156,17 @@ type ResolvedQueries = string[]; type ResolvedTests = string[]; /** - * A compilation message for a test message (either an error or a warning) + * The severity of a compilation message for a test message. */ -interface CompilationMessage { +export enum CompilationMessageSeverity { + Error = "ERROR", + Warning = "WARNING", +} + +/** + * A compilation message for a test message (either an error or a warning). + */ +export interface CompilationMessage { /** * The text of the message */ @@ -170,7 +178,7 @@ interface CompilationMessage { /** * The severity of the message */ - severity: number; + severity: CompilationMessageSeverity; } /** diff --git a/extensions/ql-vscode/src/query-testing/test-manager.ts b/extensions/ql-vscode/src/query-testing/test-manager.ts index ff1a91d88e3..b7eb7139df7 100644 --- a/extensions/ql-vscode/src/query-testing/test-manager.ts +++ b/extensions/ql-vscode/src/query-testing/test-manager.ts @@ -21,7 +21,8 @@ import { } from "vscode"; import { DisposableObject } from "../common/disposable-object"; import { QLTestDiscovery } from "./qltest-discovery"; -import type { CodeQLCliServer } from "../codeql-cli/cli"; +import type { CodeQLCliServer, CompilationMessage } from "../codeql-cli/cli"; +import { CompilationMessageSeverity } from "../codeql-cli/cli"; import { getErrorMessage } from "../common/helpers-pure"; import type { BaseLogger, LogOptions } from "../common/logging"; import type { TestRunner } from "./test-runner"; @@ -66,6 +67,23 @@ function changeExtension(p: string, ext: string): string { return p.slice(0, -extname(p).length) + ext; } +function compilationMessageToTestMessage( + compilationMessage: CompilationMessage, +): TestMessage { + const location = new Location( + Uri.file(compilationMessage.position.fileName), + new Range( + compilationMessage.position.line - 1, + compilationMessage.position.column - 1, + compilationMessage.position.endLine - 1, + compilationMessage.position.endColumn - 1, + ), + ); + const testMessage = new TestMessage(compilationMessage.message); + testMessage.location = location; + return testMessage; +} + /** * Returns the complete text content of the specified file. If there is an error reading the file, * an error message is added to `testMessages` and this function returns undefined. @@ -398,23 +416,15 @@ export class TestManager extends DisposableObject { ); } } - if (event.messages?.length > 0) { + const errorMessages = event.messages.filter( + (m) => m.severity === CompilationMessageSeverity.Error, + ); + if (errorMessages.length > 0) { // The test didn't make it far enough to produce results. Transform any error messages // into `TestMessage`s and report the test as "errored". - const testMessages = event.messages.map((m) => { - const location = new Location( - Uri.file(m.position.fileName), - new Range( - m.position.line - 1, - m.position.column - 1, - m.position.endLine - 1, - m.position.endColumn - 1, - ), - ); - const testMessage = new TestMessage(m.message); - testMessage.location = location; - return testMessage; - }); + const testMessages = event.messages.map( + compilationMessageToTestMessage, + ); testRun.errored(testItem, testMessages, duration); } else { // Results didn't match expectations. Report the test as "failed". @@ -423,6 +433,12 @@ export class TestManager extends DisposableObject { // here. Any failed test needs at least one message. testMessages.push(new TestMessage("Test failed")); } + + // Add any warnings produced by the test to the test messages. + testMessages.push( + ...event.messages.map(compilationMessageToTestMessage), + ); + testRun.failed(testItem, testMessages, duration); } } From eb71df84a7756718d197f8c703a4adb382d93b48 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 28 Feb 2024 11:15:00 +0100 Subject: [PATCH 0054/1237] Move query metadata retrieval before pack bundling --- .../variant-analysis-manager.ts | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 2e97b92e434..66abb79567a 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -309,21 +309,6 @@ export class VariantAnalysisManager message: "Getting credentials", }); - const { - actionBranch, - base64Pack, - repoSelection, - controllerRepo, - queryStartTime, - } = await prepareRemoteQueryRun( - this.cliServer, - this.app.credentials, - qlPackDetails, - progress, - token, - this.dbManager, - ); - // For now we get the metadata for the first query in the pack. // and use that in the submission and query history. In the future // we'll need to consider how to handle having multiple queries. @@ -342,6 +327,21 @@ export class VariantAnalysisManager ); } + const { + actionBranch, + base64Pack, + repoSelection, + controllerRepo, + queryStartTime, + } = await prepareRemoteQueryRun( + this.cliServer, + this.app.credentials, + qlPackDetails, + progress, + token, + this.dbManager, + ); + const queryText = await readFile(firstQueryFile, "utf8"); const queries: VariantAnalysisQueries | undefined = From 5521c003462b08b4b00589ab712eb2433f6bcfaf Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 28 Feb 2024 11:17:15 +0100 Subject: [PATCH 0055/1237] Add check for id property when running variant analysis --- .../src/variant-analysis/variant-analysis-manager.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 66abb79567a..cdcd1f28441 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -327,6 +327,17 @@ export class VariantAnalysisManager ); } + // It's not possible to interpret a BQRS file to SARIF without an id property. + if ( + queryMetadata?.kind && + ["problem", "path-problem"].includes(queryMetadata.kind) && + !queryMetadata.id + ) { + throw new UserCancellationException( + `${firstQueryFile} does not have the required @id property for a ${queryMetadata.kind} query.`, + ); + } + const { actionBranch, base64Pack, From b884cff14ddd2bd4dc7dc134bbf63687dd0fe06b Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Wed, 28 Feb 2024 11:45:49 +0000 Subject: [PATCH 0056/1237] Some restructuring around variant analysis mapper functions (#3412) --- .../variant-analysis-manager.ts | 4 +-- .../variant-analysis-mapper.ts | 31 +++++++++++++------ .../variant-analysis-monitor.ts | 6 ++-- .../variant-analysis-mapper.test.ts | 9 ++++-- .../variant-analysis-monitor.test.ts | 6 +--- 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 2e97b92e434..71d0627625d 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -45,7 +45,7 @@ import type { } from "./variant-analysis-results-manager"; import { getQueryName, prepareRemoteQueryRun } from "./run-remote-query"; import { - mapVariantAnalysis, + mapVariantAnalysisFromSubmission, mapVariantAnalysisRepositoryTask, } from "./variant-analysis-mapper"; import PQueue from "p-queue"; @@ -387,7 +387,7 @@ export class VariantAnalysisManager throw e; } - const mappedVariantAnalysis = mapVariantAnalysis( + const mappedVariantAnalysis = mapVariantAnalysisFromSubmission( variantAnalysisSubmission, variantAnalysisResponse, ); diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts index 7fee8186167..a40f3b57d1e 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts @@ -23,11 +23,11 @@ import { VariantAnalysisRepoStatus, } from "./shared/variant-analysis"; -export function mapVariantAnalysis( +export function mapVariantAnalysisFromSubmission( submission: VariantAnalysisSubmission, - response: ApiVariantAnalysis, + apiVariantAnalysis: ApiVariantAnalysis, ): VariantAnalysis { - return mapUpdatedVariantAnalysis( + return mapVariantAnalysis( { language: submission.language, query: { @@ -41,12 +41,23 @@ export function mapVariantAnalysis( executionStartTime: submission.startTime, }, undefined, - response, + apiVariantAnalysis, ); } export function mapUpdatedVariantAnalysis( - previousVariantAnalysis: Pick< + currentVariantAnalysis: VariantAnalysis, + apiVariantAnalysis: ApiVariantAnalysis, +): VariantAnalysis { + return mapVariantAnalysis( + currentVariantAnalysis, + currentVariantAnalysis.status, + apiVariantAnalysis, + ); +} + +function mapVariantAnalysis( + currentVariantAnalysis: Pick< VariantAnalysis, "language" | "query" | "queries" | "databases" | "executionStartTime" >, @@ -82,11 +93,11 @@ export function mapUpdatedVariantAnalysis( fullName: response.controller_repo.full_name, private: response.controller_repo.private, }, - language: previousVariantAnalysis.language, - query: previousVariantAnalysis.query, - queries: previousVariantAnalysis.queries, - databases: previousVariantAnalysis.databases, - executionStartTime: previousVariantAnalysis.executionStartTime, + language: currentVariantAnalysis.language, + query: currentVariantAnalysis.query, + queries: currentVariantAnalysis.queries, + databases: currentVariantAnalysis.databases, + executionStartTime: currentVariantAnalysis.executionStartTime, createdAt: response.created_at, updatedAt: response.updated_at, status, diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts index a91b948cd7f..eb6584fc635 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts @@ -128,8 +128,10 @@ export class VariantAnalysisMonitor extends DisposableObject { // may not be aware of it yet. const currentStatus = this.getVariantAnalysisStatus(variantAnalysis.id); variantAnalysis = mapUpdatedVariantAnalysis( - variantAnalysis, - currentStatus, + { + ...variantAnalysis, + status: currentStatus, + }, variantAnalysisSummary, ); diff --git a/extensions/ql-vscode/test/unit-tests/variant-analysis/variant-analysis-mapper.test.ts b/extensions/ql-vscode/test/unit-tests/variant-analysis/variant-analysis-mapper.test.ts index 2062f468afa..c1b38ccde5c 100644 --- a/extensions/ql-vscode/test/unit-tests/variant-analysis/variant-analysis-mapper.test.ts +++ b/extensions/ql-vscode/test/unit-tests/variant-analysis/variant-analysis-mapper.test.ts @@ -4,7 +4,7 @@ import type { VariantAnalysisScannedRepository } from "../../../src/variant-anal import { VariantAnalysisRepoStatus } from "../../../src/variant-analysis/shared/variant-analysis"; import { mapScannedRepository, - mapVariantAnalysis, + mapVariantAnalysisFromSubmission, mapVariantAnalysisRepositoryTask, } from "../../../src/variant-analysis/variant-analysis-mapper"; import { @@ -17,7 +17,7 @@ import { createMockSubmission } from "../../factories/variant-analysis/shared/va import { createMockVariantAnalysisRepoTask } from "../../factories/variant-analysis/gh-api/variant-analysis-repo-task"; import { QueryLanguage } from "../../../src/common/query-language"; -describe(mapVariantAnalysis.name, () => { +describe(mapVariantAnalysisFromSubmission.name, () => { const scannedRepos = createMockScannedRepos(); const skippedRepos = createMockSkippedRepos(); const mockApiResponse = createMockApiResponse( @@ -28,7 +28,10 @@ describe(mapVariantAnalysis.name, () => { const mockSubmission = createMockSubmission(); it("should map an API response and return a variant analysis", () => { - const result = mapVariantAnalysis(mockSubmission, mockApiResponse); + const result = mapVariantAnalysisFromSubmission( + mockSubmission, + mockApiResponse, + ); const { access_mismatch_repos, diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts index 1b90eb397d4..1ff7f5b5222 100644 --- a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts @@ -131,11 +131,7 @@ describe("Variant Analysis Monitor", () => { index + 1, "codeQL.autoDownloadVariantAnalysisResult", mapScannedRepository(succeededRepo), - mapUpdatedVariantAnalysis( - variantAnalysis, - variantAnalysis.status, - mockApiResponse, - ), + mapUpdatedVariantAnalysis(variantAnalysis, mockApiResponse), ); }); }); From 23bbff230f3c7e37d98a200ca0928a88ce896029 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Wed, 28 Feb 2024 12:12:27 +0000 Subject: [PATCH 0057/1237] Update variant analysis monitor to get the whole variant analysis object (#3415) --- .../variant-analysis-manager.ts | 15 +++--- .../variant-analysis-monitor.ts | 47 ++++++++++--------- .../variant-analysis-monitor.test.ts | 16 ++++--- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 71d0627625d..d1bfd8822f5 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -146,7 +146,7 @@ export class VariantAnalysisManager new VariantAnalysisMonitor( app, this.shouldCancelMonitorVariantAnalysis.bind(this), - this.getVariantAnalysisStatus.bind(this), + this.getVariantAnalysis.bind(this), ), ); this.variantAnalysisMonitor.onVariantAnalysisChange( @@ -606,17 +606,14 @@ export class VariantAnalysisManager return !this.variantAnalyses.has(variantAnalysisId); } - private getVariantAnalysisStatus( - variantAnalysisId: number, - ): VariantAnalysisStatus { - const variantAnalysis = this.variantAnalyses.get(variantAnalysisId); + private getVariantAnalysis(variantAnalysisId: number): VariantAnalysis { + const variantAnalysis = this.tryGetVariantAnalysis(variantAnalysisId); + if (!variantAnalysis) { - throw new Error( - `No variant analysis found with id: ${variantAnalysisId}.`, - ); + throw new Error(`No variant analysis with id: ${variantAnalysisId}`); } - return variantAnalysis.status; + return variantAnalysis; } public async onVariantAnalysisUpdated( diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts index eb6584fc635..8592f84353d 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts @@ -5,7 +5,6 @@ import { RequestError } from "@octokit/request-error"; import type { VariantAnalysis, VariantAnalysisScannedRepository, - VariantAnalysisStatus, } from "./shared/variant-analysis"; import { isFinalVariantAnalysisStatus, @@ -18,6 +17,7 @@ import { sleep } from "../common/time"; import { getErrorMessage } from "../common/helpers-pure"; import type { App } from "../common/app"; import { showAndLogWarningMessage } from "../common/logging"; +import type { QueryLanguage } from "../common/query-language"; export class VariantAnalysisMonitor extends DisposableObject { // With a sleep of 5 seconds, the maximum number of attempts takes @@ -37,9 +37,9 @@ export class VariantAnalysisMonitor extends DisposableObject { private readonly shouldCancelMonitor: ( variantAnalysisId: number, ) => Promise, - private readonly getVariantAnalysisStatus: ( + private readonly getVariantAnalysis: ( variantAnalysisId: number, - ) => VariantAnalysisStatus, + ) => VariantAnalysis, ) { super(); } @@ -60,20 +60,28 @@ export class VariantAnalysisMonitor extends DisposableObject { this.monitoringVariantAnalyses.add(variantAnalysis.id); try { - await this._monitorVariantAnalysis(variantAnalysis); + await this._monitorVariantAnalysis( + variantAnalysis.id, + variantAnalysis.controllerRepo.id, + variantAnalysis.executionStartTime, + variantAnalysis.query.name, + variantAnalysis.language, + ); } finally { this.monitoringVariantAnalyses.delete(variantAnalysis.id); } } private async _monitorVariantAnalysis( - variantAnalysis: VariantAnalysis, + variantAnalysisId: number, + controllerRepoId: number, + executionStartTime: number, + queryName: string, + language: QueryLanguage, ): Promise { - const variantAnalysisLabel = `${variantAnalysis.query.name} (${ - variantAnalysis.language - }) [${new Date(variantAnalysis.executionStartTime).toLocaleString( - env.language, - )}]`; + const variantAnalysisLabel = `${queryName} (${language}) [${new Date( + executionStartTime, + ).toLocaleString(env.language)}]`; let attemptCount = 0; const scannedReposDownloaded: number[] = []; @@ -83,7 +91,7 @@ export class VariantAnalysisMonitor extends DisposableObject { while (attemptCount <= VariantAnalysisMonitor.maxAttemptCount) { await sleep(VariantAnalysisMonitor.sleepTime); - if (await this.shouldCancelMonitor(variantAnalysis.id)) { + if (await this.shouldCancelMonitor(variantAnalysisId)) { return; } @@ -91,8 +99,8 @@ export class VariantAnalysisMonitor extends DisposableObject { try { variantAnalysisSummary = await getVariantAnalysis( this.app.credentials, - variantAnalysis.controllerRepo.id, - variantAnalysis.id, + controllerRepoId, + variantAnalysisId, ); } catch (e) { const errorMessage = getErrorMessage(e); @@ -123,15 +131,10 @@ export class VariantAnalysisMonitor extends DisposableObject { continue; } - // Get the current status of the variant analysis as known by the rest - // of the app, because it may have been changed by the user and this code - // may not be aware of it yet. - const currentStatus = this.getVariantAnalysisStatus(variantAnalysis.id); - variantAnalysis = mapUpdatedVariantAnalysis( - { - ...variantAnalysis, - status: currentStatus, - }, + const variantAnalysis = mapUpdatedVariantAnalysis( + // Get the variant analysis as known by the rest of the app, because it may + // have been changed by the user and the monitors may not be aware of it yet. + this.getVariantAnalysis(variantAnalysisId), variantAnalysisSummary, ); diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts index 1ff7f5b5222..736df7daf4f 100644 --- a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts @@ -32,7 +32,7 @@ describe("Variant Analysis Monitor", () => { >; let variantAnalysisMonitor: VariantAnalysisMonitor; let shouldCancelMonitor: jest.Mock, [number]>; - let mockGetVariantAnalysisStatus: jest.Mock; + let mockGetVariantAnalysis: jest.Mock; let variantAnalysis: VariantAnalysis; const onVariantAnalysisChangeSpy = jest.fn(); @@ -44,7 +44,7 @@ describe("Variant Analysis Monitor", () => { variantAnalysis = createMockVariantAnalysis({}); shouldCancelMonitor = jest.fn(); - mockGetVariantAnalysisStatus = jest.fn(); + mockGetVariantAnalysis = jest.fn(); logger = createMockLogger(); @@ -56,7 +56,7 @@ describe("Variant Analysis Monitor", () => { logger, }), shouldCancelMonitor, - mockGetVariantAnalysisStatus, + mockGetVariantAnalysis, ); variantAnalysisMonitor.onVariantAnalysisChange(onVariantAnalysisChangeSpy); @@ -64,6 +64,8 @@ describe("Variant Analysis Monitor", () => { .spyOn(ghApiClient, "getVariantAnalysis") .mockRejectedValue(new Error("Not mocked")); + mockGetVariantAnalysis.mockReturnValue(variantAnalysis); + limitNumberOfAttemptsToMonitor(); }); @@ -342,9 +344,11 @@ describe("Variant Analysis Monitor", () => { describe("cancelation", () => { it("should maintain canceling status", async () => { - mockGetVariantAnalysisStatus.mockReturnValueOnce( - VariantAnalysisStatus.Canceling, - ); + mockGetVariantAnalysis.mockReturnValueOnce({ + ...variantAnalysis, + status: VariantAnalysisStatus.Canceling, + }); + mockApiResponse = createMockApiResponse("in_progress"); mockGetVariantAnalysisFromApi.mockResolvedValue(mockApiResponse); From eaa432b9d7569c158f5f589df05b8bdbc64f0ed3 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 28 Feb 2024 14:01:06 +0100 Subject: [PATCH 0058/1237] Add tests for new test warning behavior --- .../query-testing/test-adapter.test.ts | 28 +++++++++++++++++-- .../query-testing/test-runner-helpers.ts | 23 +++++++++++++++ .../query-testing/test-runner.test.ts | 23 ++++++++++++++- 3 files changed, 70 insertions(+), 4 deletions(-) diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-adapter.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-adapter.test.ts index ee7e6148c9f..0c8aa875b84 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-adapter.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-adapter.test.ts @@ -1,6 +1,7 @@ import type { TestItem, TestItemCollection, TestRun } from "vscode"; import { CancellationTokenSource, + Location, Range, TestRunRequest, Uri, @@ -75,6 +76,11 @@ describe("test-adapter", () => { id: `test ${mockTestsInfo.hPath}`, uri: Uri.file(mockTestsInfo.hPath), } as TestItem, + { + children: { size: 0 } as TestItemCollection, + id: `test ${mockTestsInfo.kPath}`, + uri: Uri.file(mockTestsInfo.kPath), + } as TestItem, ]; const childElements: IdTestItemPair[] = childItems.map((childItem) => [ childItem.id, @@ -87,7 +93,7 @@ describe("test-adapter", () => { id: `dir ${mockTestsInfo.testsPath}`, uri: Uri.file(mockTestsInfo.testsPath), children: { - size: 3, + size: 4, [Symbol.iterator]: childIteratorFunc, } as TestItemCollection, } as TestItem; @@ -95,7 +101,7 @@ describe("test-adapter", () => { const request = new TestRunRequest([rootItem]); await testManager.run(request, new CancellationTokenSource().token); - expect(enqueuedSpy).toHaveBeenCalledTimes(3); + expect(enqueuedSpy).toHaveBeenCalledTimes(4); expect(passedSpy).toHaveBeenCalledTimes(1); expect(passedSpy).toHaveBeenCalledWith(childItems[0], 3000); expect(erroredSpy).toHaveBeenCalledTimes(1); @@ -112,6 +118,7 @@ describe("test-adapter", () => { ], 4000, ); + expect(failedSpy).toHaveBeenCalledTimes(2); expect(failedSpy).toHaveBeenCalledWith( childItems[2], [ @@ -121,7 +128,22 @@ describe("test-adapter", () => { ], 11000, ); - expect(failedSpy).toHaveBeenCalledTimes(1); + expect(failedSpy).toHaveBeenCalledWith( + childItems[3], + [ + { + message: "Test failed", + }, + { + message: "abc", + location: new Location( + Uri.file(mockTestsInfo.kPath), + new Range(0, 0, 1, 1), + ), + }, + ], + 15000, + ); expect(endSpy).toHaveBeenCalledTimes(1); }); }); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-runner-helpers.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-runner-helpers.ts index afe5969cddc..8d52b9cb409 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-runner-helpers.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-runner-helpers.ts @@ -11,6 +11,7 @@ export const mockTestsInfo = { dPath: Uri.parse("file:/ab/c/d.ql").fsPath, gPath: Uri.parse("file:/ab/c/e/f/g.ql").fsPath, hPath: Uri.parse("file:/ab/c/e/f/h.ql").fsPath, + kPath: Uri.parse("file:/ab/c/e/f/k.ql").fsPath, }; /** @@ -89,6 +90,28 @@ function mockRunTests(): jest.Mock { evaluationMs: 6000, messages: [], }); + yield Promise.resolve({ + test: mockTestsInfo.kPath, + pass: false, + diff: ["jkh", "tuv"], + failureStage: "RESULT", + compilationMs: 7000, + evaluationMs: 8000, + // a warning in an otherwise successful test + messages: [ + { + position: { + fileName: mockTestsInfo.kPath, + line: 1, + column: 1, + endLine: 2, + endColumn: 2, + }, + message: "abc", + severity: "WARNING", + }, + ], + }); })(), ); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-runner.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-runner.test.ts index 837c28a3ce0..ea33088f87a 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-runner.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-runner.test.ts @@ -94,7 +94,7 @@ describe("test-runner", () => { eventHandlerSpy, ); - expect(eventHandlerSpy).toHaveBeenCalledTimes(3); + expect(eventHandlerSpy).toHaveBeenCalledTimes(4); expect(eventHandlerSpy).toHaveBeenNthCalledWith(1, { test: mockTestsInfo.dPath, @@ -133,6 +133,27 @@ describe("test-runner", () => { failureStage: "RESULT", messages: [], }); + expect(eventHandlerSpy).toHaveBeenNthCalledWith(4, { + test: mockTestsInfo.kPath, + pass: false, + compilationMs: 7000, + evaluationMs: 8000, + diff: ["jkh", "tuv"], + failureStage: "RESULT", + messages: [ + { + position: { + fileName: mockTestsInfo.kPath, + line: 1, + column: 1, + endLine: 2, + endColumn: 2, + }, + message: "abc", + severity: "WARNING", + }, + ], + }); }); it("should reregister testproj databases around test run", async () => { From bb796b0c275cd1d8a82a04cc052e0357370db856 Mon Sep 17 00:00:00 2001 From: Anders Starcke Henriksen Date: Thu, 29 Feb 2024 10:24:32 +0100 Subject: [PATCH 0059/1237] v1.12.3 --- extensions/ql-vscode/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 8d324491d6c..315b8376e8d 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -1,6 +1,6 @@ # CodeQL for Visual Studio Code: Changelog -## [UNRELEASED] +## 1.12.3 - 29 February 2024 - Update variant analysis view to show when cancelation is in progress. [#3405](https://github.com/github/vscode-codeql/pull/3405) - Remove support for CodeQL CLI versions older than 2.13.5. [#3371](https://github.com/github/vscode-codeql/pull/3371) From 30da5d5b62d198e8ee69fb202d3eaad78361d0d8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 29 Feb 2024 10:42:04 +0000 Subject: [PATCH 0060/1237] Bump version to v1.12.4 --- extensions/ql-vscode/CHANGELOG.md | 2 ++ extensions/ql-vscode/package-lock.json | 4 ++-- extensions/ql-vscode/package.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 315b8376e8d..8db43478011 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -1,5 +1,7 @@ # CodeQL for Visual Studio Code: Changelog +## [UNRELEASED] + ## 1.12.3 - 29 February 2024 - Update variant analysis view to show when cancelation is in progress. [#3405](https://github.com/github/vscode-codeql/pull/3405) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 69daa843735..b34df6401a9 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-codeql", - "version": "1.12.3", + "version": "1.12.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscode-codeql", - "version": "1.12.3", + "version": "1.12.4", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 3499c3a2060..af96426e15b 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -4,7 +4,7 @@ "description": "CodeQL for Visual Studio Code", "author": "GitHub", "private": true, - "version": "1.12.3", + "version": "1.12.4", "publisher": "GitHub", "license": "MIT", "icon": "media/VS-marketplace-CodeQL-icon.png", From b168ce708bf9a1320a1ed68eaa93c9dbada28071 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 29 Feb 2024 14:01:32 +0100 Subject: [PATCH 0061/1237] Check against problem and path-problem aliases --- .../ql-vscode/src/common/query-metadata.ts | 17 +++++++++++++++++ .../src/variant-analysis/code-scanning-pack.ts | 6 ++---- .../variant-analysis-manager.ts | 3 ++- .../RepositoriesSearchSortRow.tsx | 6 ++---- 4 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 extensions/ql-vscode/src/common/query-metadata.ts diff --git a/extensions/ql-vscode/src/common/query-metadata.ts b/extensions/ql-vscode/src/common/query-metadata.ts new file mode 100644 index 00000000000..89644bccb6d --- /dev/null +++ b/extensions/ql-vscode/src/common/query-metadata.ts @@ -0,0 +1,17 @@ +const SARIF_RESULTS_QUERY_KINDS = [ + "problem", + "alert", + "path-problem", + "path-alert", +]; + +/** + * Returns whether this query kind supports producing SARIF results. + */ +export function isSarifResultsQueryKind(kind: string | undefined): boolean { + if (!kind) { + return false; + } + + return SARIF_RESULTS_QUERY_KINDS.includes(kind); +} diff --git a/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts b/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts index 94542b4c3f5..4da94def558 100644 --- a/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts +++ b/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts @@ -4,6 +4,7 @@ import type { QueryLanguage } from "../common/query-language"; import type { CodeQLCliServer } from "../codeql-cli/cli"; import type { QlPackDetails } from "./ql-pack-details"; import { getQlPackFilePath } from "../common/ql"; +import { isSarifResultsQueryKind } from "../common/query-metadata"; export async function resolveCodeScanningQueryPack( logger: BaseLogger, @@ -64,10 +65,7 @@ async function filterToOnlyProblemQueries( const problemQueries: string[] = []; for (const query of queries) { const queryMetadata = await cliServer.resolveMetadata(query); - if ( - queryMetadata.kind === "problem" || - queryMetadata.kind === "path-problem" - ) { + if (isSarifResultsQueryKind(queryMetadata.kind)) { problemQueries.push(query); } else { void logger.log(`Skipping non-problem query ${query}`); diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index cdcd1f28441..169e99deb3f 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -96,6 +96,7 @@ import { tryGetQueryMetadata } from "../codeql-cli/query-metadata"; import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders"; import { findVariantAnalysisQlPackRoot } from "./ql"; import { resolveCodeScanningQueryPack } from "./code-scanning-pack"; +import { isSarifResultsQueryKind } from "../common/query-metadata"; const maxRetryCount = 3; @@ -330,7 +331,7 @@ export class VariantAnalysisManager // It's not possible to interpret a BQRS file to SARIF without an id property. if ( queryMetadata?.kind && - ["problem", "path-problem"].includes(queryMetadata.kind) && + isSarifResultsQueryKind(queryMetadata.kind) && !queryMetadata.id ) { throw new UserCancellationException( diff --git a/extensions/ql-vscode/src/view/variant-analysis/RepositoriesSearchSortRow.tsx b/extensions/ql-vscode/src/view/variant-analysis/RepositoriesSearchSortRow.tsx index 6cc6434cc06..648fe43536b 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/RepositoriesSearchSortRow.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/RepositoriesSearchSortRow.tsx @@ -11,6 +11,7 @@ import { RepositoriesSort } from "./RepositoriesSort"; import { RepositoriesFilter } from "./RepositoriesFilter"; import { RepositoriesResultFormat } from "./RepositoriesResultFormat"; import type { ResultFormat } from "../../variant-analysis/shared/variant-analysis-result-format"; +import { isSarifResultsQueryKind } from "../../common/query-metadata"; type Props = { filterSortValue: RepositoriesFilterSortState; @@ -47,10 +48,7 @@ const RepositoriesResultFormatColumn = styled(RepositoriesResultFormat)` function showResultFormatColumn( variantAnalysisQueryKind: string | undefined, ): boolean { - return ( - variantAnalysisQueryKind === "problem" || - variantAnalysisQueryKind === "path-problem" - ); + return isSarifResultsQueryKind(variantAnalysisQueryKind); } export const RepositoriesSearchSortRow = ({ From 1ac92ad79c554affa57ed119cf6c93cb64293cb7 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Thu, 29 Feb 2024 13:38:21 +0000 Subject: [PATCH 0062/1237] Don't open variant analysis view when running a model evaluation run (#3424) --- .../ql-vscode/src/model-editor/model-evaluator.ts | 1 + .../src/variant-analysis/variant-analysis-manager.ts | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts index 9ad202def1d..ff38797e1d1 100644 --- a/extensions/ql-vscode/src/model-editor/model-evaluator.ts +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -60,6 +60,7 @@ export class ModelEvaluator extends DisposableObject { qlPack, progress, token, + false, ); } catch (e) { this.modelingStore.updateModelEvaluationRun(this.dbItem, undefined); diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index d1bfd8822f5..077d36c1eb1 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -300,6 +300,7 @@ export class VariantAnalysisManager qlPackDetails: QlPackDetails, progress: ProgressCallback, token: CancellationToken, + openViewAfterSubmission = true, ): Promise { await saveBeforeStart(); @@ -399,10 +400,13 @@ export class VariantAnalysisManager `Variant analysis ${mappedVariantAnalysis.query.name} submitted for processing`, ); - void this.app.commands.execute( - "codeQL.openVariantAnalysisView", - mappedVariantAnalysis.id, - ); + if (openViewAfterSubmission) { + void this.app.commands.execute( + "codeQL.openVariantAnalysisView", + mappedVariantAnalysis.id, + ); + } + void this.app.commands.execute( "codeQL.monitorNewVariantAnalysis", mappedVariantAnalysis, From a51bd76392646234ff2c9066c0434ac249525a85 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Thu, 29 Feb 2024 13:50:19 +0000 Subject: [PATCH 0063/1237] Skip logging warning for multi-query variant analyses (#3425) --- .../ql-vscode/src/query-history/query-history-manager.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extensions/ql-vscode/src/query-history/query-history-manager.ts b/extensions/ql-vscode/src/query-history/query-history-manager.ts index ea5806a0570..22562d76a48 100644 --- a/extensions/ql-vscode/src/query-history/query-history-manager.ts +++ b/extensions/ql-vscode/src/query-history/query-history-manager.ts @@ -418,6 +418,11 @@ export class QueryHistoryManager extends DisposableObject { }); await this.refreshTreeView(); } else { + if (variantAnalysis.queries !== undefined) { + // This is a variant analysis that contains multiple queries, which + // is not fully supported yet. So we ignore it from the query history. + return; + } void this.app.logger.log( "Variant analysis status update event received for unknown variant analysis", ); From a70fd835f14e1e5dbfbb50ed188d9f42227d45ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Feb 2024 06:12:11 -0800 Subject: [PATCH 0064/1237] Bump mini-css-extract-plugin in /extensions/ql-vscode (#3428) Bumps [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) from 2.8.0 to 2.8.1. - [Release notes](https://github.com/webpack-contrib/mini-css-extract-plugin/releases) - [Changelog](https://github.com/webpack-contrib/mini-css-extract-plugin/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack-contrib/mini-css-extract-plugin/compare/v2.8.0...v2.8.1) --- updated-dependencies: - dependency-name: mini-css-extract-plugin dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/ql-vscode/package-lock.json | 8 ++++---- extensions/ql-vscode/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index b34df6401a9..0f352348c0b 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -128,7 +128,7 @@ "lint-staged": "^15.0.2", "markdownlint-cli2": "^0.12.1", "markdownlint-cli2-formatter-pretty": "^0.0.5", - "mini-css-extract-plugin": "^2.8.0", + "mini-css-extract-plugin": "^2.8.1", "npm-run-all": "^4.1.5", "patch-package": "^8.0.0", "prettier": "^3.2.5", @@ -24924,9 +24924,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.0.tgz", - "integrity": "sha512-CxmUYPFcTgET1zImteG/LZOy/4T5rTojesQXkSNBiquhydn78tfbCE9sjIjnJ/UcjNjOC1bphTCCW5rrS7cXAg==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.1.tgz", + "integrity": "sha512-/1HDlyFRxWIZPI1ZpgqlZ8jMw/1Dp/dl3P0L1jtZ+zVcHqwPhGwaJwKL00WVgfnBy6PWCde9W65or7IIETImuA==", "dev": true, "dependencies": { "schema-utils": "^4.0.0", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index af96426e15b..1c9e95b47c7 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -2056,7 +2056,7 @@ "lint-staged": "^15.0.2", "markdownlint-cli2": "^0.12.1", "markdownlint-cli2-formatter-pretty": "^0.0.5", - "mini-css-extract-plugin": "^2.8.0", + "mini-css-extract-plugin": "^2.8.1", "npm-run-all": "^4.1.5", "patch-package": "^8.0.0", "prettier": "^3.2.5", From f283df0225613b972cadcb9083ea42207e92405a Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Thu, 29 Feb 2024 14:19:34 +0000 Subject: [PATCH 0065/1237] Update model evaluation notification (#3426) --- extensions/ql-vscode/src/model-editor/model-evaluator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts index ff38797e1d1..f0f0d9a75f2 100644 --- a/extensions/ql-vscode/src/model-editor/model-evaluator.ts +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -77,8 +77,8 @@ export class ModelEvaluator extends DisposableObject { } }, { - title: "Run Variant Analysis", - cancellable: true, + title: "Run model evaluation", + cancellable: false, }, ); } From 567a0bb194c1d9b59c0b8a06ae7886dce529ba1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Feb 2024 06:22:37 -0800 Subject: [PATCH 0066/1237] Bump applicationinsights from 2.9.2 to 2.9.4 in /extensions/ql-vscode (#3430) Bumps [applicationinsights](https://github.com/microsoft/ApplicationInsights-node.js) from 2.9.2 to 2.9.4. - [Release notes](https://github.com/microsoft/ApplicationInsights-node.js/releases) - [Commits](https://github.com/microsoft/ApplicationInsights-node.js/compare/2.9.2...2.9.4) --- updated-dependencies: - dependency-name: applicationinsights dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/ql-vscode/package-lock.json | 10 +++++----- extensions/ql-vscode/package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 0f352348c0b..b46fb27d8cf 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -98,7 +98,7 @@ "@vscode/test-electron": "^2.2.0", "@vscode/vsce": "^2.19.0", "ansi-colors": "^4.1.1", - "applicationinsights": "^2.9.2", + "applicationinsights": "^2.9.4", "cosmiconfig": "^9.0.0", "cross-env": "^7.0.3", "css-loader": "^6.10.0", @@ -11102,16 +11102,16 @@ } }, "node_modules/applicationinsights": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-2.9.2.tgz", - "integrity": "sha512-wlDiD7v0BQNM8oNzsf9C836R5ze25u+CuCEZsbA5xMIXYYBxkqkWE/mo9GFJM7rsKaiGqpxEwWmePHKD2Lwy2w==", + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-2.9.4.tgz", + "integrity": "sha512-mMWW/E+1wy3oreeH1xJd2N5WIwIz4xj3JKneHOi7gHFqPjxgFV68mdKMEeM3YhLK1nC6k+M6Ekhf7e9WhQCSSw==", "dev": true, "dependencies": { "@azure/core-auth": "^1.5.0", "@azure/core-rest-pipeline": "1.10.1", "@azure/core-util": "1.2.0", "@azure/opentelemetry-instrumentation-azure-sdk": "^1.0.0-beta.5", - "@microsoft/applicationinsights-web-snippet": "^1.0.1", + "@microsoft/applicationinsights-web-snippet": "1.0.1", "@opentelemetry/api": "^1.7.0", "@opentelemetry/core": "^1.19.0", "@opentelemetry/sdk-trace-base": "^1.19.0", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 1c9e95b47c7..cd019994ded 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -2026,7 +2026,7 @@ "@vscode/test-electron": "^2.2.0", "@vscode/vsce": "^2.19.0", "ansi-colors": "^4.1.1", - "applicationinsights": "^2.9.2", + "applicationinsights": "^2.9.4", "cosmiconfig": "^9.0.0", "cross-env": "^7.0.3", "css-loader": "^6.10.0", From d810ed823a4ac53a248bb9415ed07818555e58f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Feb 2024 06:23:01 -0800 Subject: [PATCH 0067/1237] Bump eslint-plugin-storybook in /extensions/ql-vscode (#3431) Bumps [eslint-plugin-storybook](https://github.com/storybookjs/eslint-plugin-storybook) from 0.6.15 to 0.8.0. - [Release notes](https://github.com/storybookjs/eslint-plugin-storybook/releases) - [Changelog](https://github.com/storybookjs/eslint-plugin-storybook/blob/main/CHANGELOG.md) - [Commits](https://github.com/storybookjs/eslint-plugin-storybook/compare/v0.6.15...v0.8.0) --- updated-dependencies: - dependency-name: eslint-plugin-storybook dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/ql-vscode/package-lock.json | 14 +++++++------- extensions/ql-vscode/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index b46fb27d8cf..9b4e4808dc3 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -114,7 +114,7 @@ "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.31.8", "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-storybook": "^0.6.4", + "eslint-plugin-storybook": "^0.8.0", "file-loader": "^6.2.0", "glob": "^10.0.0", "gulp": "^4.0.2", @@ -16036,18 +16036,18 @@ } }, "node_modules/eslint-plugin-storybook": { - "version": "0.6.15", - "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.6.15.tgz", - "integrity": "sha512-lAGqVAJGob47Griu29KXYowI4G7KwMoJDOkEip8ujikuDLxU+oWJ1l0WL6F2oDO4QiyUFXvtDkEkISMOPzo+7w==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.8.0.tgz", + "integrity": "sha512-CZeVO5EzmPY7qghO2t64oaFM+8FTaD4uzOEjHKp516exyTKo+skKAL9GI3QALS2BXhyALJjNtwbmr1XinGE8bA==", "dev": true, "dependencies": { "@storybook/csf": "^0.0.1", - "@typescript-eslint/utils": "^5.45.0", - "requireindex": "^1.1.0", + "@typescript-eslint/utils": "^5.62.0", + "requireindex": "^1.2.0", "ts-dedent": "^2.2.0" }, "engines": { - "node": "12.x || 14.x || >= 16" + "node": ">= 18" }, "peerDependencies": { "eslint": ">=6" diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index cd019994ded..e4b7ec8f1d8 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -2042,7 +2042,7 @@ "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.31.8", "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-storybook": "^0.6.4", + "eslint-plugin-storybook": "^0.8.0", "file-loader": "^6.2.0", "glob": "^10.0.0", "gulp": "^4.0.2", From 30fd1226d568de129990d33837c713abacbab8fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Feb 2024 06:47:04 -0800 Subject: [PATCH 0068/1237] Bump @babel/core from 7.23.6 to 7.24.0 in /extensions/ql-vscode (#3429) Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.23.6 to 7.24.0. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.24.0/packages/babel-core) --- updated-dependencies: - dependency-name: "@babel/core" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/ql-vscode/package-lock.json | 64 +++++++++++++------------- extensions/ql-vscode/package.json | 2 +- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 9b4e4808dc3..00b50970dc6 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -45,7 +45,7 @@ "zip-a-folder": "^3.1.6" }, "devDependencies": { - "@babel/core": "^7.18.13", + "@babel/core": "^7.24.0", "@babel/plugin-transform-modules-commonjs": "^7.18.6", "@babel/preset-env": "^7.23.9", "@babel/preset-react": "^7.18.6", @@ -358,9 +358,9 @@ } }, "node_modules/@babel/core": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.6.tgz", - "integrity": "sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", + "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", @@ -368,11 +368,11 @@ "@babel/generator": "^7.23.6", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.6", - "@babel/parser": "^7.23.6", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.6", - "@babel/types": "^7.23.6", + "@babel/helpers": "^7.24.0", + "@babel/parser": "^7.24.0", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.0", + "@babel/types": "^7.24.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -744,14 +744,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.6.tgz", - "integrity": "sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.0.tgz", + "integrity": "sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==", "dev": true, "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.6", - "@babel/types": "^7.23.6" + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.0", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -772,9 +772,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", - "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", + "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -2335,23 +2335,23 @@ } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.6.tgz", - "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz", + "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.23.5", @@ -2360,8 +2360,8 @@ "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.6", - "@babel/types": "^7.23.6", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2370,9 +2370,9 @@ } }, "node_modules/@babel/types": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", - "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index e4b7ec8f1d8..d9b32869e07 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1973,7 +1973,7 @@ "zip-a-folder": "^3.1.6" }, "devDependencies": { - "@babel/core": "^7.18.13", + "@babel/core": "^7.24.0", "@babel/plugin-transform-modules-commonjs": "^7.18.6", "@babel/preset-env": "^7.23.9", "@babel/preset-react": "^7.18.6", From df782592bce43230cb01b9e439c755d7e267f015 Mon Sep 17 00:00:00 2001 From: Shati Patel <42641846+shati-patel@users.noreply.github.com> Date: Thu, 29 Feb 2024 16:42:38 +0000 Subject: [PATCH 0069/1237] Extract "ModelEvaluation" component (#3432) --- .../src/view/model-editor/ModelEditor.tsx | 54 +------------------ .../model-editor/ModelEditorProgressRing.tsx | 8 +++ .../src/view/model-editor/ModelEvaluation.tsx | 53 ++++++++++++++++++ 3 files changed, 62 insertions(+), 53 deletions(-) create mode 100644 extensions/ql-vscode/src/view/model-editor/ModelEditorProgressRing.tsx create mode 100644 extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx index 980c0f61d33..3d42965b108 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx @@ -3,7 +3,6 @@ import type { ToModelEditorMessage } from "../../common/interface-types"; import { VSCodeButton, VSCodeCheckbox, - VSCodeProgressRing, VSCodeTag, } from "@vscode/webview-ui-toolkit/react"; import { styled } from "styled-components"; @@ -21,7 +20,7 @@ import { getLanguageDisplayName } from "../../common/query-language"; import { INITIAL_HIDE_MODELED_METHODS_VALUE } from "../../model-editor/shared/hide-modeled-methods"; import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions"; import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state"; -import { modelEvaluationRunIsRunning } from "../../model-editor/shared/model-evaluation-run-state"; +import { ModelEvaluation } from "./ModelEvaluation"; const LoadingContainer = styled.div` text-align: center; @@ -77,57 +76,6 @@ const ButtonsContainer = styled.div` margin-top: 1rem; `; -const ProgressRing = styled(VSCodeProgressRing)` - width: 16px; - height: 16px; - margin-right: 5px; -`; - -const ModelEvaluation = ({ - viewState, - modeledMethods, - modifiedSignatures, - onStartEvaluation, - onStopEvaluation, - evaluationRun, -}: { - viewState: ModelEditorViewState; - modeledMethods: Record; - modifiedSignatures: Set; - onStartEvaluation: () => void; - onStopEvaluation: () => void; - evaluationRun: ModelEvaluationRunState | undefined; -}) => { - if (!viewState.showEvaluationUi) { - return null; - } - - if (!evaluationRun || !modelEvaluationRunIsRunning(evaluationRun)) { - const customModelsExist = Object.values(modeledMethods).some( - (methods) => methods.filter((m) => m.type !== "none").length > 0, - ); - - const unsavedChanges = modifiedSignatures.size > 0; - - return ( - - Evaluate - - ); - } else { - return ( - - - Stop evaluation - - ); - } -}; - type Props = { initialViewState?: ModelEditorViewState; initialMethods?: Method[]; diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEditorProgressRing.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEditorProgressRing.tsx new file mode 100644 index 00000000000..3994caddf82 --- /dev/null +++ b/extensions/ql-vscode/src/view/model-editor/ModelEditorProgressRing.tsx @@ -0,0 +1,8 @@ +import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react"; +import { styled } from "styled-components"; + +export const ModelEditorProgressRing = styled(VSCodeProgressRing)` + width: 16px; + height: 16px; + margin-right: 5px; +`; diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx new file mode 100644 index 00000000000..b640f94e16c --- /dev/null +++ b/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx @@ -0,0 +1,53 @@ +import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"; +import type { ModeledMethod } from "../../model-editor/modeled-method"; +import type { ModelEditorViewState } from "../../model-editor/shared/view-state"; +import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state"; +import { modelEvaluationRunIsRunning } from "../../model-editor/shared/model-evaluation-run-state"; +import { ModelEditorProgressRing } from "./ModelEditorProgressRing"; + +type Props = { + viewState: ModelEditorViewState; + modeledMethods: Record; + modifiedSignatures: Set; + onStartEvaluation: () => void; + onStopEvaluation: () => void; + evaluationRun: ModelEvaluationRunState | undefined; +}; + +export const ModelEvaluation = ({ + viewState, + modeledMethods, + modifiedSignatures, + onStartEvaluation, + onStopEvaluation, + evaluationRun, +}: Props) => { + if (!viewState.showEvaluationUi) { + return null; + } + + if (!evaluationRun || !modelEvaluationRunIsRunning(evaluationRun)) { + const customModelsExist = Object.values(modeledMethods).some( + (methods) => methods.filter((m) => m.type !== "none").length > 0, + ); + + const unsavedChanges = modifiedSignatures.size > 0; + + return ( + + Evaluate + + ); + } else { + return ( + + + Stop evaluation + + ); + } +}; From 361fed622b7ed4c1c1fdef2617f1b7ceedcb61ed Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Fri, 1 Mar 2024 10:16:16 +0100 Subject: [PATCH 0070/1237] Ask user if they want to re-import outdated testproj dbs Before running a query now, do the following: 1. Check if the selected database is imported from a testproj 2. If so, check the last modified time of the `codeql-datase.yml` file of the imported database with that of its origin. 3. If the origin database has a file that is newer, assume that the database has been recreated since the last time it was imported. 4. If newer, then ask the user if they want to re-import before running the query. Also, this change appends the `(test)` label to all test databases in the database list. --- .../src/databases/local-databases-ui.ts | 3 +- .../local-databases/database-manager.ts | 80 ++++++++++++++++++- .../src/query-server/query-runner.ts | 17 +++- .../databases/database-fetcher.test.ts | 24 +++++- .../cli-integration/jest.setup.ts | 30 ++++--- .../test/vscode-tests/global.helper.ts | 2 +- 6 files changed, 136 insertions(+), 20 deletions(-) diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 8cf57b6ce6b..9343af91f48 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -146,7 +146,8 @@ class DatabaseTreeDataProvider item.iconPath = new ThemeIcon("error", new ThemeColor("errorForeground")); } item.tooltip = element.databaseUri.fsPath; - item.description = element.language; + item.description = + element.language + (element.origin?.type === "testproj" ? " (test)" : ""); return item; } diff --git a/extensions/ql-vscode/src/databases/local-databases/database-manager.ts b/extensions/ql-vscode/src/databases/local-databases/database-manager.ts index f1234b2f74a..46ec62b3791 100644 --- a/extensions/ql-vscode/src/databases/local-databases/database-manager.ts +++ b/extensions/ql-vscode/src/databases/local-databases/database-manager.ts @@ -18,7 +18,10 @@ import { import { join } from "path"; import type { FullDatabaseOptions } from "./database-options"; import { DatabaseItemImpl } from "./database-item-impl"; -import { showNeverAskAgainDialog } from "../../common/vscode/dialog"; +import { + showBinaryChoiceDialog, + showNeverAskAgainDialog, +} from "../../common/vscode/dialog"; import { getFirstWorkspaceFolder, isFolderAlreadyInWorkspace, @@ -32,7 +35,7 @@ import { QlPackGenerator } from "../../local-queries/qlpack-generator"; import { asError, getErrorMessage } from "../../common/helpers-pure"; import type { DatabaseItem, PersistedDatabaseItem } from "./database-item"; import { redactableError } from "../../common/errors"; -import { remove } from "fs-extra"; +import { copy, remove, stat } from "fs-extra"; import { containsPath } from "../../common/files"; import type { DatabaseChangedEvent } from "./database-events"; import { DatabaseEventKind } from "./database-events"; @@ -116,6 +119,7 @@ export class DatabaseManager extends DisposableObject { super(); qs.onStart(this.reregisterDatabases.bind(this)); + qs.onQueryRunStarting(this.maybeReimportTestDatabase.bind(this)); this.push( this.languageContext.onLanguageContextChanged(async () => { @@ -170,12 +174,82 @@ export class DatabaseManager extends DisposableObject { const originPath = uri.fsPath; for (const item of this._databaseItems) { if (item.origin?.type === "testproj" && item.origin.path === originPath) { - return item + return item; } } return undefined; } + public async maybeReimportTestDatabase( + databaseUri: vscode.Uri, + forceImport = false, + ): Promise { + const res = await this.isTestDatabaseOutdated(databaseUri); + if (!res) { + return; + } + const doit = + forceImport || + (await showBinaryChoiceDialog( + "This test database is outdated. Do you want to reimport it?", + )); + + if (doit) { + await this.reimportTestDatabase(databaseUri); + } + } + + /** + * Checks if the origin of the imported database is newer. + * The imported database must be a test database. + * @param databaseUri the URI of the imported database to check + * @returns true if both databases exist and the origin database is newer. + */ + private async isTestDatabaseOutdated( + databaseUri: vscode.Uri, + ): Promise { + const dbItem = this.findDatabaseItem(databaseUri); + if (dbItem === undefined || dbItem.origin?.type !== "testproj") { + return false; + } + + // Compare timestmps of the codeql-database.yml files of the original and the + // imported databases. + const originDbYml = join(dbItem.origin.path, "codeql-database.yml"); + const importedDbYml = join( + dbItem.databaseUri.fsPath, + "codeql-database.yml", + ); + + // TODO add error handling if one does not exist. + const originStat = await stat(originDbYml); + const importedStat = await stat(importedDbYml); + return originStat.mtimeMs > importedStat.mtimeMs; + } + + /** + * Reimport the specified imported database from its origin. + * The imported databsae must be a testproj database. + * + * @param databaseUri the URI of the imported database to reimport + */ + private async reimportTestDatabase(databaseUri: vscode.Uri): Promise { + const dbItem = this.findDatabaseItem(databaseUri); + if (dbItem === undefined || dbItem.origin?.type !== "testproj") { + throw new Error(`Database ${databaseUri} is not a testproj.`); + } + + await this.removeDatabaseItem(dbItem); + await copy(dbItem.origin.path, databaseUri.fsPath); + const newDbItem = new DatabaseItemImpl(databaseUri, dbItem.contents, { + dateAdded: Date.now(), + language: dbItem.language, + origin: dbItem.origin, + }); + await this.addDatabaseItem(newDbItem); + await this.setCurrentDatabaseItem(newDbItem); + } + /** * Adds a {@link DatabaseItem} to the list of open databases, if that database is not already on * the list. diff --git a/extensions/ql-vscode/src/query-server/query-runner.ts b/extensions/ql-vscode/src/query-server/query-runner.ts index 166d149d2ee..f2d8f71632b 100644 --- a/extensions/ql-vscode/src/query-server/query-runner.ts +++ b/extensions/ql-vscode/src/query-server/query-runner.ts @@ -1,4 +1,4 @@ -import { window } from "vscode"; +import { window, Uri } from "vscode"; import type { CancellationToken, MessageItem } from "vscode"; import type { CodeQLCliServer } from "../codeql-cli/cli"; import type { ProgressCallback } from "../common/vscode/progress"; @@ -63,9 +63,22 @@ export interface CoreQueryRun { export type CoreCompletedQuery = CoreQueryResults & Omit; +type OnQueryRunStargingListener = (dbPath: Uri) => Promise; export class QueryRunner { constructor(public readonly qs: QueryServerClient) {} + // Event handlers that get notified whenever a query is about to start running. + // Can't use vscode EventEmitters since they are not asynchronous. + private readonly onQueryRunStartingListeners: OnQueryRunStargingListener[] = + []; + public onQueryRunStarting(listener: OnQueryRunStargingListener) { + this.onQueryRunStartingListeners.push(listener); + } + + private async fireQueryRunStarting(dbPath: Uri) { + await Promise.all(this.onQueryRunStartingListeners.map((l) => l(dbPath))); + } + get cliServer(): CodeQLCliServer { return this.qs.cliServer; } @@ -138,6 +151,8 @@ export class QueryRunner { templates: Record | undefined, logger: BaseLogger, ): Promise { + await this.fireQueryRunStarting(Uri.file(dbPath)); + return await compileAndRunQueryAgainstDatabaseCore( this.qs, dbPath, diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/databases/database-fetcher.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/databases/database-fetcher.test.ts index 1f8f7a5c8f6..a0690f84e4d 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/databases/database-fetcher.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/databases/database-fetcher.test.ts @@ -16,7 +16,8 @@ import { testprojLoc, } from "../../global.helper"; import { createMockCommandManager } from "../../../__mocks__/commandsMock"; -import { remove } from "fs-extra"; +import { utimesSync } from "fs"; +import { remove, existsSync } from "fs-extra"; /** * Run various integration tests for databases @@ -80,7 +81,26 @@ describe("database-fetcher", () => { expect(dbItem).toBeDefined(); dbItem = dbItem!; expect(dbItem.name).toBe("db"); - expect(dbItem.databaseUri.fsPath).toBe(join(storagePath, "db", "db")); + expect(dbItem.databaseUri.fsPath).toBe(join(storagePath, "db")); + + // Now that we have fetched it. Check for re-importing + // Delete a file in the imported database and we can check if the file is recreated + const srczip = join(dbItem.databaseUri.fsPath, "src.zip"); + await remove(srczip); + + // Attempt 1: re-import database should be a no-op since timestamp of imported database is newer + await databaseManager.maybeReimportTestDatabase(dbItem.databaseUri); + expect(existsSync(srczip)).toBeFalsy(); + + // Attempt 3: re-import database should re-import the database after updating modified time + utimesSync( + join(testprojLoc, "codeql-database.yml"), + new Date(), + new Date(), + ); + + await databaseManager.maybeReimportTestDatabase(dbItem.databaseUri, true); + expect(existsSync(srczip)).toBeTruthy(); }); }); diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts index 69c2fd804cd..700b2dc38c1 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts @@ -6,7 +6,7 @@ import { beforeEachAction, } from "../jest.activated-extension.setup"; import { createWriteStream, existsSync, mkdirpSync } from "fs-extra"; -import { dirname } from "path"; +import { dirname, join } from "path"; import { DB_URL, dbLoc, testprojLoc } from "../global.helper"; import fetch from "node-fetch"; import { createReadStream, renameSync } from "fs"; @@ -14,7 +14,8 @@ import { Extract } from "unzipper"; beforeAll(async () => { // ensure the test database is downloaded - mkdirpSync(dirname(dbLoc)); + const dbParentDir = dirname(dbLoc); + mkdirpSync(dbParentDir); if (!existsSync(dbLoc)) { console.log(`Downloading test database to ${dbLoc}`); @@ -30,18 +31,23 @@ beforeAll(async () => { }); }); }); + } + + // unzip the database from dbLoc to testprojLoc + if (!existsSync(testprojLoc)) { + console.log(`Unzipping test database to ${testprojLoc}`); - // unzip the database from dbLoc to testprojLoc - if (!existsSync(testprojLoc)) { - console.log(`Unzipping test database to ${testprojLoc}`); - const dbDir = dirname(testprojLoc); - mkdirpSync(dbDir); - console.log(`Unzipping test database to ${testprojLoc}`); + await new Promise((resolve, reject) => { createReadStream(dbLoc) - .pipe(Extract({ path: dirname(dbDir) })) - .on("close", () => console.log("Unzip completed.")); - } - renameSync(dbLoc, testprojLoc); + .pipe(Extract({ path: dbParentDir })) + .on("close", () => { + console.log("Unzip completed."); + resolve(undefined); + }) + .on("error", (e) => reject(e)); + }); + + renameSync(join(dbParentDir, "db"), testprojLoc); } await beforeAllAction(); diff --git a/extensions/ql-vscode/test/vscode-tests/global.helper.ts b/extensions/ql-vscode/test/vscode-tests/global.helper.ts index 1e104d9c54a..c4f69823577 100644 --- a/extensions/ql-vscode/test/vscode-tests/global.helper.ts +++ b/extensions/ql-vscode/test/vscode-tests/global.helper.ts @@ -24,7 +24,7 @@ export const dbLoc = join( export const testprojLoc = join( realpathSync(join(__dirname, "../../../")), - "build/tests/db.zip", + "build/tests/db.testproj", ); // eslint-disable-next-line import/no-mutable-exports From 96b7722f260b44c75b8f8da309fe0b7e8fa30b85 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Fri, 1 Mar 2024 09:25:03 +0000 Subject: [PATCH 0071/1237] Add logic to stop an evaluation run (#3421) --- .../src/model-editor/model-evaluator.ts | 130 +++++++++++++----- .../src/model-editor/modeling-store.ts | 6 + .../model-editor/modelingStoreMock.ts | 6 + .../model-editor/model-evaluator.test.ts | 97 +++++++++++++ 4 files changed, 207 insertions(+), 32 deletions(-) create mode 100644 extensions/ql-vscode/test/vscode-tests/activated-extension/model-editor/model-evaluator.test.ts diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts index f0f0d9a75f2..6303aa6c7e3 100644 --- a/extensions/ql-vscode/src/model-editor/model-evaluator.ts +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -9,10 +9,22 @@ import type { CodeQLCliServer } from "../codeql-cli/cli"; import type { VariantAnalysisManager } from "../variant-analysis/variant-analysis-manager"; import type { QueryLanguage } from "../common/query-language"; import { resolveCodeScanningQueryPack } from "../variant-analysis/code-scanning-pack"; -import { withProgress } from "../common/vscode/progress"; +import type { ProgressCallback } from "../common/vscode/progress"; +import { + UserCancellationException, + withProgress, +} from "../common/vscode/progress"; import type { VariantAnalysis } from "../variant-analysis/shared/variant-analysis"; +import type { CancellationToken } from "vscode"; +import { CancellationTokenSource } from "vscode"; +import type { QlPackDetails } from "../variant-analysis/ql-pack-details"; export class ModelEvaluator extends DisposableObject { + // Cancellation token source to allow cancelling of the current run + // before a variant analysis has been submitted. Once it has been + // submitted, we use the variant analysis manager's cancellation support. + private cancellationSource: CancellationTokenSource; + public constructor( private readonly logger: BaseLogger, private readonly cliServer: CodeQLCliServer, @@ -28,6 +40,8 @@ export class ModelEvaluator extends DisposableObject { super(); this.registerToModelingEvents(); + + this.cancellationSource = new CancellationTokenSource(); } public async startEvaluation() { @@ -52,30 +66,12 @@ export class ModelEvaluator extends DisposableObject { // Submit variant analysis and monitor progress return withProgress( - async (progress, token) => { - let variantAnalysisId: number | undefined = undefined; - try { - variantAnalysisId = - await this.variantAnalysisManager.runVariantAnalysis( - qlPack, - progress, - token, - false, - ); - } catch (e) { - this.modelingStore.updateModelEvaluationRun(this.dbItem, undefined); - throw e; - } - - if (variantAnalysisId) { - this.monitorVariantAnalysis(variantAnalysisId); - } else { - this.modelingStore.updateModelEvaluationRun(this.dbItem, undefined); - throw new Error( - "Unable to trigger variant analysis for evaluation run", - ); - } - }, + (progress) => + this.runVariantAnalysis( + qlPack, + progress, + this.cancellationSource.token, + ), { title: "Run model evaluation", cancellable: false, @@ -84,13 +80,29 @@ export class ModelEvaluator extends DisposableObject { } public async stopEvaluation() { - // For now just update the store. - // This will be fleshed out in the near future. - const evaluationRun: ModelEvaluationRun = { - isPreparing: false, - variantAnalysisId: undefined, - }; - this.modelingStore.updateModelEvaluationRun(this.dbItem, evaluationRun); + const evaluationRun = this.modelingStore.getModelEvaluationRun(this.dbItem); + if (!evaluationRun) { + void this.logger.log("No active evaluation run to stop"); + return; + } + + this.cancellationSource.cancel(); + + if (evaluationRun.variantAnalysisId === undefined) { + // If the variant analysis has not been submitted yet, we can just + // update the store. + this.modelingStore.updateModelEvaluationRun(this.dbItem, { + ...evaluationRun, + isPreparing: false, + }); + } else { + // If the variant analysis has been submitted, we need to cancel it. We + // don't need to update the store here, as the event handler for + // onVariantAnalysisStatusUpdated will do that for us. + await this.variantAnalysisManager.cancelVariantAnalysis( + evaluationRun.variantAnalysisId, + ); + } } private registerToModelingEvents() { @@ -128,6 +140,60 @@ export class ModelEvaluator extends DisposableObject { return undefined; } + private async runVariantAnalysis( + qlPack: QlPackDetails, + progress: ProgressCallback, + token: CancellationToken, + ): Promise { + let result: number | void = undefined; + try { + // Use Promise.race to make sure to stop the variant analysis processing when the + // user has stopped the evaluation run. We can't simply rely on the cancellation token + // because we haven't fully implemented cancellation support for variant analysis. + // Using this approach we make sure that the process is stopped from a user's point + // of view (the notification goes away too). It won't necessarily stop any tasks + // that are not aware of the cancellation token. + result = await Promise.race([ + this.variantAnalysisManager.runVariantAnalysis( + qlPack, + progress, + token, + false, + ), + new Promise((_, reject) => { + token.onCancellationRequested(() => + reject(new UserCancellationException(undefined, true)), + ); + }), + ]); + } catch (e) { + this.modelingStore.updateModelEvaluationRun(this.dbItem, undefined); + if (!(e instanceof UserCancellationException)) { + throw e; + } else { + return; + } + } finally { + // Renew the cancellation token source for the new evaluation run. + // This is necessary because we don't want the next evaluation run + // to start as cancelled. + this.cancellationSource = new CancellationTokenSource(); + } + + // If the result is a number, it means the variant analysis was successfully submitted, + // so we need to update the store and start up the monitor. + if (typeof result === "number") { + this.modelingStore.updateModelEvaluationRun(this.dbItem, { + isPreparing: true, + variantAnalysisId: result, + }); + this.monitorVariantAnalysis(result); + } else { + this.modelingStore.updateModelEvaluationRun(this.dbItem, undefined); + throw new Error("Unable to trigger variant analysis for evaluation run"); + } + } + private monitorVariantAnalysis(variantAnalysisId: number) { this.push( this.variantAnalysisManager.onVariantAnalysisStatusUpdated( diff --git a/extensions/ql-vscode/src/model-editor/modeling-store.ts b/extensions/ql-vscode/src/model-editor/modeling-store.ts index 1d170891919..097148863d0 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-store.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-store.ts @@ -423,6 +423,12 @@ export class ModelingStore extends DisposableObject { return this.state.get(databaseItem.databaseUri.toString())!; } + public getModelEvaluationRun( + dbItem: DatabaseItem, + ): ModelEvaluationRun | undefined { + return this.getState(dbItem).modelEvaluationRun; + } + private changeMethods( dbItem: DatabaseItem, updateState: (state: InternalDbModelingState) => void, diff --git a/extensions/ql-vscode/test/__mocks__/model-editor/modelingStoreMock.ts b/extensions/ql-vscode/test/__mocks__/model-editor/modelingStoreMock.ts index 4d8c6799156..7cbad68c62c 100644 --- a/extensions/ql-vscode/test/__mocks__/model-editor/modelingStoreMock.ts +++ b/extensions/ql-vscode/test/__mocks__/model-editor/modelingStoreMock.ts @@ -4,12 +4,18 @@ import type { ModelingStore } from "../../../src/model-editor/modeling-store"; export function createMockModelingStore({ initializeStateForDb = jest.fn(), getStateForActiveDb = jest.fn(), + getModelEvaluationRun = jest.fn(), + updateModelEvaluationRun = jest.fn(), }: { initializeStateForDb?: ModelingStore["initializeStateForDb"]; getStateForActiveDb?: ModelingStore["getStateForActiveDb"]; + getModelEvaluationRun?: ModelingStore["getModelEvaluationRun"]; + updateModelEvaluationRun?: ModelingStore["updateModelEvaluationRun"]; } = {}): ModelingStore { return mockedObject({ initializeStateForDb, getStateForActiveDb, + getModelEvaluationRun, + updateModelEvaluationRun, }); } diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/model-editor/model-evaluator.test.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/model-editor/model-evaluator.test.ts new file mode 100644 index 00000000000..e5a15d0500b --- /dev/null +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/model-editor/model-evaluator.test.ts @@ -0,0 +1,97 @@ +import type { CodeQLCliServer } from "../../../../src/codeql-cli/cli"; +import type { BaseLogger } from "../../../../src/common/logging"; +import { QueryLanguage } from "../../../../src/common/query-language"; +import type { DatabaseItem } from "../../../../src/databases/local-databases"; +import type { ModelEvaluationRun } from "../../../../src/model-editor/model-evaluation-run"; +import { ModelEvaluator } from "../../../../src/model-editor/model-evaluator"; +import type { ModelingEvents } from "../../../../src/model-editor/modeling-events"; +import type { ModelingStore } from "../../../../src/model-editor/modeling-store"; +import type { VariantAnalysisManager } from "../../../../src/variant-analysis/variant-analysis-manager"; +import { createMockLogger } from "../../../__mocks__/loggerMock"; +import { createMockModelingEvents } from "../../../__mocks__/model-editor/modelingEventsMock"; +import { createMockModelingStore } from "../../../__mocks__/model-editor/modelingStoreMock"; +import { mockedObject } from "../../../mocked-object"; + +describe("Model Evaluator", () => { + let modelEvaluator: ModelEvaluator; + let logger: BaseLogger; + let cliServer: CodeQLCliServer; + let modelingStore: ModelingStore; + let modelingEvents: ModelingEvents; + let variantAnalysisManager: VariantAnalysisManager; + let dbItem: DatabaseItem; + let language: QueryLanguage; + let updateView: jest.Mock; + let getModelEvaluationRunMock = jest.fn(); + + beforeEach(() => { + logger = createMockLogger(); + cliServer = mockedObject({}); + getModelEvaluationRunMock = jest.fn(); + modelingStore = createMockModelingStore({ + getModelEvaluationRun: getModelEvaluationRunMock, + }); + modelingEvents = createMockModelingEvents(); + variantAnalysisManager = mockedObject({ + cancelVariantAnalysis: jest.fn(), + }); + dbItem = mockedObject({}); + language = QueryLanguage.Java; + updateView = jest.fn(); + + modelEvaluator = new ModelEvaluator( + logger, + cliServer, + modelingStore, + modelingEvents, + variantAnalysisManager, + dbItem, + language, + updateView, + ); + }); + + describe("stopping evaluation", () => { + it("should just log a message if it never started", async () => { + getModelEvaluationRunMock.mockReturnValue(undefined); + + await modelEvaluator.stopEvaluation(); + + expect(logger.log).toHaveBeenCalledWith( + "No active evaluation run to stop", + ); + }); + + it("should update the store if evaluation run exists", async () => { + getModelEvaluationRunMock.mockReturnValue({ + isPreparing: true, + variantAnalysisId: undefined, + }); + + await modelEvaluator.stopEvaluation(); + + expect(modelingStore.updateModelEvaluationRun).toHaveBeenCalledWith( + dbItem, + { + isPreparing: false, + varianAnalysis: undefined, + }, + ); + }); + + it("should cancel the variant analysis if one has been started", async () => { + const evaluationRun: ModelEvaluationRun = { + isPreparing: false, + variantAnalysisId: 123, + }; + getModelEvaluationRunMock.mockReturnValue(evaluationRun); + + await modelEvaluator.stopEvaluation(); + + expect(modelingStore.updateModelEvaluationRun).not.toHaveBeenCalled(); + expect(variantAnalysisManager.cancelVariantAnalysis).toHaveBeenCalledWith( + evaluationRun.variantAnalysisId, + ); + }); + }); +}); From 4450237612a183daf99e07aac3bff8c3ca340b2c Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Fri, 1 Mar 2024 15:41:26 -0500 Subject: [PATCH 0072/1237] Add changelog note And fix linting error. --- extensions/ql-vscode/CHANGELOG.md | 1 + extensions/ql-vscode/src/databases/database-fetcher.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 15cf3b35754..2a7b30a1bb8 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -4,6 +4,7 @@ - Remove support for CodeQL CLI versions older than 2.13.5. [#3371](https://github.com/github/vscode-codeql/pull/3371) - Add a timeout to downloading databases and the CodeQL CLI. These can be changed using the `codeQL.addingDatabases.downloadTimeout` and `codeQL.cli.downloadTimeout` settings respectively. [#3373](https://github.com/github/vscode-codeql/pull/3373) +- Databases created from [CodeQL test cases](https://docs.github.com/en/code-security/codeql-cli/using-the-advanced-functionality-of-the-codeql-cli/testing-custom-queries) are now copied into a shared VS Code storage location. This avoids a bug where re-running test cases would fail if the test's database is already imported into the workspace. [#3433](https://github.com/github/vscode-codeql/pull/3433) ## 1.12.2 - 14 February 2024 diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index 70c6b1509e6..73367da91fb 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -370,7 +370,7 @@ async function fetchDatabaseToWorkspaceStorage( const unzipPath = await getStorageFolder(storagePath, databaseUrl); if (isFile(databaseUrl)) { - if (origin.type == "testproj") { + if (origin.type === "testproj") { await copyDatabase(origin.path, unzipPath, progress); } else { await readAndUnzip(databaseUrl, unzipPath, cli, progress); From 3497219c682003af1210f815638e5e3fbff722a7 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Fri, 1 Mar 2024 16:16:59 -0500 Subject: [PATCH 0073/1237] Use `unzipToDirectoryConcurrently` instead of the unzipper package --- extensions/ql-vscode/package-lock.json | 141 +----------------- extensions/ql-vscode/package.json | 4 +- .../local-databases/database-origin.ts | 2 +- .../cli-integration/jest.setup.ts | 17 +-- 4 files changed, 7 insertions(+), 157 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 7eee552c114..69daa843735 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -140,8 +140,7 @@ "ts-loader": "^9.4.2", "ts-node": "^10.7.0", "ts-unused-exports": "^10.0.0", - "typescript": "^5.0.2", - "unzipper": "^0.10.14" + "typescript": "^5.0.2" }, "engines": { "node": "^18.17.1", @@ -12243,19 +12242,6 @@ "node": "*" } }, - "node_modules/binary": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", - "dev": true, - "dependencies": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" - }, - "engines": { - "node": "*" - } - }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -12309,12 +12295,6 @@ "node": ">= 6" } }, - "node_modules/bluebird": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", - "dev": true - }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -12540,24 +12520,6 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, - "node_modules/buffer-indexof-polyfill": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", - "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/buffers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", - "dev": true, - "engines": { - "node": ">=0.2.0" - } - }, "node_modules/bundle-name": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", @@ -12681,18 +12643,6 @@ "node": ">=4" } }, - "node_modules/chainsaw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", - "dev": true, - "dependencies": { - "traverse": ">=0.3.0 <0.4" - }, - "engines": { - "node": "*" - } - }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -15053,15 +15003,6 @@ "node": ">=12" } }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", - "dev": true, - "dependencies": { - "readable-stream": "^2.0.2" - } - }, "node_modules/duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -17877,53 +17818,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - }, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/fstream/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fstream/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -23893,12 +23787,6 @@ "node": ">= 8" } }, - "node_modules/listenercount": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", - "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", - "dev": true - }, "node_modules/listr2": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.0.0.tgz", @@ -30802,15 +30690,6 @@ "node": ">=12" } }, - "node_modules/traverse": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -31753,24 +31632,6 @@ "node": ">=8" } }, - "node_modules/unzipper": { - "version": "0.10.14", - "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", - "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", - "dev": true, - "dependencies": { - "big-integer": "^1.6.17", - "binary": "~0.3.0", - "bluebird": "~3.4.1", - "buffer-indexof-polyfill": "~1.0.0", - "duplexer2": "~0.1.4", - "fstream": "^1.0.12", - "graceful-fs": "^4.2.2", - "listenercount": "~1.0.1", - "readable-stream": "~2.3.6", - "setimmediate": "~1.0.4" - } - }, "node_modules/upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 249f15f3b37..8686745cb92 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -2031,7 +2031,6 @@ "@types/tar-stream": "^3.1.3", "@types/through2": "^2.0.36", "@types/tmp": "^0.2.6", - "@types/unzipper": "^0.10.1", "@types/vscode": "^1.82.0", "@types/yauzl": "^2.10.3", "@typescript-eslint/eslint-plugin": "^6.19.0", @@ -2081,8 +2080,7 @@ "ts-loader": "^9.4.2", "ts-node": "^10.7.0", "ts-unused-exports": "^10.0.0", - "typescript": "^5.0.2", - "unzipper": "^0.10.14" + "typescript": "^5.0.2" }, "lint-staged": { "./**/*.{json,css,scss}": [ diff --git a/extensions/ql-vscode/src/databases/local-databases/database-origin.ts b/extensions/ql-vscode/src/databases/local-databases/database-origin.ts index e9541b8b5e2..e81caafc9f6 100644 --- a/extensions/ql-vscode/src/databases/local-databases/database-origin.ts +++ b/extensions/ql-vscode/src/databases/local-databases/database-origin.ts @@ -24,7 +24,7 @@ interface DatabaseOriginDebugger { type: "debugger"; } -export interface DatabaseOriginTestProj { +interface DatabaseOriginTestProj { type: "testproj"; path: string; } diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts index 700b2dc38c1..7ab7fb151b3 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts @@ -9,8 +9,8 @@ import { createWriteStream, existsSync, mkdirpSync } from "fs-extra"; import { dirname, join } from "path"; import { DB_URL, dbLoc, testprojLoc } from "../global.helper"; import fetch from "node-fetch"; -import { createReadStream, renameSync } from "fs"; -import { Extract } from "unzipper"; +import { renameSync } from "fs"; +import { unzipToDirectoryConcurrently } from "../../../src/common/unzip-concurrently"; beforeAll(async () => { // ensure the test database is downloaded @@ -36,18 +36,9 @@ beforeAll(async () => { // unzip the database from dbLoc to testprojLoc if (!existsSync(testprojLoc)) { console.log(`Unzipping test database to ${testprojLoc}`); - - await new Promise((resolve, reject) => { - createReadStream(dbLoc) - .pipe(Extract({ path: dbParentDir })) - .on("close", () => { - console.log("Unzip completed."); - resolve(undefined); - }) - .on("error", (e) => reject(e)); - }); - + await unzipToDirectoryConcurrently(dbLoc, dbParentDir); renameSync(join(dbParentDir, "db"), testprojLoc); + console.log("Unzip completed."); } await beforeAllAction(); From 0ed9242b817b5b0802dd54afb08b866faa126f9f Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Sat, 2 Mar 2024 16:44:56 -0800 Subject: [PATCH 0074/1237] Try waiting a few seconds before renaming --- .../test/vscode-tests/cli-integration/jest.setup.ts | 7 +++++++ .../ql-vscode/test/vscode-tests/cli-integration/utils.ts | 4 ++++ .../local-queries/local-databases.test.ts | 3 +++ 3 files changed, 14 insertions(+) diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts index 7ab7fb151b3..bd1b0e78b34 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts @@ -11,6 +11,8 @@ import { DB_URL, dbLoc, testprojLoc } from "../global.helper"; import fetch from "node-fetch"; import { renameSync } from "fs"; import { unzipToDirectoryConcurrently } from "../../../src/common/unzip-concurrently"; +import { platform } from "os"; +import { wait } from "./utils"; beforeAll(async () => { // ensure the test database is downloaded @@ -37,6 +39,11 @@ beforeAll(async () => { if (!existsSync(testprojLoc)) { console.log(`Unzipping test database to ${testprojLoc}`); await unzipToDirectoryConcurrently(dbLoc, dbParentDir); + // On Windows, wait a few seconds to make sure all background processes + // release their lock on the files before renaming the directory. + if (platform() === "win32") { + await wait(3000); + } renameSync(join(dbParentDir, "db"), testprojLoc); console.log("Unzip completed."); } diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/utils.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/utils.ts index 1eaacc8b8eb..82c998e2236 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/utils.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/utils.ts @@ -7,3 +7,7 @@ import { join } from "path"; export function getDataFolderFilePath(path: string): string { return join(workspace.workspaceFolders![0].uri.fsPath, path); } + +export async function wait(ms = 1000) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts index 1fdfa67dba5..53a13faef3f 100644 --- a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts @@ -101,6 +101,9 @@ describe("local databases", () => { onStart: () => { /**/ }, + onQueryRunStarting: () => { + /**/ + }, }), mockedObject({ resolveDatabase: resolveDatabaseSpy, From ff664dd18ce8830d0c0ab9fdc123d6eeaaa04d85 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 4 Mar 2024 11:14:50 +0000 Subject: [PATCH 0075/1237] Mention that an automated workflow exists for updating the node version --- docs/node-version.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/node-version.md b/docs/node-version.md index c6ed72e185a..306170f5aae 100644 --- a/docs/node-version.md +++ b/docs/node-version.md @@ -1,5 +1,8 @@ # Node version +> [!NOTE] +> It should not be necessary to update the Node.js version manually. The [update-node-version.yml](https://github.com/github/vscode-codeql/blob/main/.github/workflows/update-node-version.yml) workflow runs daily and will open a pull request if the Node.js version needs to be updated. + The CodeQL for VS Code extension defines the version of Node.js that it is intended to run with. This Node.js version is used when running most CI and unit tests. When running in production (i.e. as an extension for a VS Code application) it will use the Node.js version provided by VS Code. This can mean a different Node.js version is used by different users with different versions of VS Code. From ad9f78ca2b46e3f909305d576880d3dc04a2d42c Mon Sep 17 00:00:00 2001 From: Shati Patel <42641846+shati-patel@users.noreply.github.com> Date: Mon, 4 Mar 2024 17:07:32 +0000 Subject: [PATCH 0076/1237] Add link to open "Evaluation Run" from the model editor (#3435) --- .../src/view/model-editor/ModelEditor.tsx | 5 + .../src/view/model-editor/ModelEvaluation.tsx | 71 +++++++---- .../__tests__/ModelEvaluation.spec.tsx | 120 ++++++++++++++++++ 3 files changed, 170 insertions(+), 26 deletions(-) create mode 100644 extensions/ql-vscode/src/view/model-editor/__tests__/ModelEvaluation.spec.tsx diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx index 3d42965b108..fce9ea92177 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx @@ -276,6 +276,10 @@ export function ModelEditor({ }); }, []); + const openModelAlertsView = useCallback(() => { + // TODO + }, []); + const onGenerateFromSourceClick = useCallback(() => { vscode.postMessage({ t: "generateMethod", @@ -401,6 +405,7 @@ export function ModelEditor({ modifiedSignatures={modifiedSignatures} onStartEvaluation={onStartEvaluation} onStopEvaluation={onStopEvaluation} + openModelAlertsView={openModelAlertsView} evaluationRun={evaluationRun} /> diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx index b640f94e16c..66860003c19 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx @@ -1,16 +1,18 @@ -import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"; +import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"; import type { ModeledMethod } from "../../model-editor/modeled-method"; import type { ModelEditorViewState } from "../../model-editor/shared/view-state"; import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state"; import { modelEvaluationRunIsRunning } from "../../model-editor/shared/model-evaluation-run-state"; import { ModelEditorProgressRing } from "./ModelEditorProgressRing"; +import { LinkIconButton } from "../variant-analysis/LinkIconButton"; -type Props = { +export type Props = { viewState: ModelEditorViewState; modeledMethods: Record; modifiedSignatures: Set; onStartEvaluation: () => void; onStopEvaluation: () => void; + openModelAlertsView: () => void; evaluationRun: ModelEvaluationRunState | undefined; }; @@ -20,34 +22,51 @@ export const ModelEvaluation = ({ modifiedSignatures, onStartEvaluation, onStopEvaluation, + openModelAlertsView, evaluationRun, }: Props) => { if (!viewState.showEvaluationUi) { return null; } - if (!evaluationRun || !modelEvaluationRunIsRunning(evaluationRun)) { - const customModelsExist = Object.values(modeledMethods).some( - (methods) => methods.filter((m) => m.type !== "none").length > 0, - ); - - const unsavedChanges = modifiedSignatures.size > 0; - - return ( - - Evaluate - - ); - } else { - return ( - - - Stop evaluation - - ); - } + const shouldShowEvaluateButton = + !evaluationRun || !modelEvaluationRunIsRunning(evaluationRun); + + const shouldShowStopButton = !shouldShowEvaluateButton; + + const shouldShowEvaluationRunLink = !!evaluationRun; + + const customModelsExist = Object.values(modeledMethods).some( + (methods) => methods.filter((m) => m.type !== "none").length > 0, + ); + + const unsavedChanges = modifiedSignatures.size > 0; + + return ( + <> + {shouldShowEvaluateButton && ( + + Evaluate + + )} + {shouldShowStopButton && ( + + + Stop evaluation + + )} + {shouldShowEvaluationRunLink && ( + + + + Evaluation run + + + )} + + ); }; diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/ModelEvaluation.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelEvaluation.spec.tsx new file mode 100644 index 00000000000..1d4ea9f3d35 --- /dev/null +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelEvaluation.spec.tsx @@ -0,0 +1,120 @@ +import { render as reactRender, screen } from "@testing-library/react"; +import type { Props } from "../ModelEvaluation"; +import { ModelEvaluation } from "../ModelEvaluation"; +import { createMockModelEditorViewState } from "../../../../test/factories/model-editor/view-state"; +import type { ModeledMethod } from "../../../model-editor/modeled-method"; +import { createMethod } from "../../../../test/factories/model-editor/method-factories"; +import { createMockVariantAnalysis } from "../../../../test/factories/variant-analysis/shared/variant-analysis"; +import { VariantAnalysisStatus } from "../../../variant-analysis/shared/variant-analysis"; +import { createSummaryModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories"; + +describe(ModelEvaluation.name, () => { + const method = createMethod(); + const modeledMethodsMap: Record = {}; + modeledMethodsMap[method.signature] = [createSummaryModeledMethod(method)]; + + const render = (props: Partial = {}) => + reactRender( + , + ); + + describe("when showEvaluationUi is false", () => { + it("does not render anything", () => { + render({ + viewState: createMockModelEditorViewState({ showEvaluationUi: false }), + }); + expect(screen.queryByText("Evaluate")).not.toBeInTheDocument(); + expect(screen.queryByText("Stop evaluation")).not.toBeInTheDocument(); + expect(screen.queryByText("Evaluation run")).not.toBeInTheDocument(); + }); + }); + + describe("when showEvaluationUi is true", () => { + it("renders evaluation UI with 'Evaluate' button enabled", () => { + render(); + + const evaluateButton = screen.queryByText("Evaluate"); + expect(evaluateButton).toBeInTheDocument(); + expect(evaluateButton?.getElementsByTagName("input")[0]).toBeEnabled(); + + expect(screen.queryByText("Stop evaluation")).not.toBeInTheDocument(); + + expect(screen.queryByText("Evaluation run")).not.toBeInTheDocument(); + }); + + it("disables 'Evaluate' button when there are no custom models", () => { + render({ + modeledMethods: {}, + }); + + const evaluateButton = screen.queryByText("Evaluate"); + expect(evaluateButton).toBeInTheDocument(); + expect(evaluateButton?.getElementsByTagName("input")[0]).toBeDisabled(); + + expect(screen.queryByText("Stop evaluation")).not.toBeInTheDocument(); + + expect(screen.queryByText("Evaluation run")).not.toBeInTheDocument(); + }); + + it("disables 'Evaluate' button when there are unsaved changes", () => { + render({ + modifiedSignatures: new Set([method.signature]), + }); + + const evaluateButton = screen.queryByText("Evaluate"); + expect(evaluateButton).toBeInTheDocument(); + expect(evaluateButton?.getElementsByTagName("input")[0]).toBeDisabled(); + + expect(screen.queryByText("Stop evaluation")).not.toBeInTheDocument(); + + expect(screen.queryByText("Evaluation run")).not.toBeInTheDocument(); + }); + + it("renders 'Evaluate' button and 'Evaluation run' link when there is a completed evaluation", () => { + render({ + evaluationRun: { + isPreparing: false, + variantAnalysis: createMockVariantAnalysis({ + status: VariantAnalysisStatus.Succeeded, + }), + }, + }); + + const evaluateButton = screen.queryByText("Evaluate"); + expect(evaluateButton).toBeInTheDocument(); + expect(evaluateButton?.getElementsByTagName("input")[0]).toBeEnabled(); + + expect(screen.queryByText("Evaluation run")).toBeInTheDocument(); + + expect(screen.queryByText("Stop evaluation")).not.toBeInTheDocument(); + }); + + it("renders 'Stop evaluation' button and 'Evaluation run' link when there is an in progress evaluation", () => { + render({ + evaluationRun: { + isPreparing: true, + variantAnalysis: undefined, + }, + }); + + const stopEvaluationButton = screen.queryByText("Stop evaluation"); + expect(stopEvaluationButton).toBeInTheDocument(); + expect( + stopEvaluationButton?.getElementsByTagName("input")[0], + ).toBeEnabled(); + + expect(screen.queryByText("Evaluation run")).toBeInTheDocument(); + + expect(screen.queryByText("Evaluate")).not.toBeInTheDocument(); + }); + }); +}); From 6402f05624662bdc128a56522c792bf6076df949 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 5 Mar 2024 09:42:07 +0000 Subject: [PATCH 0077/1237] Avoid using a @treemap query in testplan --- docs/test-plan.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/test-plan.md b/docs/test-plan.md index 6ce561d57f6..561d86a666b 100644 --- a/docs/test-plan.md +++ b/docs/test-plan.md @@ -53,11 +53,11 @@ choose to go through some of the Optional Test Cases. #### Test case 3: Running a non-problem query and viewing results -1. Open the [cpp FunLinesOfCode query](https://github.com/github/codeql/blob/main/cpp/ql/src/Metrics/Functions/FunLinesOfCode.ql). +1. Open the [cpp HubClasses query](https://github.com/github/codeql/blob/main/cpp/ql/src/Architecture/General%20Class-Level%20Information/HubClasses.ql). 2. Select the `google/brotli` database (or download it if you don't have one already) 3. Run a local query. 4. Once the query completes: - - Chose the `#select` result set from the drop-down + - Check that the `#select` result set is shown - Check that the results table is rendered - Check that result locations can be clicked on From b946f3a509842507931d98c293b55798ffea22c5 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 4 Mar 2024 17:07:49 +0000 Subject: [PATCH 0078/1237] Pull out setDatabaseItem method --- .../method-modeling-view-provider.ts | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts index 493c420b3a1..ad0dcf4aadd 100644 --- a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts +++ b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts @@ -52,6 +52,18 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< }); } + private async setDatabaseItem(databaseItem: DatabaseItem): Promise { + this.databaseItem = databaseItem; + + await this.postMessage({ + t: "setInModelingMode", + inModelingMode: true, + }); + + this.language = tryGetQueryLanguage(databaseItem.language); + await this.setViewState(); + } + public async setMethod( databaseItem: DatabaseItem | undefined, method: Method | undefined, @@ -201,15 +213,7 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< this.push( this.modelingEvents.onDbOpened(async (databaseItem) => { - this.databaseItem = databaseItem; - - await this.postMessage({ - t: "setInModelingMode", - inModelingMode: true, - }); - - this.language = tryGetQueryLanguage(databaseItem.language); - await this.setViewState(); + await this.setDatabaseItem(databaseItem); }), ); From a82be4258c2bcd6b61a17f85d0f8ef3b4e967f5a Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 4 Mar 2024 17:09:11 +0000 Subject: [PATCH 0079/1237] Pull out setSelectedMethod method --- .../method-modeling-view-provider.ts | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts index ad0dcf4aadd..086dbd69317 100644 --- a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts +++ b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts @@ -16,6 +16,7 @@ import type { ModelingEvents } from "../modeling-events"; import type { QueryLanguage } from "../../common/query-language"; import { tryGetQueryLanguage } from "../../common/query-language"; import { createModelConfig } from "../languages"; +import type { ModeledMethod } from "../modeled-method"; export class MethodModelingViewProvider extends AbstractWebviewViewProvider< ToMethodModelingMessage, @@ -80,6 +81,28 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< } } + private async setSelectedMethod( + databaseItem: DatabaseItem, + method: Method, + modeledMethods: readonly ModeledMethod[], + isModified: boolean, + isInProgress: boolean, + processedByAutoModel: boolean, + ): Promise { + this.method = method; + this.databaseItem = databaseItem; + this.language = tryGetQueryLanguage(databaseItem.language); + + await this.postMessage({ + t: "setSelectedMethod", + method, + modeledMethods, + isModified, + isInProgress, + processedByAutoModel, + }); + } + private async setInitialState(): Promise { if (this.modelingStore.hasStateForActiveDb()) { const selectedMethod = this.modelingStore.getSelectedMethodDetails(); @@ -195,18 +218,14 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< this.push( this.modelingEvents.onSelectedMethodChanged(async (e) => { if (this.webviewView) { - this.method = e.method; - this.databaseItem = e.databaseItem; - this.language = tryGetQueryLanguage(e.databaseItem.language); - - await this.postMessage({ - t: "setSelectedMethod", - method: e.method, - modeledMethods: e.modeledMethods, - isModified: e.isModified, - isInProgress: e.isInProgress, - processedByAutoModel: e.processedByAutoModel, - }); + await this.setSelectedMethod( + e.databaseItem, + e.method, + e.modeledMethods, + e.isModified, + e.isInProgress, + e.processedByAutoModel, + ); } }), ); From d4413424c19121b2bd5b81539d8619f9480cc814 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 4 Mar 2024 17:09:44 +0000 Subject: [PATCH 0080/1237] Rewrite setInitialState to always set all fields --- .../method-modeling-view-provider.ts | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts index 086dbd69317..208d56f861a 100644 --- a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts +++ b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts @@ -38,7 +38,7 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< } protected override async onWebViewLoaded(): Promise { - await Promise.all([this.setViewState(), this.setInitialState()]); + await this.setInitialState(); this.registerToModelingEvents(); this.registerToModelConfigEvents(); } @@ -104,29 +104,25 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< } private async setInitialState(): Promise { - if (this.modelingStore.hasStateForActiveDb()) { - const selectedMethod = this.modelingStore.getSelectedMethodDetails(); - if (selectedMethod) { - this.databaseItem = selectedMethod.databaseItem; - this.language = tryGetQueryLanguage( - selectedMethod.databaseItem.language, - ); - this.method = selectedMethod.method; - - await this.postMessage({ - t: "setSelectedMethod", - method: selectedMethod.method, - modeledMethods: selectedMethod.modeledMethods, - isModified: selectedMethod.isModified, - isInProgress: selectedMethod.isInProgress, - processedByAutoModel: selectedMethod.processedByAutoModel, - }); - } + await this.setViewState(); - await this.postMessage({ - t: "setInModelingMode", - inModelingMode: true, - }); + const stateForActiveDb = this.modelingStore.getStateForActiveDb(); + if (!stateForActiveDb) { + return; + } + + await this.setDatabaseItem(stateForActiveDb.databaseItem); + + const selectedMethod = this.modelingStore.getSelectedMethodDetails(); + if (selectedMethod) { + await this.setSelectedMethod( + stateForActiveDb.databaseItem, + selectedMethod.method, + selectedMethod.modeledMethods, + selectedMethod.isModified, + selectedMethod.isInProgress, + selectedMethod.processedByAutoModel, + ); } } From 08c6c8a50c53df05228b8f85e35d840e1da398ff Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 4 Mar 2024 17:20:34 +0000 Subject: [PATCH 0081/1237] Introduce SetNoMethodSelectedMessage --- extensions/ql-vscode/src/common/interface-types.ts | 5 +++++ .../method-modeling/method-modeling-view-provider.ts | 4 +++- .../src/view/method-modeling/MethodModelingView.tsx | 7 +++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/common/interface-types.ts b/extensions/ql-vscode/src/common/interface-types.ts index 25c54208e05..1f642c8f776 100644 --- a/extensions/ql-vscode/src/common/interface-types.ts +++ b/extensions/ql-vscode/src/common/interface-types.ts @@ -703,6 +703,10 @@ interface SetMethodModifiedMessage { isModified: boolean; } +interface SetNoMethodSelectedMessage { + t: "setNoMethodSelected"; +} + interface SetSelectedMethodMessage { t: "setSelectedMethod"; method: Method; @@ -717,6 +721,7 @@ export type ToMethodModelingMessage = | SetMethodMessage | SetMultipleModeledMethodsMessage | SetMethodModifiedMessage + | SetNoMethodSelectedMessage | SetSelectedMethodMessage | SetInModelingModeMessage | SetInProgressMessage diff --git a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts index 208d56f861a..1758eca6493 100644 --- a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts +++ b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts @@ -242,7 +242,9 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< } if (dbUri === this.databaseItem?.databaseUri.toString()) { - await this.setMethod(undefined, undefined); + await this.postMessage({ + t: "setNoMethodSelected", + }); } }), ); diff --git a/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx b/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx index 69e76240c41..380cd4f5a85 100644 --- a/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx @@ -62,6 +62,13 @@ export function MethodModelingView({ case "setMethodModified": setIsMethodModified(msg.isModified); break; + case "setNoMethodSelected": + setMethod(undefined); + setModeledMethods([]); + setIsMethodModified(false); + setIsModelingInProgress(false); + setIsProcessedByAutoModel(false); + break; case "setSelectedMethod": setMethod(msg.method); setModeledMethods(msg.modeledMethods); From e6b1d8d61618aca193a3038aefdc86e8d5871b02 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 5 Mar 2024 11:08:13 +0100 Subject: [PATCH 0082/1237] Fix `isQuickEvalCount` logic Since `false !== undefined` evaluates to true, we would assign the wrong value to `isQuickEvalCount` when `selectedQuery.quickEval?.quickEvalCount` is false --- extensions/ql-vscode/src/run-queries-shared.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions/ql-vscode/src/run-queries-shared.ts b/extensions/ql-vscode/src/run-queries-shared.ts index a52efc39c82..1265429cc10 100644 --- a/extensions/ql-vscode/src/run-queries-shared.ts +++ b/extensions/ql-vscode/src/run-queries-shared.ts @@ -495,8 +495,7 @@ export async function createInitialQueryInfo( outputDir: QueryOutputDir, ): Promise { const isQuickEval = selectedQuery.quickEval !== undefined; - const isQuickEvalCount = - selectedQuery.quickEval?.quickEvalCount !== undefined; + const isQuickEvalCount = Boolean(selectedQuery.quickEval?.quickEvalCount); return { queryPath: selectedQuery.queryPath, isQuickEval, From c69cea1f8ec1ebdc1498cc8f59fb0d17d7d744e6 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 5 Mar 2024 11:51:51 +0100 Subject: [PATCH 0083/1237] Apply suggestions from code review Co-authored-by: Robert --- extensions/ql-vscode/src/run-queries-shared.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/run-queries-shared.ts b/extensions/ql-vscode/src/run-queries-shared.ts index 1265429cc10..83471716257 100644 --- a/extensions/ql-vscode/src/run-queries-shared.ts +++ b/extensions/ql-vscode/src/run-queries-shared.ts @@ -495,7 +495,7 @@ export async function createInitialQueryInfo( outputDir: QueryOutputDir, ): Promise { const isQuickEval = selectedQuery.quickEval !== undefined; - const isQuickEvalCount = Boolean(selectedQuery.quickEval?.quickEvalCount); + const isQuickEvalCount = selectedQuery.quickEval?.quickEvalCount; return { queryPath: selectedQuery.queryPath, isQuickEval, From 2f49ac83dedebcebec3fe5fb4f7c2ea79c5ef0f1 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 4 Mar 2024 17:21:46 +0000 Subject: [PATCH 0084/1237] Remove setMethod which was only called from onSelectedMethodChanged which we already listen to --- .../ql-vscode/src/common/interface-types.ts | 6 ------ .../method-modeling/method-modeling-panel.ts | 9 --------- .../method-modeling-view-provider.ts | 16 ---------------- .../src/model-editor/model-editor-module.ts | 4 +--- .../view/method-modeling/MethodModelingView.tsx | 3 --- 5 files changed, 1 insertion(+), 37 deletions(-) diff --git a/extensions/ql-vscode/src/common/interface-types.ts b/extensions/ql-vscode/src/common/interface-types.ts index 1f642c8f776..cd0709a3ea1 100644 --- a/extensions/ql-vscode/src/common/interface-types.ts +++ b/extensions/ql-vscode/src/common/interface-types.ts @@ -693,11 +693,6 @@ interface SetMethodModelingPanelViewStateMessage { viewState: MethodModelingPanelViewState; } -interface SetMethodMessage { - t: "setMethod"; - method: Method | undefined; -} - interface SetMethodModifiedMessage { t: "setMethodModified"; isModified: boolean; @@ -718,7 +713,6 @@ interface SetSelectedMethodMessage { export type ToMethodModelingMessage = | SetMethodModelingPanelViewStateMessage - | SetMethodMessage | SetMultipleModeledMethodsMessage | SetMethodModifiedMessage | SetNoMethodSelectedMessage diff --git a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-panel.ts b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-panel.ts index 08e68a53d39..e5f9f4a0d64 100644 --- a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-panel.ts +++ b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-panel.ts @@ -2,10 +2,8 @@ import { window } from "vscode"; import type { App } from "../../common/app"; import { DisposableObject } from "../../common/disposable-object"; import { MethodModelingViewProvider } from "./method-modeling-view-provider"; -import type { Method } from "../method"; import type { ModelingStore } from "../modeling-store"; import { ModelConfigListener } from "../../config"; -import type { DatabaseItem } from "../../databases/local-databases"; import type { ModelingEvents } from "../modeling-events"; export class MethodModelingPanel extends DisposableObject { @@ -36,11 +34,4 @@ export class MethodModelingPanel extends DisposableObject { ), ); } - - public async setMethod( - databaseItem: DatabaseItem, - method: Method, - ): Promise { - await this.provider.setMethod(databaseItem, method); - } } diff --git a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts index 1758eca6493..df9c9c34672 100644 --- a/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts +++ b/extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts @@ -65,22 +65,6 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider< await this.setViewState(); } - public async setMethod( - databaseItem: DatabaseItem | undefined, - method: Method | undefined, - ): Promise { - this.method = method; - this.databaseItem = databaseItem; - this.language = databaseItem && tryGetQueryLanguage(databaseItem.language); - - if (this.isShowingView) { - await this.postMessage({ - t: "setMethod", - method, - }); - } - } - private async setSelectedMethod( databaseItem: DatabaseItem, method: Method, diff --git a/extensions/ql-vscode/src/model-editor/model-editor-module.ts b/extensions/ql-vscode/src/model-editor/model-editor-module.ts index 7551835d1e4..e6e340c00c0 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-module.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-module.ts @@ -38,7 +38,6 @@ export class ModelEditorModule extends DisposableObject { private readonly modelingStore: ModelingStore; private readonly modelingEvents: ModelingEvents; private readonly methodsUsagePanel: MethodsUsagePanel; - private readonly methodModelingPanel: MethodModelingPanel; private readonly modelConfig: ModelConfigListener; private constructor( @@ -56,7 +55,7 @@ export class ModelEditorModule extends DisposableObject { this.methodsUsagePanel = this.push( new MethodsUsagePanel(this.modelingStore, this.modelingEvents, cliServer), ); - this.methodModelingPanel = this.push( + this.push( new MethodModelingPanel(app, this.modelingStore, this.modelingEvents), ); this.modelConfig = this.push(new ModelConfigListener()); @@ -132,7 +131,6 @@ export class ModelEditorModule extends DisposableObject { usage: Usage, ): Promise { await this.methodsUsagePanel.revealItem(method.signature, usage); - await this.methodModelingPanel.setMethod(databaseItem, method); await showResolvableLocation(usage.url, databaseItem, this.app.logger); } diff --git a/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx b/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx index 380cd4f5a85..8e68a351be6 100644 --- a/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx @@ -53,9 +53,6 @@ export function MethodModelingView({ case "setInModelingMode": setInModelingMode(msg.inModelingMode); break; - case "setMethod": - setMethod(msg.method); - break; case "setMultipleModeledMethods": setModeledMethods(msg.modeledMethods); break; From fdd45e8dbdfaa18ca0a3ef93653bc0eec0a9cf62 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 4 Mar 2024 17:26:37 +0000 Subject: [PATCH 0085/1237] Make revealItem private by listening to onSelectedMethodChanged --- .../methods-usage/methods-usage-panel.ts | 8 +++++- .../src/model-editor/model-editor-module.ts | 7 ++--- .../model-editor/modelingEventsMock.ts | 3 ++ .../methods-usage/methods-usage-panel.test.ts | 28 ++++++++++++++++--- 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-panel.ts b/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-panel.ts index ad6b93db16c..a71a31f0bb3 100644 --- a/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-panel.ts +++ b/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-panel.ts @@ -57,7 +57,7 @@ export class MethodsUsagePanel extends DisposableObject { }; } - public async revealItem( + private async revealItem( methodSignature: string, usage: Usage, ): Promise { @@ -108,6 +108,12 @@ export class MethodsUsagePanel extends DisposableObject { } }), ); + + this.push( + this.modelingEvents.onSelectedMethodChanged(async (event) => { + await this.revealItem(event.method.signature, event.usage); + }), + ); } private async handleStateChangeEvent(): Promise { diff --git a/extensions/ql-vscode/src/model-editor/model-editor-module.ts b/extensions/ql-vscode/src/model-editor/model-editor-module.ts index e6e340c00c0..b00ed96e8d0 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-module.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-module.ts @@ -37,7 +37,6 @@ export class ModelEditorModule extends DisposableObject { private readonly queryStorageDir: string; private readonly modelingStore: ModelingStore; private readonly modelingEvents: ModelingEvents; - private readonly methodsUsagePanel: MethodsUsagePanel; private readonly modelConfig: ModelConfigListener; private constructor( @@ -52,7 +51,7 @@ export class ModelEditorModule extends DisposableObject { this.queryStorageDir = join(baseQueryStorageDir, "model-editor-results"); this.modelingEvents = new ModelingEvents(app); this.modelingStore = new ModelingStore(this.modelingEvents); - this.methodsUsagePanel = this.push( + this.push( new MethodsUsagePanel(this.modelingStore, this.modelingEvents, cliServer), ); this.push( @@ -106,7 +105,7 @@ export class ModelEditorModule extends DisposableObject { private registerToModelingEvents(): void { this.push( this.modelingEvents.onSelectedMethodChanged(async (event) => { - await this.showMethod(event.databaseItem, event.method, event.usage); + await this.showMethod(event.databaseItem, event.usage); }), ); @@ -127,10 +126,8 @@ export class ModelEditorModule extends DisposableObject { private async showMethod( databaseItem: DatabaseItem, - method: Method, usage: Usage, ): Promise { - await this.methodsUsagePanel.revealItem(method.signature, usage); await showResolvableLocation(usage.url, databaseItem, this.app.logger); } diff --git a/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts b/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts index 396be26796f..e307cd1efb3 100644 --- a/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts +++ b/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts @@ -4,6 +4,7 @@ import type { ModelingEvents } from "../../../src/model-editor/modeling-events"; export function createMockModelingEvents({ onActiveDbChanged = jest.fn(), onDbClosed = jest.fn(), + onSelectedMethodChanged = jest.fn(), onMethodsChanged = jest.fn(), onHideModeledMethodsChanged = jest.fn(), onModeChanged = jest.fn(), @@ -16,6 +17,7 @@ export function createMockModelingEvents({ }: { onActiveDbChanged?: ModelingEvents["onActiveDbChanged"]; onDbClosed?: ModelingEvents["onDbClosed"]; + onSelectedMethodChanged?: ModelingEvents["onSelectedMethodChanged"]; onMethodsChanged?: ModelingEvents["onMethodsChanged"]; onHideModeledMethodsChanged?: ModelingEvents["onHideModeledMethodsChanged"]; onModeChanged?: ModelingEvents["onModeChanged"]; @@ -29,6 +31,7 @@ export function createMockModelingEvents({ return mockedObject({ onActiveDbChanged, onDbClosed, + onSelectedMethodChanged, onMethodsChanged, onHideModeledMethodsChanged, onModeChanged, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-panel.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-panel.test.ts index 0d290ec29ca..c742433b097 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-panel.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/methods-usage/methods-usage-panel.test.ts @@ -1,5 +1,5 @@ import type { TreeView } from "vscode"; -import { window } from "vscode"; +import { EventEmitter, window } from "vscode"; import type { CodeQLCliServer } from "../../../../../src/codeql-cli/cli"; import type { Method } from "../../../../../src/model-editor/method"; import { MethodsUsagePanel } from "../../../../../src/model-editor/methods-usage/methods-usage-panel"; @@ -66,6 +66,8 @@ describe("MethodsUsagePanel", () => { const modeledMethods: Record = {}; const modifiedMethodSignatures: Set = new Set(); const usage = createUsage(); + const selectedMethodChangedEmitter: ModelingEvents["onSelectedMethodChangedEventEmitter"] = + new EventEmitter(); beforeEach(() => { mockTreeView = mockedObject>({ @@ -74,7 +76,9 @@ describe("MethodsUsagePanel", () => { jest.spyOn(window, "createTreeView").mockReturnValue(mockTreeView); modelingStore = createMockModelingStore(); - modelingEvents = createMockModelingEvents(); + modelingEvents = createMockModelingEvents({ + onSelectedMethodChanged: selectedMethodChangedEmitter.event, + }); }); it("should reveal the correct item in the tree view", async () => { @@ -97,7 +101,15 @@ describe("MethodsUsagePanel", () => { modifiedMethodSignatures, ); - await panel.revealItem(method.signature, usage); + selectedMethodChangedEmitter.fire({ + databaseItem: dbItem, + method, + usage, + modeledMethods: modeledMethods[method.signature], + isModified: modifiedMethodSignatures.has(method.signature), + isInProgress: false, + processedByAutoModel: false, + }); expect(mockTreeView.reveal).toHaveBeenCalledWith( expect.objectContaining({ @@ -124,7 +136,15 @@ describe("MethodsUsagePanel", () => { modifiedMethodSignatures, ); - await panel.revealItem(method.signature, usage); + selectedMethodChangedEmitter.fire({ + databaseItem: dbItem, + method, + usage, + modeledMethods: modeledMethods[method.signature], + isModified: modifiedMethodSignatures.has(method.signature), + isInProgress: false, + processedByAutoModel: false, + }); expect(mockTreeView.reveal).not.toHaveBeenCalled(); }); From dc08da924fed9b93cc343da0a71fad541d2764ea Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 4 Mar 2024 17:27:34 +0000 Subject: [PATCH 0086/1237] Inline showMethod --- .../src/model-editor/model-editor-module.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/model-editor-module.ts b/extensions/ql-vscode/src/model-editor/model-editor-module.ts index b00ed96e8d0..83edc7b0ed3 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-module.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-module.ts @@ -105,7 +105,11 @@ export class ModelEditorModule extends DisposableObject { private registerToModelingEvents(): void { this.push( this.modelingEvents.onSelectedMethodChanged(async (event) => { - await this.showMethod(event.databaseItem, event.usage); + await showResolvableLocation( + event.usage.url, + event.databaseItem, + this.app.logger, + ); }), ); @@ -124,13 +128,6 @@ export class ModelEditorModule extends DisposableObject { ); } - private async showMethod( - databaseItem: DatabaseItem, - usage: Usage, - ): Promise { - await showResolvableLocation(usage.url, databaseItem, this.app.logger); - } - private async openModelEditor(): Promise { { const db = this.databaseManager.currentDatabaseItem; From cffecafb61262a70f8363ec6e711b3ef4dba427a Mon Sep 17 00:00:00 2001 From: Shati Patel <42641846+shati-patel@users.noreply.github.com> Date: Tue, 5 Mar 2024 14:39:30 +0000 Subject: [PATCH 0087/1237] Add a new "model alerts" webview (for showing results from model evaluation) (#3436) --- .../ql-vscode/src/common/interface-types.ts | 15 ++++- .../src/common/vscode/webview-html.ts | 3 +- .../model-alerts/model-alerts-view.ts | 67 +++++++++++++++++++ .../src/model-editor/model-editor-view.ts | 5 +- .../src/model-editor/model-evaluator.ts | 14 ++-- .../src/view/model-alerts/ModelAlerts.tsx | 22 ++++++ .../ql-vscode/src/view/model-alerts/index.tsx | 8 +++ .../src/view/model-editor/ModelEditor.tsx | 4 +- extensions/ql-vscode/src/view/webview.tsx | 2 + .../model-editor/model-evaluator.test.ts | 5 +- 10 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts create mode 100644 extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx create mode 100644 extensions/ql-vscode/src/view/model-alerts/index.tsx diff --git a/extensions/ql-vscode/src/common/interface-types.ts b/extensions/ql-vscode/src/common/interface-types.ts index 25c54208e05..ffd6d423547 100644 --- a/extensions/ql-vscode/src/common/interface-types.ts +++ b/extensions/ql-vscode/src/common/interface-types.ts @@ -600,6 +600,10 @@ interface StopModelEvaluationMessage { t: "stopModelEvaluation"; } +interface OpenModelAlertsViewMessage { + t: "openModelAlertsView"; +} + interface ModelDependencyMessage { t: "modelDependency"; } @@ -671,7 +675,8 @@ export type FromModelEditorMessage = | HideModeledMethodsMessage | SetMultipleModeledMethodsMessage | StartModelEvaluationMessage - | StopModelEvaluationMessage; + | StopModelEvaluationMessage + | OpenModelAlertsViewMessage; interface RevealInEditorMessage { t: "revealInModelEditor"; @@ -721,3 +726,11 @@ export type ToMethodModelingMessage = | SetInModelingModeMessage | SetInProgressMessage | SetProcessedByAutoModelMessage; + +interface SetModelAlertsMessage { + t: "setModelAlerts"; +} + +export type ToModelAlertsMessage = SetModelAlertsMessage; + +export type FromModelAlertsMessage = CommonFromViewMessages; diff --git a/extensions/ql-vscode/src/common/vscode/webview-html.ts b/extensions/ql-vscode/src/common/vscode/webview-html.ts index 2605a754e7a..7ad1f5d08e4 100644 --- a/extensions/ql-vscode/src/common/vscode/webview-html.ts +++ b/extensions/ql-vscode/src/common/vscode/webview-html.ts @@ -10,7 +10,8 @@ export type WebviewKind = | "variant-analysis" | "data-flow-paths" | "model-editor" - | "method-modeling"; + | "method-modeling" + | "model-alerts"; export interface WebviewMessage { t: string; diff --git a/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts b/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts new file mode 100644 index 00000000000..0713ff10fee --- /dev/null +++ b/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts @@ -0,0 +1,67 @@ +import { ViewColumn } from "vscode"; +import type { WebviewPanelConfig } from "../../common/vscode/abstract-webview"; +import { AbstractWebview } from "../../common/vscode/abstract-webview"; +import { assertNever } from "../../common/helpers-pure"; +import { telemetryListener } from "../../common/vscode/telemetry"; +import type { + FromModelAlertsMessage, + ToModelAlertsMessage, +} from "../../common/interface-types"; +import type { App } from "../../common/app"; +import { redactableError } from "../../common/errors"; +import { extLogger } from "../../common/logging/vscode"; +import { showAndLogExceptionWithTelemetry } from "../../common/logging"; + +export class ModelAlertsView extends AbstractWebview< + ToModelAlertsMessage, + FromModelAlertsMessage +> { + public static readonly viewType = "codeQL.modelAlerts"; + + public constructor(app: App) { + super(app); + } + + public async showView() { + const panel = await this.getPanel(); + panel.reveal(undefined, true); + + await this.waitForPanelLoaded(); + } + + protected async getPanelConfig(): Promise { + return { + viewId: ModelAlertsView.viewType, + title: "Model Alerts", + viewColumn: ViewColumn.Active, + preserveFocus: true, + view: "model-alerts", + }; + } + + protected onPanelDispose(): void { + // Nothing to dispose + } + + protected async onMessage(msg: FromModelAlertsMessage): Promise { + switch (msg.t) { + case "viewLoaded": + this.onWebViewLoaded(); + break; + case "telemetry": + telemetryListener?.sendUIInteraction(msg.action); + break; + case "unhandledError": + void showAndLogExceptionWithTelemetry( + extLogger, + telemetryListener, + redactableError( + msg.error, + )`Unhandled error in model alerts view: ${msg.error.message}`, + ); + break; + default: + assertNever(msg); + } + } +} diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 3399a0f8745..2328e3cb44a 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -125,7 +125,7 @@ export class ModelEditorView extends AbstractWebview< this.languageDefinition = getModelsAsDataLanguage(language); this.modelEvaluator = new ModelEvaluator( - this.app.logger, + this.app, this.cliServer, modelingStore, modelingEvents, @@ -380,6 +380,9 @@ export class ModelEditorView extends AbstractWebview< case "stopModelEvaluation": await this.modelEvaluator.stopEvaluation(); break; + case "openModelAlertsView": + await this.modelEvaluator.openModelAlertsView(); + break; case "telemetry": telemetryListener?.sendUIInteraction(msg.action); break; diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts index 6303aa6c7e3..895f1927a38 100644 --- a/extensions/ql-vscode/src/model-editor/model-evaluator.ts +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -4,7 +4,6 @@ import type { DatabaseItem } from "../databases/local-databases"; import type { ModelEvaluationRun } from "./model-evaluation-run"; import { DisposableObject } from "../common/disposable-object"; import type { ModelEvaluationRunState } from "./shared/model-evaluation-run-state"; -import type { BaseLogger } from "../common/logging"; import type { CodeQLCliServer } from "../codeql-cli/cli"; import type { VariantAnalysisManager } from "../variant-analysis/variant-analysis-manager"; import type { QueryLanguage } from "../common/query-language"; @@ -18,6 +17,8 @@ import type { VariantAnalysis } from "../variant-analysis/shared/variant-analysi import type { CancellationToken } from "vscode"; import { CancellationTokenSource } from "vscode"; import type { QlPackDetails } from "../variant-analysis/ql-pack-details"; +import type { App } from "../common/app"; +import { ModelAlertsView } from "./model-alerts/model-alerts-view"; export class ModelEvaluator extends DisposableObject { // Cancellation token source to allow cancelling of the current run @@ -26,7 +27,7 @@ export class ModelEvaluator extends DisposableObject { private cancellationSource: CancellationTokenSource; public constructor( - private readonly logger: BaseLogger, + private readonly app: App, private readonly cliServer: CodeQLCliServer, private readonly modelingStore: ModelingStore, private readonly modelingEvents: ModelingEvents, @@ -54,7 +55,7 @@ export class ModelEvaluator extends DisposableObject { // Build pack const qlPack = await resolveCodeScanningQueryPack( - this.logger, + this.app.logger, this.cliServer, this.language, ); @@ -82,7 +83,7 @@ export class ModelEvaluator extends DisposableObject { public async stopEvaluation() { const evaluationRun = this.modelingStore.getModelEvaluationRun(this.dbItem); if (!evaluationRun) { - void this.logger.log("No active evaluation run to stop"); + void this.app.logger.log("No active evaluation run to stop"); return; } @@ -105,6 +106,11 @@ export class ModelEvaluator extends DisposableObject { } } + public async openModelAlertsView() { + const view = new ModelAlertsView(this.app); + await view.showView(); + } + private registerToModelingEvents() { this.push( this.modelingEvents.onModelEvaluationRunChanged(async (event) => { diff --git a/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx b/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx new file mode 100644 index 00000000000..b6da439ab4d --- /dev/null +++ b/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx @@ -0,0 +1,22 @@ +import { useEffect } from "react"; + +export function ModelAlerts(): React.JSX.Element { + useEffect(() => { + const listener = (evt: MessageEvent) => { + if (evt.origin === window.origin) { + // TODO: handle messages + } else { + // sanitize origin + const origin = evt.origin.replace(/\n|\r/g, ""); + console.error(`Invalid event origin ${origin}`); + } + }; + window.addEventListener("message", listener); + + return () => { + window.removeEventListener("message", listener); + }; + }, []); + + return <>hello world; +} diff --git a/extensions/ql-vscode/src/view/model-alerts/index.tsx b/extensions/ql-vscode/src/view/model-alerts/index.tsx new file mode 100644 index 00000000000..40786cab41d --- /dev/null +++ b/extensions/ql-vscode/src/view/model-alerts/index.tsx @@ -0,0 +1,8 @@ +import type { WebviewDefinition } from "../webview-definition"; +import { ModelAlerts } from "./ModelAlerts"; + +const definition: WebviewDefinition = { + component: , +}; + +export default definition; diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx index fce9ea92177..6d68da43ad4 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx @@ -277,7 +277,9 @@ export function ModelEditor({ }, []); const openModelAlertsView = useCallback(() => { - // TODO + vscode.postMessage({ + t: "openModelAlertsView", + }); }, []); const onGenerateFromSourceClick = useCallback(() => { diff --git a/extensions/ql-vscode/src/view/webview.tsx b/extensions/ql-vscode/src/view/webview.tsx index 5bc08c1c97a..d3adadf74a1 100644 --- a/extensions/ql-vscode/src/view/webview.tsx +++ b/extensions/ql-vscode/src/view/webview.tsx @@ -11,6 +11,7 @@ import methodModelingView from "./method-modeling"; import modelEditorView from "./model-editor"; import resultsView from "./results"; import variantAnalysisView from "./variant-analysis"; +import modelAlertsView from "./model-alerts"; // Allow all views to use Codicons import "@vscode/codicons/dist/codicon.css"; @@ -22,6 +23,7 @@ const views: Record = { "model-editor": modelEditorView, results: resultsView, "variant-analysis": variantAnalysisView, + "model-alerts": modelAlertsView, }; const render = () => { diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/model-editor/model-evaluator.test.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/model-editor/model-evaluator.test.ts index e5a15d0500b..058a4c76832 100644 --- a/extensions/ql-vscode/test/vscode-tests/activated-extension/model-editor/model-evaluator.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/model-editor/model-evaluator.test.ts @@ -1,4 +1,5 @@ import type { CodeQLCliServer } from "../../../../src/codeql-cli/cli"; +import type { App } from "../../../../src/common/app"; import type { BaseLogger } from "../../../../src/common/logging"; import { QueryLanguage } from "../../../../src/common/query-language"; import type { DatabaseItem } from "../../../../src/databases/local-databases"; @@ -15,6 +16,7 @@ import { mockedObject } from "../../../mocked-object"; describe("Model Evaluator", () => { let modelEvaluator: ModelEvaluator; let logger: BaseLogger; + let app: App; let cliServer: CodeQLCliServer; let modelingStore: ModelingStore; let modelingEvents: ModelingEvents; @@ -26,6 +28,7 @@ describe("Model Evaluator", () => { beforeEach(() => { logger = createMockLogger(); + app = mockedObject({ logger }); cliServer = mockedObject({}); getModelEvaluationRunMock = jest.fn(); modelingStore = createMockModelingStore({ @@ -40,7 +43,7 @@ describe("Model Evaluator", () => { updateView = jest.fn(); modelEvaluator = new ModelEvaluator( - logger, + app, cliServer, modelingStore, modelingEvents, From 5d263bb1a31f9c26d5600237122de4d2863c8f2e Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 5 Mar 2024 15:05:56 +0000 Subject: [PATCH 0088/1237] Push decision to use credentials or not down to where the creds are used --- .../src/databases/database-fetcher.ts | 18 ++++++++++-------- .../src/databases/local-databases-ui.ts | 6 +----- .../src/local-queries/local-queries.ts | 4 +--- .../src/local-queries/skeleton-query-wizard.ts | 4 +--- .../src/model-editor/model-editor-view.ts | 3 +-- .../skeleton-query-wizard.test.ts | 11 ----------- 6 files changed, 14 insertions(+), 32 deletions(-) diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index c9ccbdf09ae..7b66071c912 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -26,18 +26,19 @@ import { getNwoFromGitHubUrl, isValidGitHubNwo, } from "../common/github-url-identifier-helper"; -import type { Credentials } from "../common/authentication"; import type { AppCommandManager } from "../common/commands"; import { addDatabaseSourceToWorkspace, allowHttp, downloadTimeout, + isCanary, } from "../config"; import { showAndLogInformationMessage } from "../common/logging"; import { AppOctokit } from "../common/octokit"; import { getLanguageDisplayName } from "../common/query-language"; import type { DatabaseOrigin } from "./local-databases/database-origin"; import { createTimeoutSignal } from "../common/fetch-stream"; +import type { App } from "../common/app"; /** * Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file. @@ -90,9 +91,9 @@ export async function promptImportInternetDatabase( * User enters a GitHub repository and then the user is asked which language * to download (if there is more than one) * + * @param app the App * @param databaseManager the DatabaseManager * @param storagePath where to store the unzipped database. - * @param credentials the credentials to use to authenticate with GitHub * @param progress the progress callback * @param cli the CodeQL CLI server * @param language the language to download. If undefined, the user will be prompted to choose a language. @@ -100,10 +101,9 @@ export async function promptImportInternetDatabase( * @param addSourceArchiveFolder whether to add a workspace folder containing the source archive to the workspace */ export async function promptImportGithubDatabase( - commandManager: AppCommandManager, + app: App, databaseManager: DatabaseManager, storagePath: string, - credentials: Credentials | undefined, progress: ProgressCallback, cli: CodeQLCliServer, language?: string, @@ -117,9 +117,9 @@ export async function promptImportGithubDatabase( const databaseItem = await downloadGitHubDatabase( githubRepo, + app, databaseManager, storagePath, - credentials, progress, cli, language, @@ -129,7 +129,7 @@ export async function promptImportGithubDatabase( if (databaseItem) { if (makeSelected) { - await commandManager.execute("codeQLDatabases.focus"); + await app.commands.execute("codeQLDatabases.focus"); } void showAndLogInformationMessage( extLogger, @@ -169,9 +169,9 @@ export async function askForGitHubRepo( * Downloads a database from GitHub * * @param githubRepo the GitHub repository to download the database from + * @param app the App * @param databaseManager the DatabaseManager * @param storagePath where to store the unzipped database. - * @param credentials the credentials to use to authenticate with GitHub * @param progress the progress callback * @param cli the CodeQL CLI server * @param language the language to download. If undefined, the user will be prompted to choose a language. @@ -180,9 +180,9 @@ export async function askForGitHubRepo( **/ export async function downloadGitHubDatabase( githubRepo: string, + app: App, databaseManager: DatabaseManager, storagePath: string, - credentials: Credentials | undefined, progress: ProgressCallback, cli: CodeQLCliServer, language?: string, @@ -194,6 +194,8 @@ export async function downloadGitHubDatabase( throw new Error(`Invalid GitHub repository: ${githubRepo}`); } + const credentials = isCanary() ? app.credentials : undefined; + const octokit = credentials ? await credentials.getOctokit() : new AppOctokit(); diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 70f4c6eefcf..c5230ce72cb 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -49,7 +49,6 @@ import { } from "./database-fetcher"; import { asError, asyncFilter, getErrorMessage } from "../common/helpers-pure"; import type { QueryRunner } from "../query-server"; -import { isCanary } from "../config"; import type { App } from "../common/app"; import { redactableError } from "../common/errors"; import type { LocalDatabasesCommands } from "../common/commands"; @@ -558,13 +557,10 @@ export class DatabaseUI extends DisposableObject { private async handleChooseDatabaseGithub(): Promise { return withProgress( async (progress) => { - const credentials = isCanary() ? this.app.credentials : undefined; - await promptImportGithubDatabase( - this.app.commands, + this.app, this.databaseManager, this.storagePath, - credentials, progress, this.queryServer.cliServer, ); diff --git a/extensions/ql-vscode/src/local-queries/local-queries.ts b/extensions/ql-vscode/src/local-queries/local-queries.ts index 5387c9e21df..ddc29db949b 100644 --- a/extensions/ql-vscode/src/local-queries/local-queries.ts +++ b/extensions/ql-vscode/src/local-queries/local-queries.ts @@ -10,7 +10,7 @@ import { showAndLogErrorMessage, showAndLogWarningMessage, } from "../common/logging"; -import { isCanary, MAX_QUERIES } from "../config"; +import { MAX_QUERIES } from "../config"; import { gatherQlFiles } from "../common/files"; import { basename } from "path"; import { showBinaryChoiceDialog } from "../common/vscode/dialog"; @@ -322,14 +322,12 @@ export class LocalQueries extends DisposableObject { private async createSkeletonQuery(): Promise { await withProgress( async (progress: ProgressCallback) => { - const credentials = isCanary() ? this.app.credentials : undefined; const contextStoragePath = this.app.workspaceStoragePath || this.app.globalStoragePath; const language = this.languageContextStore.selectedLanguage; const skeletonQueryWizard = new SkeletonQueryWizard( this.cliServer, progress, - credentials, this.app, this.databaseManager, contextStoragePath, diff --git a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts index 61a7de920a6..0079709bbf6 100644 --- a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts +++ b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts @@ -2,7 +2,6 @@ import { dirname, join } from "path"; import { Uri, window, window as Window, workspace } from "vscode"; import type { CodeQLCliServer } from "../codeql-cli/cli"; import { showAndLogExceptionWithTelemetry } from "../common/logging"; -import type { Credentials } from "../common/authentication"; import type { QueryLanguage } from "../common/query-language"; import { getLanguageDisplayName } from "../common/query-language"; import { @@ -61,7 +60,6 @@ export class SkeletonQueryWizard { constructor( private readonly cliServer: CodeQLCliServer, private readonly progress: ProgressCallback, - private readonly credentials: Credentials | undefined, private readonly app: App, private readonly databaseManager: DatabaseManager, private readonly databaseStoragePath: string | undefined, @@ -388,9 +386,9 @@ export class SkeletonQueryWizard { await downloadGitHubDatabase( chosenRepo, + this.app, this.databaseManager, this.databaseStoragePath, - this.credentials, progress, this.cliServer, this.language, diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 2328e3cb44a..0ad90435813 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -917,10 +917,9 @@ export class ModelEditorView extends AbstractWebview< // imported to the query server, so we need to register it to our workspace. const makeSelected = false; const addedDatabase = await promptImportGithubDatabase( - this.app.commands, + this.app, this.databaseManager, this.app.workspaceStoragePath ?? this.app.globalStoragePath, - this.app.credentials, progress, this.cliServer, this.databaseItem.language, diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts index d976a008e95..7f4c6d8e2b3 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts @@ -18,7 +18,6 @@ import { removeSync, } from "fs-extra"; import { dirname, join } from "path"; -import { testCredentialsWithStub } from "../../../factories/authentication"; import type { DatabaseItem, DatabaseManager, @@ -69,7 +68,6 @@ describe("SkeletonQueryWizard", () => { typeof CodeQLCliServer.prototype.resolveQlpacks >; - const credentials = testCredentialsWithStub(); const chosenLanguage = "ruby"; const selectedItems: QueryTreeViewItem[] = []; @@ -145,7 +143,6 @@ describe("SkeletonQueryWizard", () => { wizard = new SkeletonQueryWizard( mockCli, jest.fn(), - credentials, mockApp, mockDatabaseManager, storagePath, @@ -173,7 +170,6 @@ describe("SkeletonQueryWizard", () => { wizard = new SkeletonQueryWizard( mockCli, jest.fn(), - credentials, mockApp, mockDatabaseManager, storagePath, @@ -322,7 +318,6 @@ describe("SkeletonQueryWizard", () => { wizard = new SkeletonQueryWizard( mockCli, jest.fn(), - credentials, mockApp, mockDatabaseManagerWithItems, storagePath, @@ -372,7 +367,6 @@ describe("SkeletonQueryWizard", () => { wizard = new SkeletonQueryWizard( mockCli, jest.fn(), - credentials, mockApp, mockDatabaseManagerWithItems, storagePath, @@ -508,7 +502,6 @@ describe("SkeletonQueryWizard", () => { wizard = new SkeletonQueryWizard( mockCli, jest.fn(), - credentials, mockApp, mockDatabaseManager, storagePath, @@ -730,7 +723,6 @@ describe("SkeletonQueryWizard", () => { wizard = new SkeletonQueryWizard( mockCli, jest.fn(), - credentials, mockApp, mockDatabaseManager, storagePath, @@ -760,7 +752,6 @@ describe("SkeletonQueryWizard", () => { wizard = new SkeletonQueryWizard( mockCli, jest.fn(), - credentials, mockApp, mockDatabaseManager, storagePath, @@ -794,7 +785,6 @@ describe("SkeletonQueryWizard", () => { wizard = new SkeletonQueryWizard( mockCli, jest.fn(), - credentials, mockApp, mockDatabaseManager, storagePath, @@ -838,7 +828,6 @@ describe("SkeletonQueryWizard", () => { wizard = new SkeletonQueryWizard( mockCli, jest.fn(), - credentials, mockApp, mockDatabaseManager, storagePath, From 2bc261eacfd077d3b13e144ba8c4684065dec187 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 6 Mar 2024 12:28:30 +0000 Subject: [PATCH 0089/1237] Use CancellationTokenSource instead of casting dummy objects --- .../query-history/local-query-history-item.ts | 13 +++++-------- .../vscode-tests/cli-integration/queries.test.ts | 8 ++------ .../query-history/store/query-history-store.test.ts | 9 ++------- .../vscode-tests/no-workspace/query-results.test.ts | 9 ++------- 4 files changed, 11 insertions(+), 28 deletions(-) diff --git a/extensions/ql-vscode/test/factories/query-history/local-query-history-item.ts b/extensions/ql-vscode/test/factories/query-history/local-query-history-item.ts index b77d9571aa7..d20d7fcf43a 100644 --- a/extensions/ql-vscode/test/factories/query-history/local-query-history-item.ts +++ b/extensions/ql-vscode/test/factories/query-history/local-query-history-item.ts @@ -6,7 +6,7 @@ import type { QueryWithResults, } from "../../../src/run-queries-shared"; import { QueryOutputDir } from "../../../src/local-queries/query-output-dir"; -import type { CancellationTokenSource } from "vscode"; +import { CancellationTokenSource } from "vscode"; import type { QueryMetadata } from "../../../src/common/interface-types"; import type { QueryLanguage } from "../../../src/common/query-language"; @@ -31,12 +31,6 @@ export function createMockLocalQueryInfo({ language?: QueryLanguage; outputDir?: QueryOutputDir | undefined; }): LocalQueryInfo { - const cancellationToken = { - dispose: () => { - /**/ - }, - } as CancellationTokenSource; - const initialQueryInfo = { queryText: "select 1", isQuickQuery: false, @@ -54,7 +48,10 @@ export function createMockLocalQueryInfo({ outputDir, } as InitialQueryInfo; - const localQuery = new LocalQueryInfo(initialQueryInfo, cancellationToken); + const localQuery = new LocalQueryInfo( + initialQueryInfo, + new CancellationTokenSource(), + ); localQuery.failureReason = failureReason; localQuery.cancel = () => { diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/queries.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/queries.test.ts index 3ac1c6de78f..b3bd56a2371 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/queries.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/queries.test.ts @@ -1,5 +1,5 @@ import type { CancellationToken, ExtensionContext, Range } from "vscode"; -import { Uri } from "vscode"; +import { CancellationTokenSource, Uri } from "vscode"; import { join, dirname } from "path"; import { pathExistsSync, @@ -125,11 +125,7 @@ describeWithCodeQL()("Queries", () => { safeDel(qlFile); safeDel(qlpackFile); - token = { - onCancellationRequested: (_) => { - void _; - }, - } as CancellationToken; + token = new CancellationTokenSource().token; dbItem = await ensureTestDatabase(databaseManager, cli); }); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/store/query-history-store.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/store/query-history-store.test.ts index 4016d791e61..34c092987cc 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/store/query-history-store.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/store/query-history-store.test.ts @@ -10,8 +10,7 @@ import type { QueryWithResults } from "../../../../../src/run-queries-shared"; import { QueryEvaluationInfo } from "../../../../../src/run-queries-shared"; import { QueryOutputDir } from "../../../../../src/local-queries/query-output-dir"; import type { DatabaseInfo } from "../../../../../src/common/interface-types"; -import type { CancellationTokenSource } from "vscode"; -import { Uri } from "vscode"; +import { CancellationTokenSource, Uri } from "vscode"; import { tmpDir } from "../../../../../src/tmp-dir"; import type { QueryHistoryInfo } from "../../../../../src/query-history/query-history-info"; import { createMockVariantAnalysisHistoryItem } from "../../../../factories/query-history/variant-analysis-history-item"; @@ -220,11 +219,7 @@ describe("write and read", () => { id: `some-id-${dbName}`, outputDir: outputDir ? outputDir : undefined, } as InitialQueryInfo, - { - dispose: () => { - /**/ - }, - } as CancellationTokenSource, + new CancellationTokenSource(), ); if (queryWithResults) { diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts index 326b32d8c3f..b4e27f60fdf 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts @@ -21,8 +21,7 @@ import type { SortedResultSetInfo, } from "../../../src/common/interface-types"; import type { CodeQLCliServer, SourceInfo } from "../../../src/codeql-cli/cli"; -import type { CancellationTokenSource } from "vscode"; -import { Uri } from "vscode"; +import { CancellationTokenSource, Uri } from "vscode"; import { tmpDir } from "../../../src/tmp-dir"; import { sleep } from "../../../src/common/time"; import { mockedObject } from "../utils/mocking.helpers"; @@ -462,11 +461,7 @@ describe("query-results", () => { id: `some-id-${dbName}`, outputDir: new QueryOutputDir("path/to/output/dir"), } as InitialQueryInfo, - { - dispose: () => { - /**/ - }, - } as CancellationTokenSource, + new CancellationTokenSource(), ); if (queryWithResults) { From 0cdcd2ce67d5dfbb0d2116d848c3ee6c79254fc8 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 6 Mar 2024 14:52:48 +0000 Subject: [PATCH 0090/1237] Delete hasStateForActiveDb as it is unused --- extensions/ql-vscode/src/model-editor/modeling-store.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/modeling-store.ts b/extensions/ql-vscode/src/model-editor/modeling-store.ts index 097148863d0..643b0b2a018 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-store.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-store.ts @@ -113,10 +113,6 @@ export class ModelingStore extends DisposableObject { return this.state.get(this.activeDb); } - public hasStateForActiveDb(): boolean { - return !!this.getStateForActiveDb(); - } - public anyDbsBeingModeled(): boolean { return this.state.size > 0; } From 707cdec1ec97a73ac5d20bd6751bb69b1999b889 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 6 Mar 2024 16:28:37 +0000 Subject: [PATCH 0091/1237] Update CHANGELOG.md --- extensions/ql-vscode/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 8db43478011..dc089c86ca0 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -7,6 +7,7 @@ - Update variant analysis view to show when cancelation is in progress. [#3405](https://github.com/github/vscode-codeql/pull/3405) - Remove support for CodeQL CLI versions older than 2.13.5. [#3371](https://github.com/github/vscode-codeql/pull/3371) - Add a timeout to downloading databases and the CodeQL CLI. These can be changed using the `codeQL.addingDatabases.downloadTimeout` and `codeQL.cli.downloadTimeout` settings respectively. [#3373](https://github.com/github/vscode-codeql/pull/3373) +- When downloading a CodeQL database through the model editor, only use credentials when in canary mode. [#3440](https://github.com/github/vscode-codeql/pull/3440) ## 1.12.2 - 14 February 2024 From 54cf5c55a8b544f9fc2711ffc9cdd52f479aa9b0 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 6 Mar 2024 12:24:44 +0000 Subject: [PATCH 0092/1237] Add tests of MethodModelingViewProvider viewLoaded --- .../src/model-editor/modeling-store.ts | 4 +- .../model-editor/modelingEventsMock.ts | 3 + .../model-editor/modelingStoreMock.ts | 5 +- .../method-modeling-view-provider.test.ts | 212 ++++++++++++++++++ 4 files changed, 221 insertions(+), 3 deletions(-) create mode 100644 extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/method-modeling/method-modeling-view-provider.test.ts diff --git a/extensions/ql-vscode/src/model-editor/modeling-store.ts b/extensions/ql-vscode/src/model-editor/modeling-store.ts index 643b0b2a018..3622759373b 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-store.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-store.ts @@ -22,7 +22,7 @@ interface InternalDbModelingState { modelEvaluationRun: ModelEvaluationRun | undefined; } -interface DbModelingState { +export interface DbModelingState { readonly databaseItem: DatabaseItem; readonly methods: readonly Method[]; readonly hideModeledMethods: boolean; @@ -36,7 +36,7 @@ interface DbModelingState { readonly modelEvaluationRun: ModelEvaluationRun | undefined; } -interface SelectedMethodDetails { +export interface SelectedMethodDetails { readonly databaseItem: DatabaseItem; readonly method: Method; readonly usage: Usage | undefined; diff --git a/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts b/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts index e307cd1efb3..04db82f8e0b 100644 --- a/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts +++ b/extensions/ql-vscode/test/__mocks__/model-editor/modelingEventsMock.ts @@ -3,6 +3,7 @@ import type { ModelingEvents } from "../../../src/model-editor/modeling-events"; export function createMockModelingEvents({ onActiveDbChanged = jest.fn(), + onDbOpened = jest.fn(), onDbClosed = jest.fn(), onSelectedMethodChanged = jest.fn(), onMethodsChanged = jest.fn(), @@ -16,6 +17,7 @@ export function createMockModelingEvents({ onModelEvaluationRunChanged = jest.fn(), }: { onActiveDbChanged?: ModelingEvents["onActiveDbChanged"]; + onDbOpened?: ModelingEvents["onDbOpened"]; onDbClosed?: ModelingEvents["onDbClosed"]; onSelectedMethodChanged?: ModelingEvents["onSelectedMethodChanged"]; onMethodsChanged?: ModelingEvents["onMethodsChanged"]; @@ -30,6 +32,7 @@ export function createMockModelingEvents({ } = {}): ModelingEvents { return mockedObject({ onActiveDbChanged, + onDbOpened, onDbClosed, onSelectedMethodChanged, onMethodsChanged, diff --git a/extensions/ql-vscode/test/__mocks__/model-editor/modelingStoreMock.ts b/extensions/ql-vscode/test/__mocks__/model-editor/modelingStoreMock.ts index 7cbad68c62c..8bfbd28f43d 100644 --- a/extensions/ql-vscode/test/__mocks__/model-editor/modelingStoreMock.ts +++ b/extensions/ql-vscode/test/__mocks__/model-editor/modelingStoreMock.ts @@ -3,17 +3,20 @@ import type { ModelingStore } from "../../../src/model-editor/modeling-store"; export function createMockModelingStore({ initializeStateForDb = jest.fn(), - getStateForActiveDb = jest.fn(), + getStateForActiveDb = jest.fn().mockReturnValue(undefined), + getSelectedMethodDetails = jest.fn().mockReturnValue(undefined), getModelEvaluationRun = jest.fn(), updateModelEvaluationRun = jest.fn(), }: { initializeStateForDb?: ModelingStore["initializeStateForDb"]; getStateForActiveDb?: ModelingStore["getStateForActiveDb"]; + getSelectedMethodDetails?: ModelingStore["getSelectedMethodDetails"]; getModelEvaluationRun?: ModelingStore["getModelEvaluationRun"]; updateModelEvaluationRun?: ModelingStore["updateModelEvaluationRun"]; } = {}): ModelingStore { return mockedObject({ initializeStateForDb, + getSelectedMethodDetails, getStateForActiveDb, getModelEvaluationRun, updateModelEvaluationRun, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/method-modeling/method-modeling-view-provider.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/method-modeling/method-modeling-view-provider.test.ts new file mode 100644 index 00000000000..051c626792d --- /dev/null +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/method-modeling/method-modeling-view-provider.test.ts @@ -0,0 +1,212 @@ +import type { + Uri, + Webview, + WebviewView, + WebviewViewResolveContext, +} from "vscode"; +import { CancellationTokenSource, EventEmitter } from "vscode"; +import type { ModelConfigListener } from "../../../../../src/config"; +import { MethodModelingViewProvider } from "../../../../../src/model-editor/method-modeling/method-modeling-view-provider"; +import { createMockApp } from "../../../../__mocks__/appMock"; +import { createMockModelingEvents } from "../../../../__mocks__/model-editor/modelingEventsMock"; +import { createMockModelingStore } from "../../../../__mocks__/model-editor/modelingStoreMock"; +import { mockedObject } from "../../../../mocked-object"; +import type { FromMethodModelingMessage } from "../../../../../src/common/interface-types"; +import { DisposableObject } from "../../../../../src/common/disposable-object"; +import type { ModelingEvents } from "../../../../../src/model-editor/modeling-events"; +import type { + DbModelingState, + SelectedMethodDetails, +} from "../../../../../src/model-editor/modeling-store"; +import { mockDatabaseItem } from "../../../utils/mocking.helpers"; +import { + createMethod, + createUsage, +} from "../../../../factories/model-editor/method-factories"; + +describe("method modeling view provider", () => { + // Modeling store + let getStateForActiveDb: jest.Mock; + let getSelectedMethodDetails: jest.Mock< + SelectedMethodDetails | undefined, + [] + >; + + // Modeling events + let selectedMethodChangedEventEmitter: ModelingEvents["onSelectedMethodChangedEventEmitter"]; + let dbOpenedEventEmitter: ModelingEvents["onDbOpenedEventEmitter"]; + + // View provider + let viewProvider: MethodModelingViewProvider; + let onDidReceiveMessage: (msg: FromMethodModelingMessage) => Promise; + let postMessage: (message: unknown) => Promise; + + beforeEach(async () => { + const app = createMockApp({}); + + getStateForActiveDb = jest.fn().mockReturnValue(undefined); + getSelectedMethodDetails = jest.fn().mockReturnValue(undefined); + const modelingStore = createMockModelingStore({ + getStateForActiveDb, + getSelectedMethodDetails, + }); + + selectedMethodChangedEventEmitter = new EventEmitter(); + dbOpenedEventEmitter = new EventEmitter(); + const modelingEvents = createMockModelingEvents({ + onSelectedMethodChanged: selectedMethodChangedEventEmitter.event, + onDbOpened: dbOpenedEventEmitter.event, + }); + + const modelConfigListener = mockedObject({ + showTypeModels: true, + onDidChangeConfiguration: jest.fn(), + }); + + viewProvider = new MethodModelingViewProvider( + app, + modelingStore, + modelingEvents, + modelConfigListener, + ); + + postMessage = jest.fn().mockResolvedValue(true); + const webview: Webview = { + options: {}, + html: "", + onDidReceiveMessage: (listener) => { + onDidReceiveMessage = listener; + return new DisposableObject(); + }, + postMessage, + asWebviewUri: (uri: Uri) => uri, + cspSource: "", + }; + + const webviewView = mockedObject({ + webview, + onDidDispose: jest.fn(), + }); + + viewProvider.resolveWebviewView( + webviewView, + mockedObject({}), + new CancellationTokenSource().token, + ); + + expect(onDidReceiveMessage).toBeDefined(); + }); + + it("should load webview when no active DB", async () => { + await onDidReceiveMessage({ + t: "viewLoaded", + viewName: MethodModelingViewProvider.viewType, + }); + + expect(postMessage).toHaveBeenCalledTimes(1); + expect(postMessage).toHaveBeenCalledWith({ + t: "setMethodModelingPanelViewState", + viewState: { + language: undefined, + modelConfig: { + showTypeModels: true, + }, + }, + }); + }); + + it("should load webview when active DB but no selected method", async () => { + const dbModelingState = mockedObject({ + databaseItem: mockDatabaseItem({ + language: "java", + }), + }); + getStateForActiveDb.mockReturnValue(dbModelingState); + + await onDidReceiveMessage({ + t: "viewLoaded", + viewName: MethodModelingViewProvider.viewType, + }); + + expect(postMessage).toHaveBeenCalledTimes(3); + expect(postMessage).toHaveBeenNthCalledWith(1, { + t: "setMethodModelingPanelViewState", + viewState: { + language: undefined, + modelConfig: { + showTypeModels: true, + }, + }, + }); + expect(postMessage).toHaveBeenNthCalledWith(2, { + t: "setInModelingMode", + inModelingMode: true, + }); + expect(postMessage).toHaveBeenNthCalledWith(3, { + t: "setMethodModelingPanelViewState", + viewState: { + language: "java", + modelConfig: { + showTypeModels: true, + }, + }, + }); + }); + + it("should load webview when active DB and a selected method", async () => { + const dbModelingState = mockedObject({ + databaseItem: mockDatabaseItem({ + language: "java", + }), + }); + getStateForActiveDb.mockReturnValue(dbModelingState); + + const selectedMethodDetails: SelectedMethodDetails = { + databaseItem: dbModelingState.databaseItem, + method: createMethod(), + usage: createUsage(), + modeledMethods: [], + isModified: false, + isInProgress: false, + processedByAutoModel: false, + }; + getSelectedMethodDetails.mockReturnValue(selectedMethodDetails); + + await onDidReceiveMessage({ + t: "viewLoaded", + viewName: MethodModelingViewProvider.viewType, + }); + + expect(postMessage).toHaveBeenCalledTimes(4); + expect(postMessage).toHaveBeenNthCalledWith(1, { + t: "setMethodModelingPanelViewState", + viewState: { + language: undefined, + modelConfig: { + showTypeModels: true, + }, + }, + }); + expect(postMessage).toHaveBeenNthCalledWith(2, { + t: "setInModelingMode", + inModelingMode: true, + }); + expect(postMessage).toHaveBeenNthCalledWith(3, { + t: "setMethodModelingPanelViewState", + viewState: { + language: "java", + modelConfig: { + showTypeModels: true, + }, + }, + }); + expect(postMessage).toHaveBeenNthCalledWith(4, { + t: "setSelectedMethod", + method: selectedMethodDetails.method, + modeledMethods: selectedMethodDetails.modeledMethods, + isModified: selectedMethodDetails.isModified, + isInProgress: selectedMethodDetails.isInProgress, + processedByAutoModel: selectedMethodDetails.processedByAutoModel, + }); + }); +}); From 7ad9507910a469c538cfba420bcb4b6355295167 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Mar 2024 13:36:25 +0000 Subject: [PATCH 0093/1237] Bump tar-stream from 3.1.6 to 3.1.7 in /extensions/ql-vscode Bumps [tar-stream](https://github.com/mafintosh/tar-stream) from 3.1.6 to 3.1.7. - [Commits](https://github.com/mafintosh/tar-stream/compare/v3.1.6...v3.1.7) --- updated-dependencies: - dependency-name: tar-stream dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- extensions/ql-vscode/package-lock.json | 8 ++++---- extensions/ql-vscode/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 00b50970dc6..0194dcb8cbb 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -133,7 +133,7 @@ "patch-package": "^8.0.0", "prettier": "^3.2.5", "storybook": "^7.6.15", - "tar-stream": "^3.0.0", + "tar-stream": "^3.1.7", "through2": "^4.0.2", "ts-jest": "^29.0.1", "ts-json-schema-generator": "^1.1.2", @@ -30104,9 +30104,9 @@ } }, "node_modules/tar-stream": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz", - "integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index d9b32869e07..c09568d744e 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -2061,7 +2061,7 @@ "patch-package": "^8.0.0", "prettier": "^3.2.5", "storybook": "^7.6.15", - "tar-stream": "^3.0.0", + "tar-stream": "^3.1.7", "through2": "^4.0.2", "ts-jest": "^29.0.1", "ts-json-schema-generator": "^1.1.2", From cd2fdf9d2ec654453868e182f47cd3e11e52c3f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Mar 2024 13:36:36 +0000 Subject: [PATCH 0094/1237] Bump @vscode/test-electron from 2.3.8 to 2.3.9 in /extensions/ql-vscode Bumps [@vscode/test-electron](https://github.com/Microsoft/vscode-test) from 2.3.8 to 2.3.9. - [Changelog](https://github.com/microsoft/vscode-test/blob/main/CHANGELOG.md) - [Commits](https://github.com/Microsoft/vscode-test/commits/v2.3.9) --- updated-dependencies: - dependency-name: "@vscode/test-electron" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- extensions/ql-vscode/package-lock.json | 8 ++++---- extensions/ql-vscode/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 00b50970dc6..936f254a6d8 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -95,7 +95,7 @@ "@types/yauzl": "^2.10.3", "@typescript-eslint/eslint-plugin": "^6.19.0", "@typescript-eslint/parser": "^6.21.0", - "@vscode/test-electron": "^2.2.0", + "@vscode/test-electron": "^2.3.9", "@vscode/vsce": "^2.19.0", "ansi-colors": "^4.1.1", "applicationinsights": "^2.9.4", @@ -10502,9 +10502,9 @@ "integrity": "sha512-Zhf3KvB+J04M4HPE2yCvEILGVtPixXUQMLBvx4QcAtjhc5lnwlZbbt80LCsZO2B+2BH8RMgVXk3QQ5DEzEne2Q==" }, "node_modules/@vscode/test-electron": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.8.tgz", - "integrity": "sha512-b4aZZsBKtMGdDljAsOPObnAi7+VWIaYl3ylCz1jTs+oV6BZ4TNHcVNC3xUn0azPeszBmwSBDQYfFESIaUQnrOg==", + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.9.tgz", + "integrity": "sha512-z3eiChaCQXMqBnk2aHHSEkobmC2VRalFQN0ApOAtydL172zXGxTwGrRtviT5HnUB+Q+G3vtEYFtuQkYqBzYgMA==", "dev": true, "dependencies": { "http-proxy-agent": "^4.0.1", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index d9b32869e07..9eb7edacae2 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -2023,7 +2023,7 @@ "@types/yauzl": "^2.10.3", "@typescript-eslint/eslint-plugin": "^6.19.0", "@typescript-eslint/parser": "^6.21.0", - "@vscode/test-electron": "^2.2.0", + "@vscode/test-electron": "^2.3.9", "@vscode/vsce": "^2.19.0", "ansi-colors": "^4.1.1", "applicationinsights": "^2.9.4", From 651a9a627e83f3710a2370263b537916e98d28b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Mar 2024 13:37:01 +0000 Subject: [PATCH 0095/1237] Bump nanoid from 5.0.4 to 5.0.6 in /extensions/ql-vscode Bumps [nanoid](https://github.com/ai/nanoid) from 5.0.4 to 5.0.6. - [Release notes](https://github.com/ai/nanoid/releases) - [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md) - [Commits](https://github.com/ai/nanoid/compare/5.0.4...5.0.6) --- updated-dependencies: - dependency-name: nanoid dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- extensions/ql-vscode/package-lock.json | 8 ++++---- extensions/ql-vscode/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 00b50970dc6..2258051de3d 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -25,7 +25,7 @@ "fs-extra": "^11.1.1", "js-yaml": "^4.1.0", "msw": "^2.0.13", - "nanoid": "^5.0.1", + "nanoid": "^5.0.6", "node-fetch": "^2.6.7", "p-queue": "^8.0.1", "react": "^18.2.0", @@ -25198,9 +25198,9 @@ "optional": true }, "node_modules/nanoid": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.4.tgz", - "integrity": "sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.6.tgz", + "integrity": "sha512-rRq0eMHoGZxlvaFOUdK1Ev83Bd1IgzzR+WJ3IbDJ7QOSdAxYjlurSPqFs9s4lJg29RT6nPwizFtJhQS6V5xgiA==", "funding": [ { "type": "github", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index d9b32869e07..f52b6c73b95 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1953,7 +1953,7 @@ "fs-extra": "^11.1.1", "js-yaml": "^4.1.0", "msw": "^2.0.13", - "nanoid": "^5.0.1", + "nanoid": "^5.0.6", "node-fetch": "^2.6.7", "p-queue": "^8.0.1", "react": "^18.2.0", From be06026832426e229bccf02ab1cbe7737216fb76 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Mar 2024 13:58:43 +0000 Subject: [PATCH 0096/1237] Bump @vscode/vsce from 2.22.0 to 2.24.0 in /extensions/ql-vscode Bumps [@vscode/vsce](https://github.com/Microsoft/vsce) from 2.22.0 to 2.24.0. - [Release notes](https://github.com/Microsoft/vsce/releases) - [Commits](https://github.com/Microsoft/vsce/compare/v2.22.0...v2.24.0) --- updated-dependencies: - dependency-name: "@vscode/vsce" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- extensions/ql-vscode/package-lock.json | 8 ++++---- extensions/ql-vscode/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index abf117b82ae..ecc9eb09941 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -96,7 +96,7 @@ "@typescript-eslint/eslint-plugin": "^6.19.0", "@typescript-eslint/parser": "^6.21.0", "@vscode/test-electron": "^2.3.9", - "@vscode/vsce": "^2.19.0", + "@vscode/vsce": "^2.24.0", "ansi-colors": "^4.1.1", "applicationinsights": "^2.9.4", "cosmiconfig": "^9.0.0", @@ -10517,9 +10517,9 @@ } }, "node_modules/@vscode/vsce": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-2.22.0.tgz", - "integrity": "sha512-8df4uJiM3C6GZ2Sx/KilSKVxsetrTBBIUb3c0W4B1EWHcddioVs5mkyDKtMNP0khP/xBILVSzlXxhV+nm2rC9A==", + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-2.24.0.tgz", + "integrity": "sha512-p6CIXpH5HXDqmUkgFXvIKTjZpZxy/uDx4d/UsfhS9vQUun43KDNUbYeZocyAHgqcJlPEurgArHz9te1PPiqPyA==", "dev": true, "dependencies": { "azure-devops-node-api": "^11.0.1", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index affc75a30a5..5d404057043 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -2024,7 +2024,7 @@ "@typescript-eslint/eslint-plugin": "^6.19.0", "@typescript-eslint/parser": "^6.21.0", "@vscode/test-electron": "^2.3.9", - "@vscode/vsce": "^2.19.0", + "@vscode/vsce": "^2.24.0", "ansi-colors": "^4.1.1", "applicationinsights": "^2.9.4", "cosmiconfig": "^9.0.0", From fe6dc8a7a351e500cf3a0a61e0554c026444e1b5 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 7 Mar 2024 12:38:47 +0000 Subject: [PATCH 0097/1237] Move findDirWithFile to files.ts --- extensions/ql-vscode/src/common/files.ts | 29 ++++++++++ .../src/databases/database-fetcher.ts | 33 +---------- .../test/unit-tests/common/files.test.ts | 58 ++++++++++++++++++- .../databases/database-fetcher.test.ts | 58 +------------------ 4 files changed, 88 insertions(+), 90 deletions(-) diff --git a/extensions/ql-vscode/src/common/files.ts b/extensions/ql-vscode/src/common/files.ts index 80b59a2e92b..2e631ce6f75 100644 --- a/extensions/ql-vscode/src/common/files.ts +++ b/extensions/ql-vscode/src/common/files.ts @@ -176,3 +176,32 @@ export function findCommonParentDir(...paths: string[]): string { function isTopLevelPath(path: string): boolean { return dirname(path) === path; } + +/** + * Recursively looks for a file in a directory. If the file exists, then returns the directory containing the file. + * + * @param dir The directory to search + * @param toFind The file to recursively look for in this directory + * + * @returns the directory containing the file, or undefined if not found. + */ +export async function findDirWithFile( + dir: string, + ...toFind: string[] +): Promise { + if (!(await stat(dir)).isDirectory()) { + return; + } + const files = await readdir(dir); + if (toFind.some((file) => files.includes(file))) { + return dir; + } + for (const file of files) { + const newPath = join(dir, file); + const result = await findDirWithFile(newPath, ...toFind); + if (result) { + return result; + } + } + return; +} diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index 7b66071c912..93d0229052d 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -10,8 +10,6 @@ import { pathExists, createWriteStream, remove, - stat, - readdir, } from "fs-extra"; import { basename, join } from "path"; import type { Octokit } from "@octokit/rest"; @@ -39,6 +37,7 @@ import { getLanguageDisplayName } from "../common/query-language"; import type { DatabaseOrigin } from "./local-databases/database-origin"; import { createTimeoutSignal } from "../common/fetch-stream"; import type { App } from "../common/app"; +import { findDirWithFile } from "../common/files"; /** * Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file. @@ -588,36 +587,6 @@ function isFile(databaseUrl: string) { return Uri.parse(databaseUrl).scheme === "file"; } -/** - * Recursively looks for a file in a directory. If the file exists, then returns the directory containing the file. - * - * @param dir The directory to search - * @param toFind The file to recursively look for in this directory - * - * @returns the directory containing the file, or undefined if not found. - */ -// exported for testing -export async function findDirWithFile( - dir: string, - ...toFind: string[] -): Promise { - if (!(await stat(dir)).isDirectory()) { - return; - } - const files = await readdir(dir); - if (toFind.some((file) => files.includes(file))) { - return dir; - } - for (const file of files) { - const newPath = join(dir, file); - const result = await findDirWithFile(newPath, ...toFind); - if (result) { - return result; - } - } - return; -} - export async function convertGithubNwoToDatabaseUrl( nwo: string, octokit: Octokit, diff --git a/extensions/ql-vscode/test/unit-tests/common/files.test.ts b/extensions/ql-vscode/test/unit-tests/common/files.test.ts index 789b5d52f0f..9c435a3b4c6 100644 --- a/extensions/ql-vscode/test/unit-tests/common/files.test.ts +++ b/extensions/ql-vscode/test/unit-tests/common/files.test.ts @@ -3,6 +3,7 @@ import { join, parse } from "path"; import { containsPath, findCommonParentDir, + findDirWithFile, gatherQlFiles, getDirectoryNamesInsidePath, pathsEqual, @@ -11,7 +12,13 @@ import { } from "../../../src/common/files"; import type { DirResult } from "tmp"; import { dirSync } from "tmp"; -import { ensureDirSync, symlinkSync, writeFileSync } from "fs-extra"; +import { + createFileSync, + ensureDirSync, + mkdirSync, + symlinkSync, + writeFileSync, +} from "fs-extra"; import "../../matchers/toEqualPath"; describe("files", () => { @@ -592,3 +599,52 @@ describe("findCommonParentDir", () => { expect(commonDir).toEqualPath(dataDir); }); }); + +describe("findDirWithFile", () => { + let dir: DirResult; + beforeEach(() => { + dir = dirSync({ unsafeCleanup: true }); + createFile("a"); + createFile("b"); + createFile("c"); + + createDir("dir1"); + createFile("dir1", "d"); + createFile("dir1", "e"); + createFile("dir1", "f"); + + createDir("dir2"); + createFile("dir2", "g"); + createFile("dir2", "h"); + createFile("dir2", "i"); + + createDir("dir2", "dir3"); + createFile("dir2", "dir3", "j"); + createFile("dir2", "dir3", "k"); + createFile("dir2", "dir3", "l"); + }); + + it("should find files", async () => { + expect(await findDirWithFile(dir.name, "k")).toBe( + join(dir.name, "dir2", "dir3"), + ); + expect(await findDirWithFile(dir.name, "h")).toBe(join(dir.name, "dir2")); + expect(await findDirWithFile(dir.name, "z", "a")).toBe(dir.name); + // there's some slight indeterminism when more than one name exists + // but in general, this will find files in the current directory before + // finding files in sub-dirs + expect(await findDirWithFile(dir.name, "k", "a")).toBe(dir.name); + }); + + it("should not find files", async () => { + expect(await findDirWithFile(dir.name, "x", "y", "z")).toBeUndefined(); + }); + + function createFile(...segments: string[]) { + createFileSync(join(dir.name, ...segments)); + } + + function createDir(...segments: string[]) { + mkdirSync(join(dir.name, ...segments)); + } +}); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/database-fetcher.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/database-fetcher.test.ts index 4d38cb81ec9..f96ef011e5c 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/database-fetcher.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/database-fetcher.test.ts @@ -1,13 +1,6 @@ -import { join } from "path"; -import { createFileSync, mkdirSync } from "fs-extra"; -import type { DirResult } from "tmp"; -import { dirSync } from "tmp"; import { window } from "vscode"; -import { - convertGithubNwoToDatabaseUrl, - findDirWithFile, -} from "../../../../src/databases/database-fetcher"; +import { convertGithubNwoToDatabaseUrl } from "../../../../src/databases/database-fetcher"; import type { Octokit } from "@octokit/rest"; import { mockedObject, @@ -201,53 +194,4 @@ describe("database-fetcher", () => { }); }); }); - - describe("findDirWithFile", () => { - let dir: DirResult; - beforeEach(() => { - dir = dirSync({ unsafeCleanup: true }); - createFile("a"); - createFile("b"); - createFile("c"); - - createDir("dir1"); - createFile("dir1", "d"); - createFile("dir1", "e"); - createFile("dir1", "f"); - - createDir("dir2"); - createFile("dir2", "g"); - createFile("dir2", "h"); - createFile("dir2", "i"); - - createDir("dir2", "dir3"); - createFile("dir2", "dir3", "j"); - createFile("dir2", "dir3", "k"); - createFile("dir2", "dir3", "l"); - }); - - it("should find files", async () => { - expect(await findDirWithFile(dir.name, "k")).toBe( - join(dir.name, "dir2", "dir3"), - ); - expect(await findDirWithFile(dir.name, "h")).toBe(join(dir.name, "dir2")); - expect(await findDirWithFile(dir.name, "z", "a")).toBe(dir.name); - // there's some slight indeterminism when more than one name exists - // but in general, this will find files in the current directory before - // finding files in sub-dirs - expect(await findDirWithFile(dir.name, "k", "a")).toBe(dir.name); - }); - - it("should not find files", async () => { - expect(await findDirWithFile(dir.name, "x", "y", "z")).toBeUndefined(); - }); - - function createFile(...segments: string[]) { - createFileSync(join(dir.name, ...segments)); - } - - function createDir(...segments: string[]) { - mkdirSync(join(dir.name, ...segments)); - } - }); }); From 66879ef6261e68c498a50be3d7677e57fd9ee518 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 7 Mar 2024 12:46:26 +0000 Subject: [PATCH 0098/1237] Move convertGithubNwoToDatabaseUrl to databases/github-databases/api.ts --- .../src/databases/database-fetcher.ts | 91 +------- .../src/databases/github-databases/api.ts | 94 +++++++++ .../databases/database-fetcher.test.ts | 197 ------------------ .../databases/github-databases/api.test.ts | 190 ++++++++++++++++- 4 files changed, 284 insertions(+), 288 deletions(-) delete mode 100644 extensions/ql-vscode/test/vscode-tests/no-workspace/databases/database-fetcher.test.ts diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index 93d0229052d..3e695b38e3b 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -33,11 +33,11 @@ import { } from "../config"; import { showAndLogInformationMessage } from "../common/logging"; import { AppOctokit } from "../common/octokit"; -import { getLanguageDisplayName } from "../common/query-language"; import type { DatabaseOrigin } from "./local-databases/database-origin"; import { createTimeoutSignal } from "../common/fetch-stream"; import type { App } from "../common/app"; import { findDirWithFile } from "../common/files"; +import { convertGithubNwoToDatabaseUrl } from "./github-databases/api"; /** * Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file. @@ -587,95 +587,6 @@ function isFile(databaseUrl: string) { return Uri.parse(databaseUrl).scheme === "file"; } -export async function convertGithubNwoToDatabaseUrl( - nwo: string, - octokit: Octokit, - progress: ProgressCallback, - language?: string, -): Promise< - | { - databaseUrl: string; - owner: string; - name: string; - databaseId: number; - databaseCreatedAt: string; - commitOid: string | null; - } - | undefined -> { - try { - const [owner, repo] = nwo.split("/"); - - const response = await octokit.rest.codeScanning.listCodeqlDatabases({ - owner, - repo, - }); - - const languages = response.data.map((db) => db.language); - - if (!language || !languages.includes(language)) { - language = await promptForLanguage(languages, progress); - if (!language) { - return; - } - } - - const databaseForLanguage = response.data.find( - (db) => db.language === language, - ); - if (!databaseForLanguage) { - throw new Error(`No database found for language '${language}'`); - } - - return { - databaseUrl: databaseForLanguage.url, - owner, - name: repo, - databaseId: databaseForLanguage.id, - databaseCreatedAt: databaseForLanguage.created_at, - commitOid: databaseForLanguage.commit_oid ?? null, - }; - } catch (e) { - void extLogger.log(`Error: ${getErrorMessage(e)}`); - throw new Error(`Unable to get database for '${nwo}'`); - } -} - -export async function promptForLanguage( - languages: string[], - progress: ProgressCallback | undefined, -): Promise { - progress?.({ - message: "Choose language", - step: 2, - maxStep: 2, - }); - if (!languages.length) { - throw new Error("No databases found"); - } - if (languages.length === 1) { - return languages[0]; - } - - const items = languages - .map((language) => ({ - label: getLanguageDisplayName(language), - description: language, - language, - })) - .sort((a, b) => a.label.localeCompare(b.label)); - - const selectedItem = await window.showQuickPick(items, { - placeHolder: "Select the database language to download:", - ignoreFocusOut: true, - }); - if (!selectedItem) { - return undefined; - } - - return selectedItem.language; -} - /** * Databases created by the old odasa tool will not have a zipped * source location. However, this extension works better if sources diff --git a/extensions/ql-vscode/src/databases/github-databases/api.ts b/extensions/ql-vscode/src/databases/github-databases/api.ts index bfcffa9dd4a..186b1a9c288 100644 --- a/extensions/ql-vscode/src/databases/github-databases/api.ts +++ b/extensions/ql-vscode/src/databases/github-databases/api.ts @@ -5,6 +5,11 @@ import { showNeverAskAgainDialog } from "../../common/vscode/dialog"; import type { GitHubDatabaseConfig } from "../../config"; import type { Credentials } from "../../common/authentication"; import { AppOctokit } from "../../common/octokit"; +import type { ProgressCallback } from "../../common/vscode/progress"; +import { getErrorMessage } from "../../common/helpers-pure"; +import { getLanguageDisplayName } from "../../common/query-language"; +import { window } from "vscode"; +import { extLogger } from "../../common/logging/vscode"; export type CodeqlDatabase = RestEndpointMethodTypes["codeScanning"]["listCodeqlDatabases"]["response"]["data"][number]; @@ -108,3 +113,92 @@ export async function listDatabases( octokit, }; } + +export async function convertGithubNwoToDatabaseUrl( + nwo: string, + octokit: Octokit, + progress: ProgressCallback, + language?: string, +): Promise< + | { + databaseUrl: string; + owner: string; + name: string; + databaseId: number; + databaseCreatedAt: string; + commitOid: string | null; + } + | undefined +> { + try { + const [owner, repo] = nwo.split("/"); + + const response = await octokit.rest.codeScanning.listCodeqlDatabases({ + owner, + repo, + }); + + const languages = response.data.map((db) => db.language); + + if (!language || !languages.includes(language)) { + language = await promptForLanguage(languages, progress); + if (!language) { + return; + } + } + + const databaseForLanguage = response.data.find( + (db) => db.language === language, + ); + if (!databaseForLanguage) { + throw new Error(`No database found for language '${language}'`); + } + + return { + databaseUrl: databaseForLanguage.url, + owner, + name: repo, + databaseId: databaseForLanguage.id, + databaseCreatedAt: databaseForLanguage.created_at, + commitOid: databaseForLanguage.commit_oid ?? null, + }; + } catch (e) { + void extLogger.log(`Error: ${getErrorMessage(e)}`); + throw new Error(`Unable to get database for '${nwo}'`); + } +} + +async function promptForLanguage( + languages: string[], + progress: ProgressCallback | undefined, +): Promise { + progress?.({ + message: "Choose language", + step: 2, + maxStep: 2, + }); + if (!languages.length) { + throw new Error("No databases found"); + } + if (languages.length === 1) { + return languages[0]; + } + + const items = languages + .map((language) => ({ + label: getLanguageDisplayName(language), + description: language, + language, + })) + .sort((a, b) => a.label.localeCompare(b.label)); + + const selectedItem = await window.showQuickPick(items, { + placeHolder: "Select the database language to download:", + ignoreFocusOut: true, + }); + if (!selectedItem) { + return undefined; + } + + return selectedItem.language; +} diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/database-fetcher.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/database-fetcher.test.ts deleted file mode 100644 index f96ef011e5c..00000000000 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/database-fetcher.test.ts +++ /dev/null @@ -1,197 +0,0 @@ -import { window } from "vscode"; - -import { convertGithubNwoToDatabaseUrl } from "../../../../src/databases/database-fetcher"; -import type { Octokit } from "@octokit/rest"; -import { - mockedObject, - mockedOctokitFunction, - mockedQuickPickItem, -} from "../../utils/mocking.helpers"; - -// These tests make API calls and may need extra time to complete. -jest.setTimeout(10000); - -describe("database-fetcher", () => { - describe("convertGithubNwoToDatabaseUrl", () => { - let quickPickSpy: jest.SpiedFunction; - - const progressSpy = jest.fn(); - const mockListCodeqlDatabases = mockedOctokitFunction< - "codeScanning", - "listCodeqlDatabases" - >(); - const octokit = mockedObject({ - rest: { - codeScanning: { - listCodeqlDatabases: mockListCodeqlDatabases, - }, - }, - }); - - // We can't make the real octokit request (since we need credentials), so we mock the response. - const successfullMockApiResponse = { - data: [ - { - id: 1495869, - name: "csharp-database", - language: "csharp", - uploader: {}, - content_type: "application/zip", - state: "uploaded", - size: 55599715, - created_at: "2022-03-24T10:46:24Z", - updated_at: "2022-03-24T10:46:27Z", - url: "https://api.github.com/repositories/143040428/code-scanning/codeql/databases/csharp", - }, - { - id: 1100671, - name: "database.zip", - language: "javascript", - uploader: {}, - content_type: "application/zip", - state: "uploaded", - size: 29294434, - created_at: "2022-03-01T16:00:04Z", - updated_at: "2022-03-01T16:00:06Z", - url: "https://api.github.com/repositories/143040428/code-scanning/codeql/databases/javascript", - }, - { - id: 648738, - name: "ql-database", - language: "ql", - uploader: {}, - content_type: "application/json; charset=utf-8", - state: "uploaded", - size: 39735500, - created_at: "2022-02-02T09:38:50Z", - updated_at: "2022-02-02T09:38:51Z", - url: "https://api.github.com/repositories/143040428/code-scanning/codeql/databases/ql", - }, - ], - }; - - beforeEach(() => { - quickPickSpy = jest - .spyOn(window, "showQuickPick") - .mockResolvedValue(undefined); - }); - - it("should convert a GitHub nwo to a database url", async () => { - mockListCodeqlDatabases.mockResolvedValue(successfullMockApiResponse); - quickPickSpy.mockResolvedValue( - mockedQuickPickItem({ - label: "JavaScript", - language: "javascript", - }), - ); - const githubRepo = "github/codeql"; - const result = await convertGithubNwoToDatabaseUrl( - githubRepo, - octokit, - progressSpy, - ); - expect(result).toBeDefined(); - if (result === undefined) { - return; - } - - const { databaseUrl, name, owner } = result; - - expect(databaseUrl).toBe( - "https://api.github.com/repositories/143040428/code-scanning/codeql/databases/javascript", - ); - expect(name).toBe("codeql"); - expect(owner).toBe("github"); - expect(quickPickSpy).toHaveBeenNthCalledWith( - 1, - [ - expect.objectContaining({ - label: "C#", - description: "csharp", - language: "csharp", - }), - expect.objectContaining({ - label: "JavaScript", - description: "javascript", - language: "javascript", - }), - expect.objectContaining({ - label: "ql", - description: "ql", - language: "ql", - }), - ], - expect.anything(), - ); - }); - - // Repository doesn't exist, or the user has no access to the repository. - it("should fail on an invalid/inaccessible repository", async () => { - const mockApiResponse = { - data: { - message: "Not Found", - }, - status: 404, - }; - mockListCodeqlDatabases.mockResolvedValue(mockApiResponse); - const githubRepo = "foo/bar-not-real"; - await expect( - convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy), - ).rejects.toThrow(/Unable to get database/); - expect(progressSpy).toHaveBeenCalledTimes(0); - }); - - // User has access to the repository, but there are no databases for any language. - it("should fail on a repository with no databases", async () => { - const mockApiResponse = { - data: [], - }; - - mockListCodeqlDatabases.mockResolvedValue(mockApiResponse); - const githubRepo = "foo/bar-with-no-dbs"; - await expect( - convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy), - ).rejects.toThrow(/Unable to get database/); - expect(progressSpy).toHaveBeenCalledTimes(1); - }); - - describe("when language is already provided", () => { - describe("when language is valid", () => { - it("should not prompt the user", async () => { - mockListCodeqlDatabases.mockResolvedValue(successfullMockApiResponse); - const githubRepo = "github/codeql"; - await convertGithubNwoToDatabaseUrl( - githubRepo, - octokit, - progressSpy, - "javascript", - ); - expect(quickPickSpy).not.toHaveBeenCalled(); - }); - }); - - describe("when language is invalid", () => { - it("should prompt for language", async () => { - mockListCodeqlDatabases.mockResolvedValue(successfullMockApiResponse); - const githubRepo = "github/codeql"; - await convertGithubNwoToDatabaseUrl( - githubRepo, - octokit, - progressSpy, - "invalid-language", - ); - expect(quickPickSpy).toHaveBeenCalled(); - }); - }); - }); - - describe("when language is not provided", () => { - it("should prompt for language", async () => { - mockListCodeqlDatabases.mockResolvedValue(successfullMockApiResponse); - const githubRepo = "github/codeql"; - await convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy); - expect(quickPickSpy).toHaveBeenCalled(); - }); - }); - }); -}); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/api.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/api.test.ts index f5dbadd6c94..ed1e9f031a8 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/api.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/api.test.ts @@ -1,14 +1,19 @@ import { mockedObject, mockedOctokitFunction, + mockedQuickPickItem, } from "../../../utils/mocking.helpers"; import type { GitHubDatabaseConfig } from "../../../../../src/config"; import * as dialog from "../../../../../src/common/vscode/dialog"; -import { listDatabases } from "../../../../../src/databases/github-databases/api"; +import { + convertGithubNwoToDatabaseUrl, + listDatabases, +} from "../../../../../src/databases/github-databases/api"; import type { Credentials } from "../../../../../src/common/authentication"; import type { Octokit } from "@octokit/rest"; import { AppOctokit } from "../../../../../src/common/octokit"; import { RequestError } from "@octokit/request-error"; +import { window } from "vscode"; // Mock the AppOctokit constructor to ensure we aren't making any network requests jest.mock("../../../../../src/common/octokit", () => ({ @@ -349,3 +354,186 @@ describe("listDatabases", () => { }); }); }); + +describe("convertGithubNwoToDatabaseUrl", () => { + let quickPickSpy: jest.SpiedFunction; + + const progressSpy = jest.fn(); + const mockListCodeqlDatabases = mockedOctokitFunction< + "codeScanning", + "listCodeqlDatabases" + >(); + const octokit = mockedObject({ + rest: { + codeScanning: { + listCodeqlDatabases: mockListCodeqlDatabases, + }, + }, + }); + + // We can't make the real octokit request (since we need credentials), so we mock the response. + const successfullMockApiResponse = { + data: [ + { + id: 1495869, + name: "csharp-database", + language: "csharp", + uploader: {}, + content_type: "application/zip", + state: "uploaded", + size: 55599715, + created_at: "2022-03-24T10:46:24Z", + updated_at: "2022-03-24T10:46:27Z", + url: "https://api.github.com/repositories/143040428/code-scanning/codeql/databases/csharp", + }, + { + id: 1100671, + name: "database.zip", + language: "javascript", + uploader: {}, + content_type: "application/zip", + state: "uploaded", + size: 29294434, + created_at: "2022-03-01T16:00:04Z", + updated_at: "2022-03-01T16:00:06Z", + url: "https://api.github.com/repositories/143040428/code-scanning/codeql/databases/javascript", + }, + { + id: 648738, + name: "ql-database", + language: "ql", + uploader: {}, + content_type: "application/json; charset=utf-8", + state: "uploaded", + size: 39735500, + created_at: "2022-02-02T09:38:50Z", + updated_at: "2022-02-02T09:38:51Z", + url: "https://api.github.com/repositories/143040428/code-scanning/codeql/databases/ql", + }, + ], + }; + + beforeEach(() => { + quickPickSpy = jest + .spyOn(window, "showQuickPick") + .mockResolvedValue(undefined); + }); + + it("should convert a GitHub nwo to a database url", async () => { + mockListCodeqlDatabases.mockResolvedValue(successfullMockApiResponse); + quickPickSpy.mockResolvedValue( + mockedQuickPickItem({ + label: "JavaScript", + language: "javascript", + }), + ); + const githubRepo = "github/codeql"; + const result = await convertGithubNwoToDatabaseUrl( + githubRepo, + octokit, + progressSpy, + ); + expect(result).toBeDefined(); + if (result === undefined) { + return; + } + + const { databaseUrl, name, owner } = result; + + expect(databaseUrl).toBe( + "https://api.github.com/repositories/143040428/code-scanning/codeql/databases/javascript", + ); + expect(name).toBe("codeql"); + expect(owner).toBe("github"); + expect(quickPickSpy).toHaveBeenNthCalledWith( + 1, + [ + expect.objectContaining({ + label: "C#", + description: "csharp", + language: "csharp", + }), + expect.objectContaining({ + label: "JavaScript", + description: "javascript", + language: "javascript", + }), + expect.objectContaining({ + label: "ql", + description: "ql", + language: "ql", + }), + ], + expect.anything(), + ); + }); + + // Repository doesn't exist, or the user has no access to the repository. + it("should fail on an invalid/inaccessible repository", async () => { + const mockApiResponse = { + data: { + message: "Not Found", + }, + status: 404, + }; + mockListCodeqlDatabases.mockResolvedValue(mockApiResponse); + const githubRepo = "foo/bar-not-real"; + await expect( + convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy), + ).rejects.toThrow(/Unable to get database/); + expect(progressSpy).toHaveBeenCalledTimes(0); + }); + + // User has access to the repository, but there are no databases for any language. + it("should fail on a repository with no databases", async () => { + const mockApiResponse = { + data: [], + }; + + mockListCodeqlDatabases.mockResolvedValue(mockApiResponse); + const githubRepo = "foo/bar-with-no-dbs"; + await expect( + convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy), + ).rejects.toThrow(/Unable to get database/); + expect(progressSpy).toHaveBeenCalledTimes(1); + }); + + describe("when language is already provided", () => { + describe("when language is valid", () => { + it("should not prompt the user", async () => { + mockListCodeqlDatabases.mockResolvedValue(successfullMockApiResponse); + const githubRepo = "github/codeql"; + await convertGithubNwoToDatabaseUrl( + githubRepo, + octokit, + progressSpy, + "javascript", + ); + expect(quickPickSpy).not.toHaveBeenCalled(); + }); + }); + + describe("when language is invalid", () => { + it("should prompt for language", async () => { + mockListCodeqlDatabases.mockResolvedValue(successfullMockApiResponse); + const githubRepo = "github/codeql"; + await convertGithubNwoToDatabaseUrl( + githubRepo, + octokit, + progressSpy, + "invalid-language", + ); + expect(quickPickSpy).toHaveBeenCalled(); + }); + }); + }); + + describe("when language is not provided", () => { + it("should prompt for language", async () => { + mockListCodeqlDatabases.mockResolvedValue(successfullMockApiResponse); + const githubRepo = "github/codeql"; + await convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy); + expect(quickPickSpy).toHaveBeenCalled(); + }); + }); +}); From dbd5078424f8fce759e3ad60324c657f7b781167 Mon Sep 17 00:00:00 2001 From: Shati Patel <42641846+shati-patel@users.noreply.github.com> Date: Thu, 7 Mar 2024 16:48:29 +0000 Subject: [PATCH 0099/1237] Fix warning in package.json about "editor.wordBasedSuggestions" (#3451) --- extensions/ql-vscode/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 5d404057043..1ec7d84b2b6 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -53,10 +53,10 @@ "configurationDefaults": { "[ql]": { "debug.saveBeforeStart": "nonUntitledEditorsInActiveGroup", - "editor.wordBasedSuggestions": false + "editor.wordBasedSuggestions": "off" }, "[dbscheme]": { - "editor.wordBasedSuggestions": false + "editor.wordBasedSuggestions": "off" } }, "debuggers": [ From f426178b96206d178b764e0ba37762693d945494 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 8 Mar 2024 13:45:52 +0100 Subject: [PATCH 0100/1237] Fix incomplete removal of databases --- .../src/databases/database-fetcher.ts | 1 + .../local-databases/database-item-impl.ts | 4 ++++ .../local-databases/database-item.ts | 6 +++++ .../local-databases/database-manager.ts | 24 +++++++++++++++---- .../local-databases/database-options.ts | 2 ++ .../test/factories/databases/databases.ts | 1 + .../local-queries/local-databases.test.ts | 1 + 7 files changed, 35 insertions(+), 4 deletions(-) diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index 7b66071c912..399c94e3f83 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -400,6 +400,7 @@ async function databaseArchiveFetcher( nameOverride, { addSourceArchiveFolder, + extensionManagedLocation: unzipPath, }, ); return item; diff --git a/extensions/ql-vscode/src/databases/local-databases/database-item-impl.ts b/extensions/ql-vscode/src/databases/local-databases/database-item-impl.ts index a2d846c7695..dab6c01d98e 100644 --- a/extensions/ql-vscode/src/databases/local-databases/database-item-impl.ts +++ b/extensions/ql-vscode/src/databases/local-databases/database-item-impl.ts @@ -66,6 +66,10 @@ export class DatabaseItemImpl implements DatabaseItem { return this.options.origin; } + public get extensionManagedLocation(): string | undefined { + return this.options.extensionManagedLocation; + } + public resolveSourceFile(uriStr: string | undefined): Uri { const sourceArchive = this.sourceArchive; const uri = uriStr ? Uri.parse(uriStr, true) : undefined; diff --git a/extensions/ql-vscode/src/databases/local-databases/database-item.ts b/extensions/ql-vscode/src/databases/local-databases/database-item.ts index 7feec47890d..72f5a37eed7 100644 --- a/extensions/ql-vscode/src/databases/local-databases/database-item.ts +++ b/extensions/ql-vscode/src/databases/local-databases/database-item.ts @@ -31,6 +31,12 @@ export interface DatabaseItem { */ readonly origin: DatabaseOrigin | undefined; + /** + * The location of the base storage location as managed by the extension, or undefined + * if unknown or not managed by the extension. + */ + readonly extensionManagedLocation: string | undefined; + /** If the database is invalid, describes why. */ readonly error: Error | undefined; diff --git a/extensions/ql-vscode/src/databases/local-databases/database-manager.ts b/extensions/ql-vscode/src/databases/local-databases/database-manager.ts index c78a91b9194..9454355e630 100644 --- a/extensions/ql-vscode/src/databases/local-databases/database-manager.ts +++ b/extensions/ql-vscode/src/databases/local-databases/database-manager.ts @@ -82,6 +82,10 @@ function eventFired( } type OpenDatabaseOptions = { + /** + * A location that is managed by the extension. + */ + extensionManagedLocation?: string; isTutorialDatabase?: boolean; /** * Whether to add a workspace folder containing the source archive to the workspace. Default is true. @@ -141,6 +145,7 @@ export class DatabaseManager extends DisposableObject { makeSelected = true, displayName?: string, { + extensionManagedLocation, isTutorialDatabase = false, addSourceArchiveFolder = addDatabaseSourceToWorkspace(), }: OpenDatabaseOptions = {}, @@ -149,6 +154,7 @@ export class DatabaseManager extends DisposableObject { uri, origin, displayName, + extensionManagedLocation, ); return await this.addExistingDatabaseItem( @@ -202,6 +208,7 @@ export class DatabaseManager extends DisposableObject { uri: vscode.Uri, origin: DatabaseOrigin | undefined, displayName: string | undefined, + extensionManagedLocation?: string, ): Promise { const contents = await DatabaseResolver.resolveDatabaseContents(uri); const fullOptions: FullDatabaseOptions = { @@ -210,6 +217,7 @@ export class DatabaseManager extends DisposableObject { dateAdded: Date.now(), language: await this.getPrimaryLanguage(uri.fsPath), origin, + extensionManagedLocation, }; const databaseItem = new DatabaseItemImpl(uri, contents, fullOptions); @@ -370,6 +378,7 @@ export class DatabaseManager extends DisposableObject { let dateAdded = undefined; let language = undefined; let origin = undefined; + let extensionManagedLocation = undefined; if (state.options) { if (typeof state.options.displayName === "string") { displayName = state.options.displayName; @@ -379,6 +388,7 @@ export class DatabaseManager extends DisposableObject { } language = state.options.language; origin = state.options.origin; + extensionManagedLocation = state.options.extensionManagedLocation; } const dbBaseUri = vscode.Uri.parse(state.uri, true); @@ -392,6 +402,7 @@ export class DatabaseManager extends DisposableObject { dateAdded, language, origin, + extensionManagedLocation, }; const item = new DatabaseItemImpl(dbBaseUri, undefined, fullOptions); @@ -583,15 +594,20 @@ export class DatabaseManager extends DisposableObject { // Remove this database item from the allow-list await this.deregisterDatabase(item); + // Find whether we know directly which directory we should remove + const directoryToRemove = item.extensionManagedLocation + ? vscode.Uri.file(item.extensionManagedLocation) + : item.databaseUri; + // Delete folder from file system only if it is controlled by the extension - if (this.isExtensionControlledLocation(item.databaseUri)) { + if (this.isExtensionControlledLocation(directoryToRemove)) { void extLogger.log("Deleting database from filesystem."); - await remove(item.databaseUri.fsPath).then( - () => void extLogger.log(`Deleted '${item.databaseUri.fsPath}'`), + await remove(directoryToRemove.fsPath).then( + () => void extLogger.log(`Deleted '${directoryToRemove.fsPath}'`), (e: unknown) => void extLogger.log( `Failed to delete '${ - item.databaseUri.fsPath + directoryToRemove.fsPath }'. Reason: ${getErrorMessage(e)}`, ), ); diff --git a/extensions/ql-vscode/src/databases/local-databases/database-options.ts b/extensions/ql-vscode/src/databases/local-databases/database-options.ts index 68146d16683..25b8e6a4bf4 100644 --- a/extensions/ql-vscode/src/databases/local-databases/database-options.ts +++ b/extensions/ql-vscode/src/databases/local-databases/database-options.ts @@ -5,10 +5,12 @@ export interface DatabaseOptions { dateAdded?: number | undefined; language?: string; origin?: DatabaseOrigin; + extensionManagedLocation?: string; } export interface FullDatabaseOptions extends DatabaseOptions { dateAdded: number | undefined; language: string | undefined; origin: DatabaseOrigin | undefined; + extensionManagedLocation: string | undefined; } diff --git a/extensions/ql-vscode/test/factories/databases/databases.ts b/extensions/ql-vscode/test/factories/databases/databases.ts index e4a970791f7..60a533b32f8 100644 --- a/extensions/ql-vscode/test/factories/databases/databases.ts +++ b/extensions/ql-vscode/test/factories/databases/databases.ts @@ -14,6 +14,7 @@ export function mockDbOptions(): FullDatabaseOptions { origin: { type: "folder", }, + extensionManagedLocation: undefined, }; } diff --git a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts index 1fdfa67dba5..d0c6fda51e3 100644 --- a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts @@ -604,6 +604,7 @@ describe("local databases", () => { origin: { type: "folder", }, + extensionManagedLocation: undefined, }; mockDbItem = createMockDB(dir, options); From 2c35a97b09c0769a921c677c33cf184ad662b5aa Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 8 Mar 2024 14:56:06 +0100 Subject: [PATCH 0101/1237] Make database storage paths more unique --- .../src/databases/database-fetcher.ts | 57 +++++++++++++++---- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index 7b66071c912..db278976507 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -15,6 +15,7 @@ import { } from "fs-extra"; import { basename, join } from "path"; import type { Octokit } from "@octokit/rest"; +import { nanoid } from "nanoid"; import type { DatabaseManager, DatabaseItem } from "./local-databases"; import { tmpDir } from "../tmp-dir"; @@ -365,7 +366,11 @@ async function databaseArchiveFetcher( throw new Error("No storage path specified."); } await ensureDir(storagePath); - const unzipPath = await getStorageFolder(storagePath, databaseUrl); + const unzipPath = await getStorageFolder( + storagePath, + databaseUrl, + nameOverride, + ); if (isFile(databaseUrl)) { await readAndUnzip(databaseUrl, unzipPath, cli, progress); @@ -408,16 +413,41 @@ async function databaseArchiveFetcher( } } -async function getStorageFolder(storagePath: string, urlStr: string) { - // we need to generate a folder name for the unzipped archive, - // this needs to be human readable since we may use this name as the initial - // name for the database - const url = Uri.parse(urlStr); - // MacOS has a max filename length of 255 - // and remove a few extra chars in case we need to add a counter at the end. - let lastName = basename(url.path).substring(0, 250); - if (lastName.endsWith(".zip")) { - lastName = lastName.substring(0, lastName.length - 4); +async function getStorageFolder( + storagePath: string, + urlStr: string, + nameOverrride?: string, +) { + let lastName: string; + + if (nameOverrride) { + // Lowercase everything + let name = nameOverrride.toLowerCase(); + + // Replace all spaces, dots, underscores, and forward slashes with hyphens + name = name.replaceAll(/[\s._/]+/g, "-"); + + // Replace all characters which are not allowed by empty strings + name = name.replaceAll(/[^a-z0-9-]/g, ""); + + // Remove any leading or trailing hyphens + name = name.replaceAll(/^-|-$/g, ""); + + // Remove any duplicate hyphens + name = name.replaceAll(/-{2,}/g, "-"); + + lastName = name; + } else { + // we need to generate a folder name for the unzipped archive, + // this needs to be human readable since we may use this name as the initial + // name for the database + const url = Uri.parse(urlStr); + // MacOS has a max filename length of 255 + // and remove a few extra chars in case we need to add a counter at the end. + lastName = basename(url.path).substring(0, 250); + if (lastName.endsWith(".zip")) { + lastName = lastName.substring(0, lastName.length - 4); + } } const realpath = await fs_realpath(storagePath); @@ -429,6 +459,11 @@ async function getStorageFolder(storagePath: string, urlStr: string) { counter++; folderName = join(realpath, `${lastName}-${counter}`); if (counter > 100) { + // If there are more than 100 similarly named databases, + // give up on using a counter and use a random string instead. + folderName = join(realpath, `${lastName}-${nanoid()}`); + } + if (counter > 200) { throw new Error("Could not find a unique name for downloaded database."); } } From 35267bb3fd2b9322047f6e30987acdbdae74debf Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 8 Mar 2024 16:12:55 +0100 Subject: [PATCH 0102/1237] Use suite file instead of manually filtering queries --- .../extension-pack-metadata.schema.json | 3 + .../src/packaging/qlpack-file.schema.json | 3 + .../src/packaging/suite-instruction.ts | 1 + .../variant-analysis/code-scanning-pack.ts | 56 +++++++++---------- 4 files changed, 33 insertions(+), 30 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/extension-pack-metadata.schema.json b/extensions/ql-vscode/src/model-editor/extension-pack-metadata.schema.json index ad26cc5c80a..3f69c99b5ad 100644 --- a/extensions/ql-vscode/src/model-editor/extension-pack-metadata.schema.json +++ b/extensions/ql-vscode/src/model-editor/extension-pack-metadata.schema.json @@ -111,6 +111,9 @@ "description": { "type": "string" }, + "import": { + "type": "string" + }, "from": { "type": "string" } diff --git a/extensions/ql-vscode/src/packaging/qlpack-file.schema.json b/extensions/ql-vscode/src/packaging/qlpack-file.schema.json index b17c5d14605..479c0bd323d 100644 --- a/extensions/ql-vscode/src/packaging/qlpack-file.schema.json +++ b/extensions/ql-vscode/src/packaging/qlpack-file.schema.json @@ -111,6 +111,9 @@ "description": { "type": "string" }, + "import": { + "type": "string" + }, "from": { "type": "string" } diff --git a/extensions/ql-vscode/src/packaging/suite-instruction.ts b/extensions/ql-vscode/src/packaging/suite-instruction.ts index cbab66e8704..6aedcdd9647 100644 --- a/extensions/ql-vscode/src/packaging/suite-instruction.ts +++ b/extensions/ql-vscode/src/packaging/suite-instruction.ts @@ -8,5 +8,6 @@ export interface SuiteInstruction { include?: Record; exclude?: Record; description?: string; + import?: string; from?: string; } diff --git a/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts b/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts index 4da94def558..fde97e49963 100644 --- a/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts +++ b/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts @@ -1,10 +1,13 @@ import { join } from "path"; +import { outputFile } from "fs-extra"; +import { dump } from "js-yaml"; +import { file } from "tmp-promise"; import type { BaseLogger } from "../common/logging"; import type { QueryLanguage } from "../common/query-language"; import type { CodeQLCliServer } from "../codeql-cli/cli"; import type { QlPackDetails } from "./ql-pack-details"; import { getQlPackFilePath } from "../common/ql"; -import { isSarifResultsQueryKind } from "../common/query-metadata"; +import type { SuiteInstruction } from "../packaging/suite-instruction"; export async function resolveCodeScanningQueryPack( logger: BaseLogger, @@ -25,20 +28,30 @@ export async function resolveCodeScanningQueryPack( // Resolve queries void logger.log(`Resolving queries for pack: ${packName}`); - const suitePath = join( - packDir, - "codeql-suites", - `${language}-code-scanning.qls`, - ); + + const suiteFile = await file({ + postfix: ".qls", + }); + const suitePath = suiteFile.path; + const suiteYaml: SuiteInstruction[] = [ + { + import: `codeql-suites/${language}-code-scanning.qls`, + from: `${downloadedPack.name}@${downloadedPack.version}`, + }, + // Exclude any non-problem queries + { + exclude: { + kind: ["diagnostic", "metric"], + }, + }, + ]; + await outputFile(suitePath, dump(suiteYaml), "utf8"); + const resolvedQueries = await cliServer.resolveQueries(suitePath); - const problemQueries = await filterToOnlyProblemQueries( - logger, - cliServer, - resolvedQueries, - ); + await suiteFile.cleanup(); - if (problemQueries.length === 0) { + if (resolvedQueries.length === 0) { throw Error( `No problem queries found in published query pack: ${packName}.`, ); @@ -48,7 +61,7 @@ export async function resolveCodeScanningQueryPack( const qlPackFilePath = await getQlPackFilePath(packDir); const qlPackDetails: QlPackDetails = { - queryFiles: problemQueries, + queryFiles: resolvedQueries, qlPackRootPath: packDir, qlPackFilePath, language, @@ -56,20 +69,3 @@ export async function resolveCodeScanningQueryPack( return qlPackDetails; } - -async function filterToOnlyProblemQueries( - logger: BaseLogger, - cliServer: CodeQLCliServer, - queries: string[], -): Promise { - const problemQueries: string[] = []; - for (const query of queries) { - const queryMetadata = await cliServer.resolveMetadata(query); - if (isSarifResultsQueryKind(queryMetadata.kind)) { - problemQueries.push(query); - } else { - void logger.log(`Skipping non-problem query ${query}`); - } - } - return problemQueries; -} From 82741e95c35a8bb9cbbe790ae41a727ddf9cd404 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 11 Mar 2024 11:15:15 +0100 Subject: [PATCH 0103/1237] Add test for extension-managed db location --- .../test/factories/databases/databases.ts | 14 +++++++-- .../local-queries/local-databases.test.ts | 31 +++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/extensions/ql-vscode/test/factories/databases/databases.ts b/extensions/ql-vscode/test/factories/databases/databases.ts index 60a533b32f8..6db032dbeeb 100644 --- a/extensions/ql-vscode/test/factories/databases/databases.ts +++ b/extensions/ql-vscode/test/factories/databases/databases.ts @@ -19,7 +19,7 @@ export function mockDbOptions(): FullDatabaseOptions { } export function createMockDB( - dir: DirResult, + dir: DirResult | string, dbOptions = mockDbOptions(), // source archive location must be a real(-ish) location since // tests will add this to the workspace location @@ -39,10 +39,18 @@ export function createMockDB( ); } -export function sourceLocationUri(dir: DirResult) { +export function sourceLocationUri(dir: DirResult | string) { + if (typeof dir === "string") { + return Uri.file(join(dir, "src.zip")); + } + return Uri.file(join(dir.name, "src.zip")); } -export function dbLocationUri(dir: DirResult) { +export function dbLocationUri(dir: DirResult | string) { + if (typeof dir === "string") { + return Uri.file(join(dir, "db")); + } + return Uri.file(join(dir.name, "db")); } diff --git a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts index d0c6fda51e3..25c560e80b9 100644 --- a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts @@ -242,6 +242,37 @@ describe("local databases", () => { await expect(pathExists(mockDbItem.databaseUri.fsPath)).resolves.toBe( false, ); + await expect(pathExists(dir.name)).resolves.toBe(true); + }); + + it("should remove a database item with an extension managed location", async () => { + const dbLocation = join(dir.name, "org-repo-12"); + await ensureDir(dbLocation); + + const mockDbItem = createMockDB(dbLocation, { + ...mockDbOptions(), + extensionManagedLocation: dbLocation, + }); + await ensureDir(mockDbItem.databaseUri.fsPath); + + // pretend that this item is the first workspace folder in the list + jest + .spyOn(mockDbItem, "belongsToSourceArchiveExplorerUri") + .mockReturnValue(true); + + await (databaseManager as any).addDatabaseItem(mockDbItem); + + updateSpy.mockClear(); + + await databaseManager.removeDatabaseItem(mockDbItem); + + expect(databaseManager.databaseItems).toEqual([]); + expect(updateSpy).toHaveBeenCalledWith("databaseList", []); + // should remove the folder + expect(workspace.updateWorkspaceFolders).toHaveBeenCalledWith(0, 1); + + // should delete the complete extension managed location + await expect(pathExists(dbLocation)).resolves.toBe(false); }); it("should remove a database item outside of the extension controlled area", async () => { From e8efbbb9fd6c938c8bf5c08359d7c6c6374aefc4 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 11 Mar 2024 11:31:43 +0100 Subject: [PATCH 0104/1237] Introduce `createFilenameFromString` function --- extensions/ql-vscode/src/common/filenames.ts | 43 ++++++++++ .../src/databases/database-fetcher.ts | 18 +---- .../src/model-editor/extension-pack-name.ts | 27 ++----- extensions/ql-vscode/src/model-editor/yaml.ts | 22 +---- .../test/unit-tests/common/filenames.test.ts | 80 +++++++++++++++++++ 5 files changed, 134 insertions(+), 56 deletions(-) create mode 100644 extensions/ql-vscode/src/common/filenames.ts create mode 100644 extensions/ql-vscode/test/unit-tests/common/filenames.test.ts diff --git a/extensions/ql-vscode/src/common/filenames.ts b/extensions/ql-vscode/src/common/filenames.ts new file mode 100644 index 00000000000..be872f9545b --- /dev/null +++ b/extensions/ql-vscode/src/common/filenames.ts @@ -0,0 +1,43 @@ +type FilenameOptions = { + removeDots?: boolean; +}; + +/** + * This will create a filename from an arbitrary string by removing + * all characters which are not allowed in filenames and making them + * more filesystem-friendly be replacing undesirable characters with + * hyphens. The result will always be lowercase ASCII. + * + * @param str The string to create a filename from + * @param removeDots Whether to remove dots from the filename [default: false] + * @returns The filename + */ +export function createFilenameFromString( + str: string, + { removeDots }: FilenameOptions = {}, +) { + let fileName = str; + + // Lowercase everything + fileName = fileName.toLowerCase(); + + // Replace all spaces, underscores, slashes, and backslashes with hyphens + fileName = fileName.replaceAll(/[\s_/\\]+/g, "-"); + + // Replace all characters which are not allowed by empty strings + fileName = fileName.replaceAll(/[^a-z0-9.-]/g, ""); + + // Remove any leading or trailing hyphens or dots + fileName = fileName.replaceAll(/^[.-]+|[.-]+$/g, ""); + + // Remove any duplicate hyphens + fileName = fileName.replaceAll(/-{2,}/g, "-"); + // Remove any duplicate dots + fileName = fileName.replaceAll(/\.{2,}/g, "."); + + if (removeDots) { + fileName = fileName.replaceAll(/\./g, "-"); + } + + return fileName; +} diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index db278976507..0fb5a80394a 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -40,6 +40,7 @@ import { getLanguageDisplayName } from "../common/query-language"; import type { DatabaseOrigin } from "./local-databases/database-origin"; import { createTimeoutSignal } from "../common/fetch-stream"; import type { App } from "../common/app"; +import { createFilenameFromString } from "../common/filenames"; /** * Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file. @@ -421,22 +422,7 @@ async function getStorageFolder( let lastName: string; if (nameOverrride) { - // Lowercase everything - let name = nameOverrride.toLowerCase(); - - // Replace all spaces, dots, underscores, and forward slashes with hyphens - name = name.replaceAll(/[\s._/]+/g, "-"); - - // Replace all characters which are not allowed by empty strings - name = name.replaceAll(/[^a-z0-9-]/g, ""); - - // Remove any leading or trailing hyphens - name = name.replaceAll(/^-|-$/g, ""); - - // Remove any duplicate hyphens - name = name.replaceAll(/-{2,}/g, "-"); - - lastName = name; + lastName = createFilenameFromString(nameOverrride); } else { // we need to generate a folder name for the unzipped archive, // this needs to be human readable since we may use this name as the initial diff --git a/extensions/ql-vscode/src/model-editor/extension-pack-name.ts b/extensions/ql-vscode/src/model-editor/extension-pack-name.ts index cbd331f5f2e..d35eac36aeb 100644 --- a/extensions/ql-vscode/src/model-editor/extension-pack-name.ts +++ b/extensions/ql-vscode/src/model-editor/extension-pack-name.ts @@ -1,3 +1,5 @@ +import { createFilenameFromString } from "../common/filenames"; + const packNamePartRegex = /[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/; const packNameRegex = new RegExp( `^(?${packNamePartRegex.source})/(?${packNamePartRegex.source})$`, @@ -23,7 +25,11 @@ export function autoNameExtensionPack( } const parts = packName.split("/"); - const sanitizedParts = parts.map((part) => sanitizeExtensionPackName(part)); + const sanitizedParts = parts.map((part) => + createFilenameFromString(part, { + removeDots: true, + }), + ); // If the scope is empty (e.g. if the given name is "-/b"), then we need to still set a scope if (sanitizedParts[0].length === 0) { @@ -37,25 +43,6 @@ export function autoNameExtensionPack( }; } -function sanitizeExtensionPackName(name: string) { - // Lowercase everything - name = name.toLowerCase(); - - // Replace all spaces, dots, and underscores with hyphens - name = name.replaceAll(/[\s._]+/g, "-"); - - // Replace all characters which are not allowed by empty strings - name = name.replaceAll(/[^a-z0-9-]/g, ""); - - // Remove any leading or trailing hyphens - name = name.replaceAll(/^-|-$/g, ""); - - // Remove any duplicate hyphens - name = name.replaceAll(/-{2,}/g, "-"); - - return name; -} - export function parsePackName(packName: string): ExtensionPackName | undefined { const matches = packNameRegex.exec(packName); if (!matches?.groups) { diff --git a/extensions/ql-vscode/src/model-editor/yaml.ts b/extensions/ql-vscode/src/model-editor/yaml.ts index a31df81cd88..89b275b555b 100644 --- a/extensions/ql-vscode/src/model-editor/yaml.ts +++ b/extensions/ql-vscode/src/model-editor/yaml.ts @@ -20,6 +20,7 @@ import type { ModelExtension, ModelExtensionFile, } from "./model-extension-file"; +import { createFilenameFromString } from "../common/filenames"; import type { QueryLanguage } from "../common/query-language"; import modelExtensionFileSchema from "./model-extension-file.schema.json"; @@ -275,26 +276,7 @@ export function createFilenameForLibrary( prefix = "models/", suffix = ".model", ) { - let libraryName = library; - - // Lowercase everything - libraryName = libraryName.toLowerCase(); - - // Replace all spaces and underscores with hyphens - libraryName = libraryName.replaceAll(/[\s_]+/g, "-"); - - // Replace all characters which are not allowed by empty strings - libraryName = libraryName.replaceAll(/[^a-z0-9.-]/g, ""); - - // Remove any leading or trailing hyphens or dots - libraryName = libraryName.replaceAll(/^[.-]+|[.-]+$/g, ""); - - // Remove any duplicate hyphens - libraryName = libraryName.replaceAll(/-{2,}/g, "-"); - // Remove any duplicate dots - libraryName = libraryName.replaceAll(/\.{2,}/g, "."); - - return `${prefix}${libraryName}${suffix}.yml`; + return `${prefix}${createFilenameFromString(library)}${suffix}.yml`; } export function createFilenameForPackage( diff --git a/extensions/ql-vscode/test/unit-tests/common/filenames.test.ts b/extensions/ql-vscode/test/unit-tests/common/filenames.test.ts new file mode 100644 index 00000000000..8d386256c47 --- /dev/null +++ b/extensions/ql-vscode/test/unit-tests/common/filenames.test.ts @@ -0,0 +1,80 @@ +import { createFilenameFromString } from "../../../src/common/filenames"; + +describe("createFilenameFromString", () => { + const testCases: Array<{ + input: string; + filename: string; + filenameWithoutDots?: string; + }> = [ + { + input: "sql2o", + filename: "sql2o", + }, + { + input: "spring-boot", + filename: "spring-boot", + }, + { + input: "spring--boot", + filename: "spring-boot", + }, + { + input: "rt", + filename: "rt", + }, + { + input: "System.Runtime", + filename: "system.runtime", + filenameWithoutDots: "system-runtime", + }, + { + input: "System..Runtime", + filename: "system.runtime", + filenameWithoutDots: "system-runtime", + }, + { + input: "google/brotli", + filename: "google-brotli", + }, + { + input: "github/vscode-codeql", + filename: "github-vscode-codeql", + }, + { + input: "github/vscode---codeql--", + filename: "github-vscode-codeql", + }, + { + input: "github...vscode--c..odeql", + filename: "github.vscode-c.odeql", + filenameWithoutDots: "github-vscode-c-odeql", + }, + { + input: "github\\vscode-codeql", + filename: "github-vscode-codeql", + }, + { + input: "uNetworking/uWebSockets.js", + filename: "unetworking-uwebsockets.js", + filenameWithoutDots: "unetworking-uwebsockets-js", + }, + ]; + + test.each(testCases)( + "returns $filename if string is $input", + ({ input, filename }) => { + expect(createFilenameFromString(input)).toEqual(filename); + }, + ); + + test.each(testCases)( + "returns $filename if string is $input and dots are not allowed", + ({ input, filename, filenameWithoutDots }) => { + expect( + createFilenameFromString(input, { + removeDots: true, + }), + ).toEqual(filenameWithoutDots ?? filename); + }, + ); +}); From dbd5d0f406336f420b03d210adaba271108d4bc2 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 11 Mar 2024 11:02:37 +0000 Subject: [PATCH 0105/1237] Remove unused arguments from resolveWebviewView --- .../vscode/abstract-webview-view-provider.ts | 13 ++----------- .../method-modeling-view-provider.test.ts | 15 +++------------ 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/extensions/ql-vscode/src/common/vscode/abstract-webview-view-provider.ts b/extensions/ql-vscode/src/common/vscode/abstract-webview-view-provider.ts index d82ede247b2..37181e5c549 100644 --- a/extensions/ql-vscode/src/common/vscode/abstract-webview-view-provider.ts +++ b/extensions/ql-vscode/src/common/vscode/abstract-webview-view-provider.ts @@ -1,9 +1,4 @@ -import type { - CancellationToken, - WebviewView, - WebviewViewProvider, - WebviewViewResolveContext, -} from "vscode"; +import type { WebviewView, WebviewViewProvider } from "vscode"; import { Uri } from "vscode"; import type { WebviewKind, WebviewMessage } from "./webview-html"; import { getHtmlForWebview } from "./webview-html"; @@ -28,11 +23,7 @@ export abstract class AbstractWebviewViewProvider< * This is called when a view first becomes visible. This may happen when the view is * first loaded or when the user hides and then shows a view again. */ - public resolveWebviewView( - webviewView: WebviewView, - _context: WebviewViewResolveContext, - _token: CancellationToken, - ) { + public resolveWebviewView(webviewView: WebviewView) { webviewView.webview.options = { enableScripts: true, localResourceRoots: [Uri.file(this.app.extensionPath)], diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/method-modeling/method-modeling-view-provider.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/method-modeling/method-modeling-view-provider.test.ts index 051c626792d..aae9175a49b 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/method-modeling/method-modeling-view-provider.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/method-modeling/method-modeling-view-provider.test.ts @@ -1,10 +1,5 @@ -import type { - Uri, - Webview, - WebviewView, - WebviewViewResolveContext, -} from "vscode"; -import { CancellationTokenSource, EventEmitter } from "vscode"; +import type { Uri, Webview, WebviewView } from "vscode"; +import { EventEmitter } from "vscode"; import type { ModelConfigListener } from "../../../../../src/config"; import { MethodModelingViewProvider } from "../../../../../src/model-editor/method-modeling/method-modeling-view-provider"; import { createMockApp } from "../../../../__mocks__/appMock"; @@ -88,11 +83,7 @@ describe("method modeling view provider", () => { onDidDispose: jest.fn(), }); - viewProvider.resolveWebviewView( - webviewView, - mockedObject({}), - new CancellationTokenSource().token, - ); + viewProvider.resolveWebviewView(webviewView); expect(onDidReceiveMessage).toBeDefined(); }); From 18f9b5fe15b79eed659dea919e42a5de412af2f7 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 11 Mar 2024 11:04:58 +0000 Subject: [PATCH 0106/1237] Use jest.MockedFunction --- .../method-modeling-view-provider.test.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/method-modeling/method-modeling-view-provider.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/method-modeling/method-modeling-view-provider.test.ts index aae9175a49b..6e2c555f8bf 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/method-modeling/method-modeling-view-provider.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/method-modeling/method-modeling-view-provider.test.ts @@ -11,6 +11,7 @@ import { DisposableObject } from "../../../../../src/common/disposable-object"; import type { ModelingEvents } from "../../../../../src/model-editor/modeling-events"; import type { DbModelingState, + ModelingStore, SelectedMethodDetails, } from "../../../../../src/model-editor/modeling-store"; import { mockDatabaseItem } from "../../../utils/mocking.helpers"; @@ -21,10 +22,11 @@ import { describe("method modeling view provider", () => { // Modeling store - let getStateForActiveDb: jest.Mock; - let getSelectedMethodDetails: jest.Mock< - SelectedMethodDetails | undefined, - [] + let getStateForActiveDb: jest.MockedFunction< + ModelingStore["getStateForActiveDb"] + >; + let getSelectedMethodDetails: jest.MockedFunction< + ModelingStore["getSelectedMethodDetails"] >; // Modeling events From fe0136091d37a1d34d5eb15ebe6f152a9eef49db Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 11 Mar 2024 12:41:26 +0100 Subject: [PATCH 0107/1237] Use readdir instead of repeated pathExists calls --- .../src/databases/database-fetcher.ts | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index 0fb5a80394a..fde9fc73e6e 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -437,23 +437,31 @@ async function getStorageFolder( } const realpath = await fs_realpath(storagePath); - let folderName = join(realpath, lastName); + let folderName = lastName; + + // get all existing files instead of calling pathExists on every + // single combination of realpath and folderName + const existingFiles = await readdir(realpath); // avoid overwriting existing folders let counter = 0; - while (await pathExists(folderName)) { + while (existingFiles.includes(basename(folderName))) { counter++; - folderName = join(realpath, `${lastName}-${counter}`); - if (counter > 100) { - // If there are more than 100 similarly named databases, + folderName = `${lastName}-${counter}`; + if (counter > 10_000) { + // If there are more than 10,000 similarly named databases, // give up on using a counter and use a random string instead. - folderName = join(realpath, `${lastName}-${nanoid()}`); + folderName = `${lastName}-${nanoid()}`; } - if (counter > 200) { - throw new Error("Could not find a unique name for downloaded database."); + if (counter > 20_000) { + // This should never happen, but just in case, we don't want to + // get stuck in an infinite loop. + throw new Error( + "Could not find a unique name for downloaded database. Please remove some databases and try again.", + ); } } - return folderName; + return join(realpath, folderName); } function validateUrl(databaseUrl: string) { From 7681a56a169ba8b9d867c0be412703209041e24f Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 11 Mar 2024 14:14:28 +0100 Subject: [PATCH 0108/1237] Fix location of removing dots --- extensions/ql-vscode/src/common/filenames.ts | 9 +++++---- .../ql-vscode/test/unit-tests/common/filenames.test.ts | 5 +++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/extensions/ql-vscode/src/common/filenames.ts b/extensions/ql-vscode/src/common/filenames.ts index be872f9545b..7e621a190df 100644 --- a/extensions/ql-vscode/src/common/filenames.ts +++ b/extensions/ql-vscode/src/common/filenames.ts @@ -30,14 +30,15 @@ export function createFilenameFromString( // Remove any leading or trailing hyphens or dots fileName = fileName.replaceAll(/^[.-]+|[.-]+$/g, ""); + // Replace dots by hyphens if dots are not allowed + if (removeDots) { + fileName = fileName.replaceAll(/\./g, "-"); + } + // Remove any duplicate hyphens fileName = fileName.replaceAll(/-{2,}/g, "-"); // Remove any duplicate dots fileName = fileName.replaceAll(/\.{2,}/g, "."); - if (removeDots) { - fileName = fileName.replaceAll(/\./g, "-"); - } - return fileName; } diff --git a/extensions/ql-vscode/test/unit-tests/common/filenames.test.ts b/extensions/ql-vscode/test/unit-tests/common/filenames.test.ts index 8d386256c47..24d7147f285 100644 --- a/extensions/ql-vscode/test/unit-tests/common/filenames.test.ts +++ b/extensions/ql-vscode/test/unit-tests/common/filenames.test.ts @@ -58,6 +58,11 @@ describe("createFilenameFromString", () => { filename: "unetworking-uwebsockets.js", filenameWithoutDots: "unetworking-uwebsockets-js", }, + { + input: "github/.vscode-codeql", + filename: "github-.vscode-codeql", + filenameWithoutDots: "github-vscode-codeql", + }, ]; test.each(testCases)( From 0e3665b5fce0d7fdf2c17f151debfa784acbc400 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 11 Mar 2024 14:16:53 +0100 Subject: [PATCH 0109/1237] Improve readability of duplicate filename logic --- .../ql-vscode/src/databases/database-fetcher.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index b8b2f5077f0..9f6a58c8fd3 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -415,6 +415,10 @@ async function databaseArchiveFetcher( } } +// The number of tries to use when generating a unique filename before +// giving up and using a nanoid. +const DUPLICATE_FILENAMES_TRIES = 10_000; + async function getStorageFolder( storagePath: string, urlStr: string, @@ -448,14 +452,16 @@ async function getStorageFolder( let counter = 0; while (existingFiles.includes(basename(folderName))) { counter++; - folderName = `${lastName}-${counter}`; - if (counter > 10_000) { + + if (counter <= DUPLICATE_FILENAMES_TRIES) { + // First try to use a counter to make the name unique. + folderName = `${lastName}-${counter}`; + } else if (counter <= 2 * DUPLICATE_FILENAMES_TRIES) { // If there are more than 10,000 similarly named databases, // give up on using a counter and use a random string instead. folderName = `${lastName}-${nanoid()}`; - } - if (counter > 20_000) { - // This should never happen, but just in case, we don't want to + } else { + // This should almost never happen, but just in case, we don't want to // get stuck in an infinite loop. throw new Error( "Could not find a unique name for downloaded database. Please remove some databases and try again.", From e003175f769a39e1f87ad1d7911206b03a0e18bb Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 11 Mar 2024 14:17:16 +0100 Subject: [PATCH 0110/1237] Reduce nanoid tries --- extensions/ql-vscode/src/databases/database-fetcher.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index 9f6a58c8fd3..b40975d3894 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -456,7 +456,7 @@ async function getStorageFolder( if (counter <= DUPLICATE_FILENAMES_TRIES) { // First try to use a counter to make the name unique. folderName = `${lastName}-${counter}`; - } else if (counter <= 2 * DUPLICATE_FILENAMES_TRIES) { + } else if (counter <= DUPLICATE_FILENAMES_TRIES + 5) { // If there are more than 10,000 similarly named databases, // give up on using a counter and use a random string instead. folderName = `${lastName}-${nanoid()}`; From 0feef365d9765ab30d2f95d6ef497ca45861db8f Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 11 Mar 2024 14:39:07 +0100 Subject: [PATCH 0111/1237] Clean up temporary file in finally block --- .../variant-analysis/code-scanning-pack.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts b/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts index fde97e49963..01b25849354 100644 --- a/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts +++ b/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts @@ -29,10 +29,6 @@ export async function resolveCodeScanningQueryPack( // Resolve queries void logger.log(`Resolving queries for pack: ${packName}`); - const suiteFile = await file({ - postfix: ".qls", - }); - const suitePath = suiteFile.path; const suiteYaml: SuiteInstruction[] = [ { import: `codeql-suites/${language}-code-scanning.qls`, @@ -45,11 +41,20 @@ export async function resolveCodeScanningQueryPack( }, }, ]; - await outputFile(suitePath, dump(suiteYaml), "utf8"); - const resolvedQueries = await cliServer.resolveQueries(suitePath); + let resolvedQueries: string[]; + const suiteFile = await file({ + postfix: ".qls", + }); + const suitePath = suiteFile.path; + + try { + await outputFile(suitePath, dump(suiteYaml), "utf8"); - await suiteFile.cleanup(); + resolvedQueries = await cliServer.resolveQueries(suitePath); + } finally { + await suiteFile.cleanup(); + } if (resolvedQueries.length === 0) { throw Error( From 66de756d88896b5f9ac900e0160aaf9fc3777103 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 14:10:03 -0700 Subject: [PATCH 0112/1237] Bump CLI Version to v2.16.4 for integration tests (#3459) * Bump CLI version from v2.16.3 to v2.16.4 for integration tests * Remove extraneous `v2.16.2` from supported list We only need to keep the most recent patch version for each minor version. --------- Co-authored-by: github-actions[bot] Co-authored-by: Angela P Wen --- extensions/ql-vscode/supported_cli_versions.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions/ql-vscode/supported_cli_versions.json b/extensions/ql-vscode/supported_cli_versions.json index 75a66c6d038..afb360465d8 100644 --- a/extensions/ql-vscode/supported_cli_versions.json +++ b/extensions/ql-vscode/supported_cli_versions.json @@ -1,6 +1,5 @@ [ - "v2.16.3", - "v2.16.2", + "v2.16.4", "v2.15.5", "v2.14.6", "v2.13.5", From fd3acfb1c9df200b634889ff07127ec98e71bc11 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Mon, 11 Mar 2024 16:01:25 -0700 Subject: [PATCH 0113/1237] Address pull request comments --- .../ql-vscode/src/databases/database-fetcher.ts | 9 ++++----- .../ql-vscode/src/databases/local-databases-ui.ts | 1 - .../databases/local-databases/database-manager.ts | 13 +++++++++---- .../ql-vscode/src/query-server/query-runner.ts | 6 +++--- .../test/vscode-tests/cli-integration/jest.setup.ts | 4 ++-- .../test/vscode-tests/cli-integration/utils.ts | 4 ---- 6 files changed, 18 insertions(+), 19 deletions(-) diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index 73367da91fb..72dbdb7c265 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -298,7 +298,6 @@ export async function importLocalDatabase( try { const origin: DatabaseOrigin = { type: databaseUrl.endsWith(".testproj") ? "testproj" : "archive", - // TODO validate that archive origins can use a file path, not a URI path: Uri.parse(databaseUrl).fsPath, }; const item = await fetchDatabaseToWorkspaceStorage( @@ -371,7 +370,7 @@ async function fetchDatabaseToWorkspaceStorage( if (isFile(databaseUrl)) { if (origin.type === "testproj") { - await copyDatabase(origin.path, unzipPath, progress); + await copyDatabase(databaseUrl, unzipPath, progress); } else { await readAndUnzip(databaseUrl, unzipPath, cli, progress); } @@ -458,12 +457,12 @@ function validateUrl(databaseUrl: string) { /** * Copies a database folder from the file system into the workspace storage. - * @param scrDir the original location of the database + * @param scrDirURL the original location of the database as a URL string * @param destDir the location to copy the database to. This should be a folder in the workspace storage. * @param progress callback to send progress messages to */ async function copyDatabase( - scrDir: string, + srcDirURL: string, destDir: string, progress?: ProgressCallback, ) { @@ -473,7 +472,7 @@ async function copyDatabase( message: `Copying database ${basename(destDir)} into the workspace`, }); await ensureDir(destDir); - await copy(scrDir, destDir); + await copy(Uri.parse(srcDirURL).fsPath, destDir); } async function readAndUnzip( diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 9343af91f48..ae91051fba9 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -750,7 +750,6 @@ export class DatabaseUI extends DisposableObject { return withProgress( async (progress) => { try { - // Assume user has selected an archive if the file has a .zip extension if (!uri.path.endsWith(".testproj")) { throw new Error( "Please select a valid test database to import. Test databases end with `.testproj`.", diff --git a/extensions/ql-vscode/src/databases/local-databases/database-manager.ts b/extensions/ql-vscode/src/databases/local-databases/database-manager.ts index 46ec62b3791..e472f6e58d0 100644 --- a/extensions/ql-vscode/src/databases/local-databases/database-manager.ts +++ b/extensions/ql-vscode/src/databases/local-databases/database-manager.ts @@ -221,10 +221,15 @@ export class DatabaseManager extends DisposableObject { "codeql-database.yml", ); - // TODO add error handling if one does not exist. - const originStat = await stat(originDbYml); - const importedStat = await stat(importedDbYml); - return originStat.mtimeMs > importedStat.mtimeMs; + try { + const originStat = await stat(originDbYml); + const importedStat = await stat(importedDbYml); + return originStat.mtimeMs > importedStat.mtimeMs; + } catch (e) { + // If either of the files does not exist, we assume the origin is newer. + // This shouldn't happen unless the user manually deleted one of the files. + return true; + } } /** diff --git a/extensions/ql-vscode/src/query-server/query-runner.ts b/extensions/ql-vscode/src/query-server/query-runner.ts index f2d8f71632b..73d108e08e0 100644 --- a/extensions/ql-vscode/src/query-server/query-runner.ts +++ b/extensions/ql-vscode/src/query-server/query-runner.ts @@ -63,15 +63,15 @@ export interface CoreQueryRun { export type CoreCompletedQuery = CoreQueryResults & Omit; -type OnQueryRunStargingListener = (dbPath: Uri) => Promise; +type OnQueryRunStartingListener = (dbPath: Uri) => Promise; export class QueryRunner { constructor(public readonly qs: QueryServerClient) {} // Event handlers that get notified whenever a query is about to start running. // Can't use vscode EventEmitters since they are not asynchronous. - private readonly onQueryRunStartingListeners: OnQueryRunStargingListener[] = + private readonly onQueryRunStartingListeners: OnQueryRunStartingListener[] = []; - public onQueryRunStarting(listener: OnQueryRunStargingListener) { + public onQueryRunStarting(listener: OnQueryRunStartingListener) { this.onQueryRunStartingListeners.push(listener); } diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts index bd1b0e78b34..c3990282a7a 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.setup.ts @@ -12,7 +12,7 @@ import fetch from "node-fetch"; import { renameSync } from "fs"; import { unzipToDirectoryConcurrently } from "../../../src/common/unzip-concurrently"; import { platform } from "os"; -import { wait } from "./utils"; +import { sleep } from "../../../src/common/time"; beforeAll(async () => { // ensure the test database is downloaded @@ -42,7 +42,7 @@ beforeAll(async () => { // On Windows, wait a few seconds to make sure all background processes // release their lock on the files before renaming the directory. if (platform() === "win32") { - await wait(3000); + await sleep(3000); } renameSync(join(dbParentDir, "db"), testprojLoc); console.log("Unzip completed."); diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/utils.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/utils.ts index 82c998e2236..1eaacc8b8eb 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/utils.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/utils.ts @@ -7,7 +7,3 @@ import { join } from "path"; export function getDataFolderFilePath(path: string): string { return join(workspace.workspaceFolders![0].uri.fsPath, path); } - -export async function wait(ms = 1000) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} From b71a536e0024db4cd58f9905f8820c306e41ec5a Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Tue, 12 Mar 2024 11:44:30 +0100 Subject: [PATCH 0114/1237] Use include instructions for suite file --- extensions/ql-vscode/src/common/query-metadata.ts | 2 +- .../src/variant-analysis/code-scanning-pack.ts | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/extensions/ql-vscode/src/common/query-metadata.ts b/extensions/ql-vscode/src/common/query-metadata.ts index 89644bccb6d..69c416a4a3e 100644 --- a/extensions/ql-vscode/src/common/query-metadata.ts +++ b/extensions/ql-vscode/src/common/query-metadata.ts @@ -1,4 +1,4 @@ -const SARIF_RESULTS_QUERY_KINDS = [ +export const SARIF_RESULTS_QUERY_KINDS = [ "problem", "alert", "path-problem", diff --git a/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts b/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts index 01b25849354..68650710cfc 100644 --- a/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts +++ b/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts @@ -8,6 +8,7 @@ import type { CodeQLCliServer } from "../codeql-cli/cli"; import type { QlPackDetails } from "./ql-pack-details"; import { getQlPackFilePath } from "../common/ql"; import type { SuiteInstruction } from "../packaging/suite-instruction"; +import { SARIF_RESULTS_QUERY_KINDS } from "../common/query-metadata"; export async function resolveCodeScanningQueryPack( logger: BaseLogger, @@ -34,10 +35,15 @@ export async function resolveCodeScanningQueryPack( import: `codeql-suites/${language}-code-scanning.qls`, from: `${downloadedPack.name}@${downloadedPack.version}`, }, - // Exclude any non-problem queries { - exclude: { - kind: ["diagnostic", "metric"], + // This is necessary to ensure that the next import filter + // is applied correctly + exclude: {}, + }, + { + // Only include problem queries + include: { + kind: SARIF_RESULTS_QUERY_KINDS, }, }, ]; From 23003ec8bf87457e9f1b2441eba21857d5dbf057 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 8 Mar 2024 11:34:50 +0100 Subject: [PATCH 0115/1237] Extract process output handling in CLI server --- extensions/ql-vscode/src/codeql-cli/cli.ts | 271 +++++++++++++-------- 1 file changed, 169 insertions(+), 102 deletions(-) diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index 7c0b75ae706..a593edd86c5 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -369,9 +369,6 @@ export class CodeQLCliServer implements Disposable { onLine?: OnLineCallback, silent?: boolean, ): Promise { - const stderrBuffers: Buffer[] = []; - // The current buffer of stderr of a single line. To be used for logging. - let currentLineStderrBuffer: Buffer = Buffer.alloc(0); if (this.commandInProcess) { throw new Error("runCodeQlCliInternal called while cli was running"); } @@ -383,8 +380,6 @@ export class CodeQLCliServer implements Disposable { } // Grab the process so that typescript know that it is always defined. const process = this.process; - // The array of fragments of stdout - const stdoutBuffers: Buffer[] = []; // Compute the full args array const args = command.concat(LOGGING_FLAGS).concat(commandArgs); @@ -396,26 +391,83 @@ export class CodeQLCliServer implements Disposable { ); } try { - await new Promise((resolve, reject) => { - // Start listening to stdout - process.stdout.addListener("data", (newData: Buffer) => { - if (onLine) { - void (async () => { - const response = await onLine(newData.toString("utf-8")); - - if (!response) { - return; - } - - process.stdin.write(`${response}${EOL}`); - - // Remove newData from stdoutBuffers because the data has been consumed - // by the onLine callback. - stdoutBuffers.splice(stdoutBuffers.indexOf(newData), 1); - })(); - } + return await this.handleProcessOutput(process, { + handleNullTerminator: true, + onListenStart: (process) => { + // Write the command followed by a null terminator. + process.stdin.write(JSON.stringify(args), "utf8"); + process.stdin.write(this.nullBuffer); + }, + description, + args, + silent, + onLine, + }); + } catch (err) { + // Kill the process if it isn't already dead. + this.killProcessIfRunning(); + + throw err; + } + } finally { + this.commandInProcess = false; + // start running the next command immediately + this.runNext(); + } + } + + private async handleProcessOutput( + process: ChildProcessWithoutNullStreams, + { + handleNullTerminator, + args, + description, + onLine, + onListenStart, + silent, + }: { + handleNullTerminator: boolean; + args: string[]; + description: string; + onLine?: OnLineCallback; + onListenStart?: (process: ChildProcessWithoutNullStreams) => void; + silent?: boolean; + }, + ): Promise { + const stderrBuffers: Buffer[] = []; + // The current buffer of stderr of a single line. To be used for logging. + let currentLineStderrBuffer: Buffer = Buffer.alloc(0); + + // The listeners of the process. Declared here so they can be removed in the finally block. + let stdoutListener: ((newData: Buffer) => void) | undefined = undefined; + let stderrListener: ((newData: Buffer) => void) | undefined = undefined; + let closeListener: ((code: number | null) => void) | undefined = undefined; + + try { + // The array of fragments of stdout + const stdoutBuffers: Buffer[] = []; - stdoutBuffers.push(newData); + await new Promise((resolve, reject) => { + stdoutListener = (newData: Buffer) => { + if (onLine) { + void (async () => { + const response = await onLine(newData.toString("utf-8")); + + if (!response) { + return; + } + + process.stdin.write(`${response}${EOL}`); + + // Remove newData from stdoutBuffers because the data has been consumed + // by the onLine callback. + stdoutBuffers.splice(stdoutBuffers.indexOf(newData), 1); + })(); + } + + stdoutBuffers.push(newData); + + if (handleNullTerminator) { // If the buffer ends in '0' then exit. // We don't have to check the middle as no output will be written after the null until // the next command starts @@ -425,89 +477,104 @@ export class CodeQLCliServer implements Disposable { ) { resolve(); } - }); - // Listen to stderr - process.stderr.addListener("data", (newData: Buffer) => { - stderrBuffers.push(newData); - - if (!silent) { - currentLineStderrBuffer = Buffer.concat([ - currentLineStderrBuffer, - newData, - ]); - - // Print the stderr to the logger as it comes in. We need to ensure that - // we don't split messages on the same line, so we buffer the stderr and - // split it on EOLs. - const eolBuffer = Buffer.from(EOL); - - let hasCreatedSubarray = false; - - let eolIndex; - while ( - (eolIndex = currentLineStderrBuffer.indexOf(eolBuffer)) !== -1 - ) { - const line = currentLineStderrBuffer.subarray(0, eolIndex); - void this.logger.log(line.toString("utf-8")); - currentLineStderrBuffer = currentLineStderrBuffer.subarray( - eolIndex + eolBuffer.length, - ); - hasCreatedSubarray = true; - } + } + }; + stderrListener = (newData: Buffer) => { + stderrBuffers.push(newData); + + if (!silent) { + currentLineStderrBuffer = Buffer.concat([ + currentLineStderrBuffer, + newData, + ]); + + // Print the stderr to the logger as it comes in. We need to ensure that + // we don't split messages on the same line, so we buffer the stderr and + // split it on EOLs. + const eolBuffer = Buffer.from(EOL); + + let hasCreatedSubarray = false; + + let eolIndex; + while ( + (eolIndex = currentLineStderrBuffer.indexOf(eolBuffer)) !== -1 + ) { + const line = currentLineStderrBuffer.subarray(0, eolIndex); + void this.logger.log(line.toString("utf-8")); + currentLineStderrBuffer = currentLineStderrBuffer.subarray( + eolIndex + eolBuffer.length, + ); + hasCreatedSubarray = true; + } - // We have created a subarray, which means that the complete original buffer is now referenced - // by the subarray. We need to create a new buffer to avoid memory leaks. - if (hasCreatedSubarray) { - currentLineStderrBuffer = Buffer.from(currentLineStderrBuffer); - } + // We have created a subarray, which means that the complete original buffer is now referenced + // by the subarray. We need to create a new buffer to avoid memory leaks. + if (hasCreatedSubarray) { + currentLineStderrBuffer = Buffer.from(currentLineStderrBuffer); } - }); - // Listen for process exit. - process.addListener("close", (code) => - reject(new ExitCodeError(code)), - ); - // Write the command followed by a null terminator. - process.stdin.write(JSON.stringify(args), "utf8"); - process.stdin.write(this.nullBuffer); - }); - // Join all the data together - const fullBuffer = Buffer.concat(stdoutBuffers); - // Make sure we remove the terminator; - const data = fullBuffer.toString("utf8", 0, fullBuffer.length - 1); - if (!silent) { - void this.logger.log(currentLineStderrBuffer.toString("utf8")); - currentLineStderrBuffer = Buffer.alloc(0); - void this.logger.log("CLI command succeeded."); - } - return data; - } catch (err) { - // Kill the process if it isn't already dead. - this.killProcessIfRunning(); + } + }; + closeListener = (code) => { + if (handleNullTerminator) { + reject(new ExitCodeError(code)); + } else { + if (code === 0) { + resolve(); + } else { + reject(new ExitCodeError(code)); + } + } + }; - // Report the error (if there is a stderr then use that otherwise just report the error code or nodejs error) - const cliError = getCliError( - err, - stderrBuffers.length > 0 - ? Buffer.concat(stderrBuffers).toString("utf8") - : undefined, - description, - args, - ); - cliError.stack += getErrorStack(err); - throw cliError; - } finally { - if (!silent && currentLineStderrBuffer.length > 0) { - void this.logger.log(currentLineStderrBuffer.toString("utf8")); - } - // Remove the listeners we set up. - process.stdout.removeAllListeners("data"); - process.stderr.removeAllListeners("data"); - process.removeAllListeners("close"); + // Start listening to stdout + process.stdout.addListener("data", stdoutListener); + // Listen to stderr + process.stderr.addListener("data", stderrListener); + // Listen for process exit. + process.addListener("close", closeListener); + + onListenStart?.(process); + }); + // Join all the data together + const fullBuffer = Buffer.concat(stdoutBuffers); + // Make sure we remove the terminator + const data = fullBuffer.toString( + "utf8", + 0, + handleNullTerminator ? fullBuffer.length - 1 : fullBuffer.length, + ); + if (!silent) { + void this.logger.log(currentLineStderrBuffer.toString("utf8")); + currentLineStderrBuffer = Buffer.alloc(0); + void this.logger.log("CLI command succeeded."); } + return data; + } catch (err) { + // Report the error (if there is a stderr then use that otherwise just report the error code or nodejs error) + const cliError = getCliError( + err, + stderrBuffers.length > 0 + ? Buffer.concat(stderrBuffers).toString("utf8") + : undefined, + description, + args, + ); + cliError.stack += getErrorStack(err); + throw cliError; } finally { - this.commandInProcess = false; - // start running the next command immediately - this.runNext(); + if (!silent && currentLineStderrBuffer.length > 0) { + void this.logger.log(currentLineStderrBuffer.toString("utf8")); + } + // Remove the listeners we set up. + if (stdoutListener) { + process.stdout.removeListener("data", stdoutListener); + } + if (stderrListener) { + process.stderr.removeListener("data", stderrListener); + } + if (closeListener) { + process.removeListener("close", closeListener); + } } } From b961a7ae555d65a1068201da7262712fc99dc934 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 8 Mar 2024 11:47:50 +0100 Subject: [PATCH 0116/1237] Extract RunOptions --- extensions/ql-vscode/src/codeql-cli/cli.ts | 71 +++++++++++----------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index a593edd86c5..5c3dd1dd82c 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -217,6 +217,28 @@ type VersionChangedListener = ( newVersionAndFeatures: VersionAndFeatures | undefined, ) => void; +type RunOptions = { + /** + * Used to output progress messages, e.g. to the status bar. + */ + progressReporter?: ProgressReporter; + /** + * Used for responding to interactive output on stdout/stdin. + */ + onLine?: OnLineCallback; + /** + * If true, don't print logs to the CodeQL extension log. + */ + silent?: boolean; +}; + +type JsonRunOptions = RunOptions & { + /** + * Whether to add commandline arguments to specify the format as JSON. + */ + addFormat?: boolean; +}; + /** * This class manages a cli server started by `codeql execute cli-server` to * run commands without the overhead of starting a new java @@ -691,21 +713,14 @@ export class CodeQLCliServer implements Disposable { * @param description Description of the action being run, to be shown in log and error messages. * @param progressReporter Used to output progress messages, e.g. to the status bar. * @param onLine Used for responding to interactive output on stdout/stdin. + * @param silent If true, don't print logs to the CodeQL extension log. * @returns The contents of the command's stdout, if the command succeeded. */ runCodeQlCliCommand( command: string[], commandArgs: string[], description: string, - { - progressReporter, - onLine, - silent = false, - }: { - progressReporter?: ProgressReporter; - onLine?: OnLineCallback; - silent?: boolean; - } = {}, + { progressReporter, onLine, silent = false }: RunOptions = {}, ): Promise { if (progressReporter) { progressReporter.report({ message: description }); @@ -743,24 +758,13 @@ export class CodeQLCliServer implements Disposable { * @param description Description of the action being run, to be shown in log and error messages. * @param addFormat Whether or not to add commandline arguments to specify the format as JSON. * @param progressReporter Used to output progress messages, e.g. to the status bar. - * @param onLine Used for responding to interactive output on stdout/stdin. * @returns The contents of the command's stdout, if the command succeeded. */ async runJsonCodeQlCliCommand( command: string[], commandArgs: string[], description: string, - { - addFormat = true, - progressReporter, - onLine, - silent = false, - }: { - addFormat?: boolean; - progressReporter?: ProgressReporter; - onLine?: OnLineCallback; - silent?: boolean; - } = {}, + { addFormat = true, ...runOptions }: JsonRunOptions = {}, ): Promise { let args: string[] = []; if (addFormat) { @@ -768,11 +772,12 @@ export class CodeQLCliServer implements Disposable { args = args.concat(["--format", "json"]); } args = args.concat(commandArgs); - const result = await this.runCodeQlCliCommand(command, args, description, { - progressReporter, - onLine, - silent, - }); + const result = await this.runCodeQlCliCommand( + command, + args, + description, + runOptions, + ); try { return JSON.parse(result) as OutputType; } catch (err) { @@ -800,21 +805,14 @@ export class CodeQLCliServer implements Disposable { * @param command The `codeql` command to be run, provided as an array of command/subcommand names. * @param commandArgs The arguments to pass to the `codeql` command. * @param description Description of the action being run, to be shown in log and error messages. - * @param addFormat Whether or not to add commandline arguments to specify the format as JSON. - * @param progressReporter Used to output progress messages, e.g. to the status bar. + * @param runOptions Options for running the command. * @returns The contents of the command's stdout, if the command succeeded. */ async runJsonCodeQlCliCommandWithAuthentication( command: string[], commandArgs: string[], description: string, - { - addFormat, - progressReporter, - }: { - addFormat?: boolean; - progressReporter?: ProgressReporter; - } = {}, + runOptions: Omit = {}, ): Promise { const accessToken = await this.app.credentials.getExistingAccessToken(); @@ -825,8 +823,7 @@ export class CodeQLCliServer implements Disposable { [...extraArgs, ...commandArgs], description, { - addFormat, - progressReporter, + ...runOptions, onLine: async (line) => { if (line.startsWith("Enter value for --github-auth-stdin")) { try { From 97eaaacce6c91afb9750b504c5a18a4df119e105 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 8 Mar 2024 12:03:45 +0100 Subject: [PATCH 0117/1237] Run pack bundle in separate process and allow cancelling it --- extensions/ql-vscode/src/codeql-cli/cli.ts | 99 ++++++++++++++++++- .../src/model-editor/model-evaluator.ts | 2 +- .../src/variant-analysis/run-remote-query.ts | 9 +- .../variant-analysis-manager.ts | 74 +++++++------- 4 files changed, 147 insertions(+), 37 deletions(-) diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index 5c3dd1dd82c..6d9b0ae051a 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -36,6 +36,7 @@ import type { Position } from "../query-server/messages"; import { LOGGING_FLAGS } from "./cli-command"; import type { CliFeatures, VersionAndFeatures } from "./cli-version"; import { ExitCodeError, getCliError } from "./cli-errors"; +import { UserCancellationException } from "../common/vscode/progress"; /** * The version of the SARIF format that we are using. @@ -230,6 +231,15 @@ type RunOptions = { * If true, don't print logs to the CodeQL extension log. */ silent?: boolean; + /** + * If true, run this command in a new process rather than in the CLI server. + */ + runInNewProcess?: boolean; + /** + * If runInNewProcess is true, allows cancelling the command. If runInNewProcess + * is false or not specified, this option is ignored. + */ + token?: CancellationToken; }; type JsonRunOptions = RunOptions & { @@ -438,6 +448,67 @@ export class CodeQLCliServer implements Disposable { } } + private async runCodeQlCliInNewProcess( + command: string[], + commandArgs: string[], + description: string, + onLine?: OnLineCallback, + silent?: boolean, + token?: CancellationToken, + ): Promise { + const codeqlPath = await this.getCodeQlPath(); + + const args = command.concat(LOGGING_FLAGS).concat(commandArgs); + const argsString = args.join(" "); + + // If we are running silently, we don't want to print anything to the console. + if (!silent) { + void this.logger.log(`${description} using CodeQL CLI: ${argsString}...`); + } + + const process = spawnChildProcess(codeqlPath, args); + if (!process || !process.pid) { + throw new Error( + `Failed to start ${description} using command ${codeqlPath} ${argsString}.`, + ); + } + + let cancellationRegistration: Disposable | undefined = undefined; + try { + cancellationRegistration = token?.onCancellationRequested((_e) => { + tk(process.pid || 0); + }); + + return await this.handleProcessOutput(process, { + handleNullTerminator: true, + onListenStart: (process) => { + // Write the command followed by a null terminator. + process.stdin.write(JSON.stringify(args), "utf8"); + process.stdin.write(this.nullBuffer); + }, + description, + args, + silent, + onLine, + }); + } catch (e) { + // If cancellation was requested, the error is probably just because the process was exited with SIGTERM. + if (token?.isCancellationRequested) { + void this.logger.log( + `The process was cancelled and exited with: ${getErrorMessage(e)}`, + ); + throw new UserCancellationException( + `Command ${argsString} was cancelled.`, + true, // Don't show a warning message when the user manually cancelled the command. + ); + } + + throw e; + } finally { + cancellationRegistration?.dispose(); + } + } + private async handleProcessOutput( process: ChildProcessWithoutNullStreams, { @@ -714,18 +785,38 @@ export class CodeQLCliServer implements Disposable { * @param progressReporter Used to output progress messages, e.g. to the status bar. * @param onLine Used for responding to interactive output on stdout/stdin. * @param silent If true, don't print logs to the CodeQL extension log. + * @param runInNewProcess If true, run this command in a new process rather than in the CLI server. + * @param token If runInNewProcess is true, allows cancelling the command. If runInNewProcess + * is false or not specified, this option is ignored. * @returns The contents of the command's stdout, if the command succeeded. */ runCodeQlCliCommand( command: string[], commandArgs: string[], description: string, - { progressReporter, onLine, silent = false }: RunOptions = {}, + { + progressReporter, + onLine, + silent = false, + runInNewProcess = false, + token, + }: RunOptions = {}, ): Promise { if (progressReporter) { progressReporter.report({ message: description }); } + if (runInNewProcess) { + return this.runCodeQlCliInNewProcess( + command, + commandArgs, + description, + onLine, + silent, + token, + ); + } + return new Promise((resolve, reject) => { // Construct the command that actually does the work const callback = (): void => { @@ -1537,6 +1628,7 @@ export class CodeQLCliServer implements Disposable { * @param outputBundleFile The path to the output bundle file. * @param outputPackDir The directory to contain the unbundled output pack. * @param moreOptions Additional options to be passed to `codeql pack bundle`. + * @param token Cancellation token for the operation. */ async packBundle( sourcePackDir: string, @@ -1544,6 +1636,7 @@ export class CodeQLCliServer implements Disposable { outputBundleFile: string, outputPackDir: string, moreOptions: string[], + token?: CancellationToken, ): Promise { const args = [ "-o", @@ -1559,6 +1652,10 @@ export class CodeQLCliServer implements Disposable { ["pack", "bundle"], args, "Bundling pack", + { + runInNewProcess: true, + token, + }, ); } diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts index 895f1927a38..120d2b44df4 100644 --- a/extensions/ql-vscode/src/model-editor/model-evaluator.ts +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -75,7 +75,7 @@ export class ModelEvaluator extends DisposableObject { ), { title: "Run model evaluation", - cancellable: false, + cancellable: true, }, ); } diff --git a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts index 246bebdced4..05b99d0799e 100644 --- a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts +++ b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts @@ -54,6 +54,7 @@ async function generateQueryPack( cliServer: CodeQLCliServer, qlPackDetails: QlPackDetails, tmpDir: RemoteQueryTempDir, + token: CancellationToken, ): Promise { const workspaceFolders = getOnDiskWorkspaceFolders(); const extensionPacks = await getExtensionPacksToInject( @@ -148,6 +149,7 @@ async function generateQueryPack( bundlePath, tmpDir.compiledPackDir, precompilationOpts, + token, ); const base64Pack = (await readFile(bundlePath)).toString("base64"); return base64Pack; @@ -331,7 +333,12 @@ export async function prepareRemoteQueryRun( let base64Pack: string; try { - base64Pack = await generateQueryPack(cliServer, qlPackDetails, tempDir); + base64Pack = await generateQueryPack( + cliServer, + qlPackDetails, + tempDir, + token, + ); } finally { await tempDir.remoteQueryDir.cleanup(); } diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index f0474266192..3a6d7a55ce1 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -221,42 +221,48 @@ export class VariantAnalysisManager } public async runVariantAnalysisFromPublishedPack(): Promise { - return withProgress(async (progress, token) => { - progress({ - maxStep: 7, - step: 0, - message: "Determining query language", - }); - - const language = await askForLanguage(this.cliServer); - if (!language) { - return; - } - - progress({ - maxStep: 7, - step: 2, - message: "Downloading query pack and resolving queries", - }); + return withProgress( + async (progress, token) => { + progress({ + maxStep: 7, + step: 0, + message: "Determining query language", + }); + + const language = await askForLanguage(this.cliServer); + if (!language) { + return; + } - // Build up details to pass to the functions that run the variant analysis. - const qlPackDetails = await resolveCodeScanningQueryPack( - this.app.logger, - this.cliServer, - language, - ); + progress({ + maxStep: 7, + step: 2, + message: "Downloading query pack and resolving queries", + }); + + // Build up details to pass to the functions that run the variant analysis. + const qlPackDetails = await resolveCodeScanningQueryPack( + this.app.logger, + this.cliServer, + language, + ); - await this.runVariantAnalysis( - qlPackDetails, - (p) => - progress({ - ...p, - maxStep: p.maxStep + 3, - step: p.step + 3, - }), - token, - ); - }); + await this.runVariantAnalysis( + qlPackDetails, + (p) => + progress({ + ...p, + maxStep: p.maxStep + 3, + step: p.step + 3, + }), + token, + ); + }, + { + title: "Run Variant Analysis", + cancellable: true, + }, + ); } private async runVariantAnalysisCommand(queryFiles: Uri[]): Promise { From 24b7adfb9224f7a520ee49cf61b69a7ef4d22831 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 8 Mar 2024 12:13:48 +0100 Subject: [PATCH 0118/1237] Allow cancelling pack download --- extensions/ql-vscode/src/codeql-cli/cli.ts | 17 ++++++++++------- .../src/model-editor/model-evaluator.ts | 1 + .../src/variant-analysis/code-scanning-pack.ts | 4 +++- .../variant-analysis-manager.ts | 1 + .../variant-analysis/code-scanning-pack.test.ts | 2 ++ 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index 6d9b0ae051a..18e87cb4e14 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -480,12 +480,7 @@ export class CodeQLCliServer implements Disposable { }); return await this.handleProcessOutput(process, { - handleNullTerminator: true, - onListenStart: (process) => { - // Write the command followed by a null terminator. - process.stdin.write(JSON.stringify(args), "utf8"); - process.stdin.write(this.nullBuffer); - }, + handleNullTerminator: false, description, args, silent, @@ -1587,12 +1582,20 @@ export class CodeQLCliServer implements Disposable { /** * Downloads a specified pack. * @param packs The `` of the packs to download. + * @param token The cancellation token. If not specified, the command will be run in the CLI server. */ - async packDownload(packs: string[]): Promise { + async packDownload( + packs: string[], + token?: CancellationToken, + ): Promise { return this.runJsonCodeQlCliCommandWithAuthentication( ["pack", "download"], packs, "Downloading packs", + { + runInNewProcess: !!token, // Only run in a new process if a token is provided + token, + }, ); } diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts index 120d2b44df4..098f0703dfd 100644 --- a/extensions/ql-vscode/src/model-editor/model-evaluator.ts +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -58,6 +58,7 @@ export class ModelEvaluator extends DisposableObject { this.app.logger, this.cliServer, this.language, + this.cancellationSource.token, ); if (!qlPack) { diff --git a/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts b/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts index 68650710cfc..42cb4a1e7be 100644 --- a/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts +++ b/extensions/ql-vscode/src/variant-analysis/code-scanning-pack.ts @@ -9,16 +9,18 @@ import type { QlPackDetails } from "./ql-pack-details"; import { getQlPackFilePath } from "../common/ql"; import type { SuiteInstruction } from "../packaging/suite-instruction"; import { SARIF_RESULTS_QUERY_KINDS } from "../common/query-metadata"; +import type { CancellationToken } from "vscode"; export async function resolveCodeScanningQueryPack( logger: BaseLogger, cliServer: CodeQLCliServer, language: QueryLanguage, + token: CancellationToken, ): Promise { // Get pack void logger.log(`Downloading pack for language: ${language}`); const packName = `codeql/${language}-queries`; - const packDownloadResult = await cliServer.packDownload([packName]); + const packDownloadResult = await cliServer.packDownload([packName], token); const downloadedPack = packDownloadResult.packs[0]; const packDir = join( diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 3a6d7a55ce1..05dda456617 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -245,6 +245,7 @@ export class VariantAnalysisManager this.app.logger, this.cliServer, language, + token, ); await this.runVariantAnalysis( diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/code-scanning-pack.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/code-scanning-pack.test.ts index 8f3836f0359..4545069a110 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/code-scanning-pack.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/code-scanning-pack.test.ts @@ -1,3 +1,4 @@ +import { CancellationTokenSource } from "vscode"; import type { CodeQLCliServer } from "../../../../src/codeql-cli/cli"; import type { App } from "../../../../src/common/app"; import { QueryLanguage } from "../../../../src/common/query-language"; @@ -20,6 +21,7 @@ describe("Code Scanning pack", () => { app.logger, cli, QueryLanguage.Javascript, + new CancellationTokenSource().token, ); // Should include queries. Just check that at least one known query exists. // It doesn't particularly matter which query we check for. From 27bb46ab4c37d41899d8cfd9bd3e587bcf6085ab Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 8 Mar 2024 13:15:56 +0100 Subject: [PATCH 0119/1237] Make more parts of running a variant analysis cancellable --- .../ql-vscode/src/codeql-cli/query-language.ts | 15 ++++++++++----- .../variant-analysis/variant-analysis-manager.ts | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/extensions/ql-vscode/src/codeql-cli/query-language.ts b/extensions/ql-vscode/src/codeql-cli/query-language.ts index c711f156899..ba327560dde 100644 --- a/extensions/ql-vscode/src/codeql-cli/query-language.ts +++ b/extensions/ql-vscode/src/codeql-cli/query-language.ts @@ -1,5 +1,5 @@ import type { CodeQLCliServer } from "./cli"; -import type { Uri } from "vscode"; +import type { CancellationToken, Uri } from "vscode"; import { window } from "vscode"; import { getLanguageDisplayName, @@ -50,6 +50,7 @@ export async function findLanguage( export async function askForLanguage( cliServer: CodeQLCliServer, throwOnEmpty = true, + token?: CancellationToken, ): Promise { const supportedLanguages = await cliServer.getSupportedLanguages(); @@ -62,10 +63,14 @@ export async function askForLanguage( })) .sort((a, b) => a.label.localeCompare(b.label)); - const selectedItem = await window.showQuickPick(items, { - placeHolder: "Select target query language", - ignoreFocusOut: true, - }); + const selectedItem = await window.showQuickPick( + items, + { + placeHolder: "Select target query language", + ignoreFocusOut: true, + }, + token, + ); if (!selectedItem) { // This only happens if the user cancels the quick pick. if (throwOnEmpty) { diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 05dda456617..4a39ac1b1c5 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -229,7 +229,7 @@ export class VariantAnalysisManager message: "Determining query language", }); - const language = await askForLanguage(this.cliServer); + const language = await askForLanguage(this.cliServer, true, token); if (!language) { return; } From 3050437cabd2e853821e7dbe9c8b1307b27d72f6 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 8 Mar 2024 15:08:30 +0100 Subject: [PATCH 0120/1237] Clean up process --- extensions/ql-vscode/src/codeql-cli/cli.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index 18e87cb4e14..3198f84fee8 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -500,6 +500,11 @@ export class CodeQLCliServer implements Disposable { throw e; } finally { + process.stdin.end(); + process.kill(); + process.stdout.destroy(); + process.stderr.destroy(); + cancellationRegistration?.dispose(); } } From 0cbd96ff7cdacb3bfc334d8666cf4c7cc42e7e2e Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 8 Mar 2024 15:10:12 +0100 Subject: [PATCH 0121/1237] Add error listener --- extensions/ql-vscode/src/codeql-cli/cli.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index 3198f84fee8..292e4925d78 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -535,6 +535,7 @@ export class CodeQLCliServer implements Disposable { let stdoutListener: ((newData: Buffer) => void) | undefined = undefined; let stderrListener: ((newData: Buffer) => void) | undefined = undefined; let closeListener: ((code: number | null) => void) | undefined = undefined; + let errorListener: ((err: Error) => void) | undefined = undefined; try { // The array of fragments of stdout @@ -618,6 +619,9 @@ export class CodeQLCliServer implements Disposable { } } }; + errorListener = (err) => { + reject(err); + }; // Start listening to stdout process.stdout.addListener("data", stdoutListener); @@ -625,6 +629,8 @@ export class CodeQLCliServer implements Disposable { process.stderr.addListener("data", stderrListener); // Listen for process exit. process.addListener("close", closeListener); + // Listen for errors + process.addListener("error", errorListener); onListenStart?.(process); }); @@ -668,6 +674,9 @@ export class CodeQLCliServer implements Disposable { if (closeListener) { process.removeListener("close", closeListener); } + if (errorListener) { + process.removeListener("error", errorListener); + } } } From eb1a5cfba382cbee9dddc0d46c74b592635a7788 Mon Sep 17 00:00:00 2001 From: Shati Patel <42641846+shati-patel@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:08:49 +0000 Subject: [PATCH 0122/1237] Add "model alerts indicator" component (#3441) --- .../src/view/model-editor/LibraryRow.tsx | 4 ++ .../src/view/model-editor/MethodRow.tsx | 67 +++++++++++------- .../model-editor/ModelAlertsIndicator.tsx | 46 ++++++++++++ .../src/view/model-editor/ModelEditor.tsx | 1 + .../model-editor/ModeledMethodDataGrid.tsx | 4 ++ .../view/model-editor/ModeledMethodsList.tsx | 4 ++ .../__tests__/LibraryRow.spec.tsx | 1 + .../model-editor/__tests__/MethodRow.spec.tsx | 1 + .../__tests__/ModelAlertsIndicator.spec.tsx | 70 +++++++++++++++++++ .../__tests__/ModeledMethodDataGrid.spec.tsx | 1 + .../__tests__/ModeledMethodsList.spec.tsx | 1 + 11 files changed, 176 insertions(+), 24 deletions(-) create mode 100644 extensions/ql-vscode/src/view/model-editor/ModelAlertsIndicator.tsx create mode 100644 extensions/ql-vscode/src/view/model-editor/__tests__/ModelAlertsIndicator.spec.tsx diff --git a/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx b/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx index 40fe4359bd1..0b4db064280 100644 --- a/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx @@ -15,6 +15,7 @@ import { import type { ModelEditorViewState } from "../../model-editor/shared/view-state"; import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions"; import { getCandidates } from "../../model-editor/shared/auto-model-candidates"; +import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state"; const LibraryContainer = styled.div` background-color: var(--vscode-peekViewResult-background); @@ -80,6 +81,7 @@ export type LibraryRowProps = { hideModeledMethods: boolean; revealedMethodSignature: string | null; accessPathSuggestions?: AccessPathSuggestionOptions; + evaluationRun: ModelEvaluationRunState | undefined; onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void; onMethodClick: (methodSignature: string) => void; onSaveModelClick: (methodSignatures: string[]) => void; @@ -105,6 +107,7 @@ export const LibraryRow = ({ hideModeledMethods, revealedMethodSignature, accessPathSuggestions, + evaluationRun, onChange, onMethodClick, onSaveModelClick, @@ -260,6 +263,7 @@ export const LibraryRow = ({ hideModeledMethods={hideModeledMethods} revealedMethodSignature={revealedMethodSignature} accessPathSuggestions={accessPathSuggestions} + evaluationRun={evaluationRun} onChange={onChange} onMethodClick={onMethodClick} /> diff --git a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx index 0f317ef815e..2f8c68fbb02 100644 --- a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx @@ -38,6 +38,8 @@ import type { AccessPathOption } from "../../model-editor/suggestions"; import { ModelInputSuggestBox } from "./ModelInputSuggestBox"; import { ModelOutputSuggestBox } from "./ModelOutputSuggestBox"; import { getModelsAsDataLanguage } from "../../model-editor/languages"; +import { ModelAlertsIndicator } from "./ModelAlertsIndicator"; +import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state"; const ApiOrMethodRow = styled.div` min-height: calc(var(--input-height) * 1px); @@ -47,6 +49,14 @@ const ApiOrMethodRow = styled.div` gap: 0.5em; `; +const ModelButtonsContainer = styled.div` + min-height: calc(var(--input-height) * 1px); + display: flex; + flex-direction: row; + align-items: center; + gap: 1em; +`; + const UsagesButton = styled.button` color: var(--vscode-editor-foreground); background-color: var(--vscode-input-background); @@ -82,6 +92,7 @@ export type MethodRowProps = { revealedMethodSignature: string | null; inputAccessPathSuggestions?: AccessPathOption[]; outputAccessPathSuggestions?: AccessPathOption[]; + evaluationRun: ModelEvaluationRunState | undefined; onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void; onMethodClick: (methodSignature: string) => void; }; @@ -119,6 +130,7 @@ const ModelableMethodRow = forwardRef( revealedMethodSignature, inputAccessPathSuggestions, outputAccessPathSuggestions, + evaluationRun, onChange, onMethodClick, } = props; @@ -349,30 +361,37 @@ const ModelableMethodRow = forwardRef( />
- {index === 0 ? ( - { - event.stopPropagation(); - handleAddModelClick(); - }} - disabled={addModelButtonDisabled} - > - - - ) : ( - { - event.stopPropagation(); - removeModelClickedHandlers[index](); - }} - > - - - )} + + + {index === 0 ? ( + { + event.stopPropagation(); + handleAddModelClick(); + }} + disabled={addModelButtonDisabled} + > + + + ) : ( + { + event.stopPropagation(); + removeModelClickedHandlers[index](); + }} + > + + + )} +
); diff --git a/extensions/ql-vscode/src/view/model-editor/ModelAlertsIndicator.tsx b/extensions/ql-vscode/src/view/model-editor/ModelAlertsIndicator.tsx new file mode 100644 index 00000000000..e25ba3c01d2 --- /dev/null +++ b/extensions/ql-vscode/src/view/model-editor/ModelAlertsIndicator.tsx @@ -0,0 +1,46 @@ +import { styled } from "styled-components"; +import type { ModeledMethod } from "../../model-editor/modeled-method"; +import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state"; +import type { ModelEditorViewState } from "../../model-editor/shared/view-state"; + +const ModelAlertsButton = styled.button` + color: var(--vscode-editor-foreground); + background-color: var(--vscode-input-background); + border: none; + border-radius: 40%; + cursor: pointer; +`; + +export type Props = { + viewState: ModelEditorViewState; + modeledMethod: ModeledMethod; + evaluationRun: ModelEvaluationRunState | undefined; +}; + +export const ModelAlertsIndicator = ({ + viewState, + modeledMethod, + evaluationRun, +}: Props) => { + if (!viewState.showEvaluationUi) { + return null; + } + + if (!evaluationRun || !modeledMethod) { + return null; + } + + // TODO: Once we have alert provenance, we can show actual alert counts here. + // For now, we show a random number. + const number = Math.floor(Math.random() * 10); + + return ( + { + event.stopPropagation(); + }} + > + {number} + + ); +}; diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx index 6d68da43ad4..d61876516d3 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx @@ -436,6 +436,7 @@ export function ModelEditor({ hideModeledMethods={hideModeledMethods} revealedMethodSignature={revealedMethodSignature} accessPathSuggestions={accessPathSuggestions} + evaluationRun={evaluationRun} onChange={onChange} onMethodClick={onMethodClick} onSaveModelClick={onSaveModelClick} diff --git a/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx b/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx index 172f3736b6b..44cb7ec2e46 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx @@ -8,6 +8,7 @@ import type { ModelEditorViewState } from "../../model-editor/shared/view-state" import { ScreenReaderOnly } from "../common/ScreenReaderOnly"; import { DataGrid, DataGridCell } from "../common/DataGrid"; import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions"; +import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state"; export const MULTIPLE_MODELS_GRID_TEMPLATE_COLUMNS = "0.5fr 0.125fr 0.125fr 0.125fr 0.125fr max-content"; @@ -23,6 +24,7 @@ export type ModeledMethodDataGridProps = { hideModeledMethods: boolean; revealedMethodSignature: string | null; accessPathSuggestions?: AccessPathSuggestionOptions; + evaluationRun: ModelEvaluationRunState | undefined; onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void; onMethodClick: (methodSignature: string) => void; }; @@ -38,6 +40,7 @@ export const ModeledMethodDataGrid = ({ hideModeledMethods, revealedMethodSignature, accessPathSuggestions, + evaluationRun, onChange, onMethodClick, }: ModeledMethodDataGridProps) => { @@ -101,6 +104,7 @@ export const ModeledMethodDataGrid = ({ revealedMethodSignature={revealedMethodSignature} inputAccessPathSuggestions={inputAccessPathSuggestions} outputAccessPathSuggestions={outputAccessPathSuggestions} + evaluationRun={evaluationRun} onChange={onChange} onMethodClick={onMethodClick} /> diff --git a/extensions/ql-vscode/src/view/model-editor/ModeledMethodsList.tsx b/extensions/ql-vscode/src/view/model-editor/ModeledMethodsList.tsx index 400f920c7dc..b6370ffc424 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModeledMethodsList.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModeledMethodsList.tsx @@ -9,6 +9,7 @@ import { } from "../../model-editor/shared/sorting"; import type { ModelEditorViewState } from "../../model-editor/shared/view-state"; import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions"; +import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state"; export type ModeledMethodsListProps = { methods: Method[]; @@ -19,6 +20,7 @@ export type ModeledMethodsListProps = { processedByAutoModelMethods: Set; revealedMethodSignature: string | null; accessPathSuggestions?: AccessPathSuggestionOptions; + evaluationRun: ModelEvaluationRunState | undefined; viewState: ModelEditorViewState; hideModeledMethods: boolean; onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void; @@ -48,6 +50,7 @@ export const ModeledMethodsList = ({ hideModeledMethods, revealedMethodSignature, accessPathSuggestions, + evaluationRun, onChange, onMethodClick, onSaveModelClick, @@ -98,6 +101,7 @@ export const ModeledMethodsList = ({ hideModeledMethods={hideModeledMethods} revealedMethodSignature={revealedMethodSignature} accessPathSuggestions={accessPathSuggestions} + evaluationRun={evaluationRun} onChange={onChange} onMethodClick={onMethodClick} onSaveModelClick={onSaveModelClick} diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/LibraryRow.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/LibraryRow.spec.tsx index 48a26b609f6..19caa8fd826 100644 --- a/extensions/ql-vscode/src/view/model-editor/__tests__/LibraryRow.spec.tsx +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/LibraryRow.spec.tsx @@ -37,6 +37,7 @@ describe(LibraryRow.name, () => { selectedSignatures={new Set()} inProgressMethods={new Set()} processedByAutoModelMethods={new Set()} + evaluationRun={undefined} viewState={viewState} hideModeledMethods={false} revealedMethodSignature={null} diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx index 3624b32f42c..5213dc15d3a 100644 --- a/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx @@ -45,6 +45,7 @@ describe(MethodRow.name, () => { modelingInProgress={false} processedByAutoModel={false} revealedMethodSignature={null} + evaluationRun={undefined} viewState={viewState} onChange={onChange} onMethodClick={onMethodClick} diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/ModelAlertsIndicator.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelAlertsIndicator.spec.tsx new file mode 100644 index 00000000000..761b070f770 --- /dev/null +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelAlertsIndicator.spec.tsx @@ -0,0 +1,70 @@ +import { render as reactRender, screen } from "@testing-library/react"; +import { createMethod } from "../../../../test/factories/model-editor/method-factories"; +import { createSummaryModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories"; +import { createMockModelEditorViewState } from "../../../../test/factories/model-editor/view-state"; +import type { Props } from "../ModelAlertsIndicator"; +import { ModelAlertsIndicator } from "../ModelAlertsIndicator"; +import { createMockVariantAnalysis } from "../../../../test/factories/variant-analysis/shared/variant-analysis"; +import { VariantAnalysisStatus } from "../../../variant-analysis/shared/variant-analysis"; + +describe(ModelAlertsIndicator.name, () => { + const method = createMethod(); + const modeledMethod = createSummaryModeledMethod(method); + const evaluationRun = { + isPreparing: false, + variantAnalysis: createMockVariantAnalysis({ + status: VariantAnalysisStatus.Succeeded, + }), + }; + + const render = (props: Partial = {}) => + reactRender( + , + ); + + describe("when showEvaluationUi is false", () => { + it("does not render anything", () => { + render({ + viewState: createMockModelEditorViewState({ showEvaluationUi: false }), + }); + + expect(screen.queryByRole("button")).not.toBeInTheDocument(); + }); + }); + + describe("when there is no evaluation run", () => { + it("does not render anything", () => { + render({ + evaluationRun: undefined, + }); + + expect(screen.queryByRole("button")).not.toBeInTheDocument(); + }); + }); + + describe("when there is no modeled method", () => { + it("does not render anything", () => { + render({ + modeledMethod: undefined, + }); + + expect(screen.queryByRole("button")).not.toBeInTheDocument(); + }); + }); + + describe("when there is an evaluation run and a modeled method", () => { + // TODO: Once we have alert provenance, this will be an actual alert count instead of a random number. + it("renders a button with a random number", () => { + render(); + + const button = screen.queryByRole("button"); + expect(button).toBeInTheDocument(); + expect(button).toHaveTextContent(/\d/); + }); + }); +}); diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodDataGrid.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodDataGrid.spec.tsx index 53973a6b033..36af1f825b0 100644 --- a/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodDataGrid.spec.tsx +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodDataGrid.spec.tsx @@ -59,6 +59,7 @@ describe(ModeledMethodDataGrid.name, () => { selectedSignatures={new Set()} inProgressMethods={new Set()} processedByAutoModelMethods={new Set()} + evaluationRun={undefined} viewState={viewState} hideModeledMethods={false} revealedMethodSignature={null} diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodsList.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodsList.spec.tsx index 413fc12e2cb..8136ee2df0f 100644 --- a/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodsList.spec.tsx +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodsList.spec.tsx @@ -60,6 +60,7 @@ describe(ModeledMethodsList.name, () => { selectedSignatures={new Set()} inProgressMethods={new Set()} processedByAutoModelMethods={new Set()} + evaluationRun={undefined} viewState={viewState} hideModeledMethods={false} revealedMethodSignature={null} From 18f1a464933e295471d9ce220eb7eab0939ddfbb Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 7 Mar 2024 11:51:36 +0000 Subject: [PATCH 0123/1237] Naively convert DatabaseFetcher to a class --- .../src/databases/database-fetcher.ts | 1080 +++++++++-------- .../databases/github-databases/download.ts | 5 +- .../github-databases-module.ts | 6 + .../src/databases/github-databases/updates.ts | 34 +- .../src/databases/local-databases-ui.ts | 14 +- extensions/ql-vscode/src/extension.ts | 2 + .../src/local-queries/local-queries.ts | 2 + .../local-queries/skeleton-query-wizard.ts | 13 +- .../src/model-editor/model-editor-view.ts | 23 +- .../databases/database-fetcher.test.ts | 9 +- .../skeleton-query-wizard.test.ts | 18 +- .../test/vscode-tests/global.helper.ts | 4 +- .../github-databases/download.test.ts | 10 +- .../github-databases-module.test.ts | 7 + .../github-databases/updates.test.ts | 10 +- 15 files changed, 647 insertions(+), 590 deletions(-) diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index b40975d3894..4c983e43b0f 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -42,605 +42,623 @@ import { createFilenameFromString } from "../common/filenames"; import { findDirWithFile } from "../common/files"; import { convertGithubNwoToDatabaseUrl } from "./github-databases/api"; -/** - * Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file. - * - * @param databaseManager the DatabaseManager - * @param storagePath where to store the unzipped database. - */ -export async function promptImportInternetDatabase( - commandManager: AppCommandManager, - databaseManager: DatabaseManager, - storagePath: string, - progress: ProgressCallback, - cli: CodeQLCliServer, -): Promise { - const databaseUrl = await window.showInputBox({ - prompt: "Enter URL of zipfile of database to download", - }); - if (!databaseUrl) { - return; - } +// The number of tries to use when generating a unique filename before +// giving up and using a nanoid. +const DUPLICATE_FILENAMES_TRIES = 10_000; + +export class DatabaseFetcher { + /** + * Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file. + * + * @param databaseManager the DatabaseManager + * @param storagePath where to store the unzipped database. + */ + public async promptImportInternetDatabase( + commandManager: AppCommandManager, + databaseManager: DatabaseManager, + storagePath: string, + progress: ProgressCallback, + cli: CodeQLCliServer, + ): Promise { + const databaseUrl = await window.showInputBox({ + prompt: "Enter URL of zipfile of database to download", + }); + if (!databaseUrl) { + return; + } - validateUrl(databaseUrl); - - const item = await databaseArchiveFetcher( - databaseUrl, - {}, - databaseManager, - storagePath, - undefined, - { - type: "url", - url: databaseUrl, - }, - progress, - cli, - ); - - if (item) { - await commandManager.execute("codeQLDatabases.focus"); - void showAndLogInformationMessage( - extLogger, - "Database downloaded and imported successfully.", + this.validateUrl(databaseUrl); + + const item = await this.databaseArchiveFetcher( + databaseUrl, + {}, + databaseManager, + storagePath, + undefined, + { + type: "url", + url: databaseUrl, + }, + progress, + cli, ); - } - return item; -} -/** - * Prompts a user to fetch a database from GitHub. - * User enters a GitHub repository and then the user is asked which language - * to download (if there is more than one) - * - * @param app the App - * @param databaseManager the DatabaseManager - * @param storagePath where to store the unzipped database. - * @param progress the progress callback - * @param cli the CodeQL CLI server - * @param language the language to download. If undefined, the user will be prompted to choose a language. - * @param makeSelected make the new database selected in the databases panel (default: true) - * @param addSourceArchiveFolder whether to add a workspace folder containing the source archive to the workspace - */ -export async function promptImportGithubDatabase( - app: App, - databaseManager: DatabaseManager, - storagePath: string, - progress: ProgressCallback, - cli: CodeQLCliServer, - language?: string, - makeSelected = true, - addSourceArchiveFolder = addDatabaseSourceToWorkspace(), -): Promise { - const githubRepo = await askForGitHubRepo(progress); - if (!githubRepo) { - return; + if (item) { + await commandManager.execute("codeQLDatabases.focus"); + void showAndLogInformationMessage( + extLogger, + "Database downloaded and imported successfully.", + ); + } + return item; } - const databaseItem = await downloadGitHubDatabase( - githubRepo, - app, - databaseManager, - storagePath, - progress, - cli, - language, - makeSelected, - addSourceArchiveFolder, - ); - - if (databaseItem) { - if (makeSelected) { - await app.commands.execute("codeQLDatabases.focus"); + /** + * Prompts a user to fetch a database from GitHub. + * User enters a GitHub repository and then the user is asked which language + * to download (if there is more than one) + * + * @param app the App + * @param databaseManager the DatabaseManager + * @param storagePath where to store the unzipped database. + * @param progress the progress callback + * @param cli the CodeQL CLI server + * @param language the language to download. If undefined, the user will be prompted to choose a language. + * @param makeSelected make the new database selected in the databases panel (default: true) + * @param addSourceArchiveFolder whether to add a workspace folder containing the source archive to the workspace + */ + public async promptImportGithubDatabase( + app: App, + databaseManager: DatabaseManager, + storagePath: string, + progress: ProgressCallback, + cli: CodeQLCliServer, + language?: string, + makeSelected = true, + addSourceArchiveFolder = addDatabaseSourceToWorkspace(), + ): Promise { + const githubRepo = await this.askForGitHubRepo(progress); + if (!githubRepo) { + return; } - void showAndLogInformationMessage( - extLogger, - "Database downloaded and imported successfully.", + + const databaseItem = await this.downloadGitHubDatabase( + githubRepo, + app, + databaseManager, + storagePath, + progress, + cli, + language, + makeSelected, + addSourceArchiveFolder, ); - return databaseItem; - } - return; -} + if (databaseItem) { + if (makeSelected) { + await app.commands.execute("codeQLDatabases.focus"); + } + void showAndLogInformationMessage( + extLogger, + "Database downloaded and imported successfully.", + ); + return databaseItem; + } -export async function askForGitHubRepo( - progress?: ProgressCallback, - suggestedValue?: string, -): Promise { - progress?.({ - message: "Choose repository", - step: 1, - maxStep: 2, - }); - - const options: InputBoxOptions = { - title: - 'Enter a GitHub repository URL or "name with owner" (e.g. https://github.com/github/codeql or github/codeql)', - placeHolder: "https://github.com// or /", - ignoreFocusOut: true, - }; - - if (suggestedValue) { - options.value = suggestedValue; + return; } - return await window.showInputBox(options); -} + public async askForGitHubRepo( + progress?: ProgressCallback, + suggestedValue?: string, + ): Promise { + progress?.({ + message: "Choose repository", + step: 1, + maxStep: 2, + }); + + const options: InputBoxOptions = { + title: + 'Enter a GitHub repository URL or "name with owner" (e.g. https://github.com/github/codeql or github/codeql)', + placeHolder: "https://github.com// or /", + ignoreFocusOut: true, + }; + + if (suggestedValue) { + options.value = suggestedValue; + } -/** - * Downloads a database from GitHub - * - * @param githubRepo the GitHub repository to download the database from - * @param app the App - * @param databaseManager the DatabaseManager - * @param storagePath where to store the unzipped database. - * @param progress the progress callback - * @param cli the CodeQL CLI server - * @param language the language to download. If undefined, the user will be prompted to choose a language. - * @param makeSelected make the new database selected in the databases panel (default: true) - * @param addSourceArchiveFolder whether to add a workspace folder containing the source archive to the workspace - **/ -export async function downloadGitHubDatabase( - githubRepo: string, - app: App, - databaseManager: DatabaseManager, - storagePath: string, - progress: ProgressCallback, - cli: CodeQLCliServer, - language?: string, - makeSelected = true, - addSourceArchiveFolder = addDatabaseSourceToWorkspace(), -): Promise { - const nwo = getNwoFromGitHubUrl(githubRepo) || githubRepo; - if (!isValidGitHubNwo(nwo)) { - throw new Error(`Invalid GitHub repository: ${githubRepo}`); + return await window.showInputBox(options); } - const credentials = isCanary() ? app.credentials : undefined; + /** + * Downloads a database from GitHub + * + * @param githubRepo the GitHub repository to download the database from + * @param app the App + * @param databaseManager the DatabaseManager + * @param storagePath where to store the unzipped database. + * @param progress the progress callback + * @param cli the CodeQL CLI server + * @param language the language to download. If undefined, the user will be prompted to choose a language. + * @param makeSelected make the new database selected in the databases panel (default: true) + * @param addSourceArchiveFolder whether to add a workspace folder containing the source archive to the workspace + **/ + public async downloadGitHubDatabase( + githubRepo: string, + app: App, + databaseManager: DatabaseManager, + storagePath: string, + progress: ProgressCallback, + cli: CodeQLCliServer, + language?: string, + makeSelected = true, + addSourceArchiveFolder = addDatabaseSourceToWorkspace(), + ): Promise { + const nwo = getNwoFromGitHubUrl(githubRepo) || githubRepo; + if (!isValidGitHubNwo(nwo)) { + throw new Error(`Invalid GitHub repository: ${githubRepo}`); + } - const octokit = credentials - ? await credentials.getOctokit() - : new AppOctokit(); + const credentials = isCanary() ? app.credentials : undefined; - const result = await convertGithubNwoToDatabaseUrl( - nwo, - octokit, - progress, - language, - ); - if (!result) { - return; - } + const octokit = credentials + ? await credentials.getOctokit() + : new AppOctokit(); - const { databaseUrl, name, owner, databaseId, databaseCreatedAt, commitOid } = - result; - - return downloadGitHubDatabaseFromUrl( - databaseUrl, - databaseId, - databaseCreatedAt, - commitOid, - owner, - name, - octokit, - progress, - databaseManager, - storagePath, - cli, - makeSelected, - addSourceArchiveFolder, - ); -} + const result = await convertGithubNwoToDatabaseUrl( + nwo, + octokit, + progress, + language, + ); + if (!result) { + return; + } -export async function downloadGitHubDatabaseFromUrl( - databaseUrl: string, - databaseId: number, - databaseCreatedAt: string, - commitOid: string | null, - owner: string, - name: string, - octokit: Octokit, - progress: ProgressCallback, - databaseManager: DatabaseManager, - storagePath: string, - cli: CodeQLCliServer, - makeSelected = true, - addSourceArchiveFolder = true, -): Promise { - /** - * The 'token' property of the token object returned by `octokit.auth()`. - * The object is undocumented, but looks something like this: - * { - * token: 'xxxx', - * tokenType: 'oauth', - * type: 'token', - * } - * We only need the actual token string. - */ - const octokitToken = ((await octokit.auth()) as { token: string })?.token; - return await databaseArchiveFetcher( - databaseUrl, - { - Accept: "application/zip", - Authorization: octokitToken ? `Bearer ${octokitToken}` : "", - }, - databaseManager, - storagePath, - `${owner}/${name}`, - { - type: "github", - repository: `${owner}/${name}`, + const { + databaseUrl, + name, + owner, databaseId, databaseCreatedAt, commitOid, - }, - progress, - cli, - makeSelected, - addSourceArchiveFolder, - ); -} + } = result; -/** - * Imports a database from a local archive. - * - * @param databaseUrl the file url of the archive to import - * @param databaseManager the DatabaseManager - * @param storagePath where to store the unzipped database. - * @param cli the CodeQL CLI server - */ -export async function importArchiveDatabase( - commandManager: AppCommandManager, - databaseUrl: string, - databaseManager: DatabaseManager, - storagePath: string, - progress: ProgressCallback, - cli: CodeQLCliServer, -): Promise { - try { - const item = await databaseArchiveFetcher( + return this.downloadGitHubDatabaseFromUrl( databaseUrl, - {}, + databaseId, + databaseCreatedAt, + commitOid, + owner, + name, + octokit, + progress, databaseManager, storagePath, - undefined, + cli, + makeSelected, + addSourceArchiveFolder, + ); + } + + public async downloadGitHubDatabaseFromUrl( + databaseUrl: string, + databaseId: number, + databaseCreatedAt: string, + commitOid: string | null, + owner: string, + name: string, + octokit: Octokit, + progress: ProgressCallback, + databaseManager: DatabaseManager, + storagePath: string, + cli: CodeQLCliServer, + makeSelected = true, + addSourceArchiveFolder = true, + ): Promise { + /** + * The 'token' property of the token object returned by `octokit.auth()`. + * The object is undocumented, but looks something like this: + * { + * token: 'xxxx', + * tokenType: 'oauth', + * type: 'token', + * } + * We only need the actual token string. + */ + const octokitToken = ((await octokit.auth()) as { token: string })?.token; + return await this.databaseArchiveFetcher( + databaseUrl, + { + Accept: "application/zip", + Authorization: octokitToken ? `Bearer ${octokitToken}` : "", + }, + databaseManager, + storagePath, + `${owner}/${name}`, { - type: "archive", - path: databaseUrl, + type: "github", + repository: `${owner}/${name}`, + databaseId, + databaseCreatedAt, + commitOid, }, progress, cli, + makeSelected, + addSourceArchiveFolder, ); - if (item) { - await commandManager.execute("codeQLDatabases.focus"); - void showAndLogInformationMessage( - extLogger, - "Database unzipped and imported successfully.", - ); - } - return item; - } catch (e) { - if (getErrorMessage(e).includes("unexpected end of file")) { - throw new Error( - "Database is corrupt or too large. Try unzipping outside of VS Code and importing the unzipped folder instead.", - ); - } else { - // delegate - throw e; - } } -} -/** - * Fetches an archive database. The database might be on the internet - * or in the local filesystem. - * - * @param databaseUrl URL from which to grab the database - * @param requestHeaders Headers to send with the request - * @param databaseManager the DatabaseManager - * @param storagePath where to store the unzipped database. - * @param nameOverride a name for the database that overrides the default - * @param origin the origin of the database - * @param progress callback to send progress messages to - * @param cli the CodeQL CLI server - * @param makeSelected make the new database selected in the databases panel (default: true) - * @param addSourceArchiveFolder whether to add a workspace folder containing the source archive to the workspace - */ -async function databaseArchiveFetcher( - databaseUrl: string, - requestHeaders: { [key: string]: string }, - databaseManager: DatabaseManager, - storagePath: string, - nameOverride: string | undefined, - origin: DatabaseOrigin, - progress: ProgressCallback, - cli: CodeQLCliServer, - makeSelected = true, - addSourceArchiveFolder = addDatabaseSourceToWorkspace(), -): Promise { - progress({ - message: "Getting database", - step: 1, - maxStep: 4, - }); - if (!storagePath) { - throw new Error("No storage path specified."); - } - await ensureDir(storagePath); - const unzipPath = await getStorageFolder( - storagePath, - databaseUrl, - nameOverride, - ); - - if (isFile(databaseUrl)) { - await readAndUnzip(databaseUrl, unzipPath, cli, progress); - } else { - await fetchAndUnzip(databaseUrl, requestHeaders, unzipPath, cli, progress); + /** + * Imports a database from a local archive. + * + * @param databaseUrl the file url of the archive to import + * @param databaseManager the DatabaseManager + * @param storagePath where to store the unzipped database. + * @param cli the CodeQL CLI server + */ + public async importArchiveDatabase( + commandManager: AppCommandManager, + databaseUrl: string, + databaseManager: DatabaseManager, + storagePath: string, + progress: ProgressCallback, + cli: CodeQLCliServer, + ): Promise { + try { + const item = await this.databaseArchiveFetcher( + databaseUrl, + {}, + databaseManager, + storagePath, + undefined, + { + type: "archive", + path: databaseUrl, + }, + progress, + cli, + ); + if (item) { + await commandManager.execute("codeQLDatabases.focus"); + void showAndLogInformationMessage( + extLogger, + "Database unzipped and imported successfully.", + ); + } + return item; + } catch (e) { + if (getErrorMessage(e).includes("unexpected end of file")) { + throw new Error( + "Database is corrupt or too large. Try unzipping outside of VS Code and importing the unzipped folder instead.", + ); + } else { + // delegate + throw e; + } + } } - progress({ - message: "Opening database", - step: 3, - maxStep: 4, - }); - - // find the path to the database. The actual database might be in a sub-folder - const dbPath = await findDirWithFile( - unzipPath, - ".dbinfo", - "codeql-database.yml", - ); - if (dbPath) { + /** + * Fetches an archive database. The database might be on the internet + * or in the local filesystem. + * + * @param databaseUrl URL from which to grab the database + * @param requestHeaders Headers to send with the request + * @param databaseManager the DatabaseManager + * @param storagePath where to store the unzipped database. + * @param nameOverride a name for the database that overrides the default + * @param origin the origin of the database + * @param progress callback to send progress messages to + * @param cli the CodeQL CLI server + * @param makeSelected make the new database selected in the databases panel (default: true) + * @param addSourceArchiveFolder whether to add a workspace folder containing the source archive to the workspace + */ + private async databaseArchiveFetcher( + databaseUrl: string, + requestHeaders: { [key: string]: string }, + databaseManager: DatabaseManager, + storagePath: string, + nameOverride: string | undefined, + origin: DatabaseOrigin, + progress: ProgressCallback, + cli: CodeQLCliServer, + makeSelected = true, + addSourceArchiveFolder = addDatabaseSourceToWorkspace(), + ): Promise { progress({ - message: "Validating and fixing source location", - step: 4, + message: "Getting database", + step: 1, maxStep: 4, }); - await ensureZippedSourceLocation(dbPath); - - const item = await databaseManager.openDatabase( - Uri.file(dbPath), - origin, - makeSelected, + if (!storagePath) { + throw new Error("No storage path specified."); + } + await ensureDir(storagePath); + const unzipPath = await this.getStorageFolder( + storagePath, + databaseUrl, nameOverride, - { - addSourceArchiveFolder, - extensionManagedLocation: unzipPath, - }, ); - return item; - } else { - throw new Error("Database not found in archive."); - } -} -// The number of tries to use when generating a unique filename before -// giving up and using a nanoid. -const DUPLICATE_FILENAMES_TRIES = 10_000; + if (this.isFile(databaseUrl)) { + await this.readAndUnzip(databaseUrl, unzipPath, cli, progress); + } else { + await this.fetchAndUnzip( + databaseUrl, + requestHeaders, + unzipPath, + cli, + progress, + ); + } + + progress({ + message: "Opening database", + step: 3, + maxStep: 4, + }); -async function getStorageFolder( - storagePath: string, - urlStr: string, - nameOverrride?: string, -) { - let lastName: string; - - if (nameOverrride) { - lastName = createFilenameFromString(nameOverrride); - } else { - // we need to generate a folder name for the unzipped archive, - // this needs to be human readable since we may use this name as the initial - // name for the database - const url = Uri.parse(urlStr); - // MacOS has a max filename length of 255 - // and remove a few extra chars in case we need to add a counter at the end. - lastName = basename(url.path).substring(0, 250); - if (lastName.endsWith(".zip")) { - lastName = lastName.substring(0, lastName.length - 4); + // find the path to the database. The actual database might be in a sub-folder + const dbPath = await findDirWithFile( + unzipPath, + ".dbinfo", + "codeql-database.yml", + ); + if (dbPath) { + progress({ + message: "Validating and fixing source location", + step: 4, + maxStep: 4, + }); + await this.ensureZippedSourceLocation(dbPath); + + const item = await databaseManager.openDatabase( + Uri.file(dbPath), + origin, + makeSelected, + nameOverride, + { + addSourceArchiveFolder, + extensionManagedLocation: unzipPath, + }, + ); + return item; + } else { + throw new Error("Database not found in archive."); } } - const realpath = await fs_realpath(storagePath); - let folderName = lastName; - - // get all existing files instead of calling pathExists on every - // single combination of realpath and folderName - const existingFiles = await readdir(realpath); - - // avoid overwriting existing folders - let counter = 0; - while (existingFiles.includes(basename(folderName))) { - counter++; - - if (counter <= DUPLICATE_FILENAMES_TRIES) { - // First try to use a counter to make the name unique. - folderName = `${lastName}-${counter}`; - } else if (counter <= DUPLICATE_FILENAMES_TRIES + 5) { - // If there are more than 10,000 similarly named databases, - // give up on using a counter and use a random string instead. - folderName = `${lastName}-${nanoid()}`; + private async getStorageFolder( + storagePath: string, + urlStr: string, + nameOverrride?: string, + ) { + let lastName: string; + + if (nameOverrride) { + lastName = createFilenameFromString(nameOverrride); } else { - // This should almost never happen, but just in case, we don't want to - // get stuck in an infinite loop. - throw new Error( - "Could not find a unique name for downloaded database. Please remove some databases and try again.", - ); + // we need to generate a folder name for the unzipped archive, + // this needs to be human readable since we may use this name as the initial + // name for the database + const url = Uri.parse(urlStr); + // MacOS has a max filename length of 255 + // and remove a few extra chars in case we need to add a counter at the end. + lastName = basename(url.path).substring(0, 250); + if (lastName.endsWith(".zip")) { + lastName = lastName.substring(0, lastName.length - 4); + } } - } - return join(realpath, folderName); -} -function validateUrl(databaseUrl: string) { - let uri; - try { - uri = Uri.parse(databaseUrl, true); - } catch (e) { - throw new Error(`Invalid url: ${databaseUrl}`); + const realpath = await fs_realpath(storagePath); + let folderName = lastName; + + // get all existing files instead of calling pathExists on every + // single combination of realpath and folderName + const existingFiles = await readdir(realpath); + + // avoid overwriting existing folders + let counter = 0; + while (existingFiles.includes(basename(folderName))) { + counter++; + + if (counter <= DUPLICATE_FILENAMES_TRIES) { + // First try to use a counter to make the name unique. + folderName = `${lastName}-${counter}`; + } else if (counter <= DUPLICATE_FILENAMES_TRIES + 5) { + // If there are more than 10,000 similarly named databases, + // give up on using a counter and use a random string instead. + folderName = `${lastName}-${nanoid()}`; + } else { + // This should almost never happen, but just in case, we don't want to + // get stuck in an infinite loop. + throw new Error( + "Could not find a unique name for downloaded database. Please remove some databases and try again.", + ); + } + } + return join(realpath, folderName); } - if (!allowHttp() && uri.scheme !== "https") { - throw new Error("Must use https for downloading a database."); + private validateUrl(databaseUrl: string) { + let uri; + try { + uri = Uri.parse(databaseUrl, true); + } catch (e) { + throw new Error(`Invalid url: ${databaseUrl}`); + } + + if (!allowHttp() && uri.scheme !== "https") { + throw new Error("Must use https for downloading a database."); + } } -} -async function readAndUnzip( - zipUrl: string, - unzipPath: string, - cli: CodeQLCliServer, - progress?: ProgressCallback, -) { - const zipFile = Uri.parse(zipUrl).fsPath; - progress?.({ - maxStep: 10, - step: 9, - message: `Unzipping into ${basename(unzipPath)}`, - }); - - await cli.databaseUnbundle(zipFile, unzipPath); -} + private async readAndUnzip( + zipUrl: string, + unzipPath: string, + cli: CodeQLCliServer, + progress?: ProgressCallback, + ) { + const zipFile = Uri.parse(zipUrl).fsPath; + progress?.({ + maxStep: 10, + step: 9, + message: `Unzipping into ${basename(unzipPath)}`, + }); -async function fetchAndUnzip( - databaseUrl: string, - requestHeaders: { [key: string]: string }, - unzipPath: string, - cli: CodeQLCliServer, - progress?: ProgressCallback, -) { - // Although it is possible to download and stream directly to an unzipped directory, - // we need to avoid this for two reasons. The central directory is located at the - // end of the zip file. It is the source of truth of the content locations. Individual - // file headers may be incorrect. Additionally, saving to file first will reduce memory - // pressure compared with unzipping while downloading the archive. - - const archivePath = join(tmpDir.name, `archive-${Date.now()}.zip`); - - progress?.({ - maxStep: 3, - message: "Downloading database", - step: 1, - }); - - const { - signal, - onData, - dispose: disposeTimeout, - } = createTimeoutSignal(downloadTimeout()); - - let response: Response; - try { - response = await checkForFailingResponse( - await fetch(databaseUrl, { - headers: requestHeaders, - signal, - }), - "Error downloading database", - ); - } catch (e) { - disposeTimeout(); + await cli.databaseUnbundle(zipFile, unzipPath); + } - if (e instanceof AbortError) { - const thrownError = new AbortError("The request timed out."); - thrownError.stack = e.stack; - throw thrownError; - } + private async fetchAndUnzip( + databaseUrl: string, + requestHeaders: { [key: string]: string }, + unzipPath: string, + cli: CodeQLCliServer, + progress?: ProgressCallback, + ) { + // Although it is possible to download and stream directly to an unzipped directory, + // we need to avoid this for two reasons. The central directory is located at the + // end of the zip file. It is the source of truth of the content locations. Individual + // file headers may be incorrect. Additionally, saving to file first will reduce memory + // pressure compared with unzipping while downloading the archive. + + const archivePath = join(tmpDir.name, `archive-${Date.now()}.zip`); + + progress?.({ + maxStep: 3, + message: "Downloading database", + step: 1, + }); - throw e; - } + const { + signal, + onData, + dispose: disposeTimeout, + } = createTimeoutSignal(downloadTimeout()); + + let response: Response; + try { + response = await this.checkForFailingResponse( + await fetch(databaseUrl, { + headers: requestHeaders, + signal, + }), + "Error downloading database", + ); + } catch (e) { + disposeTimeout(); - const archiveFileStream = createWriteStream(archivePath); + if (e instanceof AbortError) { + const thrownError = new AbortError("The request timed out."); + thrownError.stack = e.stack; + throw thrownError; + } - const contentLength = response.headers.get("content-length"); - const totalNumBytes = contentLength ? parseInt(contentLength, 10) : undefined; - reportStreamProgress( - response.body, - "Downloading database", - totalNumBytes, - progress, - ); + throw e; + } - response.body.on("data", onData); + const archiveFileStream = createWriteStream(archivePath); - try { - await new Promise((resolve, reject) => { - response.body - .pipe(archiveFileStream) - .on("finish", resolve) - .on("error", reject); + const contentLength = response.headers.get("content-length"); + const totalNumBytes = contentLength + ? parseInt(contentLength, 10) + : undefined; + reportStreamProgress( + response.body, + "Downloading database", + totalNumBytes, + progress, + ); - // If an error occurs on the body, we also want to reject the promise (e.g. during a timeout error). - response.body.on("error", reject); - }); - } catch (e) { - // Close and remove the file if an error occurs - archiveFileStream.close(() => { - void remove(archivePath); - }); + response.body.on("data", onData); + + try { + await new Promise((resolve, reject) => { + response.body + .pipe(archiveFileStream) + .on("finish", resolve) + .on("error", reject); + + // If an error occurs on the body, we also want to reject the promise (e.g. during a timeout error). + response.body.on("error", reject); + }); + } catch (e) { + // Close and remove the file if an error occurs + archiveFileStream.close(() => { + void remove(archivePath); + }); + + if (e instanceof AbortError) { + const thrownError = new AbortError("The download timed out."); + thrownError.stack = e.stack; + throw thrownError; + } - if (e instanceof AbortError) { - const thrownError = new AbortError("The download timed out."); - thrownError.stack = e.stack; - throw thrownError; + throw e; + } finally { + disposeTimeout(); } - throw e; - } finally { - disposeTimeout(); - } + await this.readAndUnzip( + Uri.file(archivePath).toString(true), + unzipPath, + cli, + progress, + ); - await readAndUnzip( - Uri.file(archivePath).toString(true), - unzipPath, - cli, - progress, - ); + // remove archivePath eagerly since these archives can be large. + await remove(archivePath); + } - // remove archivePath eagerly since these archives can be large. - await remove(archivePath); -} + private async checkForFailingResponse( + response: Response, + errorMessage: string, + ): Promise { + if (response.ok) { + return response; + } -async function checkForFailingResponse( - response: Response, - errorMessage: string, -): Promise { - if (response.ok) { - return response; + // An error downloading the database. Attempt to extract the reason behind it. + const text = await response.text(); + let msg: string; + try { + const obj = JSON.parse(text); + msg = + obj.error || obj.message || obj.reason || JSON.stringify(obj, null, 2); + } catch (e) { + msg = text; + } + throw new Error(`${errorMessage}.\n\nReason: ${msg}`); } - // An error downloading the database. Attempt to extract the reason behind it. - const text = await response.text(); - let msg: string; - try { - const obj = JSON.parse(text); - msg = - obj.error || obj.message || obj.reason || JSON.stringify(obj, null, 2); - } catch (e) { - msg = text; + private isFile(databaseUrl: string) { + return Uri.parse(databaseUrl).scheme === "file"; } - throw new Error(`${errorMessage}.\n\nReason: ${msg}`); -} -function isFile(databaseUrl: string) { - return Uri.parse(databaseUrl).scheme === "file"; -} - -/** - * Databases created by the old odasa tool will not have a zipped - * source location. However, this extension works better if sources - * are zipped. - * - * This function ensures that the source location is zipped. If the - * `src` folder exists and the `src.zip` file does not, the `src` - * folder will be zipped and then deleted. - * - * @param databasePath The full path to the unzipped database - */ -async function ensureZippedSourceLocation(databasePath: string): Promise { - const srcFolderPath = join(databasePath, "src"); - const srcZipPath = `${srcFolderPath}.zip`; - - if ((await pathExists(srcFolderPath)) && !(await pathExists(srcZipPath))) { - await zip(srcFolderPath, srcZipPath); - await remove(srcFolderPath); + /** + * Databases created by the old odasa tool will not have a zipped + * source location. However, this extension works better if sources + * are zipped. + * + * This function ensures that the source location is zipped. If the + * `src` folder exists and the `src.zip` file does not, the `src` + * folder will be zipped and then deleted. + * + * @param databasePath The full path to the unzipped database + */ + private async ensureZippedSourceLocation( + databasePath: string, + ): Promise { + const srcFolderPath = join(databasePath, "src"); + const srcZipPath = `${srcFolderPath}.zip`; + + if ((await pathExists(srcFolderPath)) && !(await pathExists(srcZipPath))) { + await zip(srcFolderPath, srcZipPath); + await remove(srcFolderPath); + } } } diff --git a/extensions/ql-vscode/src/databases/github-databases/download.ts b/extensions/ql-vscode/src/databases/github-databases/download.ts index 21a2e12231a..89dc7527e89 100644 --- a/extensions/ql-vscode/src/databases/github-databases/download.ts +++ b/extensions/ql-vscode/src/databases/github-databases/download.ts @@ -2,7 +2,7 @@ import { window } from "vscode"; import type { Octokit } from "@octokit/rest"; import { showNeverAskAgainDialog } from "../../common/vscode/dialog"; import { getLanguageDisplayName } from "../../common/query-language"; -import { downloadGitHubDatabaseFromUrl } from "../database-fetcher"; +import type { DatabaseFetcher } from "../database-fetcher"; import { withProgress } from "../../common/vscode/progress"; import type { DatabaseManager } from "../local-databases"; import type { CodeQLCliServer } from "../../codeql-cli/cli"; @@ -59,6 +59,7 @@ export async function downloadDatabaseFromGitHub( repo: string, databases: CodeqlDatabase[], databaseManager: DatabaseManager, + databaseFetcher: DatabaseFetcher, storagePath: string, cliServer: CodeQLCliServer, commandManager: AppCommandManager, @@ -72,7 +73,7 @@ export async function downloadDatabaseFromGitHub( selectedDatabases.map((database) => withProgress( async (progress) => { - await downloadGitHubDatabaseFromUrl( + await databaseFetcher.downloadGitHubDatabaseFromUrl( database.url, database.id, database.created_at, diff --git a/extensions/ql-vscode/src/databases/github-databases/github-databases-module.ts b/extensions/ql-vscode/src/databases/github-databases/github-databases-module.ts index 1b89840e854..382d638fa42 100644 --- a/extensions/ql-vscode/src/databases/github-databases/github-databases-module.ts +++ b/extensions/ql-vscode/src/databases/github-databases/github-databases-module.ts @@ -24,6 +24,7 @@ import { isNewerDatabaseAvailable, } from "./updates"; import type { Octokit } from "@octokit/rest"; +import type { DatabaseFetcher } from "../database-fetcher"; export class GitHubDatabasesModule extends DisposableObject { /** @@ -33,6 +34,7 @@ export class GitHubDatabasesModule extends DisposableObject { constructor( private readonly app: App, private readonly databaseManager: DatabaseManager, + private readonly databaseFetcher: DatabaseFetcher, private readonly databaseStoragePath: string, private readonly cliServer: CodeQLCliServer, private readonly config: GitHubDatabaseConfig, @@ -43,6 +45,7 @@ export class GitHubDatabasesModule extends DisposableObject { public static async initialize( app: App, databaseManager: DatabaseManager, + databaseFetcher: DatabaseFetcher, databaseStoragePath: string, cliServer: CodeQLCliServer, config: GitHubDatabaseConfig, @@ -50,6 +53,7 @@ export class GitHubDatabasesModule extends DisposableObject { const githubDatabasesModule = new GitHubDatabasesModule( app, databaseManager, + databaseFetcher, databaseStoragePath, cliServer, config, @@ -186,6 +190,7 @@ export class GitHubDatabasesModule extends DisposableObject { repo, databases, this.databaseManager, + this.databaseFetcher, this.databaseStoragePath, this.cliServer, this.app.commands, @@ -212,6 +217,7 @@ export class GitHubDatabasesModule extends DisposableObject { repo, databaseUpdates, this.databaseManager, + this.databaseFetcher, this.databaseStoragePath, this.cliServer, this.app.commands, diff --git a/extensions/ql-vscode/src/databases/github-databases/updates.ts b/extensions/ql-vscode/src/databases/github-databases/updates.ts index 317870f5c41..349b317ce04 100644 --- a/extensions/ql-vscode/src/databases/github-databases/updates.ts +++ b/extensions/ql-vscode/src/databases/github-databases/updates.ts @@ -5,7 +5,7 @@ import type { CodeQLCliServer } from "../../codeql-cli/cli"; import type { AppCommandManager } from "../../common/commands"; import { getLanguageDisplayName } from "../../common/query-language"; import { showNeverAskAgainDialog } from "../../common/vscode/dialog"; -import { downloadGitHubDatabaseFromUrl } from "../database-fetcher"; +import type { DatabaseFetcher } from "../database-fetcher"; import { withProgress } from "../../common/vscode/progress"; import { window } from "vscode"; import type { GitHubDatabaseConfig } from "../../config"; @@ -156,6 +156,7 @@ export async function downloadDatabaseUpdateFromGitHub( repo: string, updates: DatabaseUpdate[], databaseManager: DatabaseManager, + databaseFetcher: DatabaseFetcher, storagePath: string, cliServer: CodeQLCliServer, commandManager: AppCommandManager, @@ -179,21 +180,22 @@ export async function downloadDatabaseUpdateFromGitHub( return withProgress( async (progress) => { - const newDatabase = await downloadGitHubDatabaseFromUrl( - database.url, - database.id, - database.created_at, - database.commit_oid ?? null, - owner, - repo, - octokit, - progress, - databaseManager, - storagePath, - cliServer, - databaseManager.currentDatabaseItem === update.databaseItem, - update.databaseItem.hasSourceArchiveInExplorer(), - ); + const newDatabase = + await databaseFetcher.downloadGitHubDatabaseFromUrl( + database.url, + database.id, + database.created_at, + database.commit_oid ?? null, + owner, + repo, + octokit, + progress, + databaseManager, + storagePath, + cliServer, + databaseManager.currentDatabaseItem === update.databaseItem, + update.databaseItem.hasSourceArchiveInExplorer(), + ); if (newDatabase === undefined) { return; } diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index c5230ce72cb..59cfb4d7206 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -42,11 +42,7 @@ import { showAndLogExceptionWithTelemetry, showAndLogErrorMessage, } from "../common/logging"; -import { - importArchiveDatabase, - promptImportGithubDatabase, - promptImportInternetDatabase, -} from "./database-fetcher"; +import { DatabaseFetcher } from "./database-fetcher"; import { asError, asyncFilter, getErrorMessage } from "../common/helpers-pure"; import type { QueryRunner } from "../query-server"; import type { App } from "../common/app"; @@ -540,7 +536,7 @@ export class DatabaseUI extends DisposableObject { private async handleChooseDatabaseInternet(): Promise { return withProgress( async (progress) => { - await promptImportInternetDatabase( + await new DatabaseFetcher().promptImportInternetDatabase( this.app.commands, this.databaseManager, this.storagePath, @@ -557,7 +553,7 @@ export class DatabaseUI extends DisposableObject { private async handleChooseDatabaseGithub(): Promise { return withProgress( async (progress) => { - await promptImportGithubDatabase( + await new DatabaseFetcher().promptImportGithubDatabase( this.app, this.databaseManager, this.storagePath, @@ -712,7 +708,7 @@ export class DatabaseUI extends DisposableObject { try { // Assume user has selected an archive if the file has a .zip extension if (uri.path.endsWith(".zip")) { - await importArchiveDatabase( + await new DatabaseFetcher().importArchiveDatabase( this.app.commands, uri.toString(true), this.databaseManager, @@ -959,7 +955,7 @@ export class DatabaseUI extends DisposableObject { } else { // we are selecting a database archive. Must unzip into a workspace-controlled area // before importing. - return await importArchiveDatabase( + return await new DatabaseFetcher().importArchiveDatabase( this.app.commands, uri.toString(true), this.databaseManager, diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 53a3b12dfd4..5658788e582 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -133,6 +133,7 @@ import { OpenReferencedFileCodeLensProvider } from "./local-queries/open-referen import { LanguageContextStore } from "./language-context-store"; import { LanguageSelectionPanel } from "./language-selection-panel/language-selection-panel"; import { GitHubDatabasesModule } from "./databases/github-databases"; +import { DatabaseFetcher } from "./databases/database-fetcher"; /** * extension.ts @@ -881,6 +882,7 @@ async function activateWithInstalledDistribution( await GitHubDatabasesModule.initialize( app, dbm, + new DatabaseFetcher(), getContextStoragePath(ctx), cliServer, githubDatabaseConfigListener, diff --git a/extensions/ql-vscode/src/local-queries/local-queries.ts b/extensions/ql-vscode/src/local-queries/local-queries.ts index ddc29db949b..ea1c5f8e864 100644 --- a/extensions/ql-vscode/src/local-queries/local-queries.ts +++ b/extensions/ql-vscode/src/local-queries/local-queries.ts @@ -51,6 +51,7 @@ import type { QueryTreeViewItem } from "../queries-panel/query-tree-view-item"; import { tryGetQueryLanguage } from "../common/query-language"; import type { LanguageContextStore } from "../language-context-store"; import type { ExtensionApp } from "../common/vscode/vscode-app"; +import { DatabaseFetcher } from "../databases/database-fetcher"; export enum QuickEvalType { None, @@ -330,6 +331,7 @@ export class LocalQueries extends DisposableObject { progress, this.app, this.databaseManager, + new DatabaseFetcher(), contextStoragePath, this.selectedQueryTreeViewItems, language, diff --git a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts index 0079709bbf6..5305fe40a01 100644 --- a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts +++ b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts @@ -19,10 +19,7 @@ import { UserCancellationException, withProgress, } from "../common/vscode/progress"; -import { - askForGitHubRepo, - downloadGitHubDatabase, -} from "../databases/database-fetcher"; +import type { DatabaseFetcher } from "../databases/database-fetcher"; import { getQlPackLocation, isCodespacesTemplate, @@ -62,6 +59,7 @@ export class SkeletonQueryWizard { private readonly progress: ProgressCallback, private readonly app: App, private readonly databaseManager: DatabaseManager, + private readonly databaseFetcher: DatabaseFetcher, private readonly databaseStoragePath: string | undefined, private readonly selectedItems: readonly QueryTreeViewItem[], private language: QueryLanguage | undefined = undefined, @@ -378,13 +376,16 @@ export class SkeletonQueryWizard { }); const githubRepoNwo = QUERY_LANGUAGE_TO_DATABASE_REPO[this.language]; - const chosenRepo = await askForGitHubRepo(undefined, githubRepoNwo); + const chosenRepo = await this.databaseFetcher.askForGitHubRepo( + undefined, + githubRepoNwo, + ); if (!chosenRepo) { throw new UserCancellationException("No GitHub repository provided"); } - await downloadGitHubDatabase( + await this.databaseFetcher.downloadGitHubDatabase( chosenRepo, this.app, this.databaseManager, diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 0ad90435813..2e72981acd7 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -29,7 +29,7 @@ import type { } from "../databases/local-databases"; import type { CodeQLCliServer } from "../codeql-cli/cli"; import { asError, assertNever, getErrorMessage } from "../common/helpers-pure"; -import { promptImportGithubDatabase } from "../databases/database-fetcher"; +import { DatabaseFetcher } from "../databases/database-fetcher"; import type { App } from "../common/app"; import { redactableError } from "../common/errors"; import { @@ -916,16 +916,17 @@ export class ModelEditorView extends AbstractWebview< // the user to import the library database. We need to have the database // imported to the query server, so we need to register it to our workspace. const makeSelected = false; - const addedDatabase = await promptImportGithubDatabase( - this.app, - this.databaseManager, - this.app.workspaceStoragePath ?? this.app.globalStoragePath, - progress, - this.cliServer, - this.databaseItem.language, - makeSelected, - false, - ); + const addedDatabase = + await new DatabaseFetcher().promptImportGithubDatabase( + this.app, + this.databaseManager, + this.app.workspaceStoragePath ?? this.app.globalStoragePath, + progress, + this.cliServer, + this.databaseItem.language, + makeSelected, + false, + ); if (!addedDatabase) { void this.app.logger.log("No database chosen"); return; diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/databases/database-fetcher.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/databases/database-fetcher.test.ts index ed80c1f037b..cc4b6f2d5a4 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/databases/database-fetcher.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/databases/database-fetcher.test.ts @@ -3,10 +3,7 @@ import { Uri, window } from "vscode"; import type { CodeQLCliServer } from "../../../../src/codeql-cli/cli"; import type { DatabaseManager } from "../../../../src/databases/local-databases"; -import { - importArchiveDatabase, - promptImportInternetDatabase, -} from "../../../../src/databases/database-fetcher"; +import { DatabaseFetcher } from "../../../../src/databases/database-fetcher"; import { cleanDatabases, dbLoc, @@ -49,7 +46,7 @@ describe("database-fetcher", () => { describe("importArchiveDatabase", () => { it("should add a database from a folder", async () => { const uri = Uri.file(dbLoc); - let dbItem = await importArchiveDatabase( + let dbItem = await new DatabaseFetcher().importArchiveDatabase( createMockCommandManager(), uri.toString(true), databaseManager, @@ -71,7 +68,7 @@ describe("database-fetcher", () => { // Provide a database URL when prompted inputBoxStub.mockResolvedValue(DB_URL); - let dbItem = await promptImportInternetDatabase( + let dbItem = await new DatabaseFetcher().promptImportInternetDatabase( createMockCommandManager(), databaseManager, storagePath, diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts index 7f4c6d8e2b3..319a0e8cfe9 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts @@ -23,7 +23,7 @@ import type { DatabaseManager, FullDatabaseOptions, } from "../../../../src/databases/local-databases"; -import * as databaseFetcher from "../../../../src/databases/database-fetcher"; +import { DatabaseFetcher } from "../../../../src/databases/database-fetcher"; import { createMockDB } from "../../../factories/databases/databases"; import { asError } from "../../../../src/common/helpers-pure"; import { Setting } from "../../../../src/config"; @@ -42,6 +42,7 @@ describe("SkeletonQueryWizard", () => { let mockApp: App; let wizard: SkeletonQueryWizard; let mockDatabaseManager: DatabaseManager; + let databaseFetcher: DatabaseFetcher; let dir: DirResult; let storagePath: string; let quickPickSpy: jest.SpiedFunction; @@ -56,10 +57,10 @@ describe("SkeletonQueryWizard", () => { typeof QlPackGenerator.prototype.createExampleQlFile >; let downloadGitHubDatabaseSpy: jest.SpiedFunction< - typeof databaseFetcher.downloadGitHubDatabase + DatabaseFetcher["downloadGitHubDatabase"] >; let askForGitHubRepoSpy: jest.SpiedFunction< - typeof databaseFetcher.askForGitHubRepo + DatabaseFetcher["askForGitHubRepo"] >; let openTextDocumentSpy: jest.SpiedFunction< typeof workspace.openTextDocument @@ -115,6 +116,8 @@ describe("SkeletonQueryWizard", () => { }, ] as WorkspaceFolder[]); + databaseFetcher = new DatabaseFetcher(); + quickPickSpy = jest.spyOn(window, "showQuickPick").mockResolvedValueOnce( mockedQuickPickItem({ label: chosenLanguage, @@ -145,6 +148,7 @@ describe("SkeletonQueryWizard", () => { jest.fn(), mockApp, mockDatabaseManager, + databaseFetcher, storagePath, selectedItems, ); @@ -172,6 +176,7 @@ describe("SkeletonQueryWizard", () => { jest.fn(), mockApp, mockDatabaseManager, + databaseFetcher, storagePath, selectedItems, QueryLanguage.Swift, @@ -320,6 +325,7 @@ describe("SkeletonQueryWizard", () => { jest.fn(), mockApp, mockDatabaseManagerWithItems, + databaseFetcher, storagePath, selectedItems, ); @@ -369,6 +375,7 @@ describe("SkeletonQueryWizard", () => { jest.fn(), mockApp, mockDatabaseManagerWithItems, + databaseFetcher, storagePath, selectedItems, ); @@ -504,6 +511,7 @@ describe("SkeletonQueryWizard", () => { jest.fn(), mockApp, mockDatabaseManager, + databaseFetcher, storagePath, selectedItems, QueryLanguage.Javascript, @@ -725,6 +733,7 @@ describe("SkeletonQueryWizard", () => { jest.fn(), mockApp, mockDatabaseManager, + databaseFetcher, storagePath, selectedItems, ); @@ -754,6 +763,7 @@ describe("SkeletonQueryWizard", () => { jest.fn(), mockApp, mockDatabaseManager, + databaseFetcher, storagePath, selectedItems, ); @@ -787,6 +797,7 @@ describe("SkeletonQueryWizard", () => { jest.fn(), mockApp, mockDatabaseManager, + databaseFetcher, storagePath, selectedItems, QueryLanguage.Swift, @@ -830,6 +841,7 @@ describe("SkeletonQueryWizard", () => { jest.fn(), mockApp, mockDatabaseManager, + databaseFetcher, storagePath, selectedItems, ); diff --git a/extensions/ql-vscode/test/vscode-tests/global.helper.ts b/extensions/ql-vscode/test/vscode-tests/global.helper.ts index 36be9878915..b0f85a8ec09 100644 --- a/extensions/ql-vscode/test/vscode-tests/global.helper.ts +++ b/extensions/ql-vscode/test/vscode-tests/global.helper.ts @@ -7,7 +7,7 @@ import type { } from "../../src/databases/local-databases"; import type { CodeQLCliServer } from "../../src/codeql-cli/cli"; import type { CodeQLExtensionInterface } from "../../src/extension"; -import { importArchiveDatabase } from "../../src/databases/database-fetcher"; +import { DatabaseFetcher } from "../../src/databases/database-fetcher"; import { createMockCommandManager } from "../__mocks__/commandsMock"; // This file contains helpers shared between tests that work with an activated extension. @@ -34,7 +34,7 @@ export async function ensureTestDatabase( // Add a database, but make sure the database manager is empty first await cleanDatabases(databaseManager); const uri = Uri.file(dbLoc); - const maybeDbItem = await importArchiveDatabase( + const maybeDbItem = await new DatabaseFetcher().importArchiveDatabase( createMockCommandManager(), uri.toString(true), databaseManager, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/download.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/download.test.ts index df2b329c12d..3768dca4992 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/download.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/download.test.ts @@ -14,7 +14,7 @@ import type { DatabaseManager } from "../../../../../src/databases/local-databas import type { GitHubDatabaseConfig } from "../../../../../src/config"; import type { CodeQLCliServer } from "../../../../../src/codeql-cli/cli"; import { createMockCommandManager } from "../../../../__mocks__/commandsMock"; -import * as databaseFetcher from "../../../../../src/databases/database-fetcher"; +import { DatabaseFetcher } from "../../../../../src/databases/database-fetcher"; import * as dialog from "../../../../../src/common/vscode/dialog"; import type { CodeqlDatabase } from "../../../../../src/databases/github-databases/api"; @@ -97,6 +97,7 @@ describe("downloadDatabaseFromGitHub", () => { const owner = "github"; const repo = "codeql"; let databaseManager: DatabaseManager; + let databaseFetcher: DatabaseFetcher; const storagePath = "/a/b/c/d"; let cliServer: CodeQLCliServer; @@ -117,12 +118,13 @@ describe("downloadDatabaseFromGitHub", () => { let showQuickPickSpy: jest.SpiedFunction; let downloadGitHubDatabaseFromUrlSpy: jest.SpiedFunction< - typeof databaseFetcher.downloadGitHubDatabaseFromUrl + DatabaseFetcher["downloadGitHubDatabaseFromUrl"] >; beforeEach(() => { octokit = mockedObject({}); databaseManager = mockedObject({}); + databaseFetcher = new DatabaseFetcher(); cliServer = mockedObject({}); showQuickPickSpy = jest.spyOn(window, "showQuickPick").mockResolvedValue( @@ -144,6 +146,7 @@ describe("downloadDatabaseFromGitHub", () => { repo, databases, databaseManager, + databaseFetcher, storagePath, cliServer, commandManager, @@ -208,6 +211,7 @@ describe("downloadDatabaseFromGitHub", () => { repo, databases, databaseManager, + databaseFetcher, storagePath, cliServer, commandManager, @@ -264,6 +268,7 @@ describe("downloadDatabaseFromGitHub", () => { repo, databases, databaseManager, + databaseFetcher, storagePath, cliServer, commandManager, @@ -329,6 +334,7 @@ describe("downloadDatabaseFromGitHub", () => { repo, databases, databaseManager, + databaseFetcher, storagePath, cliServer, commandManager, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/github-databases-module.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/github-databases-module.test.ts index d2e605ef103..d5081a1c4d5 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/github-databases-module.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/github-databases-module.test.ts @@ -16,6 +16,7 @@ import * as githubDatabasesApi from "../../../../../src/databases/github-databas import * as githubDatabasesDownload from "../../../../../src/databases/github-databases/download"; import * as githubDatabasesUpdates from "../../../../../src/databases/github-databases/updates"; import type { DatabaseUpdate } from "../../../../../src/databases/github-databases/updates"; +import { DatabaseFetcher } from "../../../../../src/databases/database-fetcher"; describe("GitHubDatabasesModule", () => { describe("promptGitHubRepositoryDownload", () => { @@ -23,6 +24,7 @@ describe("GitHubDatabasesModule", () => { let databaseManager: DatabaseManager; let databaseStoragePath: string; let cliServer: CodeQLCliServer; + let databaseFetcher: DatabaseFetcher; let config: GitHubDatabaseConfig; let gitHubDatabasesModule: GitHubDatabasesModule; @@ -66,6 +68,7 @@ describe("GitHubDatabasesModule", () => { databaseManager = mockEmptyDatabaseManager(); databaseStoragePath = "/a/b/some-path"; cliServer = mockedObject({}); + databaseFetcher = new DatabaseFetcher(); config = mockedObject({ download: "ask", update: "ask", @@ -74,6 +77,7 @@ describe("GitHubDatabasesModule", () => { gitHubDatabasesModule = new GitHubDatabasesModule( app, databaseManager, + databaseFetcher, databaseStoragePath, cliServer, config, @@ -124,6 +128,7 @@ describe("GitHubDatabasesModule", () => { gitHubDatabasesModule = new GitHubDatabasesModule( app, databaseManager, + databaseFetcher, databaseStoragePath, cliServer, config, @@ -207,6 +212,7 @@ describe("GitHubDatabasesModule", () => { repo, databases, databaseManager, + databaseFetcher, databaseStoragePath, cliServer, app.commands, @@ -250,6 +256,7 @@ describe("GitHubDatabasesModule", () => { repo, databaseUpdates, databaseManager, + databaseFetcher, databaseStoragePath, cliServer, app.commands, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/updates.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/updates.test.ts index d612f95ffd1..d693e1a8968 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/updates.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/updates.test.ts @@ -12,7 +12,7 @@ import type { DatabaseManager } from "../../../../../src/databases/local-databas import type { GitHubDatabaseConfig } from "../../../../../src/config"; import type { CodeQLCliServer } from "../../../../../src/codeql-cli/cli"; import { createMockCommandManager } from "../../../../__mocks__/commandsMock"; -import * as databaseFetcher from "../../../../../src/databases/database-fetcher"; +import { DatabaseFetcher } from "../../../../../src/databases/database-fetcher"; import * as dialog from "../../../../../src/common/vscode/dialog"; import type { DatabaseUpdate } from "../../../../../src/databases/github-databases/updates"; import { @@ -344,6 +344,7 @@ describe("downloadDatabaseUpdateFromGitHub", () => { const owner = "github"; const repo = "codeql"; let databaseManager: DatabaseManager; + let databaseFetcher: DatabaseFetcher; const storagePath = "/a/b/c/d"; let cliServer: CodeQLCliServer; const commandManager = createMockCommandManager(); @@ -368,7 +369,7 @@ describe("downloadDatabaseUpdateFromGitHub", () => { let showQuickPickSpy: jest.SpiedFunction; let downloadGitHubDatabaseFromUrlSpy: jest.SpiedFunction< - typeof databaseFetcher.downloadGitHubDatabaseFromUrl + DatabaseFetcher["downloadGitHubDatabaseFromUrl"] >; beforeEach(() => { @@ -376,6 +377,7 @@ describe("downloadDatabaseUpdateFromGitHub", () => { databaseManager = mockedObject({ currentDatabaseItem: mockDatabaseItem(), }); + databaseFetcher = new DatabaseFetcher(); cliServer = mockedObject({}); showQuickPickSpy = jest.spyOn(window, "showQuickPick").mockResolvedValue( @@ -397,6 +399,7 @@ describe("downloadDatabaseUpdateFromGitHub", () => { repo, updates, databaseManager, + databaseFetcher, storagePath, cliServer, commandManager, @@ -476,6 +479,7 @@ describe("downloadDatabaseUpdateFromGitHub", () => { repo, updates, databaseManager, + databaseFetcher, storagePath, cliServer, commandManager, @@ -532,6 +536,7 @@ describe("downloadDatabaseUpdateFromGitHub", () => { repo, updates, databaseManager, + databaseFetcher, storagePath, cliServer, commandManager, @@ -597,6 +602,7 @@ describe("downloadDatabaseUpdateFromGitHub", () => { repo, updates, databaseManager, + databaseFetcher, storagePath, cliServer, commandManager, From 3ee081e8c6f05d571b3038e2d41e539d572541fd Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 7 Mar 2024 12:34:02 +0000 Subject: [PATCH 0124/1237] Move commonly-used field to DatabaseFetcher constructor --- .../src/databases/database-fetcher.ts | 103 ++++-------------- .../databases/github-databases/download.ts | 8 -- .../github-databases-module.ts | 12 -- .../src/databases/github-databases/updates.ts | 6 - .../src/databases/local-databases-ui.ts | 30 ++--- extensions/ql-vscode/src/extension.ts | 4 +- .../src/local-queries/local-queries.ts | 9 +- .../local-queries/skeleton-query-wizard.ts | 9 -- .../src/model-editor/model-editor-view.ts | 23 ++-- .../databases/database-fetcher.test.ts | 19 ++-- .../skeleton-query-wizard.test.ts | 16 +-- .../test/vscode-tests/global.helper.ts | 12 +- .../github-databases/download.test.ts | 43 ++------ .../github-databases-module.test.ts | 16 +-- .../github-databases/updates.test.ts | 39 ++----- 15 files changed, 115 insertions(+), 234 deletions(-) diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index 4c983e43b0f..cbaedabc6bf 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -26,7 +26,6 @@ import { getNwoFromGitHubUrl, isValidGitHubNwo, } from "../common/github-url-identifier-helper"; -import type { AppCommandManager } from "../common/commands"; import { addDatabaseSourceToWorkspace, allowHttp, @@ -48,17 +47,23 @@ const DUPLICATE_FILENAMES_TRIES = 10_000; export class DatabaseFetcher { /** - * Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file. - * + * @param app the App * @param databaseManager the DatabaseManager * @param storagePath where to store the unzipped database. + * @param cli the CodeQL CLI server + **/ + constructor( + private readonly app: App, + private readonly databaseManager: DatabaseManager, + private readonly storagePath: string, + private readonly cli: CodeQLCliServer, + ) {} + + /** + * Prompts a user to fetch a database from a remote location. Database is assumed to be an archive file. */ public async promptImportInternetDatabase( - commandManager: AppCommandManager, - databaseManager: DatabaseManager, - storagePath: string, progress: ProgressCallback, - cli: CodeQLCliServer, ): Promise { const databaseUrl = await window.showInputBox({ prompt: "Enter URL of zipfile of database to download", @@ -72,19 +77,16 @@ export class DatabaseFetcher { const item = await this.databaseArchiveFetcher( databaseUrl, {}, - databaseManager, - storagePath, undefined, { type: "url", url: databaseUrl, }, progress, - cli, ); if (item) { - await commandManager.execute("codeQLDatabases.focus"); + await this.app.commands.execute("codeQLDatabases.focus"); void showAndLogInformationMessage( extLogger, "Database downloaded and imported successfully.", @@ -98,21 +100,13 @@ export class DatabaseFetcher { * User enters a GitHub repository and then the user is asked which language * to download (if there is more than one) * - * @param app the App - * @param databaseManager the DatabaseManager - * @param storagePath where to store the unzipped database. * @param progress the progress callback - * @param cli the CodeQL CLI server * @param language the language to download. If undefined, the user will be prompted to choose a language. * @param makeSelected make the new database selected in the databases panel (default: true) * @param addSourceArchiveFolder whether to add a workspace folder containing the source archive to the workspace */ public async promptImportGithubDatabase( - app: App, - databaseManager: DatabaseManager, - storagePath: string, progress: ProgressCallback, - cli: CodeQLCliServer, language?: string, makeSelected = true, addSourceArchiveFolder = addDatabaseSourceToWorkspace(), @@ -124,11 +118,7 @@ export class DatabaseFetcher { const databaseItem = await this.downloadGitHubDatabase( githubRepo, - app, - databaseManager, - storagePath, progress, - cli, language, makeSelected, addSourceArchiveFolder, @@ -136,7 +126,7 @@ export class DatabaseFetcher { if (databaseItem) { if (makeSelected) { - await app.commands.execute("codeQLDatabases.focus"); + await this.app.commands.execute("codeQLDatabases.focus"); } void showAndLogInformationMessage( extLogger, @@ -176,22 +166,14 @@ export class DatabaseFetcher { * Downloads a database from GitHub * * @param githubRepo the GitHub repository to download the database from - * @param app the App - * @param databaseManager the DatabaseManager - * @param storagePath where to store the unzipped database. * @param progress the progress callback - * @param cli the CodeQL CLI server * @param language the language to download. If undefined, the user will be prompted to choose a language. * @param makeSelected make the new database selected in the databases panel (default: true) * @param addSourceArchiveFolder whether to add a workspace folder containing the source archive to the workspace **/ public async downloadGitHubDatabase( githubRepo: string, - app: App, - databaseManager: DatabaseManager, - storagePath: string, progress: ProgressCallback, - cli: CodeQLCliServer, language?: string, makeSelected = true, addSourceArchiveFolder = addDatabaseSourceToWorkspace(), @@ -201,7 +183,7 @@ export class DatabaseFetcher { throw new Error(`Invalid GitHub repository: ${githubRepo}`); } - const credentials = isCanary() ? app.credentials : undefined; + const credentials = isCanary() ? this.app.credentials : undefined; const octokit = credentials ? await credentials.getOctokit() @@ -235,9 +217,6 @@ export class DatabaseFetcher { name, octokit, progress, - databaseManager, - storagePath, - cli, makeSelected, addSourceArchiveFolder, ); @@ -252,9 +231,6 @@ export class DatabaseFetcher { name: string, octokit: Octokit, progress: ProgressCallback, - databaseManager: DatabaseManager, - storagePath: string, - cli: CodeQLCliServer, makeSelected = true, addSourceArchiveFolder = true, ): Promise { @@ -275,8 +251,6 @@ export class DatabaseFetcher { Accept: "application/zip", Authorization: octokitToken ? `Bearer ${octokitToken}` : "", }, - databaseManager, - storagePath, `${owner}/${name}`, { type: "github", @@ -286,7 +260,6 @@ export class DatabaseFetcher { commitOid, }, progress, - cli, makeSelected, addSourceArchiveFolder, ); @@ -296,34 +269,24 @@ export class DatabaseFetcher { * Imports a database from a local archive. * * @param databaseUrl the file url of the archive to import - * @param databaseManager the DatabaseManager - * @param storagePath where to store the unzipped database. - * @param cli the CodeQL CLI server */ public async importArchiveDatabase( - commandManager: AppCommandManager, databaseUrl: string, - databaseManager: DatabaseManager, - storagePath: string, progress: ProgressCallback, - cli: CodeQLCliServer, ): Promise { try { const item = await this.databaseArchiveFetcher( databaseUrl, {}, - databaseManager, - storagePath, undefined, { type: "archive", path: databaseUrl, }, progress, - cli, ); if (item) { - await commandManager.execute("codeQLDatabases.focus"); + await this.app.commands.execute("codeQLDatabases.focus"); void showAndLogInformationMessage( extLogger, "Database unzipped and imported successfully.", @@ -348,24 +311,18 @@ export class DatabaseFetcher { * * @param databaseUrl URL from which to grab the database * @param requestHeaders Headers to send with the request - * @param databaseManager the DatabaseManager - * @param storagePath where to store the unzipped database. * @param nameOverride a name for the database that overrides the default * @param origin the origin of the database * @param progress callback to send progress messages to - * @param cli the CodeQL CLI server * @param makeSelected make the new database selected in the databases panel (default: true) * @param addSourceArchiveFolder whether to add a workspace folder containing the source archive to the workspace */ private async databaseArchiveFetcher( databaseUrl: string, requestHeaders: { [key: string]: string }, - databaseManager: DatabaseManager, - storagePath: string, nameOverride: string | undefined, origin: DatabaseOrigin, progress: ProgressCallback, - cli: CodeQLCliServer, makeSelected = true, addSourceArchiveFolder = addDatabaseSourceToWorkspace(), ): Promise { @@ -374,24 +331,19 @@ export class DatabaseFetcher { step: 1, maxStep: 4, }); - if (!storagePath) { + if (!this.storagePath) { throw new Error("No storage path specified."); } - await ensureDir(storagePath); - const unzipPath = await this.getStorageFolder( - storagePath, - databaseUrl, - nameOverride, - ); + await ensureDir(this.storagePath); + const unzipPath = await this.getStorageFolder(databaseUrl, nameOverride); if (this.isFile(databaseUrl)) { - await this.readAndUnzip(databaseUrl, unzipPath, cli, progress); + await this.readAndUnzip(databaseUrl, unzipPath, progress); } else { await this.fetchAndUnzip( databaseUrl, requestHeaders, unzipPath, - cli, progress, ); } @@ -416,7 +368,7 @@ export class DatabaseFetcher { }); await this.ensureZippedSourceLocation(dbPath); - const item = await databaseManager.openDatabase( + const item = await this.databaseManager.openDatabase( Uri.file(dbPath), origin, makeSelected, @@ -432,11 +384,7 @@ export class DatabaseFetcher { } } - private async getStorageFolder( - storagePath: string, - urlStr: string, - nameOverrride?: string, - ) { + private async getStorageFolder(urlStr: string, nameOverrride?: string) { let lastName: string; if (nameOverrride) { @@ -454,7 +402,7 @@ export class DatabaseFetcher { } } - const realpath = await fs_realpath(storagePath); + const realpath = await fs_realpath(this.storagePath); let folderName = lastName; // get all existing files instead of calling pathExists on every @@ -500,7 +448,6 @@ export class DatabaseFetcher { private async readAndUnzip( zipUrl: string, unzipPath: string, - cli: CodeQLCliServer, progress?: ProgressCallback, ) { const zipFile = Uri.parse(zipUrl).fsPath; @@ -510,14 +457,13 @@ export class DatabaseFetcher { message: `Unzipping into ${basename(unzipPath)}`, }); - await cli.databaseUnbundle(zipFile, unzipPath); + await this.cli.databaseUnbundle(zipFile, unzipPath); } private async fetchAndUnzip( databaseUrl: string, requestHeaders: { [key: string]: string }, unzipPath: string, - cli: CodeQLCliServer, progress?: ProgressCallback, ) { // Although it is possible to download and stream directly to an unzipped directory, @@ -606,7 +552,6 @@ export class DatabaseFetcher { await this.readAndUnzip( Uri.file(archivePath).toString(true), unzipPath, - cli, progress, ); diff --git a/extensions/ql-vscode/src/databases/github-databases/download.ts b/extensions/ql-vscode/src/databases/github-databases/download.ts index 89dc7527e89..e417a18505e 100644 --- a/extensions/ql-vscode/src/databases/github-databases/download.ts +++ b/extensions/ql-vscode/src/databases/github-databases/download.ts @@ -4,8 +4,6 @@ import { showNeverAskAgainDialog } from "../../common/vscode/dialog"; import { getLanguageDisplayName } from "../../common/query-language"; import type { DatabaseFetcher } from "../database-fetcher"; import { withProgress } from "../../common/vscode/progress"; -import type { DatabaseManager } from "../local-databases"; -import type { CodeQLCliServer } from "../../codeql-cli/cli"; import type { AppCommandManager } from "../../common/commands"; import type { GitHubDatabaseConfig } from "../../config"; import type { CodeqlDatabase } from "./api"; @@ -58,10 +56,7 @@ export async function downloadDatabaseFromGitHub( owner: string, repo: string, databases: CodeqlDatabase[], - databaseManager: DatabaseManager, databaseFetcher: DatabaseFetcher, - storagePath: string, - cliServer: CodeQLCliServer, commandManager: AppCommandManager, ): Promise { const selectedDatabases = await promptForDatabases(databases); @@ -82,9 +77,6 @@ export async function downloadDatabaseFromGitHub( repo, octokit, progress, - databaseManager, - storagePath, - cliServer, true, false, ); diff --git a/extensions/ql-vscode/src/databases/github-databases/github-databases-module.ts b/extensions/ql-vscode/src/databases/github-databases/github-databases-module.ts index 382d638fa42..6c97dd36b37 100644 --- a/extensions/ql-vscode/src/databases/github-databases/github-databases-module.ts +++ b/extensions/ql-vscode/src/databases/github-databases/github-databases-module.ts @@ -14,7 +14,6 @@ import { } from "./download"; import type { GitHubDatabaseConfig } from "../../config"; import type { DatabaseManager } from "../local-databases"; -import type { CodeQLCliServer } from "../../codeql-cli/cli"; import type { CodeqlDatabase, ListDatabasesResult } from "./api"; import { listDatabases } from "./api"; import type { DatabaseUpdate } from "./updates"; @@ -35,8 +34,6 @@ export class GitHubDatabasesModule extends DisposableObject { private readonly app: App, private readonly databaseManager: DatabaseManager, private readonly databaseFetcher: DatabaseFetcher, - private readonly databaseStoragePath: string, - private readonly cliServer: CodeQLCliServer, private readonly config: GitHubDatabaseConfig, ) { super(); @@ -46,16 +43,12 @@ export class GitHubDatabasesModule extends DisposableObject { app: App, databaseManager: DatabaseManager, databaseFetcher: DatabaseFetcher, - databaseStoragePath: string, - cliServer: CodeQLCliServer, config: GitHubDatabaseConfig, ): Promise { const githubDatabasesModule = new GitHubDatabasesModule( app, databaseManager, databaseFetcher, - databaseStoragePath, - cliServer, config, ); app.subscriptions.push(githubDatabasesModule); @@ -189,10 +182,7 @@ export class GitHubDatabasesModule extends DisposableObject { owner, repo, databases, - this.databaseManager, this.databaseFetcher, - this.databaseStoragePath, - this.cliServer, this.app.commands, ); } @@ -218,8 +208,6 @@ export class GitHubDatabasesModule extends DisposableObject { databaseUpdates, this.databaseManager, this.databaseFetcher, - this.databaseStoragePath, - this.cliServer, this.app.commands, ); } diff --git a/extensions/ql-vscode/src/databases/github-databases/updates.ts b/extensions/ql-vscode/src/databases/github-databases/updates.ts index 349b317ce04..5b589649fea 100644 --- a/extensions/ql-vscode/src/databases/github-databases/updates.ts +++ b/extensions/ql-vscode/src/databases/github-databases/updates.ts @@ -1,7 +1,6 @@ import type { CodeqlDatabase } from "./api"; import type { DatabaseItem, DatabaseManager } from "../local-databases"; import type { Octokit } from "@octokit/rest"; -import type { CodeQLCliServer } from "../../codeql-cli/cli"; import type { AppCommandManager } from "../../common/commands"; import { getLanguageDisplayName } from "../../common/query-language"; import { showNeverAskAgainDialog } from "../../common/vscode/dialog"; @@ -157,8 +156,6 @@ export async function downloadDatabaseUpdateFromGitHub( updates: DatabaseUpdate[], databaseManager: DatabaseManager, databaseFetcher: DatabaseFetcher, - storagePath: string, - cliServer: CodeQLCliServer, commandManager: AppCommandManager, ): Promise { const selectedDatabases = await promptForDatabases( @@ -190,9 +187,6 @@ export async function downloadDatabaseUpdateFromGitHub( repo, octokit, progress, - databaseManager, - storagePath, - cliServer, databaseManager.currentDatabaseItem === update.databaseItem, update.databaseItem.hasSourceArchiveInExplorer(), ); diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 59cfb4d7206..74d33062210 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -536,13 +536,13 @@ export class DatabaseUI extends DisposableObject { private async handleChooseDatabaseInternet(): Promise { return withProgress( async (progress) => { - await new DatabaseFetcher().promptImportInternetDatabase( - this.app.commands, + const databaseFetcher = new DatabaseFetcher( + this.app, this.databaseManager, this.storagePath, - progress, this.queryServer.cliServer, ); + await databaseFetcher.promptImportInternetDatabase(progress); }, { title: "Adding database from URL", @@ -553,13 +553,13 @@ export class DatabaseUI extends DisposableObject { private async handleChooseDatabaseGithub(): Promise { return withProgress( async (progress) => { - await new DatabaseFetcher().promptImportGithubDatabase( + const databaseFetcher = new DatabaseFetcher( this.app, this.databaseManager, this.storagePath, - progress, this.queryServer.cliServer, ); + await databaseFetcher.promptImportGithubDatabase(progress); }, { title: "Adding database from GitHub", @@ -708,14 +708,16 @@ export class DatabaseUI extends DisposableObject { try { // Assume user has selected an archive if the file has a .zip extension if (uri.path.endsWith(".zip")) { - await new DatabaseFetcher().importArchiveDatabase( - this.app.commands, - uri.toString(true), + const databaseFetcher = new DatabaseFetcher( + this.app, this.databaseManager, this.storagePath, - progress, this.queryServer.cliServer, ); + await databaseFetcher.importArchiveDatabase( + uri.toString(true), + progress, + ); } else { await this.databaseManager.openDatabase(uri, { type: "folder", @@ -955,14 +957,16 @@ export class DatabaseUI extends DisposableObject { } else { // we are selecting a database archive. Must unzip into a workspace-controlled area // before importing. - return await new DatabaseFetcher().importArchiveDatabase( - this.app.commands, - uri.toString(true), + const databaseFetcher = new DatabaseFetcher( + this.app, this.databaseManager, this.storagePath, - progress, this.queryServer.cliServer, ); + return await databaseFetcher.importArchiveDatabase( + uri.toString(true), + progress, + ); } }, { diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 5658788e582..dc2cb4bca89 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -882,9 +882,7 @@ async function activateWithInstalledDistribution( await GitHubDatabasesModule.initialize( app, dbm, - new DatabaseFetcher(), - getContextStoragePath(ctx), - cliServer, + new DatabaseFetcher(app, dbm, getContextStoragePath(ctx), cliServer), githubDatabaseConfigListener, ); diff --git a/extensions/ql-vscode/src/local-queries/local-queries.ts b/extensions/ql-vscode/src/local-queries/local-queries.ts index ea1c5f8e864..9d47f8f909d 100644 --- a/extensions/ql-vscode/src/local-queries/local-queries.ts +++ b/extensions/ql-vscode/src/local-queries/local-queries.ts @@ -326,13 +326,18 @@ export class LocalQueries extends DisposableObject { const contextStoragePath = this.app.workspaceStoragePath || this.app.globalStoragePath; const language = this.languageContextStore.selectedLanguage; + const databaseFetcher = new DatabaseFetcher( + this.app, + this.databaseManager, + contextStoragePath, + this.cliServer, + ); const skeletonQueryWizard = new SkeletonQueryWizard( this.cliServer, progress, this.app, this.databaseManager, - new DatabaseFetcher(), - contextStoragePath, + databaseFetcher, this.selectedQueryTreeViewItems, language, ); diff --git a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts index 5305fe40a01..fb96dd666da 100644 --- a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts +++ b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts @@ -60,7 +60,6 @@ export class SkeletonQueryWizard { private readonly app: App, private readonly databaseManager: DatabaseManager, private readonly databaseFetcher: DatabaseFetcher, - private readonly databaseStoragePath: string | undefined, private readonly selectedItems: readonly QueryTreeViewItem[], private language: QueryLanguage | undefined = undefined, ) {} @@ -361,10 +360,6 @@ export class SkeletonQueryWizard { } private async downloadDatabase(progress: ProgressCallback) { - if (this.databaseStoragePath === undefined) { - throw new Error("Database storage path is undefined"); - } - if (this.language === undefined) { throw new Error("Language is undefined"); } @@ -387,11 +382,7 @@ export class SkeletonQueryWizard { await this.databaseFetcher.downloadGitHubDatabase( chosenRepo, - this.app, - this.databaseManager, - this.databaseStoragePath, progress, - this.cliServer, this.language, ); } diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 2e72981acd7..feb2581f75c 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -916,17 +916,18 @@ export class ModelEditorView extends AbstractWebview< // the user to import the library database. We need to have the database // imported to the query server, so we need to register it to our workspace. const makeSelected = false; - const addedDatabase = - await new DatabaseFetcher().promptImportGithubDatabase( - this.app, - this.databaseManager, - this.app.workspaceStoragePath ?? this.app.globalStoragePath, - progress, - this.cliServer, - this.databaseItem.language, - makeSelected, - false, - ); + const databaseFetcher = new DatabaseFetcher( + this.app, + this.databaseManager, + this.app.workspaceStoragePath ?? this.app.globalStoragePath, + this.cliServer, + ); + const addedDatabase = await databaseFetcher.promptImportGithubDatabase( + progress, + this.databaseItem.language, + makeSelected, + false, + ); if (!addedDatabase) { void this.app.logger.log("No database chosen"); return; diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/databases/database-fetcher.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/databases/database-fetcher.test.ts index cc4b6f2d5a4..ff28dced4c5 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/databases/database-fetcher.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/databases/database-fetcher.test.ts @@ -11,8 +11,8 @@ import { getActivatedExtension, storagePath, } from "../../global.helper"; -import { createMockCommandManager } from "../../../__mocks__/commandsMock"; import { remove } from "fs-extra"; +import { createMockApp } from "../../../__mocks__/appMock"; /** * Run various integration tests for databases @@ -46,14 +46,16 @@ describe("database-fetcher", () => { describe("importArchiveDatabase", () => { it("should add a database from a folder", async () => { const uri = Uri.file(dbLoc); - let dbItem = await new DatabaseFetcher().importArchiveDatabase( - createMockCommandManager(), - uri.toString(true), + const databaseFetcher = new DatabaseFetcher( + createMockApp(), databaseManager, storagePath, - progressCallback, cli, ); + let dbItem = await databaseFetcher.importArchiveDatabase( + uri.toString(true), + progressCallback, + ); expect(dbItem).toBe(databaseManager.currentDatabaseItem); expect(dbItem).toBe(databaseManager.databaseItems[0]); expect(dbItem).toBeDefined(); @@ -68,13 +70,14 @@ describe("database-fetcher", () => { // Provide a database URL when prompted inputBoxStub.mockResolvedValue(DB_URL); - let dbItem = await new DatabaseFetcher().promptImportInternetDatabase( - createMockCommandManager(), + const databaseFetcher = new DatabaseFetcher( + createMockApp(), databaseManager, storagePath, - progressCallback, cli, ); + let dbItem = + await databaseFetcher.promptImportInternetDatabase(progressCallback); expect(dbItem).toBeDefined(); dbItem = dbItem!; expect(dbItem.name).toBe("db"); diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts index 319a0e8cfe9..b2542a48b75 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts @@ -116,7 +116,12 @@ describe("SkeletonQueryWizard", () => { }, ] as WorkspaceFolder[]); - databaseFetcher = new DatabaseFetcher(); + databaseFetcher = new DatabaseFetcher( + mockApp, + mockDatabaseManager, + storagePath, + mockCli, + ); quickPickSpy = jest.spyOn(window, "showQuickPick").mockResolvedValueOnce( mockedQuickPickItem({ @@ -149,7 +154,6 @@ describe("SkeletonQueryWizard", () => { mockApp, mockDatabaseManager, databaseFetcher, - storagePath, selectedItems, ); @@ -177,7 +181,6 @@ describe("SkeletonQueryWizard", () => { mockApp, mockDatabaseManager, databaseFetcher, - storagePath, selectedItems, QueryLanguage.Swift, ); @@ -326,7 +329,6 @@ describe("SkeletonQueryWizard", () => { mockApp, mockDatabaseManagerWithItems, databaseFetcher, - storagePath, selectedItems, ); }); @@ -376,7 +378,6 @@ describe("SkeletonQueryWizard", () => { mockApp, mockDatabaseManagerWithItems, databaseFetcher, - storagePath, selectedItems, ); }); @@ -512,7 +513,6 @@ describe("SkeletonQueryWizard", () => { mockApp, mockDatabaseManager, databaseFetcher, - storagePath, selectedItems, QueryLanguage.Javascript, ); @@ -734,7 +734,6 @@ describe("SkeletonQueryWizard", () => { mockApp, mockDatabaseManager, databaseFetcher, - storagePath, selectedItems, ); }); @@ -764,7 +763,6 @@ describe("SkeletonQueryWizard", () => { mockApp, mockDatabaseManager, databaseFetcher, - storagePath, selectedItems, ); }); @@ -798,7 +796,6 @@ describe("SkeletonQueryWizard", () => { mockApp, mockDatabaseManager, databaseFetcher, - storagePath, selectedItems, QueryLanguage.Swift, ); @@ -842,7 +839,6 @@ describe("SkeletonQueryWizard", () => { mockApp, mockDatabaseManager, databaseFetcher, - storagePath, selectedItems, ); }); diff --git a/extensions/ql-vscode/test/vscode-tests/global.helper.ts b/extensions/ql-vscode/test/vscode-tests/global.helper.ts index b0f85a8ec09..1660f3d57af 100644 --- a/extensions/ql-vscode/test/vscode-tests/global.helper.ts +++ b/extensions/ql-vscode/test/vscode-tests/global.helper.ts @@ -8,7 +8,7 @@ import type { import type { CodeQLCliServer } from "../../src/codeql-cli/cli"; import type { CodeQLExtensionInterface } from "../../src/extension"; import { DatabaseFetcher } from "../../src/databases/database-fetcher"; -import { createMockCommandManager } from "../__mocks__/commandsMock"; +import { createMockApp } from "../__mocks__/appMock"; // This file contains helpers shared between tests that work with an activated extension. @@ -34,15 +34,17 @@ export async function ensureTestDatabase( // Add a database, but make sure the database manager is empty first await cleanDatabases(databaseManager); const uri = Uri.file(dbLoc); - const maybeDbItem = await new DatabaseFetcher().importArchiveDatabase( - createMockCommandManager(), - uri.toString(true), + const databaseFetcher = new DatabaseFetcher( + createMockApp(), databaseManager, storagePath, + cli, + ); + const maybeDbItem = await databaseFetcher.importArchiveDatabase( + uri.toString(true), (_p) => { /**/ }, - cli, ); if (!maybeDbItem) { diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/download.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/download.test.ts index 3768dca4992..b38c974cc24 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/download.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/download.test.ts @@ -13,10 +13,10 @@ import { import type { DatabaseManager } from "../../../../../src/databases/local-databases"; import type { GitHubDatabaseConfig } from "../../../../../src/config"; import type { CodeQLCliServer } from "../../../../../src/codeql-cli/cli"; -import { createMockCommandManager } from "../../../../__mocks__/commandsMock"; import { DatabaseFetcher } from "../../../../../src/databases/database-fetcher"; import * as dialog from "../../../../../src/common/vscode/dialog"; import type { CodeqlDatabase } from "../../../../../src/databases/github-databases/api"; +import { createMockApp } from "../../../../__mocks__/appMock"; describe("askForGitHubDatabaseDownload", () => { const setDownload = jest.fn(); @@ -101,7 +101,7 @@ describe("downloadDatabaseFromGitHub", () => { const storagePath = "/a/b/c/d"; let cliServer: CodeQLCliServer; - const commandManager = createMockCommandManager(); + const app = createMockApp(); let databases = [ mockedObject({ @@ -124,8 +124,13 @@ describe("downloadDatabaseFromGitHub", () => { beforeEach(() => { octokit = mockedObject({}); databaseManager = mockedObject({}); - databaseFetcher = new DatabaseFetcher(); cliServer = mockedObject({}); + databaseFetcher = new DatabaseFetcher( + app, + databaseManager, + storagePath, + cliServer, + ); showQuickPickSpy = jest.spyOn(window, "showQuickPick").mockResolvedValue( mockedQuickPickItem([ @@ -145,11 +150,8 @@ describe("downloadDatabaseFromGitHub", () => { owner, repo, databases, - databaseManager, databaseFetcher, - storagePath, - cliServer, - commandManager, + app.commands, ); expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(1); @@ -162,9 +164,6 @@ describe("downloadDatabaseFromGitHub", () => { repo, octokit, expect.anything(), - databaseManager, - storagePath, - cliServer, true, false, ); @@ -210,11 +209,8 @@ describe("downloadDatabaseFromGitHub", () => { owner, repo, databases, - databaseManager, databaseFetcher, - storagePath, - cliServer, - commandManager, + app.commands, ); expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(1); @@ -227,9 +223,6 @@ describe("downloadDatabaseFromGitHub", () => { repo, octokit, expect.anything(), - databaseManager, - storagePath, - cliServer, true, false, ); @@ -267,11 +260,8 @@ describe("downloadDatabaseFromGitHub", () => { owner, repo, databases, - databaseManager, databaseFetcher, - storagePath, - cliServer, - commandManager, + app.commands, ); expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(2); @@ -284,9 +274,6 @@ describe("downloadDatabaseFromGitHub", () => { repo, octokit, expect.anything(), - databaseManager, - storagePath, - cliServer, true, false, ); @@ -299,9 +286,6 @@ describe("downloadDatabaseFromGitHub", () => { repo, octokit, expect.anything(), - databaseManager, - storagePath, - cliServer, true, false, ); @@ -333,11 +317,8 @@ describe("downloadDatabaseFromGitHub", () => { owner, repo, databases, - databaseManager, databaseFetcher, - storagePath, - cliServer, - commandManager, + app.commands, ); expect(downloadGitHubDatabaseFromUrlSpy).not.toHaveBeenCalled(); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/github-databases-module.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/github-databases-module.test.ts index d5081a1c4d5..18607aab28e 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/github-databases-module.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/github-databases-module.test.ts @@ -68,7 +68,12 @@ describe("GitHubDatabasesModule", () => { databaseManager = mockEmptyDatabaseManager(); databaseStoragePath = "/a/b/some-path"; cliServer = mockedObject({}); - databaseFetcher = new DatabaseFetcher(); + databaseFetcher = new DatabaseFetcher( + app, + databaseManager, + databaseStoragePath, + cliServer, + ); config = mockedObject({ download: "ask", update: "ask", @@ -78,8 +83,6 @@ describe("GitHubDatabasesModule", () => { app, databaseManager, databaseFetcher, - databaseStoragePath, - cliServer, config, ); @@ -129,8 +132,6 @@ describe("GitHubDatabasesModule", () => { app, databaseManager, databaseFetcher, - databaseStoragePath, - cliServer, config, ); @@ -211,10 +212,7 @@ describe("GitHubDatabasesModule", () => { owner, repo, databases, - databaseManager, databaseFetcher, - databaseStoragePath, - cliServer, app.commands, ); }); @@ -257,8 +255,6 @@ describe("GitHubDatabasesModule", () => { databaseUpdates, databaseManager, databaseFetcher, - databaseStoragePath, - cliServer, app.commands, ); }); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/updates.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/updates.test.ts index d693e1a8968..d5b61957000 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/updates.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/updates.test.ts @@ -11,7 +11,6 @@ import type { CodeqlDatabase } from "../../../../../src/databases/github-databas import type { DatabaseManager } from "../../../../../src/databases/local-databases"; import type { GitHubDatabaseConfig } from "../../../../../src/config"; import type { CodeQLCliServer } from "../../../../../src/codeql-cli/cli"; -import { createMockCommandManager } from "../../../../__mocks__/commandsMock"; import { DatabaseFetcher } from "../../../../../src/databases/database-fetcher"; import * as dialog from "../../../../../src/common/vscode/dialog"; import type { DatabaseUpdate } from "../../../../../src/databases/github-databases/updates"; @@ -20,6 +19,7 @@ import { downloadDatabaseUpdateFromGitHub, isNewerDatabaseAvailable, } from "../../../../../src/databases/github-databases/updates"; +import { createMockApp } from "../../../../__mocks__/appMock"; describe("isNewerDatabaseAvailable", () => { const owner = "github"; @@ -347,7 +347,7 @@ describe("downloadDatabaseUpdateFromGitHub", () => { let databaseFetcher: DatabaseFetcher; const storagePath = "/a/b/c/d"; let cliServer: CodeQLCliServer; - const commandManager = createMockCommandManager(); + const app = createMockApp(); let updates: DatabaseUpdate[] = [ { @@ -377,8 +377,13 @@ describe("downloadDatabaseUpdateFromGitHub", () => { databaseManager = mockedObject({ currentDatabaseItem: mockDatabaseItem(), }); - databaseFetcher = new DatabaseFetcher(); cliServer = mockedObject({}); + databaseFetcher = new DatabaseFetcher( + app, + databaseManager, + storagePath, + cliServer, + ); showQuickPickSpy = jest.spyOn(window, "showQuickPick").mockResolvedValue( mockedQuickPickItem([ @@ -400,9 +405,7 @@ describe("downloadDatabaseUpdateFromGitHub", () => { updates, databaseManager, databaseFetcher, - storagePath, - cliServer, - commandManager, + app.commands, ); expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(1); @@ -415,9 +418,6 @@ describe("downloadDatabaseUpdateFromGitHub", () => { repo, octokit, expect.anything(), - databaseManager, - storagePath, - cliServer, false, false, ); @@ -480,9 +480,7 @@ describe("downloadDatabaseUpdateFromGitHub", () => { updates, databaseManager, databaseFetcher, - storagePath, - cliServer, - commandManager, + app.commands, ); expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(1); @@ -495,9 +493,6 @@ describe("downloadDatabaseUpdateFromGitHub", () => { repo, octokit, expect.anything(), - databaseManager, - storagePath, - cliServer, true, true, ); @@ -537,9 +532,7 @@ describe("downloadDatabaseUpdateFromGitHub", () => { updates, databaseManager, databaseFetcher, - storagePath, - cliServer, - commandManager, + app.commands, ); expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(2); @@ -552,9 +545,6 @@ describe("downloadDatabaseUpdateFromGitHub", () => { repo, octokit, expect.anything(), - databaseManager, - storagePath, - cliServer, false, false, ); @@ -567,9 +557,6 @@ describe("downloadDatabaseUpdateFromGitHub", () => { repo, octokit, expect.anything(), - databaseManager, - storagePath, - cliServer, true, true, ); @@ -603,9 +590,7 @@ describe("downloadDatabaseUpdateFromGitHub", () => { updates, databaseManager, databaseFetcher, - storagePath, - cliServer, - commandManager, + app.commands, ); expect(downloadGitHubDatabaseFromUrlSpy).not.toHaveBeenCalled(); From f29aff6303f4917c6ac01fecb45ad27b20b8cf3d Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 7 Mar 2024 12:39:57 +0000 Subject: [PATCH 0125/1237] Inline isFile --- extensions/ql-vscode/src/databases/database-fetcher.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index cbaedabc6bf..49b057b8fa5 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -337,7 +337,7 @@ export class DatabaseFetcher { await ensureDir(this.storagePath); const unzipPath = await this.getStorageFolder(databaseUrl, nameOverride); - if (this.isFile(databaseUrl)) { + if (Uri.parse(databaseUrl).scheme === "file") { await this.readAndUnzip(databaseUrl, unzipPath, progress); } else { await this.fetchAndUnzip( @@ -580,10 +580,6 @@ export class DatabaseFetcher { throw new Error(`${errorMessage}.\n\nReason: ${msg}`); } - private isFile(databaseUrl: string) { - return Uri.parse(databaseUrl).scheme === "file"; - } - /** * Databases created by the old odasa tool will not have a zipped * source location. However, this extension works better if sources From 9fa6d99f09414c6eb9be55b067c77fae9701b89c Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 7 Mar 2024 12:58:21 +0000 Subject: [PATCH 0126/1237] Have the skeleton query wizard call promptImportGithubDatabase so we can make more methods private --- .../src/databases/database-fetcher.ts | 7 +- .../local-queries/skeleton-query-wizard.ts | 13 +-- .../src/model-editor/model-editor-view.ts | 1 + .../skeleton-query-wizard.test.ts | 82 +++++-------------- 4 files changed, 29 insertions(+), 74 deletions(-) diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index 49b057b8fa5..7b1f6aeb3f4 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -108,10 +108,11 @@ export class DatabaseFetcher { public async promptImportGithubDatabase( progress: ProgressCallback, language?: string, + suggestedRepoNwo?: string, makeSelected = true, addSourceArchiveFolder = addDatabaseSourceToWorkspace(), ): Promise { - const githubRepo = await this.askForGitHubRepo(progress); + const githubRepo = await this.askForGitHubRepo(progress, suggestedRepoNwo); if (!githubRepo) { return; } @@ -138,7 +139,7 @@ export class DatabaseFetcher { return; } - public async askForGitHubRepo( + private async askForGitHubRepo( progress?: ProgressCallback, suggestedValue?: string, ): Promise { @@ -171,7 +172,7 @@ export class DatabaseFetcher { * @param makeSelected make the new database selected in the databases panel (default: true) * @param addSourceArchiveFolder whether to add a workspace folder containing the source archive to the workspace **/ - public async downloadGitHubDatabase( + private async downloadGitHubDatabase( githubRepo: string, progress: ProgressCallback, language?: string, diff --git a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts index fb96dd666da..8d9390eab96 100644 --- a/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts +++ b/extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts @@ -371,19 +371,10 @@ export class SkeletonQueryWizard { }); const githubRepoNwo = QUERY_LANGUAGE_TO_DATABASE_REPO[this.language]; - const chosenRepo = await this.databaseFetcher.askForGitHubRepo( - undefined, - githubRepoNwo, - ); - - if (!chosenRepo) { - throw new UserCancellationException("No GitHub repository provided"); - } - - await this.databaseFetcher.downloadGitHubDatabase( - chosenRepo, + await this.databaseFetcher.promptImportGithubDatabase( progress, this.language, + githubRepoNwo, ); } diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index feb2581f75c..1d32867572b 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -925,6 +925,7 @@ export class ModelEditorView extends AbstractWebview< const addedDatabase = await databaseFetcher.promptImportGithubDatabase( progress, this.databaseItem.language, + undefined, makeSelected, false, ); diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts index b2542a48b75..ece55e4b07c 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts @@ -56,11 +56,8 @@ describe("SkeletonQueryWizard", () => { let createExampleQlFileSpy: jest.SpiedFunction< typeof QlPackGenerator.prototype.createExampleQlFile >; - let downloadGitHubDatabaseSpy: jest.SpiedFunction< - DatabaseFetcher["downloadGitHubDatabase"] - >; - let askForGitHubRepoSpy: jest.SpiedFunction< - DatabaseFetcher["askForGitHubRepo"] + let promptImportGithubDatabaseSpy: jest.SpiedFunction< + DatabaseFetcher["promptImportGithubDatabase"] >; let openTextDocumentSpy: jest.SpiedFunction< typeof workspace.openTextDocument @@ -141,8 +138,8 @@ describe("SkeletonQueryWizard", () => { createExampleQlFileSpy = jest .spyOn(QlPackGenerator.prototype, "createExampleQlFile") .mockResolvedValue(undefined); - downloadGitHubDatabaseSpy = jest - .spyOn(databaseFetcher, "downloadGitHubDatabase") + promptImportGithubDatabaseSpy = jest + .spyOn(databaseFetcher, "promptImportGithubDatabase") .mockResolvedValue(undefined); openTextDocumentSpy = jest .spyOn(workspace, "openTextDocument") @@ -156,10 +153,6 @@ describe("SkeletonQueryWizard", () => { databaseFetcher, selectedItems, ); - - askForGitHubRepoSpy = jest - .spyOn(databaseFetcher, "askForGitHubRepo") - .mockResolvedValue(QUERY_LANGUAGE_TO_DATABASE_REPO[chosenLanguage]); }); afterEach(async () => { @@ -210,7 +203,7 @@ describe("SkeletonQueryWizard", () => { title: "Download database", }), ); - expect(downloadGitHubDatabaseSpy).not.toHaveBeenCalled(); + expect(promptImportGithubDatabaseSpy).not.toHaveBeenCalled(); }); it("should download database for selected language when selecting download in prompt", async () => { @@ -227,7 +220,7 @@ describe("SkeletonQueryWizard", () => { await wizard.execute(); await wizard.waitForDownload(); - expect(downloadGitHubDatabaseSpy).toHaveBeenCalled(); + expect(promptImportGithubDatabaseSpy).toHaveBeenCalled(); }); it("should open the query file", async () => { @@ -336,7 +329,7 @@ describe("SkeletonQueryWizard", () => { it("should not download a new database for language", async () => { await wizard.execute(); - expect(downloadGitHubDatabaseSpy).not.toHaveBeenCalled(); + expect(promptImportGithubDatabaseSpy).not.toHaveBeenCalled(); }); it("should not select the database", async () => { @@ -385,7 +378,7 @@ describe("SkeletonQueryWizard", () => { it("should not download a new database for language", async () => { await wizard.execute(); - expect(downloadGitHubDatabaseSpy).not.toHaveBeenCalled(); + expect(promptImportGithubDatabaseSpy).not.toHaveBeenCalled(); }); it("should select an existing database", async () => { @@ -417,54 +410,23 @@ describe("SkeletonQueryWizard", () => { }); describe("if database is missing", () => { - describe("if the user chooses to downloaded the suggested database from GitHub", () => { - beforeEach(() => { - showInformationMessageSpy.mockImplementation( - async (_message, options, item) => { - if (item === undefined) { - return options as MessageItem; - } - - return item; - }, - ); - }); - - it("should download a new database for language", async () => { - await wizard.execute(); - await wizard.waitForDownload(); - - expect(askForGitHubRepoSpy).toHaveBeenCalled(); - expect(downloadGitHubDatabaseSpy).toHaveBeenCalled(); - }); + beforeEach(() => { + showInformationMessageSpy.mockImplementation( + async (_message, options, item) => { + if (item === undefined) { + return options as MessageItem; + } + + return item; + }, + ); }); - describe("if the user choses to download a different database from GitHub than the one suggested", () => { - beforeEach(() => { - showInformationMessageSpy.mockImplementation( - async (_message, options, item) => { - if (item === undefined) { - return options as MessageItem; - } - - return item; - }, - ); - - const chosenGitHubRepo = "pickles-owner/pickles-repo"; - - askForGitHubRepoSpy = jest - .spyOn(databaseFetcher, "askForGitHubRepo") - .mockResolvedValue(chosenGitHubRepo); - }); - - it("should download the newly chosen database", async () => { - await wizard.execute(); - await wizard.waitForDownload(); + it("should download a new database for language", async () => { + await wizard.execute(); + await wizard.waitForDownload(); - expect(askForGitHubRepoSpy).toHaveBeenCalled(); - expect(downloadGitHubDatabaseSpy).toHaveBeenCalled(); - }); + expect(promptImportGithubDatabaseSpy).toHaveBeenCalled(); }); }); }); From ec427283678d5e659cf11b66fb6e37254ed6dab7 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 7 Mar 2024 14:58:45 +0000 Subject: [PATCH 0127/1237] Only construct DatabaseFetcher once in extension.ts --- .../src/databases/local-databases-ui.ts | 35 ++++--------------- extensions/ql-vscode/src/extension.ts | 12 ++++++- .../src/local-queries/local-queries.ts | 13 ++----- .../src/model-editor/model-editor-module.ts | 5 +++ .../src/model-editor/model-editor-view.ts | 12 +++---- .../github-databases-module.test.ts | 15 ++------ .../databases/local-databases-ui.test.ts | 4 +++ .../model-editor/model-editor-view.test.ts | 3 ++ 8 files changed, 38 insertions(+), 61 deletions(-) diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 74d33062210..d4cc9d16537 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -42,7 +42,7 @@ import { showAndLogExceptionWithTelemetry, showAndLogErrorMessage, } from "../common/logging"; -import { DatabaseFetcher } from "./database-fetcher"; +import type { DatabaseFetcher } from "./database-fetcher"; import { asError, asyncFilter, getErrorMessage } from "../common/helpers-pure"; import type { QueryRunner } from "../query-server"; import type { App } from "../common/app"; @@ -248,6 +248,7 @@ export class DatabaseUI extends DisposableObject { public constructor( private app: App, private databaseManager: DatabaseManager, + private readonly databaseFetcher: DatabaseFetcher, languageContext: LanguageContextStore, private readonly queryServer: QueryRunner, private readonly storagePath: string, @@ -536,13 +537,7 @@ export class DatabaseUI extends DisposableObject { private async handleChooseDatabaseInternet(): Promise { return withProgress( async (progress) => { - const databaseFetcher = new DatabaseFetcher( - this.app, - this.databaseManager, - this.storagePath, - this.queryServer.cliServer, - ); - await databaseFetcher.promptImportInternetDatabase(progress); + await this.databaseFetcher.promptImportInternetDatabase(progress); }, { title: "Adding database from URL", @@ -553,13 +548,7 @@ export class DatabaseUI extends DisposableObject { private async handleChooseDatabaseGithub(): Promise { return withProgress( async (progress) => { - const databaseFetcher = new DatabaseFetcher( - this.app, - this.databaseManager, - this.storagePath, - this.queryServer.cliServer, - ); - await databaseFetcher.promptImportGithubDatabase(progress); + await this.databaseFetcher.promptImportGithubDatabase(progress); }, { title: "Adding database from GitHub", @@ -708,13 +697,7 @@ export class DatabaseUI extends DisposableObject { try { // Assume user has selected an archive if the file has a .zip extension if (uri.path.endsWith(".zip")) { - const databaseFetcher = new DatabaseFetcher( - this.app, - this.databaseManager, - this.storagePath, - this.queryServer.cliServer, - ); - await databaseFetcher.importArchiveDatabase( + await this.databaseFetcher.importArchiveDatabase( uri.toString(true), progress, ); @@ -957,13 +940,7 @@ export class DatabaseUI extends DisposableObject { } else { // we are selecting a database archive. Must unzip into a workspace-controlled area // before importing. - const databaseFetcher = new DatabaseFetcher( - this.app, - this.databaseManager, - this.storagePath, - this.queryServer.cliServer, - ); - return await databaseFetcher.importArchiveDatabase( + return await this.databaseFetcher.importArchiveDatabase( uri.toString(true), progress, ); diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index dc2cb4bca89..f2f61d90920 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -800,12 +800,20 @@ async function activateWithInstalledDistribution( // Let this run async. void dbm.loadPersistedState(); + const databaseFetcher = new DatabaseFetcher( + app, + dbm, + getContextStoragePath(ctx), + cliServer, + ); + ctx.subscriptions.push(dbm); void extLogger.log("Initializing database panel."); const databaseUI = new DatabaseUI( app, dbm, + databaseFetcher, languageContext, qs, getContextStoragePath(ctx), @@ -882,7 +890,7 @@ async function activateWithInstalledDistribution( await GitHubDatabasesModule.initialize( app, dbm, - new DatabaseFetcher(app, dbm, getContextStoragePath(ctx), cliServer), + databaseFetcher, githubDatabaseConfigListener, ); @@ -953,6 +961,7 @@ async function activateWithInstalledDistribution( qs, qhm, dbm, + databaseFetcher, cliServer, databaseUI, localQueryResultsView, @@ -977,6 +986,7 @@ async function activateWithInstalledDistribution( const modelEditorModule = await ModelEditorModule.initialize( app, dbm, + databaseFetcher, variantAnalysisManager, cliServer, qs, diff --git a/extensions/ql-vscode/src/local-queries/local-queries.ts b/extensions/ql-vscode/src/local-queries/local-queries.ts index 9d47f8f909d..d2f89eb1699 100644 --- a/extensions/ql-vscode/src/local-queries/local-queries.ts +++ b/extensions/ql-vscode/src/local-queries/local-queries.ts @@ -51,7 +51,7 @@ import type { QueryTreeViewItem } from "../queries-panel/query-tree-view-item"; import { tryGetQueryLanguage } from "../common/query-language"; import type { LanguageContextStore } from "../language-context-store"; import type { ExtensionApp } from "../common/vscode/vscode-app"; -import { DatabaseFetcher } from "../databases/database-fetcher"; +import type { DatabaseFetcher } from "../databases/database-fetcher"; export enum QuickEvalType { None, @@ -67,6 +67,7 @@ export class LocalQueries extends DisposableObject { private readonly queryRunner: QueryRunner, private readonly queryHistoryManager: QueryHistoryManager, private readonly databaseManager: DatabaseManager, + private readonly databaseFetcher: DatabaseFetcher, private readonly cliServer: CodeQLCliServer, private readonly databaseUI: DatabaseUI, private readonly localQueryResultsView: ResultsView, @@ -323,21 +324,13 @@ export class LocalQueries extends DisposableObject { private async createSkeletonQuery(): Promise { await withProgress( async (progress: ProgressCallback) => { - const contextStoragePath = - this.app.workspaceStoragePath || this.app.globalStoragePath; const language = this.languageContextStore.selectedLanguage; - const databaseFetcher = new DatabaseFetcher( - this.app, - this.databaseManager, - contextStoragePath, - this.cliServer, - ); const skeletonQueryWizard = new SkeletonQueryWizard( this.cliServer, progress, this.app, this.databaseManager, - databaseFetcher, + this.databaseFetcher, this.selectedQueryTreeViewItems, language, ); diff --git a/extensions/ql-vscode/src/model-editor/model-editor-module.ts b/extensions/ql-vscode/src/model-editor/model-editor-module.ts index 83edc7b0ed3..2aa914e377f 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-module.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-module.ts @@ -32,6 +32,7 @@ import { INITIAL_MODE } from "./shared/mode"; import { isSupportedLanguage } from "./supported-languages"; import { DefaultNotifier, checkConsistency } from "./consistency-check"; import type { VariantAnalysisManager } from "../variant-analysis/variant-analysis-manager"; +import type { DatabaseFetcher } from "../databases/database-fetcher"; export class ModelEditorModule extends DisposableObject { private readonly queryStorageDir: string; @@ -42,6 +43,7 @@ export class ModelEditorModule extends DisposableObject { private constructor( private readonly app: App, private readonly databaseManager: DatabaseManager, + private readonly databaseFetcher: DatabaseFetcher, private readonly variantAnalysisManager: VariantAnalysisManager, private readonly cliServer: CodeQLCliServer, private readonly queryRunner: QueryRunner, @@ -65,6 +67,7 @@ export class ModelEditorModule extends DisposableObject { public static async initialize( app: App, databaseManager: DatabaseManager, + databaseFetcher: DatabaseFetcher, variantAnalysisManager: VariantAnalysisManager, cliServer: CodeQLCliServer, queryRunner: QueryRunner, @@ -73,6 +76,7 @@ export class ModelEditorModule extends DisposableObject { const modelEditorModule = new ModelEditorModule( app, databaseManager, + databaseFetcher, variantAnalysisManager, cliServer, queryRunner, @@ -236,6 +240,7 @@ export class ModelEditorModule extends DisposableObject { this.modelingEvents, this.modelConfig, this.databaseManager, + this.databaseFetcher, this.variantAnalysisManager, this.cliServer, this.queryRunner, diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 1d32867572b..57dca0991c5 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -29,7 +29,7 @@ import type { } from "../databases/local-databases"; import type { CodeQLCliServer } from "../codeql-cli/cli"; import { asError, assertNever, getErrorMessage } from "../common/helpers-pure"; -import { DatabaseFetcher } from "../databases/database-fetcher"; +import type { DatabaseFetcher } from "../databases/database-fetcher"; import type { App } from "../common/app"; import { redactableError } from "../common/errors"; import { @@ -86,6 +86,7 @@ export class ModelEditorView extends AbstractWebview< private readonly modelingEvents: ModelingEvents, private readonly modelConfig: ModelConfigListener, private readonly databaseManager: DatabaseManager, + private readonly databaseFetcher: DatabaseFetcher, private readonly variantAnalysisManager: VariantAnalysisManager, private readonly cliServer: CodeQLCliServer, private readonly queryRunner: QueryRunner, @@ -848,6 +849,7 @@ export class ModelEditorView extends AbstractWebview< this.modelingEvents, this.modelConfig, this.databaseManager, + this.databaseFetcher, this.variantAnalysisManager, this.cliServer, this.queryRunner, @@ -916,13 +918,7 @@ export class ModelEditorView extends AbstractWebview< // the user to import the library database. We need to have the database // imported to the query server, so we need to register it to our workspace. const makeSelected = false; - const databaseFetcher = new DatabaseFetcher( - this.app, - this.databaseManager, - this.app.workspaceStoragePath ?? this.app.globalStoragePath, - this.cliServer, - ); - const addedDatabase = await databaseFetcher.promptImportGithubDatabase( + const addedDatabase = await this.databaseFetcher.promptImportGithubDatabase( progress, this.databaseItem.language, undefined, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/github-databases-module.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/github-databases-module.test.ts index 18607aab28e..bf22c35c9eb 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/github-databases-module.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/github-databases-module.test.ts @@ -4,7 +4,6 @@ import { createMockApp } from "../../../../__mocks__/appMock"; import type { App } from "../../../../../src/common/app"; import type { DatabaseManager } from "../../../../../src/databases/local-databases"; import { mockEmptyDatabaseManager } from "../../query-testing/test-runner-helpers"; -import type { CodeQLCliServer } from "../../../../../src/codeql-cli/cli"; import { mockDatabaseItem, mockedObject } from "../../../utils/mocking.helpers"; import type { GitHubDatabaseConfig } from "../../../../../src/config"; import { GitHubDatabasesModule } from "../../../../../src/databases/github-databases"; @@ -16,15 +15,13 @@ import * as githubDatabasesApi from "../../../../../src/databases/github-databas import * as githubDatabasesDownload from "../../../../../src/databases/github-databases/download"; import * as githubDatabasesUpdates from "../../../../../src/databases/github-databases/updates"; import type { DatabaseUpdate } from "../../../../../src/databases/github-databases/updates"; -import { DatabaseFetcher } from "../../../../../src/databases/database-fetcher"; +import type { DatabaseFetcher } from "../../../../../src/databases/database-fetcher"; describe("GitHubDatabasesModule", () => { describe("promptGitHubRepositoryDownload", () => { let app: App; let databaseManager: DatabaseManager; - let databaseStoragePath: string; - let cliServer: CodeQLCliServer; - let databaseFetcher: DatabaseFetcher; + const databaseFetcher = mockedObject({}); let config: GitHubDatabaseConfig; let gitHubDatabasesModule: GitHubDatabasesModule; @@ -66,14 +63,6 @@ describe("GitHubDatabasesModule", () => { beforeEach(() => { app = createMockApp(); databaseManager = mockEmptyDatabaseManager(); - databaseStoragePath = "/a/b/some-path"; - cliServer = mockedObject({}); - databaseFetcher = new DatabaseFetcher( - app, - databaseManager, - databaseStoragePath, - cliServer, - ); config = mockedObject({ download: "ask", update: "ask", diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/local-databases-ui.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/local-databases-ui.test.ts index 17470c1660d..90a72495e4d 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/local-databases-ui.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/local-databases-ui.test.ts @@ -20,6 +20,7 @@ import { testDisposeHandler } from "../../test-dispose-handler"; import { createMockApp } from "../../../__mocks__/appMock"; import { QueryLanguage } from "../../../../src/common/query-language"; import { mockedQuickPickItem, mockedObject } from "../../utils/mocking.helpers"; +import type { DatabaseFetcher } from "../../../../src/databases/database-fetcher"; describe("local-databases-ui", () => { const storageDir = dirSync({ unsafeCleanup: true }).name; @@ -104,6 +105,7 @@ describe("local-databases-ui", () => { }, setCurrentDatabaseItem: () => {}, } as any, + mockedObject({}), { onLanguageContextChanged: () => { /**/ @@ -142,6 +144,7 @@ describe("local-databases-ui", () => { setCurrentDatabaseItem: () => {}, currentDatabaseItem: { databaseUri: Uri.file(db1) }, } as any, + mockedObject({}), { onLanguageContextChanged: () => { /**/ @@ -178,6 +181,7 @@ describe("local-databases-ui", () => { const databaseUI = new DatabaseUI( app, databaseManager, + mockedObject({}), { onLanguageContextChanged: () => { /**/ diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/model-editor-view.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/model-editor-view.test.ts index e410c5ad156..c6a3237be6c 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/model-editor-view.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/model-editor/model-editor-view.test.ts @@ -13,6 +13,7 @@ import type { ModelConfigListener } from "../../../../src/config"; import { createMockModelingEvents } from "../../../__mocks__/model-editor/modelingEventsMock"; import { QueryLanguage } from "../../../../src/common/query-language"; import type { VariantAnalysisManager } from "../../../../src/variant-analysis/variant-analysis-manager"; +import type { DatabaseFetcher } from "../../../../src/databases/database-fetcher"; describe("ModelEditorView", () => { const app = createMockApp({}); @@ -22,6 +23,7 @@ describe("ModelEditorView", () => { onDidChangeConfiguration: jest.fn(), }); const databaseManager = mockEmptyDatabaseManager(); + const databaseFetcher = mockedObject({}); const variantAnalysisManager = mockedObject({}); const cliServer = mockedObject({}); const queryRunner = mockedObject({}); @@ -50,6 +52,7 @@ describe("ModelEditorView", () => { modelingEvents, modelConfig, databaseManager, + databaseFetcher, variantAnalysisManager, cliServer, queryRunner, From 55b9b2e89158f3dda37b5dddb4d34a975c803802 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 7 Mar 2024 15:05:32 +0000 Subject: [PATCH 0128/1237] Use mocks instead of spies in tests --- .../skeleton-query-wizard.test.ts | 29 +++++------- .../github-databases/download.test.ts | 44 +++++++----------- .../github-databases/updates.test.ts | 45 ++++++++----------- 3 files changed, 46 insertions(+), 72 deletions(-) diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts index ece55e4b07c..aacac5e7a10 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts @@ -23,7 +23,7 @@ import type { DatabaseManager, FullDatabaseOptions, } from "../../../../src/databases/local-databases"; -import { DatabaseFetcher } from "../../../../src/databases/database-fetcher"; +import type { DatabaseFetcher } from "../../../../src/databases/database-fetcher"; import { createMockDB } from "../../../factories/databases/databases"; import { asError } from "../../../../src/common/helpers-pure"; import { Setting } from "../../../../src/config"; @@ -56,9 +56,7 @@ describe("SkeletonQueryWizard", () => { let createExampleQlFileSpy: jest.SpiedFunction< typeof QlPackGenerator.prototype.createExampleQlFile >; - let promptImportGithubDatabaseSpy: jest.SpiedFunction< - DatabaseFetcher["promptImportGithubDatabase"] - >; + let promptImportGithubDatabaseMock: jest.Mock; let openTextDocumentSpy: jest.SpiedFunction< typeof workspace.openTextDocument >; @@ -113,12 +111,10 @@ describe("SkeletonQueryWizard", () => { }, ] as WorkspaceFolder[]); - databaseFetcher = new DatabaseFetcher( - mockApp, - mockDatabaseManager, - storagePath, - mockCli, - ); + promptImportGithubDatabaseMock = jest.fn().mockReturnValue(undefined); + databaseFetcher = mockedObject({ + promptImportGithubDatabase: promptImportGithubDatabaseMock, + }); quickPickSpy = jest.spyOn(window, "showQuickPick").mockResolvedValueOnce( mockedQuickPickItem({ @@ -138,9 +134,6 @@ describe("SkeletonQueryWizard", () => { createExampleQlFileSpy = jest .spyOn(QlPackGenerator.prototype, "createExampleQlFile") .mockResolvedValue(undefined); - promptImportGithubDatabaseSpy = jest - .spyOn(databaseFetcher, "promptImportGithubDatabase") - .mockResolvedValue(undefined); openTextDocumentSpy = jest .spyOn(workspace, "openTextDocument") .mockResolvedValue({} as TextDocument); @@ -203,7 +196,7 @@ describe("SkeletonQueryWizard", () => { title: "Download database", }), ); - expect(promptImportGithubDatabaseSpy).not.toHaveBeenCalled(); + expect(promptImportGithubDatabaseMock).not.toHaveBeenCalled(); }); it("should download database for selected language when selecting download in prompt", async () => { @@ -220,7 +213,7 @@ describe("SkeletonQueryWizard", () => { await wizard.execute(); await wizard.waitForDownload(); - expect(promptImportGithubDatabaseSpy).toHaveBeenCalled(); + expect(promptImportGithubDatabaseMock).toHaveBeenCalled(); }); it("should open the query file", async () => { @@ -329,7 +322,7 @@ describe("SkeletonQueryWizard", () => { it("should not download a new database for language", async () => { await wizard.execute(); - expect(promptImportGithubDatabaseSpy).not.toHaveBeenCalled(); + expect(promptImportGithubDatabaseMock).not.toHaveBeenCalled(); }); it("should not select the database", async () => { @@ -378,7 +371,7 @@ describe("SkeletonQueryWizard", () => { it("should not download a new database for language", async () => { await wizard.execute(); - expect(promptImportGithubDatabaseSpy).not.toHaveBeenCalled(); + expect(promptImportGithubDatabaseMock).not.toHaveBeenCalled(); }); it("should select an existing database", async () => { @@ -426,7 +419,7 @@ describe("SkeletonQueryWizard", () => { await wizard.execute(); await wizard.waitForDownload(); - expect(promptImportGithubDatabaseSpy).toHaveBeenCalled(); + expect(promptImportGithubDatabaseMock).toHaveBeenCalled(); }); }); }); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/download.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/download.test.ts index b38c974cc24..96018ea6ba0 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/download.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/download.test.ts @@ -10,10 +10,9 @@ import { askForGitHubDatabaseDownload, downloadDatabaseFromGitHub, } from "../../../../../src/databases/github-databases/download"; -import type { DatabaseManager } from "../../../../../src/databases/local-databases"; +import type { DatabaseItem } from "../../../../../src/databases/local-databases"; import type { GitHubDatabaseConfig } from "../../../../../src/config"; -import type { CodeQLCliServer } from "../../../../../src/codeql-cli/cli"; -import { DatabaseFetcher } from "../../../../../src/databases/database-fetcher"; +import type { DatabaseFetcher } from "../../../../../src/databases/database-fetcher"; import * as dialog from "../../../../../src/common/vscode/dialog"; import type { CodeqlDatabase } from "../../../../../src/databases/github-databases/api"; import { createMockApp } from "../../../../__mocks__/appMock"; @@ -96,11 +95,8 @@ describe("downloadDatabaseFromGitHub", () => { let octokit: Octokit; const owner = "github"; const repo = "codeql"; - let databaseManager: DatabaseManager; let databaseFetcher: DatabaseFetcher; - const storagePath = "/a/b/c/d"; - let cliServer: CodeQLCliServer; const app = createMockApp(); let databases = [ @@ -117,20 +113,15 @@ describe("downloadDatabaseFromGitHub", () => { ]; let showQuickPickSpy: jest.SpiedFunction; - let downloadGitHubDatabaseFromUrlSpy: jest.SpiedFunction< - DatabaseFetcher["downloadGitHubDatabaseFromUrl"] - >; + let downloadGitHubDatabaseFromUrlMock: jest.Mock; beforeEach(() => { octokit = mockedObject({}); - databaseManager = mockedObject({}); - cliServer = mockedObject({}); - databaseFetcher = new DatabaseFetcher( - app, - databaseManager, - storagePath, - cliServer, - ); + + downloadGitHubDatabaseFromUrlMock = jest.fn().mockReturnValue(undefined); + databaseFetcher = mockedObject({ + downloadGitHubDatabaseFromUrl: downloadGitHubDatabaseFromUrlMock, + }); showQuickPickSpy = jest.spyOn(window, "showQuickPick").mockResolvedValue( mockedQuickPickItem([ @@ -139,9 +130,6 @@ describe("downloadDatabaseFromGitHub", () => { }), ]), ); - downloadGitHubDatabaseFromUrlSpy = jest - .spyOn(databaseFetcher, "downloadGitHubDatabaseFromUrl") - .mockResolvedValue(undefined); }); it("downloads the database", async () => { @@ -154,8 +142,8 @@ describe("downloadDatabaseFromGitHub", () => { app.commands, ); - expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(1); - expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith( + expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledTimes(1); + expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledWith( databases[0].url, databases[0].id, databases[0].created_at, @@ -213,8 +201,8 @@ describe("downloadDatabaseFromGitHub", () => { app.commands, ); - expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(1); - expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith( + expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledTimes(1); + expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledWith( databases[1].url, databases[1].id, databases[1].created_at, @@ -264,8 +252,8 @@ describe("downloadDatabaseFromGitHub", () => { app.commands, ); - expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(2); - expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith( + expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledTimes(2); + expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledWith( databases[0].url, databases[0].id, databases[0].created_at, @@ -277,7 +265,7 @@ describe("downloadDatabaseFromGitHub", () => { true, false, ); - expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith( + expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledWith( databases[1].url, databases[1].id, databases[1].created_at, @@ -321,7 +309,7 @@ describe("downloadDatabaseFromGitHub", () => { app.commands, ); - expect(downloadGitHubDatabaseFromUrlSpy).not.toHaveBeenCalled(); + expect(downloadGitHubDatabaseFromUrlMock).not.toHaveBeenCalled(); }); }); }); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/updates.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/updates.test.ts index d5b61957000..f4233d8f71b 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/updates.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/updates.test.ts @@ -8,10 +8,12 @@ import { mockedQuickPickItem, } from "../../../utils/mocking.helpers"; import type { CodeqlDatabase } from "../../../../../src/databases/github-databases/api"; -import type { DatabaseManager } from "../../../../../src/databases/local-databases"; +import type { + DatabaseItem, + DatabaseManager, +} from "../../../../../src/databases/local-databases"; import type { GitHubDatabaseConfig } from "../../../../../src/config"; -import type { CodeQLCliServer } from "../../../../../src/codeql-cli/cli"; -import { DatabaseFetcher } from "../../../../../src/databases/database-fetcher"; +import type { DatabaseFetcher } from "../../../../../src/databases/database-fetcher"; import * as dialog from "../../../../../src/common/vscode/dialog"; import type { DatabaseUpdate } from "../../../../../src/databases/github-databases/updates"; import { @@ -345,8 +347,6 @@ describe("downloadDatabaseUpdateFromGitHub", () => { const repo = "codeql"; let databaseManager: DatabaseManager; let databaseFetcher: DatabaseFetcher; - const storagePath = "/a/b/c/d"; - let cliServer: CodeQLCliServer; const app = createMockApp(); let updates: DatabaseUpdate[] = [ @@ -368,22 +368,18 @@ describe("downloadDatabaseUpdateFromGitHub", () => { ]; let showQuickPickSpy: jest.SpiedFunction; - let downloadGitHubDatabaseFromUrlSpy: jest.SpiedFunction< - DatabaseFetcher["downloadGitHubDatabaseFromUrl"] - >; + let downloadGitHubDatabaseFromUrlMock: jest.Mock; beforeEach(() => { octokit = mockedObject({}); databaseManager = mockedObject({ currentDatabaseItem: mockDatabaseItem(), }); - cliServer = mockedObject({}); - databaseFetcher = new DatabaseFetcher( - app, - databaseManager, - storagePath, - cliServer, - ); + + downloadGitHubDatabaseFromUrlMock = jest.fn().mockReturnValue(undefined); + databaseFetcher = mockedObject({ + downloadGitHubDatabaseFromUrl: downloadGitHubDatabaseFromUrlMock, + }); showQuickPickSpy = jest.spyOn(window, "showQuickPick").mockResolvedValue( mockedQuickPickItem([ @@ -392,9 +388,6 @@ describe("downloadDatabaseUpdateFromGitHub", () => { }), ]), ); - downloadGitHubDatabaseFromUrlSpy = jest - .spyOn(databaseFetcher, "downloadGitHubDatabaseFromUrl") - .mockResolvedValue(undefined); }); it("downloads the database", async () => { @@ -408,8 +401,8 @@ describe("downloadDatabaseUpdateFromGitHub", () => { app.commands, ); - expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(1); - expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith( + expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledTimes(1); + expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledWith( updates[0].database.url, updates[0].database.id, updates[0].database.created_at, @@ -483,8 +476,8 @@ describe("downloadDatabaseUpdateFromGitHub", () => { app.commands, ); - expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(1); - expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith( + expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledTimes(1); + expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledWith( updates[1].database.url, updates[1].database.id, updates[1].database.created_at, @@ -535,8 +528,8 @@ describe("downloadDatabaseUpdateFromGitHub", () => { app.commands, ); - expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledTimes(2); - expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith( + expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledTimes(2); + expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledWith( updates[0].database.url, updates[0].database.id, updates[0].database.created_at, @@ -548,7 +541,7 @@ describe("downloadDatabaseUpdateFromGitHub", () => { false, false, ); - expect(downloadGitHubDatabaseFromUrlSpy).toHaveBeenCalledWith( + expect(downloadGitHubDatabaseFromUrlMock).toHaveBeenCalledWith( updates[1].database.url, updates[1].database.id, updates[1].database.created_at, @@ -593,7 +586,7 @@ describe("downloadDatabaseUpdateFromGitHub", () => { app.commands, ); - expect(downloadGitHubDatabaseFromUrlSpy).not.toHaveBeenCalled(); + expect(downloadGitHubDatabaseFromUrlMock).not.toHaveBeenCalled(); }); }); }); From 2d85018d1d6e1165858a61a07d7ecfc201cf03a3 Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Tue, 12 Mar 2024 14:50:59 -0700 Subject: [PATCH 0129/1237] Add better log messages --- .../ql-vscode/src/databases/database-fetcher.ts | 4 +++- .../ql-vscode/src/databases/local-databases-ui.ts | 13 ++++++++++--- .../databases/local-databases/database-manager.ts | 1 + 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index c8653eb13e1..995786dffa0 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -318,7 +318,9 @@ export async function importLocalDatabase( await commandManager.execute("codeQLDatabases.focus"); void showAndLogInformationMessage( extLogger, - "Database unzipped and imported successfully.", + origin.type === "testproj" + ? "Test database imported successfully." + : "Database unzipped and imported successfully.", ); } return item; diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index d79968c9423..9344f58dae3 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -755,13 +755,12 @@ export class DatabaseUI extends DisposableObject { // Check if the database is already in the workspace. If // so, delete it first before importing the new one. const existingItem = this.databaseManager.findTestDatabase(uri); + const baseName = basename(uri.fsPath); if (existingItem !== undefined) { progress({ maxStep: 9, step: 1, - message: `Removing existing test database ${basename( - uri.fsPath, - )}`, + message: `Removing existing test database ${baseName}`, }); await this.databaseManager.removeDatabaseItem(existingItem); } @@ -774,6 +773,14 @@ export class DatabaseUI extends DisposableObject { progress, this.queryServer.cliServer, ); + + if (existingItem !== undefined) { + progress({ + maxStep: 9, + step: 9, + message: `Successfully re-imported ${baseName}`, + }); + } } catch (e) { // rethrow and let this be handled by default error handling. throw new Error( diff --git a/extensions/ql-vscode/src/databases/local-databases/database-manager.ts b/extensions/ql-vscode/src/databases/local-databases/database-manager.ts index c53dabc1760..a1a7dc28be4 100644 --- a/extensions/ql-vscode/src/databases/local-databases/database-manager.ts +++ b/extensions/ql-vscode/src/databases/local-databases/database-manager.ts @@ -256,6 +256,7 @@ export class DatabaseManager extends DisposableObject { dateAdded: Date.now(), language: dbItem.language, origin: dbItem.origin, + extensionManagedLocation: dbItem.extensionManagedLocation, }); await this.addDatabaseItem(newDbItem); await this.setCurrentDatabaseItem(newDbItem); From 992a836ccbd9f956afbcef94148c0de47e75baf7 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Tue, 12 Mar 2024 14:17:33 +0100 Subject: [PATCH 0130/1237] Use tree-kill for killing CLI process --- extensions/ql-vscode/src/codeql-cli/cli.ts | 29 ++++++++++++++++------ 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index 292e4925d78..fe31a34f95c 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -466,19 +466,32 @@ export class CodeQLCliServer implements Disposable { void this.logger.log(`${description} using CodeQL CLI: ${argsString}...`); } - const process = spawnChildProcess(codeqlPath, args); + const abortController = new AbortController(); + + const process = spawnChildProcess(codeqlPath, args, { + signal: abortController.signal, + }); if (!process || !process.pid) { throw new Error( `Failed to start ${description} using command ${codeqlPath} ${argsString}.`, ); } - let cancellationRegistration: Disposable | undefined = undefined; - try { - cancellationRegistration = token?.onCancellationRequested((_e) => { - tk(process.pid || 0); - }); + // We need to ensure that we're not killing the same process twice (since this may kill + // another process with the same PID), so keep track of whether we've already exited. + let exited = false; + process.on("exit", () => { + exited = true; + }); + const cancellationRegistration = token?.onCancellationRequested((_e) => { + abortController.abort("Token was cancelled."); + if (process.pid && !exited) { + tk(process.pid); + } + }); + + try { return await this.handleProcessOutput(process, { handleNullTerminator: false, description, @@ -501,7 +514,9 @@ export class CodeQLCliServer implements Disposable { throw e; } finally { process.stdin.end(); - process.kill(); + if (!exited) { + tk(process.pid); + } process.stdout.destroy(); process.stderr.destroy(); From 8bb0085992c4151b23980fce92e89c4730d52f26 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 13 Mar 2024 10:20:55 +0100 Subject: [PATCH 0131/1237] Enable forceExit for CLI tests --- .../ql-vscode/test/vscode-tests/cli-integration/jest.config.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.config.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.config.ts index fe634b66c95..38ebfa094b5 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.config.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.config.ts @@ -9,6 +9,8 @@ const config: Config = { // CLI integration tests call into the CLI and execute queries, so these are expected to take a lot longer // than the default 5 seconds. testTimeout: 180_000, // 3 minutes + // Ensure that Jest exits even when there are some remaining handles open + forceExit: true, }; export default config; From 3005dacf4ed480aa76e541b7d3697f5a34571faf Mon Sep 17 00:00:00 2001 From: Shati Patel <42641846+shati-patel@users.noreply.github.com> Date: Wed, 13 Mar 2024 10:12:48 +0000 Subject: [PATCH 0132/1237] Use VSCodeBadge component (#3462) --- extensions/ql-vscode/src/view/model-editor/MethodRow.tsx | 7 ++----- .../src/view/model-editor/ModelAlertsIndicator.tsx | 9 ++++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx index 2f8c68fbb02..b0b0c428d08 100644 --- a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx @@ -1,4 +1,5 @@ import { + VSCodeBadge, VSCodeButton, VSCodeLink, VSCodeProgressRing, @@ -57,11 +58,7 @@ const ModelButtonsContainer = styled.div` gap: 1em; `; -const UsagesButton = styled.button` - color: var(--vscode-editor-foreground); - background-color: var(--vscode-input-background); - border: none; - border-radius: 40%; +const UsagesButton = styled(VSCodeBadge)` cursor: pointer; `; diff --git a/extensions/ql-vscode/src/view/model-editor/ModelAlertsIndicator.tsx b/extensions/ql-vscode/src/view/model-editor/ModelAlertsIndicator.tsx index e25ba3c01d2..04f0c2688cf 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelAlertsIndicator.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelAlertsIndicator.tsx @@ -2,12 +2,9 @@ import { styled } from "styled-components"; import type { ModeledMethod } from "../../model-editor/modeled-method"; import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state"; import type { ModelEditorViewState } from "../../model-editor/shared/view-state"; +import { VSCodeBadge } from "@vscode/webview-ui-toolkit/react"; -const ModelAlertsButton = styled.button` - color: var(--vscode-editor-foreground); - background-color: var(--vscode-input-background); - border: none; - border-radius: 40%; +const ModelAlertsButton = styled(VSCodeBadge)` cursor: pointer; `; @@ -36,6 +33,8 @@ export const ModelAlertsIndicator = ({ return ( { event.stopPropagation(); }} From 826175ccd53cae939289ff08d7b29401b34e4b7d Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 13 Mar 2024 11:32:57 +0000 Subject: [PATCH 0133/1237] Create token-not-used.ql --- .github/codeql/queries/token-not-used.ql | 41 ++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/codeql/queries/token-not-used.ql diff --git a/.github/codeql/queries/token-not-used.ql b/.github/codeql/queries/token-not-used.ql new file mode 100644 index 00000000000..4b7af8b0b8c --- /dev/null +++ b/.github/codeql/queries/token-not-used.ql @@ -0,0 +1,41 @@ +/** + * @name Don't ignore the token for a cancelable progress bar + * @kind problem + * @problem.severity warning + * @id vscode-codeql/token-not-used + * @description If we call `withProgress` with `cancellable: true` but then + * ignore the token that is given to us, it will lead to a poor user experience + * because the progress bar will appear to be canceled but it will not actually + * affect the background process. + */ + +import javascript + +class NewTokenSource extends CallExpr { + NewTokenSource() { + this.getCalleeName() = "withProgress" or this.getCalleeName() = "withInheritedProgress" + } + + Function getCallback() { + this.getCalleeName() = "withProgress" and result = this.getArgument(0) + or + this.getCalleeName() = "withInheritedProgress" and result = this.getArgument(1) + } + + ObjectExpr getOptions() { + this.getCalleeName() = "withProgress" and result = this.getArgument(1) + or + this.getCalleeName() = "withInheritedProgress" and result = this.getArgument(2) + } + + predicate usesToken() { this.getCallback().getNumParameter() >= 2 } + + predicate isCancellable() { + this.getOptions().getPropertyByName("cancellable").getInit().(BooleanLiteral).getBoolValue() = + true + } +} + +from NewTokenSource t +where t.isCancellable() and not t.usesToken() +select t, "This progress bar is cancelable but the token is not used" From d56f71710ec44513058d572e519653870cd289e8 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 13 Mar 2024 13:03:46 +0100 Subject: [PATCH 0134/1237] Use MultiCancellationToken in model evaluator --- extensions/ql-vscode/src/model-editor/model-evaluator.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts index 098f0703dfd..b6b2eec59e8 100644 --- a/extensions/ql-vscode/src/model-editor/model-evaluator.ts +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -18,6 +18,7 @@ import type { CancellationToken } from "vscode"; import { CancellationTokenSource } from "vscode"; import type { QlPackDetails } from "../variant-analysis/ql-pack-details"; import type { App } from "../common/app"; +import { MultiCancellationToken } from "../common/vscode/multi-cancellation-token"; import { ModelAlertsView } from "./model-alerts/model-alerts-view"; export class ModelEvaluator extends DisposableObject { @@ -68,11 +69,11 @@ export class ModelEvaluator extends DisposableObject { // Submit variant analysis and monitor progress return withProgress( - (progress) => + (progress, token) => this.runVariantAnalysis( qlPack, progress, - this.cancellationSource.token, + new MultiCancellationToken(token, this.cancellationSource.token), ), { title: "Run model evaluation", From 84a8ffdad23b8737a61c25c906fae53a990f9c1a Mon Sep 17 00:00:00 2001 From: Shati Patel <42641846+shati-patel@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:05:47 +0000 Subject: [PATCH 0135/1237] Only allow one open model alerts view (#3461) --- .../model-alerts/model-alerts-view.ts | 28 +++++++++++++++++-- .../src/model-editor/model-evaluator.ts | 17 +++++++++-- .../src/model-editor/modeling-events.ts | 15 ++++++++++ .../src/model-editor/modeling-store.ts | 22 +++++++++++++++ .../src/view/model-editor/ModelEvaluation.tsx | 3 +- .../__tests__/ModelEvaluation.spec.tsx | 23 ++++++++++++++- 6 files changed, 102 insertions(+), 6 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts b/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts index 0713ff10fee..132e7322609 100644 --- a/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts @@ -11,6 +11,9 @@ import type { App } from "../../common/app"; import { redactableError } from "../../common/errors"; import { extLogger } from "../../common/logging/vscode"; import { showAndLogExceptionWithTelemetry } from "../../common/logging"; +import type { ModelingEvents } from "../modeling-events"; +import type { ModelingStore } from "../modeling-store"; +import type { DatabaseItem } from "../../databases/local-databases"; export class ModelAlertsView extends AbstractWebview< ToModelAlertsMessage, @@ -18,8 +21,15 @@ export class ModelAlertsView extends AbstractWebview< > { public static readonly viewType = "codeQL.modelAlerts"; - public constructor(app: App) { + public constructor( + app: App, + private readonly modelingEvents: ModelingEvents, + private readonly modelingStore: ModelingStore, + private readonly dbItem: DatabaseItem, + ) { super(app); + + this.registerToModelingEvents(); } public async showView() { @@ -40,7 +50,7 @@ export class ModelAlertsView extends AbstractWebview< } protected onPanelDispose(): void { - // Nothing to dispose + this.modelingStore.updateIsModelAlertsViewOpen(this.dbItem, false); } protected async onMessage(msg: FromModelAlertsMessage): Promise { @@ -64,4 +74,18 @@ export class ModelAlertsView extends AbstractWebview< assertNever(msg); } } + + public async focusView(): Promise { + this.panel?.reveal(); + } + + private registerToModelingEvents() { + this.push( + this.modelingEvents.onFocusModelAlertsView(async (event) => { + if (event.dbUri === this.dbItem.databaseUri.toString()) { + await this.focusView(); + } + }), + ); + } } diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts index 895f1927a38..ad54b4a8c4f 100644 --- a/extensions/ql-vscode/src/model-editor/model-evaluator.ts +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -107,8 +107,21 @@ export class ModelEvaluator extends DisposableObject { } public async openModelAlertsView() { - const view = new ModelAlertsView(this.app); - await view.showView(); + if (this.modelingStore.isModelAlertsViewOpen(this.dbItem)) { + this.modelingEvents.fireFocusModelAlertsViewEvent( + this.dbItem.databaseUri.toString(), + ); + return; + } else { + this.modelingStore.updateIsModelAlertsViewOpen(this.dbItem, true); + const view = new ModelAlertsView( + this.app, + this.modelingEvents, + this.modelingStore, + this.dbItem, + ); + await view.showView(); + } } private registerToModelingEvents() { diff --git a/extensions/ql-vscode/src/model-editor/modeling-events.ts b/extensions/ql-vscode/src/model-editor/modeling-events.ts index 867e85d3677..c1f3a7d6e80 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-events.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-events.ts @@ -65,6 +65,10 @@ interface FocusModelEditorEvent { dbUri: string; } +interface FocusModelAlertsViewEvent { + dbUri: string; +} + export class ModelingEvents extends DisposableObject { public readonly onActiveDbChanged: AppEvent; public readonly onDbOpened: AppEvent; @@ -79,6 +83,7 @@ export class ModelingEvents extends DisposableObject { public readonly onModelEvaluationRunChanged: AppEvent; public readonly onRevealInModelEditor: AppEvent; public readonly onFocusModelEditor: AppEvent; + public readonly onFocusModelAlertsView: AppEvent; private readonly onActiveDbChangedEventEmitter: AppEventEmitter; private readonly onDbOpenedEventEmitter: AppEventEmitter; @@ -93,6 +98,7 @@ export class ModelingEvents extends DisposableObject { private readonly onModelEvaluationRunChangedEventEmitter: AppEventEmitter; private readonly onRevealInModelEditorEventEmitter: AppEventEmitter; private readonly onFocusModelEditorEventEmitter: AppEventEmitter; + private readonly onFocusModelAlertsViewEventEmitter: AppEventEmitter; constructor(app: App) { super(); @@ -165,6 +171,11 @@ export class ModelingEvents extends DisposableObject { app.createEventEmitter(), ); this.onFocusModelEditor = this.onFocusModelEditorEventEmitter.event; + + this.onFocusModelAlertsViewEventEmitter = this.push( + app.createEventEmitter(), + ); + this.onFocusModelAlertsView = this.onFocusModelAlertsViewEventEmitter.event; } public fireActiveDbChangedEvent() { @@ -286,4 +297,8 @@ export class ModelingEvents extends DisposableObject { dbUri, }); } + + public fireFocusModelAlertsViewEvent(dbUri: string) { + this.onFocusModelAlertsViewEventEmitter.fire({ dbUri }); + } } diff --git a/extensions/ql-vscode/src/model-editor/modeling-store.ts b/extensions/ql-vscode/src/model-editor/modeling-store.ts index 3622759373b..d4ac7e46df7 100644 --- a/extensions/ql-vscode/src/model-editor/modeling-store.ts +++ b/extensions/ql-vscode/src/model-editor/modeling-store.ts @@ -20,6 +20,7 @@ interface InternalDbModelingState { selectedMethod: Method | undefined; selectedUsage: Usage | undefined; modelEvaluationRun: ModelEvaluationRun | undefined; + isModelAlertsViewOpen: boolean; } export interface DbModelingState { @@ -34,6 +35,7 @@ export interface DbModelingState { readonly selectedMethod: Method | undefined; readonly selectedUsage: Usage | undefined; readonly modelEvaluationRun: ModelEvaluationRun | undefined; + readonly isModelAlertsViewOpen: boolean; } export interface SelectedMethodDetails { @@ -71,6 +73,7 @@ export class ModelingStore extends DisposableObject { selectedUsage: undefined, inProgressMethods: new Set(), modelEvaluationRun: undefined, + isModelAlertsViewOpen: false, }); this.modelingEvents.fireDbOpenedEvent(databaseItem); @@ -498,4 +501,23 @@ export class ModelingStore extends DisposableObject { state.modelEvaluationRun, ); } + + public isModelAlertsViewOpen(dbItem: DatabaseItem): boolean { + return this.getState(dbItem).isModelAlertsViewOpen ?? false; + } + + private changeIsModelAlertsViewOpen( + dbItem: DatabaseItem, + updateState: (state: InternalDbModelingState) => void, + ) { + const state = this.getState(dbItem); + + updateState(state); + } + + public updateIsModelAlertsViewOpen(dbItem: DatabaseItem, isOpen: boolean) { + this.changeIsModelAlertsViewOpen(dbItem, (state) => { + state.isModelAlertsViewOpen = isOpen; + }); + } } diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx index 66860003c19..1a0e4b602a4 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx @@ -34,7 +34,8 @@ export const ModelEvaluation = ({ const shouldShowStopButton = !shouldShowEvaluateButton; - const shouldShowEvaluationRunLink = !!evaluationRun; + const shouldShowEvaluationRunLink = + !!evaluationRun && evaluationRun.variantAnalysis; const customModelsExist = Object.values(modeledMethods).some( (methods) => methods.filter((m) => m.type !== "none").length > 0, diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/ModelEvaluation.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelEvaluation.spec.tsx index 1d4ea9f3d35..24897bfa098 100644 --- a/extensions/ql-vscode/src/view/model-editor/__tests__/ModelEvaluation.spec.tsx +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelEvaluation.spec.tsx @@ -98,7 +98,7 @@ describe(ModelEvaluation.name, () => { expect(screen.queryByText("Stop evaluation")).not.toBeInTheDocument(); }); - it("renders 'Stop evaluation' button and 'Evaluation run' link when there is an in progress evaluation", () => { + it("renders 'Stop evaluation' button when there is an in progress evaluation, but no variant analysis yet", () => { render({ evaluationRun: { isPreparing: true, @@ -112,6 +112,27 @@ describe(ModelEvaluation.name, () => { stopEvaluationButton?.getElementsByTagName("input")[0], ).toBeEnabled(); + expect(screen.queryByText("Evaluation run")).not.toBeInTheDocument(); + + expect(screen.queryByText("Evaluate")).not.toBeInTheDocument(); + }); + + it("renders 'Stop evaluation' button and 'Evaluation run' link when there is an in progress evaluation with variant analysis", () => { + render({ + evaluationRun: { + isPreparing: false, + variantAnalysis: createMockVariantAnalysis({ + status: VariantAnalysisStatus.InProgress, + }), + }, + }); + + const stopEvaluationButton = screen.queryByText("Stop evaluation"); + expect(stopEvaluationButton).toBeInTheDocument(); + expect( + stopEvaluationButton?.getElementsByTagName("input")[0], + ).toBeEnabled(); + expect(screen.queryByText("Evaluation run")).toBeInTheDocument(); expect(screen.queryByText("Evaluate")).not.toBeInTheDocument(); From 695cf340b7bc80baa201e5283594bdd58abbac87 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 13 Mar 2024 15:48:00 +0100 Subject: [PATCH 0136/1237] Do not make model evaluator cancellable --- extensions/ql-vscode/src/model-editor/model-evaluator.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts index b6b2eec59e8..ea0ee6fcc39 100644 --- a/extensions/ql-vscode/src/model-editor/model-evaluator.ts +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -18,7 +18,6 @@ import type { CancellationToken } from "vscode"; import { CancellationTokenSource } from "vscode"; import type { QlPackDetails } from "../variant-analysis/ql-pack-details"; import type { App } from "../common/app"; -import { MultiCancellationToken } from "../common/vscode/multi-cancellation-token"; import { ModelAlertsView } from "./model-alerts/model-alerts-view"; export class ModelEvaluator extends DisposableObject { @@ -69,15 +68,15 @@ export class ModelEvaluator extends DisposableObject { // Submit variant analysis and monitor progress return withProgress( - (progress, token) => + (progress) => this.runVariantAnalysis( qlPack, progress, - new MultiCancellationToken(token, this.cancellationSource.token), + this.cancellationSource.token, ), { title: "Run model evaluation", - cancellable: true, + cancellable: false, }, ); } From 280bf8b7b2941c90e490db20f85d458e069da48c Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 13 Mar 2024 16:43:42 +0000 Subject: [PATCH 0137/1237] Convert to an abstract class --- .github/codeql/queries/ProgressBar.qll | 32 ++++++++++++++++++++++++ .github/codeql/queries/token-not-used.ql | 28 ++------------------- 2 files changed, 34 insertions(+), 26 deletions(-) create mode 100644 .github/codeql/queries/ProgressBar.qll diff --git a/.github/codeql/queries/ProgressBar.qll b/.github/codeql/queries/ProgressBar.qll new file mode 100644 index 00000000000..601dca3d46c --- /dev/null +++ b/.github/codeql/queries/ProgressBar.qll @@ -0,0 +1,32 @@ +import javascript + +abstract class ProgressBar extends CallExpr { + ProgressBar() { any() } + + abstract Function getCallback(); + + abstract ObjectExpr getOptions(); + + predicate usesToken() { this.getCallback().getNumParameter() >= 2 } + + predicate isCancellable() { + this.getOptions().getPropertyByName("cancellable").getInit().(BooleanLiteral).getBoolValue() = + true + } +} + +class WithProgressCall extends ProgressBar { + WithProgressCall() { this.getCalleeName() = "withProgress" } + + override Function getCallback() { result = this.getArgument(0) } + + override ObjectExpr getOptions() { result = this.getArgument(1) } +} + +class WithInheritedProgressCall extends ProgressBar { + WithInheritedProgressCall() { this.getCalleeName() = "withInheritedProgress" } + + override Function getCallback() { result = this.getArgument(1) } + + override ObjectExpr getOptions() { result = this.getArgument(2) } +} diff --git a/.github/codeql/queries/token-not-used.ql b/.github/codeql/queries/token-not-used.ql index 4b7af8b0b8c..5a01d5f922f 100644 --- a/.github/codeql/queries/token-not-used.ql +++ b/.github/codeql/queries/token-not-used.ql @@ -10,32 +10,8 @@ */ import javascript +import ProgressBar -class NewTokenSource extends CallExpr { - NewTokenSource() { - this.getCalleeName() = "withProgress" or this.getCalleeName() = "withInheritedProgress" - } - - Function getCallback() { - this.getCalleeName() = "withProgress" and result = this.getArgument(0) - or - this.getCalleeName() = "withInheritedProgress" and result = this.getArgument(1) - } - - ObjectExpr getOptions() { - this.getCalleeName() = "withProgress" and result = this.getArgument(1) - or - this.getCalleeName() = "withInheritedProgress" and result = this.getArgument(2) - } - - predicate usesToken() { this.getCallback().getNumParameter() >= 2 } - - predicate isCancellable() { - this.getOptions().getPropertyByName("cancellable").getInit().(BooleanLiteral).getBoolValue() = - true - } -} - -from NewTokenSource t +from ProgressBar t where t.isCancellable() and not t.usesToken() select t, "This progress bar is cancelable but the token is not used" From 73bece6f38b85c674b4039a1d1ba0b0d2b01acba Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 13 Mar 2024 16:43:59 +0000 Subject: [PATCH 0138/1237] Add query for using token when not cancellable --- .../codeql/queries/progress-not-cancellable.ql | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/codeql/queries/progress-not-cancellable.ql diff --git a/.github/codeql/queries/progress-not-cancellable.ql b/.github/codeql/queries/progress-not-cancellable.ql new file mode 100644 index 00000000000..7477d084b3f --- /dev/null +++ b/.github/codeql/queries/progress-not-cancellable.ql @@ -0,0 +1,15 @@ +/** + * @name Using token for non-cancellable progress bar + * @kind problem + * @problem.severity warning + * @id vscode-codeql/progress-not-cancellable + * @description If we call `withProgress` with `cancellable: false` then the + * token that is given to us should be ignored because it won't ever be cancelled. + */ + +import javascript +import ProgressBar + +from ProgressBar t +where not t.isCancellable() and t.usesToken() +select t, "The token should not be used when the progress bar is not cancelable" From 4346cc6e984d3f7c60b0cb799debbb808cdca414 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 13 Mar 2024 17:08:55 +0000 Subject: [PATCH 0139/1237] Link to cancellable property --- .github/codeql/queries/ProgressBar.qll | 4 +++- .github/codeql/queries/token-not-used.ql | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/codeql/queries/ProgressBar.qll b/.github/codeql/queries/ProgressBar.qll index 601dca3d46c..042f152087f 100644 --- a/.github/codeql/queries/ProgressBar.qll +++ b/.github/codeql/queries/ProgressBar.qll @@ -9,8 +9,10 @@ abstract class ProgressBar extends CallExpr { predicate usesToken() { this.getCallback().getNumParameter() >= 2 } + Property getCancellableProperty() { result = this.getOptions().getPropertyByName("cancellable") } + predicate isCancellable() { - this.getOptions().getPropertyByName("cancellable").getInit().(BooleanLiteral).getBoolValue() = + this.getCancellableProperty().getInit().(BooleanLiteral).getBoolValue() = true } } diff --git a/.github/codeql/queries/token-not-used.ql b/.github/codeql/queries/token-not-used.ql index 5a01d5f922f..75de929981a 100644 --- a/.github/codeql/queries/token-not-used.ql +++ b/.github/codeql/queries/token-not-used.ql @@ -14,4 +14,4 @@ import ProgressBar from ProgressBar t where t.isCancellable() and not t.usesToken() -select t, "This progress bar is cancelable but the token is not used" +select t, "This progress bar is $@ but the token is not used", t.getCancellableProperty(), "cancellable" From 84df4b64d8191d4db7be8a16b64b7813cc2f4686 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 13 Mar 2024 17:24:09 +0000 Subject: [PATCH 0140/1237] Update messages/descriptions with how to fix the alert --- .github/codeql/queries/progress-not-cancellable.ql | 6 ++++-- .github/codeql/queries/token-not-used.ql | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/codeql/queries/progress-not-cancellable.ql b/.github/codeql/queries/progress-not-cancellable.ql index 7477d084b3f..ae46351eead 100644 --- a/.github/codeql/queries/progress-not-cancellable.ql +++ b/.github/codeql/queries/progress-not-cancellable.ql @@ -3,8 +3,10 @@ * @kind problem * @problem.severity warning * @id vscode-codeql/progress-not-cancellable - * @description If we call `withProgress` with `cancellable: false` then the + * @description If we call `withProgress` without `cancellable: true` then the * token that is given to us should be ignored because it won't ever be cancelled. + * This makes the code more confusing as it tries to account for cases that can't + * happen. The fix is to either not use the token or make the progress bar cancellable. */ import javascript @@ -12,4 +14,4 @@ import ProgressBar from ProgressBar t where not t.isCancellable() and t.usesToken() -select t, "The token should not be used when the progress bar is not cancelable" +select t, "The token should not be used when the progress bar is not cancelable. Either stop using the token or mark the progress bar as cancellable." diff --git a/.github/codeql/queries/token-not-used.ql b/.github/codeql/queries/token-not-used.ql index 75de929981a..ffea53e6390 100644 --- a/.github/codeql/queries/token-not-used.ql +++ b/.github/codeql/queries/token-not-used.ql @@ -6,7 +6,8 @@ * @description If we call `withProgress` with `cancellable: true` but then * ignore the token that is given to us, it will lead to a poor user experience * because the progress bar will appear to be canceled but it will not actually - * affect the background process. + * affect the background process. Either check the token and respect when it + * has been cancelled, or mark the progress bar as not cancellable. */ import javascript @@ -14,4 +15,4 @@ import ProgressBar from ProgressBar t where t.isCancellable() and not t.usesToken() -select t, "This progress bar is $@ but the token is not used", t.getCancellableProperty(), "cancellable" +select t, "This progress bar is $@ but the token is not used. Either use the token or mark the progress bar as not cancellable.", t.getCancellableProperty(), "cancellable" From bf58e311dd69afdbbaae5c5dea029a76a4be3289 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 13 Mar 2024 17:37:21 +0000 Subject: [PATCH 0141/1237] Delete withInheritedProgress because we always have a progress context --- .../ql-vscode/src/common/vscode/progress.ts | 18 ----- .../src/databases/local-databases-ui.ts | 72 ++++++++----------- 2 files changed, 29 insertions(+), 61 deletions(-) diff --git a/extensions/ql-vscode/src/common/vscode/progress.ts b/extensions/ql-vscode/src/common/vscode/progress.ts index 49828c87918..8b7f9ddaa42 100644 --- a/extensions/ql-vscode/src/common/vscode/progress.ts +++ b/extensions/ql-vscode/src/common/vscode/progress.ts @@ -90,24 +90,6 @@ export interface ProgressContext { token: CancellationToken; } -/** - * Like `withProgress()`, except that the caller is not required to provide a progress context. If - * the caller does provide one, any long-running operations performed by `task` will use the - * supplied progress context. Otherwise, this function wraps `task` in a new progress context with - * the supplied options. - */ -export function withInheritedProgress( - parent: ProgressContext | undefined, - task: ProgressTask, - options: ProgressOptions, -): Thenable { - if (parent !== undefined) { - return task(parent.progress, parent.token); - } else { - return withProgress(task, options); - } -} - /** * Displays a progress monitor that indicates how much progess has been made * reading from a stream. diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index c5230ce72cb..27896260fd5 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -16,7 +16,6 @@ import { ThemeIcon, ThemeColor, workspace, - ProgressLocation, } from "vscode"; import { pathExists, stat, readdir, remove } from "fs-extra"; @@ -31,7 +30,6 @@ import type { } from "../common/vscode/progress"; import { UserCancellationException, - withInheritedProgress, withProgress, } from "../common/vscode/progress"; import { @@ -330,10 +328,9 @@ export class DatabaseUI extends DisposableObject { private async chooseDatabaseFolder( progress: ProgressCallback, - token: CancellationToken, ): Promise { try { - await this.chooseAndSetDatabase(true, { progress, token }); + await this.chooseAndSetDatabase(true, progress); } catch (e) { void showAndLogExceptionWithTelemetry( this.app.logger, @@ -347,8 +344,8 @@ export class DatabaseUI extends DisposableObject { private async handleChooseDatabaseFolder(): Promise { return withProgress( - async (progress, token) => { - await this.chooseDatabaseFolder(progress, token); + async (progress) => { + await this.chooseDatabaseFolder(progress); }, { title: "Adding database from folder", @@ -358,8 +355,8 @@ export class DatabaseUI extends DisposableObject { private async handleChooseDatabaseFolderFromPalette(): Promise { return withProgress( - async (progress, token) => { - await this.chooseDatabaseFolder(progress, token); + async (progress) => { + await this.chooseDatabaseFolder(progress); }, { title: "Choose a Database from a Folder", @@ -500,10 +497,9 @@ export class DatabaseUI extends DisposableObject { private async chooseDatabaseArchive( progress: ProgressCallback, - token: CancellationToken, ): Promise { try { - await this.chooseAndSetDatabase(false, { progress, token }); + await this.chooseAndSetDatabase(false, progress); } catch (e: unknown) { void showAndLogExceptionWithTelemetry( this.app.logger, @@ -517,8 +513,8 @@ export class DatabaseUI extends DisposableObject { private async handleChooseDatabaseArchive(): Promise { return withProgress( - async (progress, token) => { - await this.chooseDatabaseArchive(progress, token); + async (progress) => { + await this.chooseDatabaseArchive(progress); }, { title: "Adding database from archive", @@ -528,8 +524,8 @@ export class DatabaseUI extends DisposableObject { private async handleChooseDatabaseArchiveFromPalette(): Promise { return withProgress( - async (progress, token) => { - await this.chooseDatabaseArchive(progress, token); + async (progress) => { + await this.chooseDatabaseArchive(progress); }, { title: "Choose a Database from an Archive", @@ -940,41 +936,31 @@ export class DatabaseUI extends DisposableObject { */ private async chooseAndSetDatabase( byFolder: boolean, - progress: ProgressContext | undefined, + progress: ProgressCallback, ): Promise { const uri = await chooseDatabaseDir(byFolder); if (!uri) { return undefined; } - return await withInheritedProgress( - progress, - async (progress) => { - if (byFolder) { - const fixedUri = await this.fixDbUri(uri); - // we are selecting a database folder - return await this.databaseManager.openDatabase(fixedUri, { - type: "folder", - }); - } else { - // we are selecting a database archive. Must unzip into a workspace-controlled area - // before importing. - return await importArchiveDatabase( - this.app.commands, - uri.toString(true), - this.databaseManager, - this.storagePath, - progress, - this.queryServer.cliServer, - ); - } - }, - { - location: ProgressLocation.Notification, - cancellable: true, - title: "Opening database", - }, - ); + if (byFolder) { + const fixedUri = await this.fixDbUri(uri); + // we are selecting a database folder + return await this.databaseManager.openDatabase(fixedUri, { + type: "folder", + }); + } else { + // we are selecting a database archive. Must unzip into a workspace-controlled area + // before importing. + return await importArchiveDatabase( + this.app.commands, + uri.toString(true), + this.databaseManager, + this.storagePath, + progress, + this.queryServer.cliServer, + ); + } } /** From 16b8f848b50d04c588de04a74a3523c440118a91 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 13 Mar 2024 17:45:06 +0000 Subject: [PATCH 0142/1237] Delete ProgressContext --- extensions/ql-vscode/src/common/vscode/progress.ts | 5 ----- .../ql-vscode/src/databases/local-databases-ui.ts | 12 ++++-------- .../ql-vscode/src/local-queries/local-queries.ts | 12 +++--------- .../ql-vscode/src/local-queries/quick-query.ts | 4 +--- .../databases/local-databases-ui.test.ts | 11 +++++------ 5 files changed, 13 insertions(+), 31 deletions(-) diff --git a/extensions/ql-vscode/src/common/vscode/progress.ts b/extensions/ql-vscode/src/common/vscode/progress.ts index 8b7f9ddaa42..edf0f46507f 100644 --- a/extensions/ql-vscode/src/common/vscode/progress.ts +++ b/extensions/ql-vscode/src/common/vscode/progress.ts @@ -85,11 +85,6 @@ export function withProgress( ); } -export interface ProgressContext { - progress: ProgressCallback; - token: CancellationToken; -} - /** * Displays a progress monitor that indicates how much progess has been made * reading from a stream. diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 27896260fd5..9261ae7befd 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -24,10 +24,7 @@ import type { DatabaseItem, DatabaseManager, } from "./local-databases"; -import type { - ProgressCallback, - ProgressContext, -} from "../common/vscode/progress"; +import type { ProgressCallback } from "../common/vscode/progress"; import { UserCancellationException, withProgress, @@ -791,9 +788,8 @@ export class DatabaseUI extends DisposableObject { */ public async getDatabaseItem( progress: ProgressCallback, - token: CancellationToken, ): Promise { - return await this.getDatabaseItemInternal({ progress, token }); + return await this.getDatabaseItemInternal(progress); } /** @@ -806,10 +802,10 @@ export class DatabaseUI extends DisposableObject { * notification if it tries to perform any long-running operations. */ private async getDatabaseItemInternal( - progressContext: ProgressContext | undefined, + progress: ProgressCallback | undefined, ): Promise { if (this.databaseManager.currentDatabaseItem === undefined) { - progressContext?.progress({ + progress?.({ maxStep: 2, step: 1, message: "Choosing database", diff --git a/extensions/ql-vscode/src/local-queries/local-queries.ts b/extensions/ql-vscode/src/local-queries/local-queries.ts index ddc29db949b..c1a5dac785c 100644 --- a/extensions/ql-vscode/src/local-queries/local-queries.ts +++ b/extensions/ql-vscode/src/local-queries/local-queries.ts @@ -290,14 +290,8 @@ export class LocalQueries extends DisposableObject { private async quickQuery(): Promise { await withProgress( - async (progress, token) => - displayQuickQuery( - this.app, - this.cliServer, - this.databaseUI, - progress, - token, - ), + async (progress) => + displayQuickQuery(this.app, this.cliServer, this.databaseUI, progress), { title: "Run Quick Query", }, @@ -445,7 +439,7 @@ export class LocalQueries extends DisposableObject { // If no databaseItem is specified, use the database currently selected in the Databases UI databaseItem = - databaseItem ?? (await this.databaseUI.getDatabaseItem(progress, token)); + databaseItem ?? (await this.databaseUI.getDatabaseItem(progress)); if (databaseItem === undefined) { throw new Error("Can't run query without a selected database"); } diff --git a/extensions/ql-vscode/src/local-queries/quick-query.ts b/extensions/ql-vscode/src/local-queries/quick-query.ts index 075cf127686..19c23877f10 100644 --- a/extensions/ql-vscode/src/local-queries/quick-query.ts +++ b/extensions/ql-vscode/src/local-queries/quick-query.ts @@ -1,7 +1,6 @@ import { ensureDir, writeFile, pathExists, readFile } from "fs-extra"; import { dump, load } from "js-yaml"; import { basename, join } from "path"; -import type { CancellationToken } from "vscode"; import { window as Window, workspace, Uri } from "vscode"; import { LSPErrorCodes, ResponseError } from "vscode-languageclient"; import type { CodeQLCliServer } from "../codeql-cli/cli"; @@ -56,7 +55,6 @@ export async function displayQuickQuery( cliServer: CodeQLCliServer, databaseUI: DatabaseUI, progress: ProgressCallback, - token: CancellationToken, ) { try { // If there is already a quick query open, don't clobber it, just @@ -111,7 +109,7 @@ export async function displayQuickQuery( } // We're going to infer which qlpack to use from the current database - const dbItem = await databaseUI.getDatabaseItem(progress, token); + const dbItem = await databaseUI.getDatabaseItem(progress); if (dbItem === undefined) { throw new Error("Can't start quick query without a selected database"); } diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/local-databases-ui.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/local-databases-ui.test.ts index 17470c1660d..87deb111300 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/local-databases-ui.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/local-databases-ui.test.ts @@ -7,7 +7,7 @@ import { createFileSync, pathExistsSync, } from "fs-extra"; -import { CancellationTokenSource, Uri, window } from "vscode"; +import { Uri, window } from "vscode"; import type { DatabaseImportQuickPickItems, @@ -127,7 +127,6 @@ describe("local-databases-ui", () => { describe("getDatabaseItem", () => { const progress = jest.fn(); - const token = new CancellationTokenSource().token; describe("when there is a current database", () => { const databaseUI = new DatabaseUI( app, @@ -153,7 +152,7 @@ describe("local-databases-ui", () => { ); it("should return current database", async () => { - const databaseItem = await databaseUI.getDatabaseItem(progress, token); + const databaseItem = await databaseUI.getDatabaseItem(progress); expect(databaseItem).toEqual({ databaseUri: Uri.file(db1) }); }); @@ -211,7 +210,7 @@ describe("local-databases-ui", () => { "setCurrentDatabaseItem", ); - await databaseUI.getDatabaseItem(progress, token); + await databaseUI.getDatabaseItem(progress); expect(showQuickPickSpy).toHaveBeenCalledTimes(2); expect(setCurrentDatabaseItemSpy).toHaveBeenCalledWith({ @@ -241,7 +240,7 @@ describe("local-databases-ui", () => { .spyOn(databaseUI as any, "handleChooseDatabaseGithub") .mockResolvedValue(undefined); - await databaseUI.getDatabaseItem(progress, token); + await databaseUI.getDatabaseItem(progress); expect(showQuickPickSpy).toHaveBeenCalledTimes(2); expect(handleChooseDatabaseGithubSpy).toHaveBeenCalledTimes(1); @@ -264,7 +263,7 @@ describe("local-databases-ui", () => { .spyOn(databaseUI as any, "handleChooseDatabaseGithub") .mockResolvedValue(undefined); - await databaseUI.getDatabaseItem(progress, token); + await databaseUI.getDatabaseItem(progress); expect(showQuickPickSpy).toHaveBeenCalledTimes(1); expect(handleChooseDatabaseGithubSpy).toHaveBeenCalledTimes(1); From 834b6a649ed0d8bbd1781d737acab08f43d243f1 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 13 Mar 2024 18:06:44 +0000 Subject: [PATCH 0143/1237] Use jest.MockedFunction --- .../local-queries/skeleton-query-wizard.test.ts | 4 +++- .../databases/github-databases/download.test.ts | 5 +++-- .../databases/github-databases/updates.test.ts | 9 ++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts index aacac5e7a10..9076ec9bed4 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/local-queries/skeleton-query-wizard.test.ts @@ -56,7 +56,9 @@ describe("SkeletonQueryWizard", () => { let createExampleQlFileSpy: jest.SpiedFunction< typeof QlPackGenerator.prototype.createExampleQlFile >; - let promptImportGithubDatabaseMock: jest.Mock; + let promptImportGithubDatabaseMock: jest.MockedFunction< + DatabaseFetcher["promptImportGithubDatabase"] + >; let openTextDocumentSpy: jest.SpiedFunction< typeof workspace.openTextDocument >; diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/download.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/download.test.ts index 96018ea6ba0..0803e8baa34 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/download.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/download.test.ts @@ -10,7 +10,6 @@ import { askForGitHubDatabaseDownload, downloadDatabaseFromGitHub, } from "../../../../../src/databases/github-databases/download"; -import type { DatabaseItem } from "../../../../../src/databases/local-databases"; import type { GitHubDatabaseConfig } from "../../../../../src/config"; import type { DatabaseFetcher } from "../../../../../src/databases/database-fetcher"; import * as dialog from "../../../../../src/common/vscode/dialog"; @@ -113,7 +112,9 @@ describe("downloadDatabaseFromGitHub", () => { ]; let showQuickPickSpy: jest.SpiedFunction; - let downloadGitHubDatabaseFromUrlMock: jest.Mock; + let downloadGitHubDatabaseFromUrlMock: jest.MockedFunction< + DatabaseFetcher["downloadGitHubDatabaseFromUrl"] + >; beforeEach(() => { octokit = mockedObject({}); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/updates.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/updates.test.ts index f4233d8f71b..64ce5c54693 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/updates.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/github-databases/updates.test.ts @@ -8,10 +8,7 @@ import { mockedQuickPickItem, } from "../../../utils/mocking.helpers"; import type { CodeqlDatabase } from "../../../../../src/databases/github-databases/api"; -import type { - DatabaseItem, - DatabaseManager, -} from "../../../../../src/databases/local-databases"; +import type { DatabaseManager } from "../../../../../src/databases/local-databases"; import type { GitHubDatabaseConfig } from "../../../../../src/config"; import type { DatabaseFetcher } from "../../../../../src/databases/database-fetcher"; import * as dialog from "../../../../../src/common/vscode/dialog"; @@ -368,7 +365,9 @@ describe("downloadDatabaseUpdateFromGitHub", () => { ]; let showQuickPickSpy: jest.SpiedFunction; - let downloadGitHubDatabaseFromUrlMock: jest.Mock; + let downloadGitHubDatabaseFromUrlMock: jest.MockedFunction< + DatabaseFetcher["downloadGitHubDatabaseFromUrl"] + >; beforeEach(() => { octokit = mockedObject({}); From ff889b73e10af3dd3889acb946060cf722a3df53 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 13 Mar 2024 18:09:51 +0000 Subject: [PATCH 0144/1237] Make documentation of fields more consistent --- extensions/ql-vscode/src/databases/database-fetcher.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index 7b1f6aeb3f4..1299ab317d6 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -102,6 +102,7 @@ export class DatabaseFetcher { * * @param progress the progress callback * @param language the language to download. If undefined, the user will be prompted to choose a language. + * @param suggestedRepoNwo the suggested value to use when prompting for a github repo * @param makeSelected make the new database selected in the databases panel (default: true) * @param addSourceArchiveFolder whether to add a workspace folder containing the source archive to the workspace */ @@ -270,6 +271,7 @@ export class DatabaseFetcher { * Imports a database from a local archive. * * @param databaseUrl the file url of the archive to import + * @param progress the progress callback */ public async importArchiveDatabase( databaseUrl: string, From bbc881e7c76c66a533151edb6ae54323531759b0 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Thu, 14 Mar 2024 09:39:59 +0000 Subject: [PATCH 0145/1237] Move LinkIconButton to common dir (#3467) --- .../src/view/{variant-analysis => common}/LinkIconButton.tsx | 0 extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx | 2 +- extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx | 2 +- extensions/ql-vscode/src/view/variant-analysis/QueryDetails.tsx | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename extensions/ql-vscode/src/view/{variant-analysis => common}/LinkIconButton.tsx (100%) diff --git a/extensions/ql-vscode/src/view/variant-analysis/LinkIconButton.tsx b/extensions/ql-vscode/src/view/common/LinkIconButton.tsx similarity index 100% rename from extensions/ql-vscode/src/view/variant-analysis/LinkIconButton.tsx rename to extensions/ql-vscode/src/view/common/LinkIconButton.tsx diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx index d61876516d3..6b478d84272 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx @@ -11,7 +11,7 @@ import type { ModeledMethod } from "../../model-editor/modeled-method"; import { assertNever } from "../../common/helpers-pure"; import { vscode } from "../vscode-api"; import { calculateModeledPercentage } from "../../model-editor/shared/modeled-percentage"; -import { LinkIconButton } from "../variant-analysis/LinkIconButton"; +import { LinkIconButton } from "../common/LinkIconButton"; import type { ModelEditorViewState } from "../../model-editor/shared/view-state"; import { ModeledMethodsList } from "./ModeledMethodsList"; import { percentFormatter } from "./formatters"; diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx index 1a0e4b602a4..2dd25cfe77d 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx @@ -4,7 +4,7 @@ import type { ModelEditorViewState } from "../../model-editor/shared/view-state" import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state"; import { modelEvaluationRunIsRunning } from "../../model-editor/shared/model-evaluation-run-state"; import { ModelEditorProgressRing } from "./ModelEditorProgressRing"; -import { LinkIconButton } from "../variant-analysis/LinkIconButton"; +import { LinkIconButton } from "../common/LinkIconButton"; export type Props = { viewState: ModelEditorViewState; diff --git a/extensions/ql-vscode/src/view/variant-analysis/QueryDetails.tsx b/extensions/ql-vscode/src/view/variant-analysis/QueryDetails.tsx index d35d031d1f2..6c01ea9d8d7 100644 --- a/extensions/ql-vscode/src/view/variant-analysis/QueryDetails.tsx +++ b/extensions/ql-vscode/src/view/variant-analysis/QueryDetails.tsx @@ -1,6 +1,6 @@ import { styled } from "styled-components"; import { ViewTitle } from "../common"; -import { LinkIconButton } from "./LinkIconButton"; +import { LinkIconButton } from "../common/LinkIconButton"; export type QueryDetailsProps = { queryName: string; From aa2f371583ba04b2d149bb514640ddcb89b90699 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Thu, 14 Mar 2024 10:39:44 +0000 Subject: [PATCH 0146/1237] Close Model Alerts view when Model editor closes (#3468) --- .../src/model-editor/model-alerts/model-alerts-view.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts b/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts index 132e7322609..ebd901c382d 100644 --- a/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts @@ -87,5 +87,13 @@ export class ModelAlertsView extends AbstractWebview< } }), ); + + this.push( + this.modelingEvents.onDbClosed(async (event) => { + if (event === this.dbItem.databaseUri.toString()) { + this.dispose(); + } + }), + ); } } From dd90e38b7ad3ad418630e404ce6fa130f02d5847 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Thu, 14 Mar 2024 11:34:34 +0000 Subject: [PATCH 0147/1237] Add new ModelPacks component (#3469) --- .../src/common/model-pack-details.ts | 4 ++ .../model-alerts/ModelPacks.stories.tsx | 34 +++++++++++++++ .../src/view/model-alerts/ModelPacks.tsx | 43 +++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 extensions/ql-vscode/src/common/model-pack-details.ts create mode 100644 extensions/ql-vscode/src/stories/model-alerts/ModelPacks.stories.tsx create mode 100644 extensions/ql-vscode/src/view/model-alerts/ModelPacks.tsx diff --git a/extensions/ql-vscode/src/common/model-pack-details.ts b/extensions/ql-vscode/src/common/model-pack-details.ts new file mode 100644 index 00000000000..5bd8b158841 --- /dev/null +++ b/extensions/ql-vscode/src/common/model-pack-details.ts @@ -0,0 +1,4 @@ +export interface ModelPackDetails { + name: string; + path: string; +} diff --git a/extensions/ql-vscode/src/stories/model-alerts/ModelPacks.stories.tsx b/extensions/ql-vscode/src/stories/model-alerts/ModelPacks.stories.tsx new file mode 100644 index 00000000000..b39773e6473 --- /dev/null +++ b/extensions/ql-vscode/src/stories/model-alerts/ModelPacks.stories.tsx @@ -0,0 +1,34 @@ +import type { Meta, StoryFn } from "@storybook/react"; + +import { ModelPacks as ModelPacksComponent } from "../../view/model-alerts/ModelPacks"; + +export default { + title: "Model Alerts/Model Packs", + component: ModelPacksComponent, + argTypes: { + openModelPackClick: { + action: "open-model-pack-clicked", + table: { + disable: true, + }, + }, + }, +} as Meta; + +const Template: StoryFn = (args) => ( + +); + +export const ModelPacks = Template.bind({}); +ModelPacks.args = { + modelPacks: [ + { + name: "Model pack 1", + path: "/path/to/model-pack-1", + }, + { + name: "Model pack 2", + path: "/path/to/model-pack-2", + }, + ], +}; diff --git a/extensions/ql-vscode/src/view/model-alerts/ModelPacks.tsx b/extensions/ql-vscode/src/view/model-alerts/ModelPacks.tsx new file mode 100644 index 00000000000..89e65664b86 --- /dev/null +++ b/extensions/ql-vscode/src/view/model-alerts/ModelPacks.tsx @@ -0,0 +1,43 @@ +import { styled } from "styled-components"; +import { LinkIconButton } from "../common/LinkIconButton"; +import type { ModelPackDetails } from "../../common/model-pack-details"; + +const Title = styled.h3` + font-size: medium; + font-weight: 500; + margin: 0; +`; + +const List = styled.ul` + list-style: none; + padding: 0; + margin-top: 5px; +`; + +export const ModelPacks = ({ + modelPacks, + openModelPackClick, +}: { + modelPacks: ModelPackDetails[]; + openModelPackClick: (path: string) => void; +}): React.JSX.Element => { + if (modelPacks.length <= 0) { + return <>; + } + + return ( + <> + Model packs + + {modelPacks.map((modelPack) => ( +
  • + openModelPackClick(modelPack.path)}> + + {modelPack.name} + +
  • + ))} +
    + + ); +}; From 06f9f2c68725edf0f0d3f8ab63dd9889a595596d Mon Sep 17 00:00:00 2001 From: Shati Patel <42641846+shati-patel@users.noreply.github.com> Date: Thu, 14 Mar 2024 11:46:36 +0000 Subject: [PATCH 0148/1237] Add title to model alerts view (#3465) --- .../ql-vscode/src/common/interface-types.ts | 8 +++-- .../model-alerts/model-alerts-view.ts | 12 ++++++++ .../src/model-editor/model-editor-view.ts | 1 + .../src/model-editor/model-evaluator.ts | 3 ++ .../src/model-editor/shared/view-state.ts | 4 +++ .../model-alerts/ModelAlerts.stories.tsx | 17 +++++++++++ .../src/view/model-alerts/ModelAlerts.tsx | 29 ++++++++++++++++--- .../view/model-alerts/ModelAlertsHeader.tsx | 8 +++++ .../model-editor/model-evaluator.test.ts | 4 +++ 9 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 extensions/ql-vscode/src/stories/model-alerts/ModelAlerts.stories.tsx create mode 100644 extensions/ql-vscode/src/view/model-alerts/ModelAlertsHeader.tsx diff --git a/extensions/ql-vscode/src/common/interface-types.ts b/extensions/ql-vscode/src/common/interface-types.ts index 2ca4ce29753..525867d6c52 100644 --- a/extensions/ql-vscode/src/common/interface-types.ts +++ b/extensions/ql-vscode/src/common/interface-types.ts @@ -14,6 +14,7 @@ import type { Method } from "../model-editor/method"; import type { ModeledMethod } from "../model-editor/modeled-method"; import type { MethodModelingPanelViewState, + ModelAlertsViewState, ModelEditorViewState, } from "../model-editor/shared/view-state"; import type { Mode } from "../model-editor/shared/mode"; @@ -726,10 +727,11 @@ export type ToMethodModelingMessage = | SetInProgressMessage | SetProcessedByAutoModelMessage; -interface SetModelAlertsMessage { - t: "setModelAlerts"; +interface SetModelAlertsViewStateMessage { + t: "setModelAlertsViewState"; + viewState: ModelAlertsViewState; } -export type ToModelAlertsMessage = SetModelAlertsMessage; +export type ToModelAlertsMessage = SetModelAlertsViewStateMessage; export type FromModelAlertsMessage = CommonFromViewMessages; diff --git a/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts b/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts index ebd901c382d..5acf6bc500d 100644 --- a/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts @@ -14,6 +14,7 @@ import { showAndLogExceptionWithTelemetry } from "../../common/logging"; import type { ModelingEvents } from "../modeling-events"; import type { ModelingStore } from "../modeling-store"; import type { DatabaseItem } from "../../databases/local-databases"; +import type { ExtensionPack } from "../shared/extension-pack"; export class ModelAlertsView extends AbstractWebview< ToModelAlertsMessage, @@ -26,6 +27,7 @@ export class ModelAlertsView extends AbstractWebview< private readonly modelingEvents: ModelingEvents, private readonly modelingStore: ModelingStore, private readonly dbItem: DatabaseItem, + private readonly extensionPack: ExtensionPack, ) { super(app); @@ -37,6 +39,7 @@ export class ModelAlertsView extends AbstractWebview< panel.reveal(undefined, true); await this.waitForPanelLoaded(); + await this.setViewState(); } protected async getPanelConfig(): Promise { @@ -75,6 +78,15 @@ export class ModelAlertsView extends AbstractWebview< } } + private async setViewState(): Promise { + await this.postMessage({ + t: "setModelAlertsViewState", + viewState: { + title: this.extensionPack.name, + }, + }); + } + public async focusView(): Promise { this.panel?.reveal(); } diff --git a/extensions/ql-vscode/src/model-editor/model-editor-view.ts b/extensions/ql-vscode/src/model-editor/model-editor-view.ts index 0ad90435813..02abf5bbe98 100644 --- a/extensions/ql-vscode/src/model-editor/model-editor-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-editor-view.ts @@ -132,6 +132,7 @@ export class ModelEditorView extends AbstractWebview< this.variantAnalysisManager, databaseItem, language, + this.extensionPack, this.updateModelEvaluationRun.bind(this), ); this.push(this.modelEvaluator); diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts index a2d3fa659ce..f8bf7c045fe 100644 --- a/extensions/ql-vscode/src/model-editor/model-evaluator.ts +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -19,6 +19,7 @@ import { CancellationTokenSource } from "vscode"; import type { QlPackDetails } from "../variant-analysis/ql-pack-details"; import type { App } from "../common/app"; import { ModelAlertsView } from "./model-alerts/model-alerts-view"; +import type { ExtensionPack } from "./shared/extension-pack"; export class ModelEvaluator extends DisposableObject { // Cancellation token source to allow cancelling of the current run @@ -34,6 +35,7 @@ export class ModelEvaluator extends DisposableObject { private readonly variantAnalysisManager: VariantAnalysisManager, private readonly dbItem: DatabaseItem, private readonly language: QueryLanguage, + private readonly extensionPack: ExtensionPack, private readonly updateView: ( run: ModelEvaluationRunState, ) => Promise, @@ -120,6 +122,7 @@ export class ModelEvaluator extends DisposableObject { this.modelingEvents, this.modelingStore, this.dbItem, + this.extensionPack, ); await view.showView(); } diff --git a/extensions/ql-vscode/src/model-editor/shared/view-state.ts b/extensions/ql-vscode/src/model-editor/shared/view-state.ts index 62c4bcf3858..1389103bc10 100644 --- a/extensions/ql-vscode/src/model-editor/shared/view-state.ts +++ b/extensions/ql-vscode/src/model-editor/shared/view-state.ts @@ -19,3 +19,7 @@ export interface MethodModelingPanelViewState { language: QueryLanguage | undefined; modelConfig: ModelConfig; } + +export interface ModelAlertsViewState { + title: string; +} diff --git a/extensions/ql-vscode/src/stories/model-alerts/ModelAlerts.stories.tsx b/extensions/ql-vscode/src/stories/model-alerts/ModelAlerts.stories.tsx new file mode 100644 index 00000000000..b52c47d701b --- /dev/null +++ b/extensions/ql-vscode/src/stories/model-alerts/ModelAlerts.stories.tsx @@ -0,0 +1,17 @@ +import type { Meta, StoryFn } from "@storybook/react"; + +import { ModelAlerts as ModelAlertsComponent } from "../../view/model-alerts/ModelAlerts"; + +export default { + title: "CodeQL Model Alerts/CodeQL Model Alerts", + component: ModelAlertsComponent, +} as Meta; + +const Template: StoryFn = (args) => ( + +); + +export const ModelAlerts = Template.bind({}); +ModelAlerts.args = { + initialViewState: { title: "codeql/sql2o-models" }, +}; diff --git a/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx b/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx index b6da439ab4d..b128676f28e 100644 --- a/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx +++ b/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx @@ -1,10 +1,27 @@ -import { useEffect } from "react"; +import { useEffect, useState } from "react"; +import { ModelAlertsHeader } from "./ModelAlertsHeader"; +import type { ModelAlertsViewState } from "../../model-editor/shared/view-state"; +import type { ToModelAlertsMessage } from "../../common/interface-types"; + +type Props = { + initialViewState?: ModelAlertsViewState; +}; + +export function ModelAlerts({ initialViewState }: Props): React.JSX.Element { + const [viewState, setViewState] = useState( + initialViewState, + ); -export function ModelAlerts(): React.JSX.Element { useEffect(() => { const listener = (evt: MessageEvent) => { if (evt.origin === window.origin) { - // TODO: handle messages + const msg: ToModelAlertsMessage = evt.data; + switch (msg.t) { + case "setModelAlertsViewState": { + setViewState(msg.viewState); + break; + } + } } else { // sanitize origin const origin = evt.origin.replace(/\n|\r/g, ""); @@ -18,5 +35,9 @@ export function ModelAlerts(): React.JSX.Element { }; }, []); - return <>hello world; + if (viewState === undefined) { + return <>; + } + + return ; } diff --git a/extensions/ql-vscode/src/view/model-alerts/ModelAlertsHeader.tsx b/extensions/ql-vscode/src/view/model-alerts/ModelAlertsHeader.tsx new file mode 100644 index 00000000000..5add201d89e --- /dev/null +++ b/extensions/ql-vscode/src/view/model-alerts/ModelAlertsHeader.tsx @@ -0,0 +1,8 @@ +import type { ModelAlertsViewState } from "../../model-editor/shared/view-state"; +import { ViewTitle } from "../common"; + +type Props = { viewState: ModelAlertsViewState }; + +export const ModelAlertsHeader = ({ viewState }: Props) => { + return Model evaluation results for {viewState.title}; +}; diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/model-editor/model-evaluator.test.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/model-editor/model-evaluator.test.ts index 058a4c76832..8b94d4f5d02 100644 --- a/extensions/ql-vscode/test/vscode-tests/activated-extension/model-editor/model-evaluator.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/model-editor/model-evaluator.test.ts @@ -7,6 +7,7 @@ import type { ModelEvaluationRun } from "../../../../src/model-editor/model-eval import { ModelEvaluator } from "../../../../src/model-editor/model-evaluator"; import type { ModelingEvents } from "../../../../src/model-editor/modeling-events"; import type { ModelingStore } from "../../../../src/model-editor/modeling-store"; +import type { ExtensionPack } from "../../../../src/model-editor/shared/extension-pack"; import type { VariantAnalysisManager } from "../../../../src/variant-analysis/variant-analysis-manager"; import { createMockLogger } from "../../../__mocks__/loggerMock"; import { createMockModelingEvents } from "../../../__mocks__/model-editor/modelingEventsMock"; @@ -23,6 +24,7 @@ describe("Model Evaluator", () => { let variantAnalysisManager: VariantAnalysisManager; let dbItem: DatabaseItem; let language: QueryLanguage; + let extensionPack: ExtensionPack; let updateView: jest.Mock; let getModelEvaluationRunMock = jest.fn(); @@ -40,6 +42,7 @@ describe("Model Evaluator", () => { }); dbItem = mockedObject({}); language = QueryLanguage.Java; + extensionPack = mockedObject({}); updateView = jest.fn(); modelEvaluator = new ModelEvaluator( @@ -50,6 +53,7 @@ describe("Model Evaluator", () => { variantAnalysisManager, dbItem, language, + extensionPack, updateView, ); }); From fffb0349a65d029613daf7fd930ea21c57a72c59 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 14 Mar 2024 13:33:06 +0000 Subject: [PATCH 0149/1237] Apply suggestions from code review Co-authored-by: Koen Vlaswinkel --- .github/codeql/queries/ProgressBar.qll | 4 +++- .github/codeql/queries/progress-not-cancellable.ql | 5 ++++- .github/codeql/queries/token-not-used.ql | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/codeql/queries/ProgressBar.qll b/.github/codeql/queries/ProgressBar.qll index 042f152087f..d9e0d04c01f 100644 --- a/.github/codeql/queries/ProgressBar.qll +++ b/.github/codeql/queries/ProgressBar.qll @@ -7,7 +7,9 @@ abstract class ProgressBar extends CallExpr { abstract ObjectExpr getOptions(); - predicate usesToken() { this.getCallback().getNumParameter() >= 2 } + predicate usesToken() { exists(this.getTokenParameter()) } + + Parameter getTokenParameter() { result = this.getCallback().getParameter(1) } Property getCancellableProperty() { result = this.getOptions().getPropertyByName("cancellable") } diff --git a/.github/codeql/queries/progress-not-cancellable.ql b/.github/codeql/queries/progress-not-cancellable.ql index ae46351eead..2cb1abd4c64 100644 --- a/.github/codeql/queries/progress-not-cancellable.ql +++ b/.github/codeql/queries/progress-not-cancellable.ql @@ -14,4 +14,7 @@ import ProgressBar from ProgressBar t where not t.isCancellable() and t.usesToken() -select t, "The token should not be used when the progress bar is not cancelable. Either stop using the token or mark the progress bar as cancellable." +select t, + "The $@ should not be used when the progress bar is not cancellable. Either stop using the $@ or mark the progress bar as cancellable.", + t.getTokenParameter(), t.getTokenParameter().getName(), t.getTokenParameter(), + t.getTokenParameter().getName() diff --git a/.github/codeql/queries/token-not-used.ql b/.github/codeql/queries/token-not-used.ql index ffea53e6390..66d153bec78 100644 --- a/.github/codeql/queries/token-not-used.ql +++ b/.github/codeql/queries/token-not-used.ql @@ -1,5 +1,5 @@ /** - * @name Don't ignore the token for a cancelable progress bar + * @name Don't ignore the token for a cancellable progress bar * @kind problem * @problem.severity warning * @id vscode-codeql/token-not-used From 41524cf45c744e2078cefd28770b345c8e084570 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 14 Mar 2024 15:05:29 +0100 Subject: [PATCH 0150/1237] Group some npm dependency updates --- .github/dependabot.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 27d73d973ee..3c22e6757b2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,6 +13,20 @@ updates: # are unrelated to the Node version, so we allow those. - dependency-name: "@types/node" update-types: ["version-update:semver-major", "version-update:semver-minor"] + groups: + octokit: + patterns: + - "@octokit/*" + storybook: + patterns: + - "@storybook/*" + - "storybook" + testing-library: + patterns: + - "@testing-library/*" + typescript-eslint: + patterns: + - "@typescript-eslint/*" - package-ecosystem: "github-actions" directory: "/" schedule: From 115ff838c53a65cbcb916ebb83e072308002150e Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Thu, 14 Mar 2024 14:39:30 +0000 Subject: [PATCH 0151/1237] Add model packs to variant analyses (#3472) --- .../src/variant-analysis/run-remote-query.ts | 35 ++++++++++++------- .../shared/variant-analysis.ts | 2 ++ .../variant-analysis-manager.ts | 2 ++ .../variant-analysis-mapper.ts | 11 +++++- .../variant-analysis-mapper.test.ts | 2 ++ 5 files changed, 39 insertions(+), 13 deletions(-) diff --git a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts index 05b99d0799e..6e6d877eb98 100644 --- a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts +++ b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts @@ -37,25 +37,31 @@ import { import type { QlPackFile } from "../packaging/qlpack-file"; import { expandShortPaths } from "../common/short-paths"; import type { QlPackDetails } from "./ql-pack-details"; +import type { ModelPackDetails } from "../common/model-pack-details"; /** * Well-known names for the query pack used by the server. */ const QUERY_PACK_NAME = "codeql-remote/query"; +interface GeneratedQlPackDetails { + base64Pack: string; + modelPacks: ModelPackDetails[]; +} + /** * Two possibilities: * 1. There is no qlpack.yml (or codeql-pack.yml) in this directory. Assume this is a lone query and generate a synthetic qlpack for it. * 2. There is a qlpack.yml (or codeql-pack.yml) in this directory. Assume this is a query pack and use the yml to pack the query before uploading it. * - * @returns the entire qlpack as a base64 string. + * @returns details about the generated QL pack. */ async function generateQueryPack( cliServer: CodeQLCliServer, qlPackDetails: QlPackDetails, tmpDir: RemoteQueryTempDir, token: CancellationToken, -): Promise { +): Promise { const workspaceFolders = getOnDiskWorkspaceFolders(); const extensionPacks = await getExtensionPacksToInject( cliServer, @@ -129,7 +135,7 @@ async function generateQueryPack( ...queryOpts, // We need to specify the extension packs as dependencies so that they are included in the MRVA pack. // The version range doesn't matter, since they'll always be found by source lookup. - ...extensionPacks.map((p) => `--extension-pack=${p}@*`), + ...extensionPacks.map((p) => `--extension-pack=${p.name}@*`), ]; } else { precompilationOpts = ["--qlx"]; @@ -152,7 +158,10 @@ async function generateQueryPack( token, ); const base64Pack = (await readFile(bundlePath)).toString("base64"); - return base64Pack; + return { + base64Pack, + modelPacks: extensionPacks, + }; } async function createNewQueryPack( @@ -278,6 +287,7 @@ async function getPackedBundlePath(remoteQueryDir: string): Promise { interface PreparedRemoteQuery { actionBranch: string; base64Pack: string; + modelPacks: ModelPackDetails[]; repoSelection: RepositorySelection; controllerRepo: Repository; queryStartTime: number; @@ -330,10 +340,10 @@ export async function prepareRemoteQueryRun( const tempDir = await createRemoteQueriesTempDirectory(); - let base64Pack: string; + let generatedPack: GeneratedQlPackDetails; try { - base64Pack = await generateQueryPack( + generatedPack = await generateQueryPack( cliServer, qlPackDetails, tempDir, @@ -358,7 +368,8 @@ export async function prepareRemoteQueryRun( return { actionBranch, - base64Pack, + base64Pack: generatedPack.base64Pack, + modelPacks: generatedPack.modelPacks, repoSelection, controllerRepo, queryStartTime, @@ -404,8 +415,8 @@ async function fixPackFile( async function getExtensionPacksToInject( cliServer: CodeQLCliServer, workspaceFolders: string[], -): Promise { - const result: string[] = []; +): Promise { + const result: ModelPackDetails[] = []; if (cliServer.useExtensionPacks()) { const extensionPacks = await cliServer.resolveQlpacks( workspaceFolders, @@ -422,7 +433,7 @@ async function getExtensionPacksToInject( )}`, ); } - result.push(name); + result.push({ name, path: paths[0] }); }); } @@ -431,7 +442,7 @@ async function getExtensionPacksToInject( async function addExtensionPacksAsDependencies( queryPackDir: string, - extensionPacks: string[], + extensionPacks: ModelPackDetails[], ): Promise { const qlpackFile = await getQlPackFilePath(queryPackDir); if (!qlpackFile) { @@ -447,7 +458,7 @@ async function addExtensionPacksAsDependencies( ) as QlPackFile; const dependencies = syntheticQueryPack.dependencies ?? {}; - extensionPacks.forEach((name) => { + extensionPacks.forEach(({ name }) => { // Add this extension pack as a dependency. It doesn't matter which // version we specify, since we are guaranteed that the extension pack // is resolved from source at the given path. diff --git a/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts b/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts index 3a698d24cc7..16da2ecf40c 100644 --- a/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts +++ b/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts @@ -1,6 +1,7 @@ import type { Repository, RepositoryWithMetadata } from "./repository"; import type { AnalysisAlert, AnalysisRawResults } from "./analysis-result"; import { QueryLanguage } from "../../common/query-language"; +import type { ModelPackDetails } from "../../common/model-pack-details"; export interface VariantAnalysis { id: number; @@ -13,6 +14,7 @@ export interface VariantAnalysis { kind?: string; }; queries?: VariantAnalysisQueries; + modelPacks?: ModelPackDetails[]; databases: { repositories?: string[]; repositoryLists?: string[]; diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 4a39ac1b1c5..989e94344f9 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -350,6 +350,7 @@ export class VariantAnalysisManager const { actionBranch, base64Pack, + modelPacks, repoSelection, controllerRepo, queryStartTime, @@ -410,6 +411,7 @@ export class VariantAnalysisManager const mappedVariantAnalysis = mapVariantAnalysisFromSubmission( variantAnalysisSubmission, variantAnalysisResponse, + modelPacks, ); await this.onVariantAnalysisSubmitted(mappedVariantAnalysis); diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts index a40f3b57d1e..c3a72a26705 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts @@ -1,3 +1,4 @@ +import type { ModelPackDetails } from "../common/model-pack-details"; import type { VariantAnalysis as ApiVariantAnalysis, VariantAnalysisScannedRepository as ApiVariantAnalysisScannedRepository, @@ -26,6 +27,7 @@ import { export function mapVariantAnalysisFromSubmission( submission: VariantAnalysisSubmission, apiVariantAnalysis: ApiVariantAnalysis, + modelPacks: ModelPackDetails[], ): VariantAnalysis { return mapVariantAnalysis( { @@ -37,6 +39,7 @@ export function mapVariantAnalysisFromSubmission( kind: submission.query.kind, }, queries: submission.queries, + modelPacks, databases: submission.databases, executionStartTime: submission.startTime, }, @@ -59,7 +62,12 @@ export function mapUpdatedVariantAnalysis( function mapVariantAnalysis( currentVariantAnalysis: Pick< VariantAnalysis, - "language" | "query" | "queries" | "databases" | "executionStartTime" + | "language" + | "query" + | "queries" + | "databases" + | "executionStartTime" + | "modelPacks" >, currentStatus: VariantAnalysisStatus | undefined, response: ApiVariantAnalysis, @@ -96,6 +104,7 @@ function mapVariantAnalysis( language: currentVariantAnalysis.language, query: currentVariantAnalysis.query, queries: currentVariantAnalysis.queries, + modelPacks: currentVariantAnalysis.modelPacks, databases: currentVariantAnalysis.databases, executionStartTime: currentVariantAnalysis.executionStartTime, createdAt: response.created_at, diff --git a/extensions/ql-vscode/test/unit-tests/variant-analysis/variant-analysis-mapper.test.ts b/extensions/ql-vscode/test/unit-tests/variant-analysis/variant-analysis-mapper.test.ts index c1b38ccde5c..c033d28d8bd 100644 --- a/extensions/ql-vscode/test/unit-tests/variant-analysis/variant-analysis-mapper.test.ts +++ b/extensions/ql-vscode/test/unit-tests/variant-analysis/variant-analysis-mapper.test.ts @@ -31,6 +31,7 @@ describe(mapVariantAnalysisFromSubmission.name, () => { const result = mapVariantAnalysisFromSubmission( mockSubmission, mockApiResponse, + [], ); const { @@ -54,6 +55,7 @@ describe(mapVariantAnalysisFromSubmission.name, () => { text: mockSubmission.query.text, kind: "table", }, + modelPacks: [], databases: { repositories: ["1", "2", "3"], }, From d65e6bea5308d7539f142bea95e15d9d181af0d6 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 14 Mar 2024 16:02:37 +0000 Subject: [PATCH 0152/1237] Simplify now we've deleted withInheritedProgress --- .github/codeql/queries/ProgressBar.qll | 28 +++---------------- .../queries/progress-not-cancellable.ql | 2 +- .github/codeql/queries/token-not-used.ql | 2 +- 3 files changed, 6 insertions(+), 26 deletions(-) diff --git a/.github/codeql/queries/ProgressBar.qll b/.github/codeql/queries/ProgressBar.qll index d9e0d04c01f..289140f6f6d 100644 --- a/.github/codeql/queries/ProgressBar.qll +++ b/.github/codeql/queries/ProgressBar.qll @@ -1,36 +1,16 @@ import javascript -abstract class ProgressBar extends CallExpr { - ProgressBar() { any() } - - abstract Function getCallback(); - - abstract ObjectExpr getOptions(); +class WithProgressCall extends CallExpr { + WithProgressCall() { this.getCalleeName() = "withProgress" } predicate usesToken() { exists(this.getTokenParameter()) } - Parameter getTokenParameter() { result = this.getCallback().getParameter(1) } + Parameter getTokenParameter() { result = this.getArgument(0).(Function).getParameter(1) } - Property getCancellableProperty() { result = this.getOptions().getPropertyByName("cancellable") } + Property getCancellableProperty() { result = this.getArgument(1).(ObjectExpr).getPropertyByName("cancellable") } predicate isCancellable() { this.getCancellableProperty().getInit().(BooleanLiteral).getBoolValue() = true } } - -class WithProgressCall extends ProgressBar { - WithProgressCall() { this.getCalleeName() = "withProgress" } - - override Function getCallback() { result = this.getArgument(0) } - - override ObjectExpr getOptions() { result = this.getArgument(1) } -} - -class WithInheritedProgressCall extends ProgressBar { - WithInheritedProgressCall() { this.getCalleeName() = "withInheritedProgress" } - - override Function getCallback() { result = this.getArgument(1) } - - override ObjectExpr getOptions() { result = this.getArgument(2) } -} diff --git a/.github/codeql/queries/progress-not-cancellable.ql b/.github/codeql/queries/progress-not-cancellable.ql index 2cb1abd4c64..3ed2a2a1d92 100644 --- a/.github/codeql/queries/progress-not-cancellable.ql +++ b/.github/codeql/queries/progress-not-cancellable.ql @@ -12,7 +12,7 @@ import javascript import ProgressBar -from ProgressBar t +from WithProgressCall t where not t.isCancellable() and t.usesToken() select t, "The $@ should not be used when the progress bar is not cancellable. Either stop using the $@ or mark the progress bar as cancellable.", diff --git a/.github/codeql/queries/token-not-used.ql b/.github/codeql/queries/token-not-used.ql index 66d153bec78..947138f00a9 100644 --- a/.github/codeql/queries/token-not-used.ql +++ b/.github/codeql/queries/token-not-used.ql @@ -13,6 +13,6 @@ import javascript import ProgressBar -from ProgressBar t +from WithProgressCall t where t.isCancellable() and not t.usesToken() select t, "This progress bar is $@ but the token is not used. Either use the token or mark the progress bar as not cancellable.", t.getCancellableProperty(), "cancellable" From de2fc2966da76085822345a3b84a382949fa926f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 16:19:27 +0000 Subject: [PATCH 0153/1237] Bump the storybook group in /extensions/ql-vscode with 11 updates Bumps the storybook group in /extensions/ql-vscode with 11 updates: | Package | From | To | | --- | --- | --- | | [@storybook/addon-a11y](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/a11y) | `7.6.15` | `8.0.0` | | [@storybook/addon-actions](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/actions) | `7.6.4` | `8.0.0` | | [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/essentials) | `7.6.4` | `8.0.0` | | [@storybook/addon-interactions](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/interactions) | `7.6.4` | `8.0.0` | | [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links) | `7.6.4` | `8.0.0` | | [@storybook/components](https://github.com/storybookjs/storybook/tree/HEAD/code/ui/components) | `7.6.17` | `8.0.0` | | [@storybook/manager-api](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/manager-api) | `7.6.7` | `8.0.0` | | [@storybook/react](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/react) | `7.6.12` | `8.0.0` | | [@storybook/react-webpack5](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/react-webpack5) | `7.6.12` | `8.0.0` | | [@storybook/theming](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/theming) | `7.6.17` | `8.0.0` | | [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/cli) | `7.6.15` | `8.0.0` | Updates `@storybook/addon-a11y` from 7.6.15 to 8.0.0 - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v8.0.0/code/addons/a11y) Updates `@storybook/addon-actions` from 7.6.4 to 8.0.0 - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v8.0.0/code/addons/actions) Updates `@storybook/addon-essentials` from 7.6.4 to 8.0.0 - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v8.0.0/code/addons/essentials) Updates `@storybook/addon-interactions` from 7.6.4 to 8.0.0 - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v8.0.0/code/addons/interactions) Updates `@storybook/addon-links` from 7.6.4 to 8.0.0 - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v8.0.0/code/addons/links) Updates `@storybook/components` from 7.6.17 to 8.0.0 - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v8.0.0/code/ui/components) Updates `@storybook/manager-api` from 7.6.7 to 8.0.0 - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v8.0.0/code/lib/manager-api) Updates `@storybook/react` from 7.6.12 to 8.0.0 - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v8.0.0/code/renderers/react) Updates `@storybook/react-webpack5` from 7.6.12 to 8.0.0 - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v8.0.0/code/frameworks/react-webpack5) Updates `@storybook/theming` from 7.6.17 to 8.0.0 - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v8.0.0/code/lib/theming) Updates `storybook` from 7.6.15 to 8.0.0 - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v8.0.0/code/lib/cli) --- updated-dependencies: - dependency-name: "@storybook/addon-a11y" dependency-type: direct:development update-type: version-update:semver-major dependency-group: storybook - dependency-name: "@storybook/addon-actions" dependency-type: direct:development update-type: version-update:semver-major dependency-group: storybook - dependency-name: "@storybook/addon-essentials" dependency-type: direct:development update-type: version-update:semver-major dependency-group: storybook - dependency-name: "@storybook/addon-interactions" dependency-type: direct:development update-type: version-update:semver-major dependency-group: storybook - dependency-name: "@storybook/addon-links" dependency-type: direct:development update-type: version-update:semver-major dependency-group: storybook - dependency-name: "@storybook/components" dependency-type: direct:development update-type: version-update:semver-major dependency-group: storybook - dependency-name: "@storybook/manager-api" dependency-type: direct:development update-type: version-update:semver-major dependency-group: storybook - dependency-name: "@storybook/react" dependency-type: direct:development update-type: version-update:semver-major dependency-group: storybook - dependency-name: "@storybook/react-webpack5" dependency-type: direct:development update-type: version-update:semver-major dependency-group: storybook - dependency-name: "@storybook/theming" dependency-type: direct:development update-type: version-update:semver-major dependency-group: storybook - dependency-name: storybook dependency-type: direct:development update-type: version-update:semver-major dependency-group: storybook ... Signed-off-by: dependabot[bot] --- extensions/ql-vscode/package-lock.json | 5604 +++++------------------- extensions/ql-vscode/package.json | 22 +- 2 files changed, 1161 insertions(+), 4465 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index ecc9eb09941..1a374bffaf8 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -54,17 +54,17 @@ "@github/markdownlint-github": "^0.6.0", "@octokit/plugin-throttling": "^8.0.0", "@playwright/test": "^1.40.1", - "@storybook/addon-a11y": "^7.6.15", - "@storybook/addon-actions": "^7.1.0", - "@storybook/addon-essentials": "^7.1.0", - "@storybook/addon-interactions": "^7.1.0", - "@storybook/addon-links": "^7.1.0", - "@storybook/components": "^7.6.17", + "@storybook/addon-a11y": "^8.0.0", + "@storybook/addon-actions": "^8.0.0", + "@storybook/addon-essentials": "^8.0.0", + "@storybook/addon-interactions": "^8.0.0", + "@storybook/addon-links": "^8.0.0", + "@storybook/components": "^8.0.0", "@storybook/csf": "^0.1.1", - "@storybook/manager-api": "^7.6.7", - "@storybook/react": "^7.1.0", - "@storybook/react-webpack5": "^7.6.12", - "@storybook/theming": "^7.6.12", + "@storybook/manager-api": "^8.0.0", + "@storybook/react": "^8.0.0", + "@storybook/react-webpack5": "^8.0.0", + "@storybook/theming": "^8.0.0", "@testing-library/dom": "^9.3.4", "@testing-library/jest-dom": "^6.4.2", "@testing-library/react": "^14.2.1", @@ -90,7 +90,6 @@ "@types/tar-stream": "^3.1.3", "@types/through2": "^2.0.36", "@types/tmp": "^0.2.6", - "@types/unzipper": "^0.10.1", "@types/vscode": "^1.82.0", "@types/yauzl": "^2.10.3", "@typescript-eslint/eslint-plugin": "^6.19.0", @@ -132,7 +131,7 @@ "npm-run-all": "^4.1.5", "patch-package": "^8.0.0", "prettier": "^3.2.5", - "storybook": "^7.6.15", + "storybook": "^8.0.0", "tar-stream": "^3.1.7", "through2": "^4.0.2", "ts-jest": "^29.0.1", @@ -624,9 +623,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", + "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", "dev": true, "engines": { "node": ">=6.9.0" @@ -2110,13 +2109,13 @@ } }, "node_modules/@babel/preset-flow": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.23.3.tgz", - "integrity": "sha512-7yn6hl8RIv+KNk6iIrGZ+D06VhVY35wLVf23Cz/mMu1zOr7u4MMP4j0nZ9tLf8+4ZFpnib8cFYgB/oYg9hfswA==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.24.0.tgz", + "integrity": "sha512-cum/nSi82cDaSJ21I4PgLTVlj0OXovFk6GRguJYe/IKg6y6JHLTbJhybtX4k35WT9wdeJfEVjycTixMhBHd0Dg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", "@babel/plugin-transform-flow-strip-types": "^7.23.3" }, "engines": { @@ -3867,14 +3866,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -3890,22 +3889,22 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, "node_modules/@jridgewell/sourcemap-codec": { @@ -3915,35 +3914,29 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@juggle/resize-observer": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", - "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", - "dev": true - }, "node_modules/@mdx-js/react": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-2.3.0.tgz", - "integrity": "sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.1.tgz", + "integrity": "sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==", "dev": true, "dependencies": { - "@types/mdx": "^2.0.0", - "@types/react": ">=16" + "@types/mdx": "^2.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" }, "peerDependencies": { + "@types/react": ">=16", "react": ">=16" } }, @@ -4458,3035 +4451,333 @@ "node": ">=16" } }, - "node_modules/@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.11", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.11.tgz", - "integrity": "sha512-7j/6vdTym0+qZ6u4XbSAxrWBGYSdCfTzySkj7WAFgDLmSyWlOrWvpyzxlFh5jtw9dn0oL/jtW+06XfFiisN3JQ==", - "dev": true, - "dependencies": { - "ansi-html-community": "^0.0.8", - "common-path-prefix": "^3.0.0", - "core-js-pure": "^3.23.3", - "error-stack-parser": "^2.0.6", - "find-up": "^5.0.0", - "html-entities": "^2.1.0", - "loader-utils": "^2.0.4", - "schema-utils": "^3.0.0", - "source-map": "^0.7.3" - }, - "engines": { - "node": ">= 10.13" - }, - "peerDependencies": { - "@types/webpack": "4.x || 5.x", - "react-refresh": ">=0.10.0 <1.0.0", - "sockjs-client": "^1.4.0", - "type-fest": ">=0.17.0 <5.0.0", - "webpack": ">=4.43.0 <6.0.0", - "webpack-dev-server": "3.x || 4.x", - "webpack-hot-middleware": "2.x", - "webpack-plugin-serve": "0.x || 1.x" - }, - "peerDependenciesMeta": { - "@types/webpack": { - "optional": true - }, - "sockjs-client": { - "optional": true - }, - "type-fest": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - }, - "webpack-hot-middleware": { - "optional": true - }, - "webpack-plugin-serve": { - "optional": true - } - } - }, - "node_modules/@radix-ui/number": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", - "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10" - } - }, - "node_modules/@radix-ui/primitive": { + "node_modules/@radix-ui/react-compose-refs": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", - "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", + "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", "dev": true, "dependencies": { "@babel/runtime": "^7.13.10" - } - }, - "node_modules/@radix-ui/react-arrow": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", - "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-collection": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", - "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", + "node_modules/@radix-ui/react-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", + "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", "dev": true, "dependencies": { "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2" + "@radix-ui/react-compose-refs": "1.0.1" }, "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", - "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz", + "integrity": "sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==", "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "engines": { + "node": ">=18" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@radix-ui/react-context": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", - "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "type-detect": "4.0.8" } }, - "node_modules/@radix-ui/react-direction": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", - "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==", + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@sinonjs/commons": "^3.0.0" } }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz", - "integrity": "sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==", + "node_modules/@storybook/addon-a11y": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-8.0.0.tgz", + "integrity": "sha512-lxvXOMYqeM6DOUvNP3e18GzPDVhcOZtLnjkJ8XQbRk2xx2qHodAEiFhiKBhfStsn6/rg0zWLRpZ1NJxjnZ0l/Q==", "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-escape-keydown": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "@storybook/addon-highlight": "8.0.0", + "axe-core": "^4.2.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" } }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", - "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", + "node_modules/@storybook/addon-actions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.0.0.tgz", + "integrity": "sha512-QXfnEWZt5k35cPYsLvxq505XrCgXujc4UEkky1lBtSMI9SLzlXZg3fC/lW0c0hiu2c0+zI+y4fj5vTE9AZJdjw==", "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "@storybook/core-events": "8.0.0", + "@storybook/global": "^5.0.0", + "@types/uuid": "^9.0.1", + "dequal": "^2.0.2", + "polished": "^4.2.2", + "uuid": "^9.0.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" } }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.3.tgz", - "integrity": "sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==", + "node_modules/@storybook/addon-backgrounds": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.0.0.tgz", + "integrity": "sha512-hJLrtJa3paAL1DdArdqRFSPWji7s2kJlPh8mUhDpMHy0AOWrcslUanHWVmmgYpnBsYBgQcldt6eRIROtqgpSeA==", "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "@storybook/global": "^5.0.0", + "memoizerific": "^1.11.3", + "ts-dedent": "^2.0.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" } }, - "node_modules/@radix-ui/react-id": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", - "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", + "node_modules/@storybook/addon-controls": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.0.0.tgz", + "integrity": "sha512-hBYJ9O6G+lN43TxNPnw78GhLirjRVN8kFJSVg2Bha87hIvS3c/zx5ZWqtiXjp4wL4/r/IFe4EvBcBQh4Mpi8uw==", "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "@storybook/blocks": "8.0.0", + "lodash": "^4.17.21", + "ts-dedent": "^2.0.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" } }, - "node_modules/@radix-ui/react-popper": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz", - "integrity": "sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==", + "node_modules/@storybook/addon-docs": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.0.0.tgz", + "integrity": "sha512-P86M4Mo3FKtMIzSc8Hao46NmrlBs4w81BVf3AWNVka5aIPdWP2pINgDDDweASPgFKMVQNWUreR5pl0DHZfaJ5g==", "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1", - "@radix-ui/react-use-rect": "1.0.1", - "@radix-ui/react-use-size": "1.0.1", - "@radix-ui/rect": "1.0.1" + "@babel/core": "^7.12.3", + "@mdx-js/react": "^3.0.0", + "@storybook/blocks": "8.0.0", + "@storybook/client-logger": "8.0.0", + "@storybook/components": "8.0.0", + "@storybook/csf-plugin": "8.0.0", + "@storybook/csf-tools": "8.0.0", + "@storybook/global": "^5.0.0", + "@storybook/node-logger": "8.0.0", + "@storybook/preview-api": "8.0.0", + "@storybook/react-dom-shim": "8.0.0", + "@storybook/theming": "8.0.0", + "@storybook/types": "8.0.0", + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "fs-extra": "^11.1.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "rehype-external-links": "^3.0.0", + "rehype-slug": "^6.0.0", + "ts-dedent": "^2.0.0" }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-essentials": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.0.0.tgz", + "integrity": "sha512-n5uNerxBj2PrL8NJhzSUL3ctsW3Wy0ySBBrrChhBaXLoAkTP+KpJlX8h55abxdMkI0i+dreS//XQ0lpw1KX4pw==", + "dev": true, + "dependencies": { + "@storybook/addon-actions": "8.0.0", + "@storybook/addon-backgrounds": "8.0.0", + "@storybook/addon-controls": "8.0.0", + "@storybook/addon-docs": "8.0.0", + "@storybook/addon-highlight": "8.0.0", + "@storybook/addon-measure": "8.0.0", + "@storybook/addon-outline": "8.0.0", + "@storybook/addon-toolbars": "8.0.0", + "@storybook/addon-viewport": "8.0.0", + "@storybook/core-common": "8.0.0", + "@storybook/manager-api": "8.0.0", + "@storybook/node-logger": "8.0.0", + "@storybook/preview-api": "8.0.0", + "ts-dedent": "^2.0.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" } }, - "node_modules/@radix-ui/react-portal": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.3.tgz", - "integrity": "sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==", + "node_modules/@storybook/addon-highlight": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.0.0.tgz", + "integrity": "sha512-bSba9UTcPJBFUy5peIU8XPlKK/7lT054977oLGgVYup2u88km6pWaMNSGMWhb3xXdseTgrj96k/b+md4X+WrMg==", "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "@storybook/global": "^5.0.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" } }, - "node_modules/@radix-ui/react-primitive": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", - "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", + "node_modules/@storybook/addon-interactions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.0.0.tgz", + "integrity": "sha512-lb6WZAeF3MIT05wSVbz2ZKDpTIoTmHW5e8hImdquNlOm8qNm4fl5BLpVrZT1YkC6v42MM8yU/DeeUw+8w7rXDg==", "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-roving-focus": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz", - "integrity": "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-collection": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-controllable-state": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-select": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-1.2.2.tgz", - "integrity": "sha512-zI7McXr8fNaSrUY9mZe4x/HC0jTLY9fWNhO1oLWYMQGDXuV4UCivIGTxwioSzO0ZCYX9iSLyWmAh/1TOmX3Cnw==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/number": "1.0.1", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-collection": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.4", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.3", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.2", - "@radix-ui/react-portal": "1.0.3", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-controllable-state": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1", - "@radix-ui/react-use-previous": "1.0.1", - "@radix-ui/react-visually-hidden": "1.0.3", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-separator": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.0.3.tgz", - "integrity": "sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-slot": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", - "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-toggle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz", - "integrity": "sha512-Pkqg3+Bc98ftZGsl60CLANXQBBQ4W3mTFS9EJvNxKMZ7magklKV69/id1mlAlOFDDfHvlCms0fx8fA4CMKDJHg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-controllable-state": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-toggle-group": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.0.4.tgz", - "integrity": "sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-roving-focus": "1.0.4", - "@radix-ui/react-toggle": "1.0.3", - "@radix-ui/react-use-controllable-state": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-toolbar": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toolbar/-/react-toolbar-1.0.4.tgz", - "integrity": "sha512-tBgmM/O7a07xbaEkYJWYTXkIdU/1pW4/KZORR43toC/4XWyBCURK0ei9kMUdp+gTPPKBgYLxXmRSH1EVcIDp8Q==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-roving-focus": "1.0.4", - "@radix-ui/react-separator": "1.0.3", - "@radix-ui/react-toggle-group": "1.0.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", - "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", - "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", - "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", - "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-previous": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz", - "integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-rect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", - "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/rect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-size": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz", - "integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", - "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/rect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", - "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sindresorhus/merge-streams": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz", - "integrity": "sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@storybook/addon-a11y": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-7.6.15.tgz", - "integrity": "sha512-8PxRMBJUSxNoceo2IYXFyZp3VU+/ONK/DsD0dj/fVrv7izFrS8aw2GWSsSMK8xAbEUpANXWMKGaSyvrRFVgsVQ==", - "dev": true, - "dependencies": { - "@storybook/addon-highlight": "7.6.15", - "axe-core": "^4.2.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-a11y/node_modules/@storybook/addon-highlight": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-7.6.15.tgz", - "integrity": "sha512-ptidWZJJcEM83YsxCjf+m1q8Rr9sN8piJ4PJlM2vyc4MLZY4q6htb1JJFeq3ov1Iz6SY9KjKc/zOkWo4L54nxw==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-actions": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-7.6.4.tgz", - "integrity": "sha512-91UD5KPDik74VKVioPMcbwwvDXN/non8p1wArYAHCHCmd/Pts5MJRiFueSdfomSpNjUtjtn6eSXtwpIL3XVOfQ==", - "dev": true, - "dependencies": { - "@storybook/core-events": "7.6.4", - "@storybook/global": "^5.0.0", - "@types/uuid": "^9.0.1", - "dequal": "^2.0.2", - "polished": "^4.2.2", - "uuid": "^9.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-backgrounds": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-7.6.4.tgz", - "integrity": "sha512-gNy3kIkHSr+Lg/jVDHwbZjIe1po5SDGZNVe39vrJwnqGz8T1clWes9WHCL6zk/uaCDA3yUna2Nt/KlOFAWDSoQ==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-controls": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-7.6.4.tgz", - "integrity": "sha512-k4AtZfazmD/nL3JAtLGAB7raPhkhUo0jWnaZWrahd9h1Fm13mBU/RW+JzTRhCw3Mp2HPERD7NI5Qcd2fUP6WDA==", - "dev": true, - "dependencies": { - "@storybook/blocks": "7.6.4", - "lodash": "^4.17.21", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-docs": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-7.6.4.tgz", - "integrity": "sha512-PbFMbvC9sK3sGdMhwmagXs9TqopTp9FySji+L8O7W9SHRC6wSmdwoWWPWybkOYxr/z/wXi7EM0azSAX7yQxLbw==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.3.1", - "@mdx-js/react": "^2.1.5", - "@storybook/blocks": "7.6.4", - "@storybook/client-logger": "7.6.4", - "@storybook/components": "7.6.4", - "@storybook/csf-plugin": "7.6.4", - "@storybook/csf-tools": "7.6.4", - "@storybook/global": "^5.0.0", - "@storybook/mdx2-csf": "^1.0.0", - "@storybook/node-logger": "7.6.4", - "@storybook/postinstall": "7.6.4", - "@storybook/preview-api": "7.6.4", - "@storybook/react-dom-shim": "7.6.4", - "@storybook/theming": "7.6.4", - "@storybook/types": "7.6.4", - "fs-extra": "^11.1.0", - "remark-external-links": "^8.0.0", - "remark-slug": "^6.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/addon-docs/node_modules/@storybook/components": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.6.4.tgz", - "integrity": "sha512-K5RvEObJAnX+SbGJbkM1qrZEk+VR2cUhRCSrFnlfMwsn8/60T3qoH7U8bCXf8krDgbquhMwqev5WzDB+T1VV8g==", - "dev": true, - "dependencies": { - "@radix-ui/react-select": "^1.2.2", - "@radix-ui/react-toolbar": "^1.0.4", - "@storybook/client-logger": "7.6.4", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/theming": "7.6.4", - "@storybook/types": "7.6.4", - "memoizerific": "^1.11.3", - "use-resize-observer": "^9.1.0", - "util-deprecate": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/addon-docs/node_modules/@storybook/theming": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.4.tgz", - "integrity": "sha512-Z/dcC5EpkIXelYCkt9ojnX6D7qGOng8YHxV/OWlVE9TrEGYVGPOEfwQryR0RhmGpDha1TYESLYrsDb4A8nJ1EA==", - "dev": true, - "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.4", - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/addon-essentials": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-7.6.4.tgz", - "integrity": "sha512-J+zPmP4pbuuFxQ3pjLRYQRnxEtp7jF3xRXGFO8brVnEqtqoxwJ6j3euUrRLe0rpGAU3AD7dYfaaFjd3xkENgTw==", - "dev": true, - "dependencies": { - "@storybook/addon-actions": "7.6.4", - "@storybook/addon-backgrounds": "7.6.4", - "@storybook/addon-controls": "7.6.4", - "@storybook/addon-docs": "7.6.4", - "@storybook/addon-highlight": "7.6.4", - "@storybook/addon-measure": "7.6.4", - "@storybook/addon-outline": "7.6.4", - "@storybook/addon-toolbars": "7.6.4", - "@storybook/addon-viewport": "7.6.4", - "@storybook/core-common": "7.6.4", - "@storybook/manager-api": "7.6.4", - "@storybook/node-logger": "7.6.4", - "@storybook/preview-api": "7.6.4", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/addon-essentials/node_modules/@storybook/manager-api": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.6.4.tgz", - "integrity": "sha512-RFb/iaBJfXygSgXkINPRq8dXu7AxBicTGX7MxqKXbz5FU7ANwV7abH6ONBYURkSDOH9//TQhRlVkF5u8zWg3bw==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.4", - "@storybook/client-logger": "7.6.4", - "@storybook/core-events": "7.6.4", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/router": "7.6.4", - "@storybook/theming": "7.6.4", - "@storybook/types": "7.6.4", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "semver": "^7.3.7", - "store2": "^2.14.2", - "telejson": "^7.2.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-essentials/node_modules/@storybook/router": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.6.4.tgz", - "integrity": "sha512-5MQ7Z4D7XNPN2yhFgjey7hXOYd6s8CggUqeAwhzGTex90SMCkKHSz1hfkcXn1ZqBPaall2b53uK553OvPLp9KQ==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.4", - "memoizerific": "^1.11.3", - "qs": "^6.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-essentials/node_modules/@storybook/theming": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.4.tgz", - "integrity": "sha512-Z/dcC5EpkIXelYCkt9ojnX6D7qGOng8YHxV/OWlVE9TrEGYVGPOEfwQryR0RhmGpDha1TYESLYrsDb4A8nJ1EA==", - "dev": true, - "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.4", - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/addon-highlight": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-7.6.4.tgz", - "integrity": "sha512-0kvjDzquoPwWWU61QYmEtcSGWXufnV7Z/bfBTYh132uxvV/X9YzDFcXXrxGL7sBJkK32gNUUBDuiTOxs5NxyOQ==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-interactions": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-7.6.4.tgz", - "integrity": "sha512-LjK9uhkgnbGyDwwa7pQhLptDEHeTIFmy+KurfJs9T08DpvRFfuuzyW4mj/hA63R1W5yjFSAhRiZj26+D7kBIyw==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.4", - "jest-mock": "^27.0.6", - "polished": "^4.2.2", - "ts-dedent": "^2.2.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-links": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-7.6.4.tgz", - "integrity": "sha512-TEhxYdMhJO28gD84ej1FCwLv9oLuCPt77bRXip9ndaNPRTdHYdWv6IP94dhbuDi8eHux7Z4A/mllciFuDFrnCw==", - "dev": true, - "dependencies": { - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - } - } - }, - "node_modules/@storybook/addon-measure": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-7.6.4.tgz", - "integrity": "sha512-73wsJ8PALsgWniR3MA/cmxcFuU6cRruWdIyYzOMgM8ife2Jm3xSkV7cTTXAqXt2H9Uuki4PGnuMHWWFLpPeyVA==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-outline": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-7.6.4.tgz", - "integrity": "sha512-CFxGASRse/qeFocetDKFNeWZ3Aa2wapVtRciDNa4Zx7k1wCnTjEsPIm54waOuCaNVcrvO+nJUAZG5WyiorQvcg==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-toolbars": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-7.6.4.tgz", - "integrity": "sha512-ENMQJgU4sRCLLDVXYfa+P3cQVV9PC0ZxwVAKeM3NPYPNH/ODoryGNtq+Q68LwHlM4ObCE2oc9MzaQqPxloFcCw==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-viewport": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-7.6.4.tgz", - "integrity": "sha512-SoTcHIoqybhYD28v7QExF1EZnl7FfxuP74VDhtze5LyMd2CbqmVnUfwewLCz/3IvCNce0GqdNyg1m6QJ7Eq1uw==", - "dev": true, - "dependencies": { - "memoizerific": "^1.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/blocks": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-7.6.4.tgz", - "integrity": "sha512-iXinXXhTUBtReREP1Jifpu35DnGg7FidehjvCM8sM4E4aymfb8czdg9DdvG46T2UFUPUct36nnjIdMLWOya8Bw==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.4", - "@storybook/client-logger": "7.6.4", - "@storybook/components": "7.6.4", - "@storybook/core-events": "7.6.4", - "@storybook/csf": "^0.1.2", - "@storybook/docs-tools": "7.6.4", - "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.6.4", - "@storybook/preview-api": "7.6.4", - "@storybook/theming": "7.6.4", - "@storybook/types": "7.6.4", - "@types/lodash": "^4.14.167", - "color-convert": "^2.0.1", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "markdown-to-jsx": "^7.1.8", - "memoizerific": "^1.11.3", - "polished": "^4.2.2", - "react-colorful": "^5.1.2", - "telejson": "^7.2.0", - "tocbot": "^4.20.1", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/blocks/node_modules/@storybook/components": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.6.4.tgz", - "integrity": "sha512-K5RvEObJAnX+SbGJbkM1qrZEk+VR2cUhRCSrFnlfMwsn8/60T3qoH7U8bCXf8krDgbquhMwqev5WzDB+T1VV8g==", - "dev": true, - "dependencies": { - "@radix-ui/react-select": "^1.2.2", - "@radix-ui/react-toolbar": "^1.0.4", - "@storybook/client-logger": "7.6.4", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/theming": "7.6.4", - "@storybook/types": "7.6.4", - "memoizerific": "^1.11.3", - "use-resize-observer": "^9.1.0", - "util-deprecate": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/blocks/node_modules/@storybook/manager-api": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.6.4.tgz", - "integrity": "sha512-RFb/iaBJfXygSgXkINPRq8dXu7AxBicTGX7MxqKXbz5FU7ANwV7abH6ONBYURkSDOH9//TQhRlVkF5u8zWg3bw==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.4", - "@storybook/client-logger": "7.6.4", - "@storybook/core-events": "7.6.4", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/router": "7.6.4", - "@storybook/theming": "7.6.4", - "@storybook/types": "7.6.4", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "semver": "^7.3.7", - "store2": "^2.14.2", - "telejson": "^7.2.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/blocks/node_modules/@storybook/router": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.6.4.tgz", - "integrity": "sha512-5MQ7Z4D7XNPN2yhFgjey7hXOYd6s8CggUqeAwhzGTex90SMCkKHSz1hfkcXn1ZqBPaall2b53uK553OvPLp9KQ==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.4", - "memoizerific": "^1.11.3", - "qs": "^6.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/blocks/node_modules/@storybook/theming": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.4.tgz", - "integrity": "sha512-Z/dcC5EpkIXelYCkt9ojnX6D7qGOng8YHxV/OWlVE9TrEGYVGPOEfwQryR0RhmGpDha1TYESLYrsDb4A8nJ1EA==", - "dev": true, - "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.4", - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/builder-manager": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/builder-manager/-/builder-manager-7.6.15.tgz", - "integrity": "sha512-vfpfCywiasyP7vtbgLJhjssBEwUjZhBsRsubDAzumgOochPiKKPNwsSc5NU/4ZIGaC5zRO26kUaUqFIbJdTEUQ==", - "dev": true, - "dependencies": { - "@fal-works/esbuild-plugin-global-externals": "^2.1.2", - "@storybook/core-common": "7.6.15", - "@storybook/manager": "7.6.15", - "@storybook/node-logger": "7.6.15", - "@types/ejs": "^3.1.1", - "@types/find-cache-dir": "^3.2.1", - "@yarnpkg/esbuild-plugin-pnp": "^3.0.0-rc.10", - "browser-assert": "^1.2.1", - "ejs": "^3.1.8", - "esbuild": "^0.18.0", - "esbuild-plugin-alias": "^0.2.1", - "express": "^4.17.3", - "find-cache-dir": "^3.0.0", - "fs-extra": "^11.1.0", - "process": "^0.11.10", - "util": "^0.12.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@storybook/channels": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.15.tgz", - "integrity": "sha512-UPDYRzGkygYFa8QUpEiumWrvZm4u4RKVzgiBt9C4RmHORqkkZzL9LXhaZJp2SmIz1ND5gx6KR5ze8ZnAdwxxoQ==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.15", - "@storybook/core-events": "7.6.15", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@storybook/client-logger": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.15.tgz", - "integrity": "sha512-n+K8IqnombqiQNnywVovS+lK61tvv/XSfgPt0cgvoF/hJZB0VDOMRjWsV+v9qQpj1TQEl1lLWeJwZMthTWupJA==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@storybook/core-common": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.15.tgz", - "integrity": "sha512-VGmcLJ5U1r1s8/YnLbKcyB4GnNL+/sZIPqwlcSKzDXO76HoVFv1kywf7PbASote7P3gdhLSxBdg95LH2bdIbmw==", - "dev": true, - "dependencies": { - "@storybook/core-events": "7.6.15", - "@storybook/node-logger": "7.6.15", - "@storybook/types": "7.6.15", - "@types/find-cache-dir": "^3.2.1", - "@types/node": "^18.0.0", - "@types/node-fetch": "^2.6.4", - "@types/pretty-hrtime": "^1.0.0", - "chalk": "^4.1.0", - "esbuild": "^0.18.0", - "esbuild-register": "^3.5.0", - "file-system-cache": "2.3.0", - "find-cache-dir": "^3.0.0", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "glob": "^10.0.0", - "handlebars": "^4.7.7", - "lazy-universal-dotenv": "^4.0.0", - "node-fetch": "^2.0.0", - "picomatch": "^2.3.0", - "pkg-dir": "^5.0.0", - "pretty-hrtime": "^1.0.3", - "resolve-from": "^5.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@storybook/core-events": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.15.tgz", - "integrity": "sha512-i4YnjGecbpGyrFe0340sPhQ9QjZZEBqvMy6kF4XWt6DYLHxZmsTj1HEdvxVl4Ej7V49Vw0Dm8MepJ1d4Y8MKrQ==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@storybook/node-logger": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.15.tgz", - "integrity": "sha512-C+sCvRjR+5uVU3VTrfyv7/RlPBxesAjIucUAK0keGyIZ7sFQYCPdkm4m/C4s+TcubgAzVvuoUHlRrSppdA7WzQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@storybook/types": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.15.tgz", - "integrity": "sha512-tLH0lK6SXECSfMpKin9bge+7XiHZII17n6jc9ZI1TfSBZJyq3M6VzWh2r1C2lC97FlkcKXjIwM3n8h1xNjnI+A==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.15", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/builder-manager/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@storybook/builder-manager/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@storybook/builder-manager/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/builder-manager/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/builder-webpack5": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-7.6.12.tgz", - "integrity": "sha512-6y5hfMV2rqKbloGZ8CicCH1UQd6sdiFdHf6/5Wo4tBoaGYzQjPM/cV1fizsO/01GG0yGJg7J6BohTiCCbNdGCA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.2", - "@storybook/channels": "7.6.12", - "@storybook/client-logger": "7.6.12", - "@storybook/core-common": "7.6.12", - "@storybook/core-events": "7.6.12", - "@storybook/core-webpack": "7.6.12", - "@storybook/node-logger": "7.6.12", - "@storybook/preview": "7.6.12", - "@storybook/preview-api": "7.6.12", - "@swc/core": "^1.3.82", - "@types/node": "^18.0.0", - "@types/semver": "^7.3.4", - "babel-loader": "^9.0.0", - "browser-assert": "^1.2.1", - "case-sensitive-paths-webpack-plugin": "^2.4.0", - "cjs-module-lexer": "^1.2.3", - "constants-browserify": "^1.0.0", - "css-loader": "^6.7.1", - "es-module-lexer": "^1.4.1", - "express": "^4.17.3", - "fork-ts-checker-webpack-plugin": "^8.0.0", - "fs-extra": "^11.1.0", - "html-webpack-plugin": "^5.5.0", - "magic-string": "^0.30.5", - "path-browserify": "^1.0.1", - "process": "^0.11.10", - "semver": "^7.3.7", - "style-loader": "^3.3.1", - "swc-loader": "^0.2.3", - "terser-webpack-plugin": "^5.3.1", - "ts-dedent": "^2.0.0", - "url": "^0.11.0", - "util": "^0.12.4", - "util-deprecate": "^1.0.2", - "webpack": "5", - "webpack-dev-middleware": "^6.1.1", - "webpack-hot-middleware": "^2.25.1", - "webpack-virtual-modules": "^0.5.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@storybook/builder-webpack5/node_modules/@storybook/channels": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.12.tgz", - "integrity": "sha512-TaPl5Y3lOoVi5kTLgKNRX8xh2sUPekH0Id1l4Ymw+lpgriEY6r60bmkZLysLG1GhlskpQ/da2+S2ap2ht8P2TQ==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.12", - "@storybook/core-events": "7.6.12", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/builder-webpack5/node_modules/@storybook/client-logger": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.12.tgz", - "integrity": "sha512-hiRv6dXsOttMPqm9SxEuFoAtDe9rs7TUS8XcO5rmJ9BgfwBJsYlHzAxXkazxmvlyZtKL7gMx6m8OYbCdZgUqtA==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/builder-webpack5/node_modules/@storybook/core-common": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.12.tgz", - "integrity": "sha512-kM9YiBBMM2x5v/oylL7gdO1PS4oehgJC21MivS9p5QZ8uuXKtCQ6UQvI3rzaV+1ZzUA4n+I8MyaMrNIQk8KDbw==", - "dev": true, - "dependencies": { - "@storybook/core-events": "7.6.12", - "@storybook/node-logger": "7.6.12", - "@storybook/types": "7.6.12", - "@types/find-cache-dir": "^3.2.1", - "@types/node": "^18.0.0", - "@types/node-fetch": "^2.6.4", - "@types/pretty-hrtime": "^1.0.0", - "chalk": "^4.1.0", - "esbuild": "^0.18.0", - "esbuild-register": "^3.5.0", - "file-system-cache": "2.3.0", - "find-cache-dir": "^3.0.0", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "glob": "^10.0.0", - "handlebars": "^4.7.7", - "lazy-universal-dotenv": "^4.0.0", - "node-fetch": "^2.0.0", - "picomatch": "^2.3.0", - "pkg-dir": "^5.0.0", - "pretty-hrtime": "^1.0.3", - "resolve-from": "^5.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/builder-webpack5/node_modules/@storybook/core-events": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.12.tgz", - "integrity": "sha512-IO4cwk7bBCKH6lLnnIlHO9FwQXt/9CzLUAoZSY9msWsdPppCdKlw8ynJI5YarSNKDBUn8ArIfnRf0Mve0KQr9Q==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/builder-webpack5/node_modules/@storybook/node-logger": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.12.tgz", - "integrity": "sha512-iS44/EjfF6hLecKzICmcpQoB9bmVi4tXx5gVXnbI5ZyziBibRQcg/U191Njl7wY2ScN/RCQOr8lh5k57rI3Prg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/builder-webpack5/node_modules/@storybook/preview-api": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.12.tgz", - "integrity": "sha512-uSzeMSLnCRROjiofJP0F0niLWL+sboQ5ktHW6BAYoPwprumXduPxKBUVEZNxMbVYoAz9v/kEZmaLauh8LRP2Hg==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.12", - "@storybook/client-logger": "7.6.12", - "@storybook/core-events": "7.6.12", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.12", - "@types/qs": "^6.9.5", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "qs": "^6.10.0", - "synchronous-promise": "^2.0.15", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/builder-webpack5/node_modules/@storybook/types": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.12.tgz", - "integrity": "sha512-Wsbd+NS10/2yMHQ/26rXHflXam0hm2qufTFiHOX6VXZWxij3slRU88Fnwzp+1QSyjXb0qkEr8dOx7aG00+ItVw==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.12", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/builder-webpack5/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@storybook/builder-webpack5/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@storybook/builder-webpack5/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/builder-webpack5/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/channels": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.4.tgz", - "integrity": "sha512-Z4PY09/Czl70ap4ObmZ4bgin+EQhPaA3HdrEDNwpnH7A9ttfEO5u5KThytIjMq6kApCCihmEPDaYltoVrfYJJA==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.4", - "@storybook/core-events": "7.6.4", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/cli": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/cli/-/cli-7.6.15.tgz", - "integrity": "sha512-2QRqCyVGDSkraHxX2JPYkkFccbu5Uo+JYFaFJo4vmMXzDurjWON+Ga2B8FCTd4A8P4C02Ca/79jgQoyBB3xoew==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.2", - "@babel/preset-env": "^7.23.2", - "@babel/types": "^7.23.0", - "@ndelangen/get-tarball": "^3.0.7", - "@storybook/codemod": "7.6.15", - "@storybook/core-common": "7.6.15", - "@storybook/core-events": "7.6.15", - "@storybook/core-server": "7.6.15", - "@storybook/csf-tools": "7.6.15", - "@storybook/node-logger": "7.6.15", - "@storybook/telemetry": "7.6.15", - "@storybook/types": "7.6.15", - "@types/semver": "^7.3.4", - "@yarnpkg/fslib": "2.10.3", - "@yarnpkg/libzip": "2.3.0", - "chalk": "^4.1.0", - "commander": "^6.2.1", - "cross-spawn": "^7.0.3", - "detect-indent": "^6.1.0", - "envinfo": "^7.7.3", - "execa": "^5.0.0", - "express": "^4.17.3", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "get-npm-tarball-url": "^2.0.3", - "get-port": "^5.1.1", - "giget": "^1.0.0", - "globby": "^11.0.2", - "jscodeshift": "^0.15.1", - "leven": "^3.1.0", - "ora": "^5.4.1", - "prettier": "^2.8.0", - "prompts": "^2.4.0", - "puppeteer-core": "^2.1.1", - "read-pkg-up": "^7.0.1", - "semver": "^7.3.7", - "strip-json-comments": "^3.0.1", - "tempy": "^1.0.1", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" - }, - "bin": { - "getstorybook": "bin/index.js", - "sb": "bin/index.js" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/cli/node_modules/@storybook/channels": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.15.tgz", - "integrity": "sha512-UPDYRzGkygYFa8QUpEiumWrvZm4u4RKVzgiBt9C4RmHORqkkZzL9LXhaZJp2SmIz1ND5gx6KR5ze8ZnAdwxxoQ==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.15", - "@storybook/core-events": "7.6.15", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/cli/node_modules/@storybook/client-logger": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.15.tgz", - "integrity": "sha512-n+K8IqnombqiQNnywVovS+lK61tvv/XSfgPt0cgvoF/hJZB0VDOMRjWsV+v9qQpj1TQEl1lLWeJwZMthTWupJA==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/cli/node_modules/@storybook/core-common": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.15.tgz", - "integrity": "sha512-VGmcLJ5U1r1s8/YnLbKcyB4GnNL+/sZIPqwlcSKzDXO76HoVFv1kywf7PbASote7P3gdhLSxBdg95LH2bdIbmw==", - "dev": true, - "dependencies": { - "@storybook/core-events": "7.6.15", - "@storybook/node-logger": "7.6.15", - "@storybook/types": "7.6.15", - "@types/find-cache-dir": "^3.2.1", - "@types/node": "^18.0.0", - "@types/node-fetch": "^2.6.4", - "@types/pretty-hrtime": "^1.0.0", - "chalk": "^4.1.0", - "esbuild": "^0.18.0", - "esbuild-register": "^3.5.0", - "file-system-cache": "2.3.0", - "find-cache-dir": "^3.0.0", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "glob": "^10.0.0", - "handlebars": "^4.7.7", - "lazy-universal-dotenv": "^4.0.0", - "node-fetch": "^2.0.0", - "picomatch": "^2.3.0", - "pkg-dir": "^5.0.0", - "pretty-hrtime": "^1.0.3", - "resolve-from": "^5.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/cli/node_modules/@storybook/core-events": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.15.tgz", - "integrity": "sha512-i4YnjGecbpGyrFe0340sPhQ9QjZZEBqvMy6kF4XWt6DYLHxZmsTj1HEdvxVl4Ej7V49Vw0Dm8MepJ1d4Y8MKrQ==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/cli/node_modules/@storybook/csf-tools": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-7.6.15.tgz", - "integrity": "sha512-8iKgg2cmbFTpVhRRJOqouhPcEh0c8ywabG4S8ICZvnJooSXUI9mD9p3tYCS7MYuSiHj0epa1Kkn9DtXJRo9o6g==", - "dev": true, - "dependencies": { - "@babel/generator": "^7.23.0", - "@babel/parser": "^7.23.0", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", - "@storybook/csf": "^0.1.2", - "@storybook/types": "7.6.15", - "fs-extra": "^11.1.0", - "recast": "^0.23.1", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/cli/node_modules/@storybook/node-logger": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.15.tgz", - "integrity": "sha512-C+sCvRjR+5uVU3VTrfyv7/RlPBxesAjIucUAK0keGyIZ7sFQYCPdkm4m/C4s+TcubgAzVvuoUHlRrSppdA7WzQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/cli/node_modules/@storybook/types": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.15.tgz", - "integrity": "sha512-tLH0lK6SXECSfMpKin9bge+7XiHZII17n6jc9ZI1TfSBZJyq3M6VzWh2r1C2lC97FlkcKXjIwM3n8h1xNjnI+A==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.15", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@storybook/cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@storybook/cli/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@storybook/cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/cli/node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/@storybook/cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/cli/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@storybook/client-logger": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.4.tgz", - "integrity": "sha512-vJwMShC98tcoFruRVQ4FphmFqvAZX1FqZqjFyk6IxtFumPKTVSnXJjlU1SnUIkSK2x97rgdUMqkdI+wAv/tugQ==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/codemod": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/codemod/-/codemod-7.6.15.tgz", - "integrity": "sha512-NiEbTLCdacj6TMxC7G49IImXeMzkG8wpPr8Ayxm9HeG6q5UkiF5/DiZdqbJm2zaosOsOKWwvXg1t6Pq6Nivytg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.2", - "@babel/preset-env": "^7.23.2", - "@babel/types": "^7.23.0", - "@storybook/csf": "^0.1.2", - "@storybook/csf-tools": "7.6.15", - "@storybook/node-logger": "7.6.15", - "@storybook/types": "7.6.15", - "@types/cross-spawn": "^6.0.2", - "cross-spawn": "^7.0.3", - "globby": "^11.0.2", - "jscodeshift": "^0.15.1", - "lodash": "^4.17.21", - "prettier": "^2.8.0", - "recast": "^0.23.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/codemod/node_modules/@storybook/channels": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.15.tgz", - "integrity": "sha512-UPDYRzGkygYFa8QUpEiumWrvZm4u4RKVzgiBt9C4RmHORqkkZzL9LXhaZJp2SmIz1ND5gx6KR5ze8ZnAdwxxoQ==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.15", - "@storybook/core-events": "7.6.15", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/codemod/node_modules/@storybook/client-logger": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.15.tgz", - "integrity": "sha512-n+K8IqnombqiQNnywVovS+lK61tvv/XSfgPt0cgvoF/hJZB0VDOMRjWsV+v9qQpj1TQEl1lLWeJwZMthTWupJA==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/codemod/node_modules/@storybook/core-events": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.15.tgz", - "integrity": "sha512-i4YnjGecbpGyrFe0340sPhQ9QjZZEBqvMy6kF4XWt6DYLHxZmsTj1HEdvxVl4Ej7V49Vw0Dm8MepJ1d4Y8MKrQ==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/codemod/node_modules/@storybook/csf-tools": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-7.6.15.tgz", - "integrity": "sha512-8iKgg2cmbFTpVhRRJOqouhPcEh0c8ywabG4S8ICZvnJooSXUI9mD9p3tYCS7MYuSiHj0epa1Kkn9DtXJRo9o6g==", - "dev": true, - "dependencies": { - "@babel/generator": "^7.23.0", - "@babel/parser": "^7.23.0", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", - "@storybook/csf": "^0.1.2", - "@storybook/types": "7.6.15", - "fs-extra": "^11.1.0", - "recast": "^0.23.1", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/codemod/node_modules/@storybook/node-logger": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.15.tgz", - "integrity": "sha512-C+sCvRjR+5uVU3VTrfyv7/RlPBxesAjIucUAK0keGyIZ7sFQYCPdkm4m/C4s+TcubgAzVvuoUHlRrSppdA7WzQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/codemod/node_modules/@storybook/types": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.15.tgz", - "integrity": "sha512-tLH0lK6SXECSfMpKin9bge+7XiHZII17n6jc9ZI1TfSBZJyq3M6VzWh2r1C2lC97FlkcKXjIwM3n8h1xNjnI+A==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.15", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/codemod/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@storybook/codemod/node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/@storybook/codemod/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@storybook/components": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.6.17.tgz", - "integrity": "sha512-lbh7GynMidA+CZcJnstVku6Nhs+YkqjYaZ+mKPugvlVhGVWv0DaaeQFVuZ8cJtUGJ/5FFU4Y+n+gylYUHkGBMA==", - "dev": true, - "dependencies": { - "@radix-ui/react-select": "^1.2.2", - "@radix-ui/react-toolbar": "^1.0.4", - "@storybook/client-logger": "7.6.17", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/theming": "7.6.17", - "@storybook/types": "7.6.17", - "memoizerific": "^1.11.3", - "use-resize-observer": "^9.1.0", - "util-deprecate": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/components/node_modules/@storybook/channels": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.17.tgz", - "integrity": "sha512-GFG40pzaSxk1hUr/J/TMqW5AFDDPUSu+HkeE/oqSWJbOodBOLJzHN6CReJS6y1DjYSZLNFt1jftPWZZInG/XUA==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/components/node_modules/@storybook/client-logger": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.17.tgz", - "integrity": "sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/components/node_modules/@storybook/core-events": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.17.tgz", - "integrity": "sha512-AriWMCm/k1cxlv10f+jZ1wavThTRpLaN3kY019kHWbYT9XgaSuLU67G7GPr3cGnJ6HuA6uhbzu8qtqVCd6OfXA==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/components/node_modules/@storybook/types": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.17.tgz", - "integrity": "sha512-GRY0xEJQ0PrL7DY2qCNUdIfUOE0Gsue6N+GBJw9ku1IUDFLJRDOF+4Dx2BvYcVCPI5XPqdWKlEyZdMdKjiQN7Q==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.17", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-client": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/core-client/-/core-client-7.6.12.tgz", - "integrity": "sha512-VzVp32tMZsCzM4UIqfvCoJF7N9mBf6dsAxh1/ZgViy75Fht78pGo3JwZXW8osMbFSRpmWD7fxlUM5S7TQOYQug==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.12", - "@storybook/preview-api": "7.6.12" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-client/node_modules/@storybook/channels": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.12.tgz", - "integrity": "sha512-TaPl5Y3lOoVi5kTLgKNRX8xh2sUPekH0Id1l4Ymw+lpgriEY6r60bmkZLysLG1GhlskpQ/da2+S2ap2ht8P2TQ==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.12", - "@storybook/core-events": "7.6.12", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-client/node_modules/@storybook/client-logger": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.12.tgz", - "integrity": "sha512-hiRv6dXsOttMPqm9SxEuFoAtDe9rs7TUS8XcO5rmJ9BgfwBJsYlHzAxXkazxmvlyZtKL7gMx6m8OYbCdZgUqtA==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-client/node_modules/@storybook/core-events": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.12.tgz", - "integrity": "sha512-IO4cwk7bBCKH6lLnnIlHO9FwQXt/9CzLUAoZSY9msWsdPppCdKlw8ynJI5YarSNKDBUn8ArIfnRf0Mve0KQr9Q==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-client/node_modules/@storybook/preview-api": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.12.tgz", - "integrity": "sha512-uSzeMSLnCRROjiofJP0F0niLWL+sboQ5ktHW6BAYoPwprumXduPxKBUVEZNxMbVYoAz9v/kEZmaLauh8LRP2Hg==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.12", - "@storybook/client-logger": "7.6.12", - "@storybook/core-events": "7.6.12", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.12", - "@types/qs": "^6.9.5", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "qs": "^6.10.0", - "synchronous-promise": "^2.0.15", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-client/node_modules/@storybook/types": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.12.tgz", - "integrity": "sha512-Wsbd+NS10/2yMHQ/26rXHflXam0hm2qufTFiHOX6VXZWxij3slRU88Fnwzp+1QSyjXb0qkEr8dOx7aG00+ItVw==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.12", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-common": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.4.tgz", - "integrity": "sha512-qes4+mXqINu0kCgSMFjk++GZokmYjb71esId0zyJsk0pcIPkAiEjnhbSEQkMhbUfcvO1lztoaQTBW2P7Rd1tag==", - "dev": true, - "dependencies": { - "@storybook/core-events": "7.6.4", - "@storybook/node-logger": "7.6.4", - "@storybook/types": "7.6.4", - "@types/find-cache-dir": "^3.2.1", - "@types/node": "^18.0.0", - "@types/node-fetch": "^2.6.4", - "@types/pretty-hrtime": "^1.0.0", - "chalk": "^4.1.0", - "esbuild": "^0.18.0", - "esbuild-register": "^3.5.0", - "file-system-cache": "2.3.0", - "find-cache-dir": "^3.0.0", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "glob": "^10.0.0", - "handlebars": "^4.7.7", - "lazy-universal-dotenv": "^4.0.0", - "node-fetch": "^2.0.0", - "picomatch": "^2.3.0", - "pkg-dir": "^5.0.0", - "pretty-hrtime": "^1.0.3", - "resolve-from": "^5.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-common/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@storybook/core-common/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@storybook/core-common/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/core-common/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/core-events": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.4.tgz", - "integrity": "sha512-i3xzcJ19ILSy4oJL5Dz9y0IlyApynn5RsGhAMIsW+mcfri+hGfeakq1stNCo0o7jW4Y3A7oluFTtIoK8DOxQdQ==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-server": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/core-server/-/core-server-7.6.15.tgz", - "integrity": "sha512-iIlxEAkrmKTSA3iGNqt/4QG7hf5suxBGYIB3DZAOfBo8EdZogMYaEmuCm5dbuaJr0mcVwlqwdhQiWb1VsR/NhA==", - "dev": true, - "dependencies": { - "@aw-web-design/x-default-browser": "1.4.126", - "@discoveryjs/json-ext": "^0.5.3", - "@storybook/builder-manager": "7.6.15", - "@storybook/channels": "7.6.15", - "@storybook/core-common": "7.6.15", - "@storybook/core-events": "7.6.15", - "@storybook/csf": "^0.1.2", - "@storybook/csf-tools": "7.6.15", - "@storybook/docs-mdx": "^0.1.0", - "@storybook/global": "^5.0.0", - "@storybook/manager": "7.6.15", - "@storybook/node-logger": "7.6.15", - "@storybook/preview-api": "7.6.15", - "@storybook/telemetry": "7.6.15", - "@storybook/types": "7.6.15", - "@types/detect-port": "^1.3.0", - "@types/node": "^18.0.0", - "@types/pretty-hrtime": "^1.0.0", - "@types/semver": "^7.3.4", - "better-opn": "^3.0.2", - "chalk": "^4.1.0", - "cli-table3": "^0.6.1", - "compression": "^1.7.4", - "detect-port": "^1.3.0", - "express": "^4.17.3", - "fs-extra": "^11.1.0", - "globby": "^11.0.2", - "ip": "^2.0.0", - "lodash": "^4.17.21", - "open": "^8.4.0", - "pretty-hrtime": "^1.0.3", - "prompts": "^2.4.0", - "read-pkg-up": "^7.0.1", - "semver": "^7.3.7", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1", - "ts-dedent": "^2.0.0", - "util": "^0.12.4", - "util-deprecate": "^1.0.2", - "watchpack": "^2.2.0", - "ws": "^8.2.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-server/node_modules/@storybook/channels": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.15.tgz", - "integrity": "sha512-UPDYRzGkygYFa8QUpEiumWrvZm4u4RKVzgiBt9C4RmHORqkkZzL9LXhaZJp2SmIz1ND5gx6KR5ze8ZnAdwxxoQ==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.15", - "@storybook/core-events": "7.6.15", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-server/node_modules/@storybook/client-logger": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.15.tgz", - "integrity": "sha512-n+K8IqnombqiQNnywVovS+lK61tvv/XSfgPt0cgvoF/hJZB0VDOMRjWsV+v9qQpj1TQEl1lLWeJwZMthTWupJA==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-server/node_modules/@storybook/core-common": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.15.tgz", - "integrity": "sha512-VGmcLJ5U1r1s8/YnLbKcyB4GnNL+/sZIPqwlcSKzDXO76HoVFv1kywf7PbASote7P3gdhLSxBdg95LH2bdIbmw==", - "dev": true, - "dependencies": { - "@storybook/core-events": "7.6.15", - "@storybook/node-logger": "7.6.15", - "@storybook/types": "7.6.15", - "@types/find-cache-dir": "^3.2.1", - "@types/node": "^18.0.0", - "@types/node-fetch": "^2.6.4", - "@types/pretty-hrtime": "^1.0.0", - "chalk": "^4.1.0", - "esbuild": "^0.18.0", - "esbuild-register": "^3.5.0", - "file-system-cache": "2.3.0", - "find-cache-dir": "^3.0.0", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "glob": "^10.0.0", - "handlebars": "^4.7.7", - "lazy-universal-dotenv": "^4.0.0", - "node-fetch": "^2.0.0", - "picomatch": "^2.3.0", - "pkg-dir": "^5.0.0", - "pretty-hrtime": "^1.0.3", - "resolve-from": "^5.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-server/node_modules/@storybook/core-events": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.15.tgz", - "integrity": "sha512-i4YnjGecbpGyrFe0340sPhQ9QjZZEBqvMy6kF4XWt6DYLHxZmsTj1HEdvxVl4Ej7V49Vw0Dm8MepJ1d4Y8MKrQ==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-server/node_modules/@storybook/csf-tools": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-7.6.15.tgz", - "integrity": "sha512-8iKgg2cmbFTpVhRRJOqouhPcEh0c8ywabG4S8ICZvnJooSXUI9mD9p3tYCS7MYuSiHj0epa1Kkn9DtXJRo9o6g==", - "dev": true, - "dependencies": { - "@babel/generator": "^7.23.0", - "@babel/parser": "^7.23.0", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", - "@storybook/csf": "^0.1.2", - "@storybook/types": "7.6.15", - "fs-extra": "^11.1.0", - "recast": "^0.23.1", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-server/node_modules/@storybook/node-logger": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.15.tgz", - "integrity": "sha512-C+sCvRjR+5uVU3VTrfyv7/RlPBxesAjIucUAK0keGyIZ7sFQYCPdkm4m/C4s+TcubgAzVvuoUHlRrSppdA7WzQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-server/node_modules/@storybook/preview-api": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.15.tgz", - "integrity": "sha512-2KN9vlizF6sFlYsJEGnFqcQaJXs4TTdawC1VazVdtaMSHANDxxDu8F1cP+u7lpPH3DkNZUmTGQDBYfYY9xR0eQ==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.15", - "@storybook/client-logger": "7.6.15", - "@storybook/core-events": "7.6.15", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.15", - "@types/qs": "^6.9.5", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "qs": "^6.10.0", - "synchronous-promise": "^2.0.15", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-server/node_modules/@storybook/types": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.15.tgz", - "integrity": "sha512-tLH0lK6SXECSfMpKin9bge+7XiHZII17n6jc9ZI1TfSBZJyq3M6VzWh2r1C2lC97FlkcKXjIwM3n8h1xNjnI+A==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.15", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-server/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@storybook/core-server/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@storybook/core-server/node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/core-server/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/core-server/node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "dev": true, - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@storybook/core-server/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/core-webpack": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-7.6.12.tgz", - "integrity": "sha512-Dm42mZHXHaroqrZyY8pMWjAQIxzZDFC8JI9uEWboFfE8xm+UXMmW7E0bsa+xQCZ5iAt2SusAUcwSOaYacXHb+Q==", - "dev": true, - "dependencies": { - "@storybook/core-common": "7.6.12", - "@storybook/node-logger": "7.6.12", - "@storybook/types": "7.6.12", - "@types/node": "^18.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-webpack/node_modules/@storybook/channels": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.12.tgz", - "integrity": "sha512-TaPl5Y3lOoVi5kTLgKNRX8xh2sUPekH0Id1l4Ymw+lpgriEY6r60bmkZLysLG1GhlskpQ/da2+S2ap2ht8P2TQ==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.12", - "@storybook/core-events": "7.6.12", "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-webpack/node_modules/@storybook/client-logger": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.12.tgz", - "integrity": "sha512-hiRv6dXsOttMPqm9SxEuFoAtDe9rs7TUS8XcO5rmJ9BgfwBJsYlHzAxXkazxmvlyZtKL7gMx6m8OYbCdZgUqtA==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-webpack/node_modules/@storybook/core-common": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.12.tgz", - "integrity": "sha512-kM9YiBBMM2x5v/oylL7gdO1PS4oehgJC21MivS9p5QZ8uuXKtCQ6UQvI3rzaV+1ZzUA4n+I8MyaMrNIQk8KDbw==", - "dev": true, - "dependencies": { - "@storybook/core-events": "7.6.12", - "@storybook/node-logger": "7.6.12", - "@storybook/types": "7.6.12", - "@types/find-cache-dir": "^3.2.1", - "@types/node": "^18.0.0", - "@types/node-fetch": "^2.6.4", - "@types/pretty-hrtime": "^1.0.0", - "chalk": "^4.1.0", - "esbuild": "^0.18.0", - "esbuild-register": "^3.5.0", - "file-system-cache": "2.3.0", - "find-cache-dir": "^3.0.0", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "glob": "^10.0.0", - "handlebars": "^4.7.7", - "lazy-universal-dotenv": "^4.0.0", - "node-fetch": "^2.0.0", - "picomatch": "^2.3.0", - "pkg-dir": "^5.0.0", - "pretty-hrtime": "^1.0.3", - "resolve-from": "^5.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-webpack/node_modules/@storybook/core-events": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.12.tgz", - "integrity": "sha512-IO4cwk7bBCKH6lLnnIlHO9FwQXt/9CzLUAoZSY9msWsdPppCdKlw8ynJI5YarSNKDBUn8ArIfnRf0Mve0KQr9Q==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-webpack/node_modules/@storybook/node-logger": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.12.tgz", - "integrity": "sha512-iS44/EjfF6hLecKzICmcpQoB9bmVi4tXx5gVXnbI5ZyziBibRQcg/U191Njl7wY2ScN/RCQOr8lh5k57rI3Prg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-webpack/node_modules/@storybook/types": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.12.tgz", - "integrity": "sha512-Wsbd+NS10/2yMHQ/26rXHflXam0hm2qufTFiHOX6VXZWxij3slRU88Fnwzp+1QSyjXb0qkEr8dOx7aG00+ItVw==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.12", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-webpack/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@storybook/core-webpack/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@storybook/core-webpack/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/core-webpack/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/csf": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.1.2.tgz", - "integrity": "sha512-ePrvE/pS1vsKR9Xr+o+YwdqNgHUyXvg+1Xjx0h9LrVx7Zq4zNe06pd63F5EvzTbCbJsHj7GHr9tkiaqm7U8WRA==", - "dev": true, - "dependencies": { - "type-fest": "^2.19.0" - } - }, - "node_modules/@storybook/csf-plugin": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-7.6.4.tgz", - "integrity": "sha512-7g9p8s2ITX+Z9iThK5CehPhJOcusVN7JcUEEW+gVF5PlYT+uk/x+66gmQno+scQuNkV9+8UJD6RLFjP+zg2uCA==", - "dev": true, - "dependencies": { - "@storybook/csf-tools": "7.6.4", - "unplugin": "^1.3.1" + "@storybook/types": "8.0.0", + "jest-mock": "^27.0.6", + "polished": "^4.2.2", + "ts-dedent": "^2.2.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/csf-tools": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-7.6.4.tgz", - "integrity": "sha512-6sLayuhgReIK3/QauNj5BW4o4ZfEMJmKf+EWANPEM/xEOXXqrog6Un8sjtBuJS9N1DwyhHY6xfkEiPAwdttwqw==", + "node_modules/@storybook/addon-links": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-8.0.0.tgz", + "integrity": "sha512-UjB68EJwSvRsD326KJAzYkuzhCdJmkliiitaqSJ7GUdgGgTkKC7cqH8QmRC0SK5qRi0lN59ARIKPiP5wjsEeOw==", "dev": true, "dependencies": { - "@babel/generator": "^7.23.0", - "@babel/parser": "^7.23.0", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", "@storybook/csf": "^0.1.2", - "@storybook/types": "7.6.4", - "fs-extra": "^11.1.0", - "recast": "^0.23.1", + "@storybook/global": "^5.0.0", "ts-dedent": "^2.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/docs-mdx": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@storybook/docs-mdx/-/docs-mdx-0.1.0.tgz", - "integrity": "sha512-JDaBR9lwVY4eSH5W8EGHrhODjygPd6QImRbwjAuJNEnY0Vw4ie3bPkeGfnacB3OBW6u/agqPv2aRlR46JcAQLg==", - "dev": true - }, - "node_modules/@storybook/docs-tools": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-7.6.4.tgz", - "integrity": "sha512-2eGam43aD7O3cocA72Z63kRi7t/ziMSpst0qB218QwBWAeZjT4EYDh8V6j/Xhv6zVQL3msW7AglrQP5kCKPvPA==", - "dev": true, - "dependencies": { - "@storybook/core-common": "7.6.4", - "@storybook/preview-api": "7.6.4", - "@storybook/types": "7.6.4", - "@types/doctrine": "^0.0.3", - "assert": "^2.1.0", - "doctrine": "^3.0.0", - "lodash": "^4.17.21" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/global": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@storybook/global/-/global-5.0.0.tgz", - "integrity": "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==", - "dev": true - }, - "node_modules/@storybook/manager": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/manager/-/manager-7.6.15.tgz", - "integrity": "sha512-GGV2ElV5AOIApy/FSDzoSlLUbyd2VhQVD3TdNGRxNauYRjEO8ulXHw2tNbT6ludtpYpDTAILzI6zT/iag8hmPQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } } }, - "node_modules/@storybook/manager-api": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.6.7.tgz", - "integrity": "sha512-3Wk/BvuGUlw/X05s57zZO7gJbzfUeE9Xe+CSIvuH7RY5jx9PYnNwqNlTXPXhJ5LPvwMthae7WJVn3SuBpbptoQ==", + "node_modules/@storybook/addon-measure": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.0.0.tgz", + "integrity": "sha512-vSqQMxNHO++1XIyOF4HkQ/9UNADYCVCzoWG/JwOmWJ1NdfaPffN+QxLn+MYq+ex9R174nBdbjVqb2+e4MdYzPw==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.7", - "@storybook/client-logger": "7.6.7", - "@storybook/core-events": "7.6.7", - "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/router": "7.6.7", - "@storybook/theming": "7.6.7", - "@storybook/types": "7.6.7", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "store2": "^2.14.2", - "telejson": "^7.2.0", - "ts-dedent": "^2.0.0" + "tiny-invariant": "^1.3.1" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/manager-api/node_modules/@storybook/channels": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.7.tgz", - "integrity": "sha512-u1hURhfQHHtZyRIDUENRCp+CRRm7IQfcjQaoWI06XCevQPuhVEtFUfXHjG+J74aA/JuuTLFUtqwNm1zGqbXTAQ==", + "node_modules/@storybook/addon-outline": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.0.0.tgz", + "integrity": "sha512-8/rs+4UYSQNE2J2CgeeAMJuz7UmJRN4T2Id4oESv7nfM+aUXXF1cOBw1EnofBie2ukVad9lATlsPaNx6ldoWsg==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.7", - "@storybook/core-events": "7.6.7", "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" + "ts-dedent": "^2.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/manager-api/node_modules/@storybook/client-logger": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.7.tgz", - "integrity": "sha512-A16zpWgsa0gSdXMR9P3bWVdC9u/1B1oG4H7Z1+JhNzgnL3CdyOYO0qFSiAtNBso4nOjIAJVb6/AoBzdRhmSVQg==", + "node_modules/@storybook/addon-toolbars": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.0.0.tgz", + "integrity": "sha512-+nNe52DAs42VIJxJnsg3d3BAVf+svR9lvaf3dD/HgS9vBWtp2wIumDM6b05umnVuR/dXviSpdpy+gm/cCdIQGQ==", "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/manager-api/node_modules/@storybook/core-events": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.7.tgz", - "integrity": "sha512-KZ5d03c47pnr5/kY26pJtWq7WpmCPXLbgyjJZDSc+TTY153BdZksvlBXRHtqM1yj2UM6QsSyIuiJaADJNAbP2w==", + "node_modules/@storybook/addon-viewport": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.0.0.tgz", + "integrity": "sha512-eqgyZszJSz6C3GXJTn8/8bmL8zqALr4dnBFg8w/RJ+gydVCk17Ow3ifYTWrEGVLXCCwd0XbCZGj9tAmfhovjTQ==", "dev": true, "dependencies": { - "ts-dedent": "^2.0.0" + "memoizerific": "^1.11.3" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/manager-api/node_modules/@storybook/theming": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.7.tgz", - "integrity": "sha512-+42rfC4rZtWVAXJ7JBUQKnQ6vWBXJVHZ9HtNUWzQLPR9sJSMmHnnSMV6y5tizGgZqmBnAIkuoYk+Tt6NfwUmSA==", + "node_modules/@storybook/blocks": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.0.0.tgz", + "integrity": "sha512-Sxy7pOa6B3ci/XhfKca6u97Kz6pGZV5ieQBUWRYByUZTjiOp12RVLFptexxrJHyNBA00BHJPek4fvFSJfn6nOQ==", "dev": true, "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.7", + "@storybook/channels": "8.0.0", + "@storybook/client-logger": "8.0.0", + "@storybook/components": "8.0.0", + "@storybook/core-events": "8.0.0", + "@storybook/csf": "^0.1.2", + "@storybook/docs-tools": "8.0.0", "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" + "@storybook/icons": "^1.2.5", + "@storybook/manager-api": "8.0.0", + "@storybook/preview-api": "8.0.0", + "@storybook/theming": "8.0.0", + "@storybook/types": "8.0.0", + "@types/lodash": "^4.14.167", + "color-convert": "^2.0.1", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "markdown-to-jsx": "7.3.2", + "memoizerific": "^1.11.3", + "polished": "^4.2.2", + "react-colorful": "^5.1.2", + "telejson": "^7.2.0", + "tocbot": "^4.20.1", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" }, "funding": { "type": "opencollective", @@ -7495,246 +4786,378 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } } }, - "node_modules/@storybook/manager-api/node_modules/@storybook/types": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.7.tgz", - "integrity": "sha512-VcGwrI4AkBENxkoAUJ+Z7SyMK73hpoY0TTtw2J7tc05/xdiXhkQTX15Qa12IBWIkoXCyNrtaU+q7KR8Tjzi+uw==", + "node_modules/@storybook/builder-manager": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/builder-manager/-/builder-manager-8.0.0.tgz", + "integrity": "sha512-cUj1YKOvk+pemom9QXdLm+yWRovTQiV2HPfdjVftASD++Bau2hVpZKDhII0dLKg9mluojJ6Rt83F1daAyA2njQ==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.7", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" + "@fal-works/esbuild-plugin-global-externals": "^2.1.2", + "@storybook/core-common": "8.0.0", + "@storybook/manager": "8.0.0", + "@storybook/node-logger": "8.0.0", + "@types/ejs": "^3.1.1", + "@yarnpkg/esbuild-plugin-pnp": "^3.0.0-rc.10", + "browser-assert": "^1.2.1", + "ejs": "^3.1.8", + "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0", + "esbuild-plugin-alias": "^0.2.1", + "express": "^4.17.3", + "fs-extra": "^11.1.0", + "process": "^0.11.10", + "util": "^0.12.4" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/mdx2-csf": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@storybook/mdx2-csf/-/mdx2-csf-1.1.0.tgz", - "integrity": "sha512-TXJJd5RAKakWx4BtpwvSNdgTDkKM6RkXU8GK34S/LhidQ5Pjz3wcnqb0TxEkfhK/ztbP8nKHqXFwLfa2CYkvQw==", - "dev": true - }, - "node_modules/@storybook/node-logger": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.4.tgz", - "integrity": "sha512-GDkEnnDj4Op+PExs8ZY/P6ox3wg453CdEIaR8PR9TxF/H/T2fBL6puzma3hN2CMam6yzfAL8U+VeIIDLQ5BZdQ==", - "dev": true, + "node_modules/@storybook/builder-webpack5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-8.0.0.tgz", + "integrity": "sha512-Pkqeume16aXR1jkMFfafTuhFXviBZWguCqSsTCzH+fyN28k9QYfcsUUZ5LlEGz9ZKFEO2+ZIuq2Mg1iBeSzUSw==", + "dev": true, + "dependencies": { + "@storybook/channels": "8.0.0", + "@storybook/client-logger": "8.0.0", + "@storybook/core-common": "8.0.0", + "@storybook/core-events": "8.0.0", + "@storybook/core-webpack": "8.0.0", + "@storybook/node-logger": "8.0.0", + "@storybook/preview": "8.0.0", + "@storybook/preview-api": "8.0.0", + "@types/node": "^18.0.0", + "@types/semver": "^7.3.4", + "browser-assert": "^1.2.1", + "case-sensitive-paths-webpack-plugin": "^2.4.0", + "cjs-module-lexer": "^1.2.3", + "constants-browserify": "^1.0.0", + "css-loader": "^6.7.1", + "es-module-lexer": "^1.4.1", + "express": "^4.17.3", + "fork-ts-checker-webpack-plugin": "^8.0.0", + "fs-extra": "^11.1.0", + "html-webpack-plugin": "^5.5.0", + "magic-string": "^0.30.5", + "path-browserify": "^1.0.1", + "process": "^0.11.10", + "semver": "^7.3.7", + "style-loader": "^3.3.1", + "terser-webpack-plugin": "^5.3.1", + "ts-dedent": "^2.0.0", + "url": "^0.11.0", + "util": "^0.12.4", + "util-deprecate": "^1.0.2", + "webpack": "5", + "webpack-dev-middleware": "^6.1.1", + "webpack-hot-middleware": "^2.25.1", + "webpack-virtual-modules": "^0.5.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@storybook/postinstall": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/postinstall/-/postinstall-7.6.4.tgz", - "integrity": "sha512-7uoB82hSzlFSdDMS3hKQD+AaeSvPit/fAMvXCBxn0/D0UGJUZcq4M9JcKBwEHkZJcbuDROgOTJ6TUeXi/FWO0w==", + "node_modules/@storybook/channels": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-8.0.0.tgz", + "integrity": "sha512-uykCBlSIMVodsgTFC/XAgO7JeaTJrKtDmmM6Z4liGkPS6EUvurOEu2vK6FuvojzhLHdVJ5bP+VXSJerfm7aE4Q==", "dev": true, + "dependencies": { + "@storybook/client-logger": "8.0.0", + "@storybook/core-events": "8.0.0", + "@storybook/global": "^5.0.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/preset-react-webpack": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/preset-react-webpack/-/preset-react-webpack-7.6.12.tgz", - "integrity": "sha512-j6gyC2KVyjO0zIvGtGqL4NoQKbTgMAoUYjF6w1UigoiU53rjxkrq2NMt+BnMxXnYwD+iXMoxyUIex01NBUpNnA==", + "node_modules/@storybook/cli": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/cli/-/cli-8.0.0.tgz", + "integrity": "sha512-4W99ldBUJjrEbZlxI4rvqW8lRY+AP2+wLGRMp4nyI/XW5cp7R+OryZf4imHgecunBQyKGXVek+poDlgKPQsxsg==", "dev": true, "dependencies": { - "@babel/preset-flow": "^7.22.15", - "@babel/preset-react": "^7.22.15", - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", - "@storybook/core-webpack": "7.6.12", - "@storybook/docs-tools": "7.6.12", - "@storybook/node-logger": "7.6.12", - "@storybook/react": "7.6.12", - "@storybook/react-docgen-typescript-plugin": "1.0.6--canary.9.0c3f3b7.0", - "@types/node": "^18.0.0", + "@babel/core": "^7.23.0", + "@babel/types": "^7.23.0", + "@ndelangen/get-tarball": "^3.0.7", + "@storybook/codemod": "8.0.0", + "@storybook/core-common": "8.0.0", + "@storybook/core-events": "8.0.0", + "@storybook/core-server": "8.0.0", + "@storybook/csf-tools": "8.0.0", + "@storybook/node-logger": "8.0.0", + "@storybook/telemetry": "8.0.0", + "@storybook/types": "8.0.0", "@types/semver": "^7.3.4", - "babel-plugin-add-react-displayname": "^0.0.5", + "@yarnpkg/fslib": "2.10.3", + "@yarnpkg/libzip": "2.3.0", + "chalk": "^4.1.0", + "commander": "^6.2.1", + "cross-spawn": "^7.0.3", + "detect-indent": "^6.1.0", + "envinfo": "^7.7.3", + "execa": "^5.0.0", + "find-up": "^5.0.0", "fs-extra": "^11.1.0", - "magic-string": "^0.30.5", - "react-docgen": "^7.0.0", - "react-refresh": "^0.14.0", + "get-npm-tarball-url": "^2.0.3", + "giget": "^1.0.0", + "globby": "^11.0.2", + "jscodeshift": "^0.15.1", + "leven": "^3.1.0", + "ora": "^5.4.1", + "prettier": "^3.1.1", + "prompts": "^2.4.0", + "read-pkg-up": "^7.0.1", "semver": "^7.3.7", - "webpack": "5" + "strip-json-comments": "^3.0.1", + "tempy": "^1.0.1", + "tiny-invariant": "^1.3.1", + "ts-dedent": "^2.0.0" }, - "engines": { - "node": ">=16.0.0" + "bin": { + "getstorybook": "bin/index.js", + "sb": "bin/index.js" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" }, - "peerDependencies": { - "@babel/core": "^7.22.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "engines": { + "node": ">=8" }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "typescript": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@storybook/preset-react-webpack/node_modules/@storybook/channels": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.12.tgz", - "integrity": "sha512-TaPl5Y3lOoVi5kTLgKNRX8xh2sUPekH0Id1l4Ymw+lpgriEY6r60bmkZLysLG1GhlskpQ/da2+S2ap2ht8P2TQ==", + "node_modules/@storybook/cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.12", - "@storybook/core-events": "7.6.12", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@storybook/preset-react-webpack/node_modules/@storybook/client-logger": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.12.tgz", - "integrity": "sha512-hiRv6dXsOttMPqm9SxEuFoAtDe9rs7TUS8XcO5rmJ9BgfwBJsYlHzAxXkazxmvlyZtKL7gMx6m8OYbCdZgUqtA==", + "node_modules/@storybook/cli/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "dependencies": { - "@storybook/global": "^5.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "engines": { + "node": ">= 8" + } + }, + "node_modules/@storybook/cli/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" } }, - "node_modules/@storybook/preset-react-webpack/node_modules/@storybook/core-common": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.12.tgz", - "integrity": "sha512-kM9YiBBMM2x5v/oylL7gdO1PS4oehgJC21MivS9p5QZ8uuXKtCQ6UQvI3rzaV+1ZzUA4n+I8MyaMrNIQk8KDbw==", + "node_modules/@storybook/cli/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "@storybook/core-events": "7.6.12", - "@storybook/node-logger": "7.6.12", - "@storybook/types": "7.6.12", - "@types/find-cache-dir": "^3.2.1", - "@types/node": "^18.0.0", - "@types/node-fetch": "^2.6.4", - "@types/pretty-hrtime": "^1.0.0", - "chalk": "^4.1.0", - "esbuild": "^0.18.0", - "esbuild-register": "^3.5.0", - "file-system-cache": "2.3.0", - "find-cache-dir": "^3.0.0", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "glob": "^10.0.0", - "handlebars": "^4.7.7", - "lazy-universal-dotenv": "^4.0.0", - "node-fetch": "^2.0.0", - "picomatch": "^2.3.0", - "pkg-dir": "^5.0.0", - "pretty-hrtime": "^1.0.3", - "resolve-from": "^5.0.0", - "ts-dedent": "^2.0.0" + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/cli/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@storybook/client-logger": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-8.0.0.tgz", + "integrity": "sha512-olc1vUfaZNkXc7L8UoCdGmyBieHQbsaB+0vVoivYMSa1DHYtXE75RefU3lhMSGrkvIZmXMvfaIDmnyJIOB5FxA==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/preset-react-webpack/node_modules/@storybook/core-events": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.12.tgz", - "integrity": "sha512-IO4cwk7bBCKH6lLnnIlHO9FwQXt/9CzLUAoZSY9msWsdPppCdKlw8ynJI5YarSNKDBUn8ArIfnRf0Mve0KQr9Q==", + "node_modules/@storybook/codemod": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/codemod/-/codemod-8.0.0.tgz", + "integrity": "sha512-rLY3M1xL+4S5dUB8XoSfDF46FxdntSsaFH4sjHZ08itVbwAAl7XqhYElVGueuobTgicJcOVTY8CJNkWcY6ETzA==", "dev": true, "dependencies": { - "ts-dedent": "^2.0.0" + "@babel/core": "^7.23.2", + "@babel/preset-env": "^7.23.2", + "@babel/types": "^7.23.0", + "@storybook/csf": "^0.1.2", + "@storybook/csf-tools": "8.0.0", + "@storybook/node-logger": "8.0.0", + "@storybook/types": "8.0.0", + "@types/cross-spawn": "^6.0.2", + "cross-spawn": "^7.0.3", + "globby": "^11.0.2", + "jscodeshift": "^0.15.1", + "lodash": "^4.17.21", + "prettier": "^3.1.1", + "recast": "^0.23.5", + "tiny-invariant": "^1.3.1" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/preset-react-webpack/node_modules/@storybook/docs-tools": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-7.6.12.tgz", - "integrity": "sha512-nY2lqEDTd/fR/D91ZLlIp+boSuJtkb8DqHW7pECy61rJqzGq4QpepRaWjQDKnGTgPItrsPfTPOu6iXvXNK07Ow==", + "node_modules/@storybook/codemod/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "dependencies": { - "@storybook/core-common": "7.6.12", - "@storybook/preview-api": "7.6.12", - "@storybook/types": "7.6.12", - "@types/doctrine": "^0.0.3", - "assert": "^2.1.0", - "doctrine": "^3.0.0", - "lodash": "^4.17.21" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "engines": { + "node": ">= 8" } }, - "node_modules/@storybook/preset-react-webpack/node_modules/@storybook/node-logger": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.12.tgz", - "integrity": "sha512-iS44/EjfF6hLecKzICmcpQoB9bmVi4tXx5gVXnbI5ZyziBibRQcg/U191Njl7wY2ScN/RCQOr8lh5k57rI3Prg==", + "node_modules/@storybook/codemod/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/@storybook/preset-react-webpack/node_modules/@storybook/preview-api": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.12.tgz", - "integrity": "sha512-uSzeMSLnCRROjiofJP0F0niLWL+sboQ5ktHW6BAYoPwprumXduPxKBUVEZNxMbVYoAz9v/kEZmaLauh8LRP2Hg==", + "node_modules/@storybook/components": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.0.0.tgz", + "integrity": "sha512-+LmHnR2XQQ76uyWW5u+9ZBlS5sPyJWE6cbMdmkJ0PMGaZdZuF07urcg4z4/qBsDxRZDquBPu/Li5xx6OjXhVKw==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.12", - "@storybook/client-logger": "7.6.12", - "@storybook/core-events": "7.6.12", + "@radix-ui/react-slot": "^1.0.2", + "@storybook/client-logger": "8.0.0", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.12", - "@types/qs": "^6.9.5", - "dequal": "^2.0.2", - "lodash": "^4.17.21", + "@storybook/icons": "^1.2.5", + "@storybook/theming": "8.0.0", + "@storybook/types": "8.0.0", "memoizerific": "^1.11.3", - "qs": "^6.10.0", - "synchronous-promise": "^2.0.15", - "ts-dedent": "^2.0.0", "util-deprecate": "^1.0.2" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@storybook/preset-react-webpack/node_modules/@storybook/types": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.12.tgz", - "integrity": "sha512-Wsbd+NS10/2yMHQ/26rXHflXam0hm2qufTFiHOX6VXZWxij3slRU88Fnwzp+1QSyjXb0qkEr8dOx7aG00+ItVw==", + "node_modules/@storybook/core-common": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-8.0.0.tgz", + "integrity": "sha512-fqlQYw5/PDW/oj34QwU5u0HkNLPgELfszsvLFsUcwI7uAzwb/WC2WdPvncT7qRPNcSZLXKJcA8QAqKL4t4I8bg==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.12", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" + "@storybook/core-events": "8.0.0", + "@storybook/csf-tools": "8.0.0", + "@storybook/node-logger": "8.0.0", + "@storybook/types": "8.0.0", + "@yarnpkg/fslib": "2.10.3", + "@yarnpkg/libzip": "2.3.0", + "chalk": "^4.1.0", + "cross-spawn": "^7.0.3", + "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0", + "esbuild-register": "^3.5.0", + "execa": "^5.0.0", + "file-system-cache": "2.3.0", + "find-cache-dir": "^3.0.0", + "find-up": "^5.0.0", + "fs-extra": "^11.1.0", + "glob": "^10.0.0", + "handlebars": "^4.7.7", + "lazy-universal-dotenv": "^4.0.0", + "node-fetch": "^2.0.0", + "picomatch": "^2.3.0", + "pkg-dir": "^5.0.0", + "pretty-hrtime": "^1.0.3", + "resolve-from": "^5.0.0", + "semver": "^7.3.7", + "tempy": "^1.0.1", + "tiny-invariant": "^1.3.1", + "ts-dedent": "^2.0.0", + "util": "^0.12.4" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/preset-react-webpack/node_modules/ansi-styles": { + "node_modules/@storybook/core-common/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -7749,7 +5172,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@storybook/preset-react-webpack/node_modules/chalk": { + "node_modules/@storybook/core-common/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -7765,7 +5188,21 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@storybook/preset-react-webpack/node_modules/has-flag": { + "node_modules/@storybook/core-common/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@storybook/core-common/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -7774,7 +5211,7 @@ "node": ">=8" } }, - "node_modules/@storybook/preset-react-webpack/node_modules/supports-color": { + "node_modules/@storybook/core-common/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -7786,226 +5223,221 @@ "node": ">=8" } }, - "node_modules/@storybook/preview": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/preview/-/preview-7.6.12.tgz", - "integrity": "sha512-7vbeqQY3X+FCt/ccgCuBmj4rkbQebLHGEBAt8elcX0E2pr7SGW57lWhnasU3jeMaz7tNrkcs0gfl4hyVRWUHDg==", + "node_modules/@storybook/core-common/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@storybook/core-events": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-8.0.0.tgz", + "integrity": "sha512-kkabj4V99gOTBW+y3HM/LTCDekglqb+lslZMamM+Ytxv1lCqCEOIR/OGfnYOyEaK4BLcx61Zp+fO30FZxtoT1w==", "dev": true, + "dependencies": { + "ts-dedent": "^2.0.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/preview-api": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.4.tgz", - "integrity": "sha512-KhisNdQX5NdfAln+spLU4B82d804GJQp/CnI5M1mm/taTnjvMgs/wTH9AmR89OPoq+tFZVW0vhy2zgPS3ar71A==", + "node_modules/@storybook/core-server": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/core-server/-/core-server-8.0.0.tgz", + "integrity": "sha512-uVvS4psu/wQ+m9JTAvEvSwxjNKiCviNmNX1fv/VYRhQiAHhdb3e58NfeHd6QBffyOF80hY1RJWe3vAPcNIoZxA==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.4", - "@storybook/client-logger": "7.6.4", - "@storybook/core-events": "7.6.4", + "@aw-web-design/x-default-browser": "1.4.126", + "@babel/core": "^7.23.9", + "@discoveryjs/json-ext": "^0.5.3", + "@storybook/builder-manager": "8.0.0", + "@storybook/channels": "8.0.0", + "@storybook/core-common": "8.0.0", + "@storybook/core-events": "8.0.0", "@storybook/csf": "^0.1.2", + "@storybook/csf-tools": "8.0.0", + "@storybook/docs-mdx": "3.0.0", "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.4", - "@types/qs": "^6.9.5", - "dequal": "^2.0.2", + "@storybook/manager": "8.0.0", + "@storybook/manager-api": "8.0.0", + "@storybook/node-logger": "8.0.0", + "@storybook/preview-api": "8.0.0", + "@storybook/telemetry": "8.0.0", + "@storybook/types": "8.0.0", + "@types/detect-port": "^1.3.0", + "@types/node": "^18.0.0", + "@types/pretty-hrtime": "^1.0.0", + "@types/semver": "^7.3.4", + "better-opn": "^3.0.2", + "chalk": "^4.1.0", + "cli-table3": "^0.6.1", + "compression": "^1.7.4", + "detect-port": "^1.3.0", + "express": "^4.17.3", + "fs-extra": "^11.1.0", + "globby": "^11.0.2", + "ip": "^2.0.1", "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "qs": "^6.10.0", - "synchronous-promise": "^2.0.15", + "open": "^8.4.0", + "pretty-hrtime": "^1.0.3", + "prompts": "^2.4.0", + "read-pkg-up": "^7.0.1", + "semver": "^7.3.7", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1", "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" + "util": "^0.12.4", + "util-deprecate": "^1.0.2", + "watchpack": "^2.2.0", + "ws": "^8.2.3" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/react": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/react/-/react-7.6.12.tgz", - "integrity": "sha512-ITDRGi79Qg+z1kGYv+yyJESz/5AsJVdBTMO7tr1qV7gmHElkASt6UR8SBSqKgePOnYgy3k/1PLfbzOs6G4OgYQ==", + "node_modules/@storybook/core-server/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.12", - "@storybook/core-client": "7.6.12", - "@storybook/docs-tools": "7.6.12", - "@storybook/global": "^5.0.0", - "@storybook/preview-api": "7.6.12", - "@storybook/react-dom-shim": "7.6.12", - "@storybook/types": "7.6.12", - "@types/escodegen": "^0.0.6", - "@types/estree": "^0.0.51", - "@types/node": "^18.0.0", - "acorn": "^7.4.1", - "acorn-jsx": "^5.3.1", - "acorn-walk": "^7.2.0", - "escodegen": "^2.1.0", - "html-tags": "^3.1.0", - "lodash": "^4.17.21", - "prop-types": "^15.7.2", - "react-element-to-jsx-string": "^15.0.0", - "ts-dedent": "^2.0.0", - "type-fest": "~2.19", - "util-deprecate": "^1.0.2" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=16.0.0" + "node": ">=8" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@storybook/react-docgen-typescript-plugin": { - "version": "1.0.6--canary.9.0c3f3b7.0", - "resolved": "https://registry.npmjs.org/@storybook/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-1.0.6--canary.9.0c3f3b7.0.tgz", - "integrity": "sha512-KUqXC3oa9JuQ0kZJLBhVdS4lOneKTOopnNBK4tUAgoxWQ3u/IjzdueZjFr7gyBrXMoU6duutk3RQR9u8ZpYJ4Q==", + "node_modules/@storybook/core-server/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { - "debug": "^4.1.1", - "endent": "^2.0.1", - "find-cache-dir": "^3.3.1", - "flat-cache": "^3.0.4", - "micromatch": "^4.0.2", - "react-docgen-typescript": "^2.2.2", - "tslib": "^2.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, - "peerDependencies": { - "typescript": ">= 4.x", - "webpack": ">= 4" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@storybook/react-dom-shim": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-7.6.4.tgz", - "integrity": "sha512-wGJfomlDEBnowNmhmumWDu/AcUInxSoPqUUJPgk2f5oL0EW17fR9fDP/juG3XOEdieMDM0jDX48GML7lyvL2fg==", + "node_modules/@storybook/core-server/node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/core-server/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/core-server/node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@storybook/react-webpack5": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/react-webpack5/-/react-webpack5-7.6.12.tgz", - "integrity": "sha512-MyIqGF8QrL6v5iCLDG3zQ1Yh8faUJcwt155BOjKWCjXXpWkCklCucuSHkhN79FkWMO6xMwjAlV2AuYBL8wraeg==", + "node_modules/@storybook/core-server/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "@storybook/builder-webpack5": "7.6.12", - "@storybook/preset-react-webpack": "7.6.12", - "@storybook/react": "7.6.12", - "@types/node": "^18.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "@babel/core": "^7.22.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", - "typescript": "*" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "typescript": { - "optional": true - } + "node": ">=8" } }, - "node_modules/@storybook/react/node_modules/@storybook/channels": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.12.tgz", - "integrity": "sha512-TaPl5Y3lOoVi5kTLgKNRX8xh2sUPekH0Id1l4Ymw+lpgriEY6r60bmkZLysLG1GhlskpQ/da2+S2ap2ht8P2TQ==", + "node_modules/@storybook/core-webpack": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-8.0.0.tgz", + "integrity": "sha512-JhZwPFoL92ntTdhwGSokodNZlpogs/u2OjImynfcXpnz7FqEQVJ/d3GiPwG9Wx+Ek2mUOn8XeorZI1LNTj+ihA==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.12", - "@storybook/core-events": "7.6.12", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" + "@storybook/core-common": "8.0.0", + "@storybook/node-logger": "8.0.0", + "@storybook/types": "8.0.0", + "@types/node": "^18.0.0", + "ts-dedent": "^2.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/react/node_modules/@storybook/client-logger": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.12.tgz", - "integrity": "sha512-hiRv6dXsOttMPqm9SxEuFoAtDe9rs7TUS8XcO5rmJ9BgfwBJsYlHzAxXkazxmvlyZtKL7gMx6m8OYbCdZgUqtA==", + "node_modules/@storybook/csf": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.1.2.tgz", + "integrity": "sha512-ePrvE/pS1vsKR9Xr+o+YwdqNgHUyXvg+1Xjx0h9LrVx7Zq4zNe06pd63F5EvzTbCbJsHj7GHr9tkiaqm7U8WRA==", "dev": true, "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "type-fest": "^2.19.0" } }, - "node_modules/@storybook/react/node_modules/@storybook/core-common": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.12.tgz", - "integrity": "sha512-kM9YiBBMM2x5v/oylL7gdO1PS4oehgJC21MivS9p5QZ8uuXKtCQ6UQvI3rzaV+1ZzUA4n+I8MyaMrNIQk8KDbw==", + "node_modules/@storybook/csf-plugin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.0.0.tgz", + "integrity": "sha512-bCX3XvZ8X1dS08ung0IhugtTUOK+rWwRjWjyj5WC7fl5HYyFYQ91MC2f8EccYQaDYl9Dfvo1cw685gnk6PoLbw==", "dev": true, "dependencies": { - "@storybook/core-events": "7.6.12", - "@storybook/node-logger": "7.6.12", - "@storybook/types": "7.6.12", - "@types/find-cache-dir": "^3.2.1", - "@types/node": "^18.0.0", - "@types/node-fetch": "^2.6.4", - "@types/pretty-hrtime": "^1.0.0", - "chalk": "^4.1.0", - "esbuild": "^0.18.0", - "esbuild-register": "^3.5.0", - "file-system-cache": "2.3.0", - "find-cache-dir": "^3.0.0", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "glob": "^10.0.0", - "handlebars": "^4.7.7", - "lazy-universal-dotenv": "^4.0.0", - "node-fetch": "^2.0.0", - "picomatch": "^2.3.0", - "pkg-dir": "^5.0.0", - "pretty-hrtime": "^1.0.3", - "resolve-from": "^5.0.0", - "ts-dedent": "^2.0.0" + "@storybook/csf-tools": "8.0.0", + "unplugin": "^1.3.1" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/react/node_modules/@storybook/core-events": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.12.tgz", - "integrity": "sha512-IO4cwk7bBCKH6lLnnIlHO9FwQXt/9CzLUAoZSY9msWsdPppCdKlw8ynJI5YarSNKDBUn8ArIfnRf0Mve0KQr9Q==", + "node_modules/@storybook/csf-tools": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-8.0.0.tgz", + "integrity": "sha512-VIMaZJiGM2NVzlgxaOyaVlH1pw/VSrJygDqOZyANh/kl4KHA+6xIqOkZC+X0+5K295dTFx2nR6S3btTjwT/Wrg==", "dev": true, "dependencies": { + "@babel/generator": "^7.23.0", + "@babel/parser": "^7.23.0", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0", + "@storybook/csf": "^0.1.2", + "@storybook/types": "8.0.0", + "fs-extra": "^11.1.0", + "recast": "^0.23.5", "ts-dedent": "^2.0.0" }, "funding": { @@ -8013,15 +5445,21 @@ "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/react/node_modules/@storybook/docs-tools": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-7.6.12.tgz", - "integrity": "sha512-nY2lqEDTd/fR/D91ZLlIp+boSuJtkb8DqHW7pECy61rJqzGq4QpepRaWjQDKnGTgPItrsPfTPOu6iXvXNK07Ow==", + "node_modules/@storybook/docs-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@storybook/docs-mdx/-/docs-mdx-3.0.0.tgz", + "integrity": "sha512-NmiGXl2HU33zpwTv1XORe9XG9H+dRUC1Jl11u92L4xr062pZtrShLmD4VKIsOQujxhhOrbxpwhNOt+6TdhyIdQ==", + "dev": true + }, + "node_modules/@storybook/docs-tools": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-8.0.0.tgz", + "integrity": "sha512-d6slxGMosurSTPp1zOTnr7EILnm9xmUrT0xF3Vxr3Yat5/YQEe3WSADktIFyWwlqvIu7MQ8Lh+oelAb5TuxiDw==", "dev": true, "dependencies": { - "@storybook/core-common": "7.6.12", - "@storybook/preview-api": "7.6.12", - "@storybook/types": "7.6.12", + "@storybook/core-common": "8.0.0", + "@storybook/preview-api": "8.0.0", + "@storybook/types": "8.0.0", "@types/doctrine": "^0.0.3", "assert": "^2.1.0", "doctrine": "^3.0.0", @@ -8032,292 +5470,306 @@ "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/react/node_modules/@storybook/node-logger": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.12.tgz", - "integrity": "sha512-iS44/EjfF6hLecKzICmcpQoB9bmVi4tXx5gVXnbI5ZyziBibRQcg/U191Njl7wY2ScN/RCQOr8lh5k57rI3Prg==", + "node_modules/@storybook/global": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@storybook/global/-/global-5.0.0.tgz", + "integrity": "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==", + "dev": true + }, + "node_modules/@storybook/icons": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-1.2.9.tgz", + "integrity": "sha512-cOmylsz25SYXaJL/gvTk/dl3pyk7yBFRfeXTsHvTA3dfhoU/LWSq0NKL9nM7WBasJyn6XPSGnLS4RtKXLw5EUg==", + "dev": true, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/manager": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/manager/-/manager-8.0.0.tgz", + "integrity": "sha512-1aCHzc+A4IOdDves+mE0K9bjyyPzPAIlR7oI6kSuO416/HXXJDdN5G825OQB/VIBYc1b8cNElMdNVKQK2FQorQ==", "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/react/node_modules/@storybook/preview-api": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.12.tgz", - "integrity": "sha512-uSzeMSLnCRROjiofJP0F0niLWL+sboQ5ktHW6BAYoPwprumXduPxKBUVEZNxMbVYoAz9v/kEZmaLauh8LRP2Hg==", + "node_modules/@storybook/manager-api": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.0.0.tgz", + "integrity": "sha512-vJcCc2hG78RjIyhmooqnBlVrTdIomzRqG5WO025tXFgRV1eRUkWJRqSSudcLJO6wk77ZSAtI1ihsDrjsrBFWZw==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.12", - "@storybook/client-logger": "7.6.12", - "@storybook/core-events": "7.6.12", + "@storybook/channels": "8.0.0", + "@storybook/client-logger": "8.0.0", + "@storybook/core-events": "8.0.0", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.12", - "@types/qs": "^6.9.5", + "@storybook/router": "8.0.0", + "@storybook/theming": "8.0.0", + "@storybook/types": "8.0.0", "dequal": "^2.0.2", "lodash": "^4.17.21", "memoizerific": "^1.11.3", - "qs": "^6.10.0", - "synchronous-promise": "^2.0.15", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" + "store2": "^2.14.2", + "telejson": "^7.2.0", + "ts-dedent": "^2.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/react/node_modules/@storybook/react-dom-shim": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-7.6.12.tgz", - "integrity": "sha512-P8eu/s/RQlc/7Yvr260lqNa6rttxIYiPUuHQBu9oCacwkpB3Xep2R/PUY2CifRHqlDhaOINO/Z79oGZl4EBQRQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/react/node_modules/@storybook/types": { - "version": "7.6.12", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.12.tgz", - "integrity": "sha512-Wsbd+NS10/2yMHQ/26rXHflXam0hm2qufTFiHOX6VXZWxij3slRU88Fnwzp+1QSyjXb0qkEr8dOx7aG00+ItVw==", + "node_modules/@storybook/node-logger": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-8.0.0.tgz", + "integrity": "sha512-C/sMNQqCIYVtJaLpe92RSkPgW3GXcWp6QeH5+glfP42kh+G9axxnEJJ996tyAnNQRzUuI+Eh+B7ytPZU1/WseQ==", "dev": true, - "dependencies": { - "@storybook/channels": "7.6.12", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/react/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@storybook/preset-react-webpack": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/preset-react-webpack/-/preset-react-webpack-8.0.0.tgz", + "integrity": "sha512-3XGtKR684A91CZVcD7l7CfuaaVl6ih5hKy+ITAGSulTyxz4Ym2MakZwz0VrenPHdndu1+vFvBWkf4Kh3WbyjYQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@storybook/core-webpack": "8.0.0", + "@storybook/docs-tools": "8.0.0", + "@storybook/node-logger": "8.0.0", + "@storybook/react": "8.0.0", + "@storybook/react-docgen-typescript-plugin": "1.0.6--canary.9.0c3f3b7.0", + "@types/node": "^18.0.0", + "@types/semver": "^7.3.4", + "find-up": "^5.0.0", + "fs-extra": "^11.1.0", + "magic-string": "^0.30.5", + "react-docgen": "^7.0.0", + "resolve": "^1.22.8", + "semver": "^7.3.7", + "tsconfig-paths": "^4.2.0", + "webpack": "5" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@storybook/react/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "type": "opencollective", + "url": "https://opencollective.com/storybook" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@storybook/react/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@storybook/preset-react-webpack/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/@storybook/react/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@storybook/preset-react-webpack/node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/router": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.6.7.tgz", - "integrity": "sha512-kkhNSdC3fXaQxILg8a26RKk4/ZbF/AUVrepUEyO8lwvbJ6LItTyWSE/4I9Ih4qV2Mjx33ncc8vLqM9p8r5qnMA==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.7", - "memoizerific": "^1.11.3", - "qs": "^6.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/router/node_modules/@storybook/client-logger": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.7.tgz", - "integrity": "sha512-A16zpWgsa0gSdXMR9P3bWVdC9u/1B1oG4H7Z1+JhNzgnL3CdyOYO0qFSiAtNBso4nOjIAJVb6/AoBzdRhmSVQg==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "node": ">=6" } }, - "node_modules/@storybook/telemetry": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/telemetry/-/telemetry-7.6.15.tgz", - "integrity": "sha512-klhKXLUS3OXozGEtMbbhKZLDfm+m3nNk2jvGwD6kkBenzFUzb0P2m8awxU7h1pBcKZKH/27U9t3KVzNFzWoWPw==", + "node_modules/@storybook/preview": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/preview/-/preview-8.0.0.tgz", + "integrity": "sha512-cFV7+6LYe1qr1HXm+oc74Z6ygAKgkjkhfGsfDhdS+UrzoFL9JF/+++RcE+xSBNVfzZjL19U1CsPEN0v0smIbkQ==", "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.15", - "@storybook/core-common": "7.6.15", - "@storybook/csf-tools": "7.6.15", - "chalk": "^4.1.0", - "detect-package-manager": "^2.0.1", - "fetch-retry": "^5.0.2", - "fs-extra": "^11.1.0", - "read-pkg-up": "^7.0.1" - }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/telemetry/node_modules/@storybook/channels": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.15.tgz", - "integrity": "sha512-UPDYRzGkygYFa8QUpEiumWrvZm4u4RKVzgiBt9C4RmHORqkkZzL9LXhaZJp2SmIz1ND5gx6KR5ze8ZnAdwxxoQ==", + "node_modules/@storybook/preview-api": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.0.0.tgz", + "integrity": "sha512-R2NBKtvHi+i1b/3PZe4u4YdJ7dlqr8YTqLn7syB/YSnKRAa7DYed+GJLu4qFJisE6IuYi+57AsdW16otRFEVvg==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.15", - "@storybook/core-events": "7.6.15", + "@storybook/channels": "8.0.0", + "@storybook/client-logger": "8.0.0", + "@storybook/core-events": "8.0.0", + "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", + "@storybook/types": "8.0.0", + "@types/qs": "^6.9.5", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/telemetry/node_modules/@storybook/client-logger": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.15.tgz", - "integrity": "sha512-n+K8IqnombqiQNnywVovS+lK61tvv/XSfgPt0cgvoF/hJZB0VDOMRjWsV+v9qQpj1TQEl1lLWeJwZMthTWupJA==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" + "tiny-invariant": "^1.3.1", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/telemetry/node_modules/@storybook/core-common": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.15.tgz", - "integrity": "sha512-VGmcLJ5U1r1s8/YnLbKcyB4GnNL+/sZIPqwlcSKzDXO76HoVFv1kywf7PbASote7P3gdhLSxBdg95LH2bdIbmw==", + "node_modules/@storybook/react": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-8.0.0.tgz", + "integrity": "sha512-Nl3jEd8Ezd2aDXfoQAgfGmwna3U6NOLMcCRSYOR63bH4/u16MnnDE8ACy+mH/+yWGEoxNjqcWUJiDk2h4C07LA==", "dev": true, "dependencies": { - "@storybook/core-events": "7.6.15", - "@storybook/node-logger": "7.6.15", - "@storybook/types": "7.6.15", - "@types/find-cache-dir": "^3.2.1", + "@storybook/client-logger": "8.0.0", + "@storybook/docs-tools": "8.0.0", + "@storybook/global": "^5.0.0", + "@storybook/preview-api": "8.0.0", + "@storybook/react-dom-shim": "8.0.0", + "@storybook/types": "8.0.0", + "@types/escodegen": "^0.0.6", + "@types/estree": "^0.0.51", "@types/node": "^18.0.0", - "@types/node-fetch": "^2.6.4", - "@types/pretty-hrtime": "^1.0.0", - "chalk": "^4.1.0", - "esbuild": "^0.18.0", - "esbuild-register": "^3.5.0", - "file-system-cache": "2.3.0", - "find-cache-dir": "^3.0.0", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "glob": "^10.0.0", - "handlebars": "^4.7.7", - "lazy-universal-dotenv": "^4.0.0", - "node-fetch": "^2.0.0", - "picomatch": "^2.3.0", - "pkg-dir": "^5.0.0", - "pretty-hrtime": "^1.0.3", - "resolve-from": "^5.0.0", - "ts-dedent": "^2.0.0" + "acorn": "^7.4.1", + "acorn-jsx": "^5.3.1", + "acorn-walk": "^7.2.0", + "escodegen": "^2.1.0", + "html-tags": "^3.1.0", + "lodash": "^4.17.21", + "prop-types": "^15.7.2", + "react-element-to-jsx-string": "^15.0.0", + "semver": "^7.3.7", + "ts-dedent": "^2.0.0", + "type-fest": "~2.19", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "typescript": ">= 4.2.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@storybook/telemetry/node_modules/@storybook/core-events": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.15.tgz", - "integrity": "sha512-i4YnjGecbpGyrFe0340sPhQ9QjZZEBqvMy6kF4XWt6DYLHxZmsTj1HEdvxVl4Ej7V49Vw0Dm8MepJ1d4Y8MKrQ==", + "node_modules/@storybook/react-docgen-typescript-plugin": { + "version": "1.0.6--canary.9.0c3f3b7.0", + "resolved": "https://registry.npmjs.org/@storybook/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-1.0.6--canary.9.0c3f3b7.0.tgz", + "integrity": "sha512-KUqXC3oa9JuQ0kZJLBhVdS4lOneKTOopnNBK4tUAgoxWQ3u/IjzdueZjFr7gyBrXMoU6duutk3RQR9u8ZpYJ4Q==", "dev": true, "dependencies": { - "ts-dedent": "^2.0.0" + "debug": "^4.1.1", + "endent": "^2.0.1", + "find-cache-dir": "^3.3.1", + "flat-cache": "^3.0.4", + "micromatch": "^4.0.2", + "react-docgen-typescript": "^2.2.2", + "tslib": "^2.0.0" }, + "peerDependencies": { + "typescript": ">= 4.x", + "webpack": ">= 4" + } + }, + "node_modules/@storybook/react-dom-shim": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.0.0.tgz", + "integrity": "sha512-bpT/7XyO9T+mWJojAblnuScum/UI65UksaL1jKYySMpBuW4jTJVE1YPzN1oe9A4me8HQCPeDw4Rg+ZB91H5sKA==", + "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@storybook/telemetry/node_modules/@storybook/csf-tools": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-7.6.15.tgz", - "integrity": "sha512-8iKgg2cmbFTpVhRRJOqouhPcEh0c8ywabG4S8ICZvnJooSXUI9mD9p3tYCS7MYuSiHj0epa1Kkn9DtXJRo9o6g==", + "node_modules/@storybook/react-webpack5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/react-webpack5/-/react-webpack5-8.0.0.tgz", + "integrity": "sha512-60+eDG5ajbJ/56KaQ7hIO5+tZZ+tX1dS9Gkq8twIKNjoAx/bUgfM9mD30YaP389bYdJDau30RnCZLAkpEv5ZIg==", "dev": true, "dependencies": { - "@babel/generator": "^7.23.0", - "@babel/parser": "^7.23.0", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", - "@storybook/csf": "^0.1.2", - "@storybook/types": "7.6.15", - "fs-extra": "^11.1.0", - "recast": "^0.23.1", - "ts-dedent": "^2.0.0" + "@storybook/builder-webpack5": "8.0.0", + "@storybook/preset-react-webpack": "8.0.0", + "@storybook/react": "8.0.0", + "@types/node": "^18.0.0" + }, + "engines": { + "node": ">=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "typescript": ">= 4.2.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/@storybook/telemetry/node_modules/@storybook/node-logger": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.15.tgz", - "integrity": "sha512-C+sCvRjR+5uVU3VTrfyv7/RlPBxesAjIucUAK0keGyIZ7sFQYCPdkm4m/C4s+TcubgAzVvuoUHlRrSppdA7WzQ==", + "node_modules/@storybook/router": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-8.0.0.tgz", + "integrity": "sha512-NPV4pb7TBOepPymHBLDmnwPcH4SnrNsD3LiHaVoaE4xaKMZBse2slWxeWM6IGb6Ynoy6pQpsHhAnt+rTjlcv9w==", "dev": true, + "dependencies": { + "@storybook/client-logger": "8.0.0", + "memoizerific": "^1.11.3", + "qs": "^6.10.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/telemetry/node_modules/@storybook/types": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.15.tgz", - "integrity": "sha512-tLH0lK6SXECSfMpKin9bge+7XiHZII17n6jc9ZI1TfSBZJyq3M6VzWh2r1C2lC97FlkcKXjIwM3n8h1xNjnI+A==", + "node_modules/@storybook/telemetry": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/telemetry/-/telemetry-8.0.0.tgz", + "integrity": "sha512-TpPswQYvhpFCyojWdKKOL7JMUhGqAr6Rqc/KQx4KEkHZat4K1yP7idNqpEIo/gavhlS1xVCNyp+WtzBI7d1PFw==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.15", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" + "@storybook/client-logger": "8.0.0", + "@storybook/core-common": "8.0.0", + "@storybook/csf-tools": "8.0.0", + "chalk": "^4.1.0", + "detect-package-manager": "^2.0.1", + "fetch-retry": "^5.0.2", + "fs-extra": "^11.1.0", + "read-pkg-up": "^7.0.1" }, "funding": { "type": "opencollective", @@ -8377,13 +5829,13 @@ } }, "node_modules/@storybook/theming": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.17.tgz", - "integrity": "sha512-ZbaBt3KAbmBtfjNqgMY7wPMBshhSJlhodyMNQypv+95xLD/R+Az6aBYbpVAOygLaUQaQk4ar7H/Ww6lFIoiFbA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.0.0.tgz", + "integrity": "sha512-Yu6ybemarPN3RBdsljtvpEVNqnqG1YxDLOmkzl1MFtJ1uA5Zd5mTMjc37iD0WDvLOk8mc1HmEqB5+fDrX0U4Vw==", "dev": true, "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.17", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@storybook/client-logger": "8.0.0", "@storybook/global": "^5.0.0", "memoizerific": "^1.11.3" }, @@ -8394,29 +5846,23 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/theming/node_modules/@storybook/client-logger": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.17.tgz", - "integrity": "sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } } }, "node_modules/@storybook/types": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.4.tgz", - "integrity": "sha512-qyiiXPCvol5uVgfubcIMzJBA0awAyFPU+TyUP1mkPYyiTHnsHYel/mKlSdPjc8a97N3SlJXHOCx41Hde4IyJgg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-8.0.0.tgz", + "integrity": "sha512-6nJipdgoAkVFk2JpRPCm9vb/Yuak2lmdZRv9qzl8cNRttlbOESVlzbmhgxCmWV0OYUaMeYge9L8NWhJ14LKbzw==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.4", - "@types/babel__core": "^7.0.0", + "@storybook/channels": "8.0.0", "@types/express": "^4.7.0", "file-system-cache": "2.3.0" }, @@ -8425,216 +5871,6 @@ "url": "https://opencollective.com/storybook" } }, - "node_modules/@swc/core": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.107.tgz", - "integrity": "sha512-zKhqDyFcTsyLIYK1iEmavljZnf4CCor5pF52UzLAz4B6Nu/4GLU+2LQVAf+oRHjusG39PTPjd2AlRT3f3QWfsQ==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@swc/counter": "^0.1.1", - "@swc/types": "^0.1.5" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-darwin-arm64": "1.3.107", - "@swc/core-darwin-x64": "1.3.107", - "@swc/core-linux-arm-gnueabihf": "1.3.107", - "@swc/core-linux-arm64-gnu": "1.3.107", - "@swc/core-linux-arm64-musl": "1.3.107", - "@swc/core-linux-x64-gnu": "1.3.107", - "@swc/core-linux-x64-musl": "1.3.107", - "@swc/core-win32-arm64-msvc": "1.3.107", - "@swc/core-win32-ia32-msvc": "1.3.107", - "@swc/core-win32-x64-msvc": "1.3.107" - }, - "peerDependencies": { - "@swc/helpers": "^0.5.0" - }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } - } - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.107.tgz", - "integrity": "sha512-47tD/5vSXWxPd0j/ZllyQUg4bqalbQTsmqSw0J4dDdS82MWqCAwUErUrAZPRjBkjNQ6Kmrf5rpCWaGTtPw+ngw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.107.tgz", - "integrity": "sha512-hwiLJ2ulNkBGAh1m1eTfeY1417OAYbRGcb/iGsJ+LuVLvKAhU/itzsl535CvcwAlt2LayeCFfcI8gdeOLeZa9A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.107.tgz", - "integrity": "sha512-I2wzcC0KXqh0OwymCmYwNRgZ9nxX7DWnOOStJXV3pS0uB83TXAkmqd7wvMBuIl9qu4Hfomi9aDM7IlEEn9tumQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.107.tgz", - "integrity": "sha512-HWgnn7JORYlOYnGsdunpSF8A+BCZKPLzLtEUA27/M/ZuANcMZabKL9Zurt7XQXq888uJFAt98Gy+59PU90aHKg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.107.tgz", - "integrity": "sha512-vfPF74cWfAm8hyhS8yvYI94ucMHIo8xIYU+oFOW9uvDlGQRgnUf/6DEVbLyt/3yfX5723Ln57U8uiMALbX5Pyw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.107.tgz", - "integrity": "sha512-uBVNhIg0ip8rH9OnOsCARUFZ3Mq3tbPHxtmWk9uAa5u8jQwGWeBx5+nTHpDOVd3YxKb6+5xDEI/edeeLpha/9g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.107.tgz", - "integrity": "sha512-mvACkUvzSIB12q1H5JtabWATbk3AG+pQgXEN95AmEX2ZA5gbP9+B+mijsg7Sd/3tboHr7ZHLz/q3SHTvdFJrEw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.107.tgz", - "integrity": "sha512-J3P14Ngy/1qtapzbguEH41kY109t6DFxfbK4Ntz9dOWNuVY3o9/RTB841ctnJk0ZHEG+BjfCJjsD2n8H5HcaOA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.107.tgz", - "integrity": "sha512-ZBUtgyjTHlz8TPJh7kfwwwFma+ktr6OccB1oXC8fMSopD0AxVnQasgun3l3099wIsAB9eEsJDQ/3lDkOLs1gBA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.3.107", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.107.tgz", - "integrity": "sha512-Eyzo2XRqWOxqhE1gk9h7LWmUf4Bp4Xn2Ttb0ayAXFp6YSTxQIThXcT9kipXZqcpxcmDwoq8iWbbf2P8XL743EA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/counter": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz", - "integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==", - "dev": true - }, - "node_modules/@swc/types": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", - "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==", - "dev": true - }, "node_modules/@testing-library/dom": { "version": "9.3.4", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", @@ -9304,9 +6540,9 @@ "dev": true }, "node_modules/@types/eslint": { - "version": "8.56.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz", - "integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==", + "version": "8.56.5", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.5.tgz", + "integrity": "sha512-u5/YPJHo1tvkSF2CE0USEkxon82Z5DBy2xR+qfyYNszpX9qcs4sT6uq2kBbj4BXY1+DBGDPnrhMZV3pKWGNukw==", "dev": true, "dependencies": { "@types/estree": "*", @@ -9359,12 +6595,6 @@ "@types/send": "*" } }, - "node_modules/@types/find-cache-dir": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz", - "integrity": "sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==", - "dev": true - }, "node_modules/@types/fs-extra": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", @@ -9423,6 +6653,15 @@ "gulp-replace": "*" } }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", @@ -9555,15 +6794,15 @@ } }, "node_modules/@types/lodash": { - "version": "4.14.202", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", - "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz", + "integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==", "dev": true }, "node_modules/@types/mdx": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.10.tgz", - "integrity": "sha512-Rllzc5KHk0Al5/WANwgSPl1/CwjqCy+AZrGd78zuK+jO9aDM6ffblZ+zIjgPNAaEBmlO0RYDvLNh7wD0zKVgEg==", + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.11.tgz", + "integrity": "sha512-HM5bwOaIQJIQbAYfax35HCKxx7a3KrK3nBtIqJgSOitivTD1y3oW9P3rxY9RkXYPUk7y/AjAohfHKmFpGE79zw==", "dev": true }, "node_modules/@types/mime": { @@ -9572,12 +6811,6 @@ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, - "node_modules/@types/mime-types": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", - "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", - "dev": true - }, "node_modules/@types/nanoid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/nanoid/-/nanoid-3.0.0.tgz", @@ -9820,20 +7053,11 @@ "dev": true }, "node_modules/@types/unist": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", - "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==", "dev": true }, - "node_modules/@types/unzipper": { - "version": "0.10.9", - "resolved": "https://registry.npmjs.org/@types/unzipper/-/unzipper-0.10.9.tgz", - "integrity": "sha512-vHbmFZAw8emNAOVkHVbS3qBnbr0x/qHQZ+ei1HE7Oy6Tyrptl+jpqnOX+BF5owcu/HZLOV0nJK+K9sjs1Ox2JA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/uuid": { "version": "9.0.7", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz", @@ -10588,9 +7812,9 @@ } }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "dev": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.11.6", @@ -10610,9 +7834,9 @@ "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { @@ -10633,15 +7857,15 @@ "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" + "@webassemblyjs/wasm-gen": "1.12.1" } }, "node_modules/@webassemblyjs/ieee754": { @@ -10669,28 +7893,28 @@ "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-opt": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6", - "@webassemblyjs/wast-printer": "1.11.6" + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", "@webassemblyjs/leb128": "1.11.6", @@ -10698,24 +7922,24 @@ } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-api-error": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", @@ -10724,12 +7948,12 @@ } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" } }, @@ -11246,18 +8470,6 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "node_modules/aria-hidden": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz", - "integrity": "sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==", - "dev": true, - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/aria-query": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", @@ -11621,12 +8833,6 @@ "node": "^4.7 || >=6.9 || >=7.3" } }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true - }, "node_modules/async-listener": { "version": "0.6.10", "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz", @@ -11822,173 +9028,6 @@ "node": ">=8" } }, - "node_modules/babel-loader": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", - "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", - "dev": true, - "dependencies": { - "find-cache-dir": "^4.0.0", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0", - "webpack": ">=5" - } - }, - "node_modules/babel-loader/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/babel-loader/node_modules/find-cache-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", - "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", - "dev": true, - "dependencies": { - "common-path-prefix": "^3.0.0", - "pkg-dir": "^7.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "dev": true, - "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dev": true, - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dev": true, - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/babel-loader/node_modules/pkg-dir": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", - "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", - "dev": true, - "dependencies": { - "find-up": "^6.3.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/babel-loader/node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-plugin-add-react-displayname": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/babel-plugin-add-react-displayname/-/babel-plugin-add-react-displayname-0.0.5.tgz", - "integrity": "sha512-LY3+Y0XVDYcShHHorshrDbt4KFWL4bSeniCtl4SYZbask+Syngk1uMPCeN9+nSiZo6zX5s0RTq/J9Pnaaf/KHw==", - "dev": true - }, "node_modules/babel-plugin-istanbul": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", @@ -13268,12 +10307,6 @@ "node": ">= 6" } }, - "node_modules/common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", - "dev": true - }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -13493,17 +10526,6 @@ "url": "https://opencollective.com/core-js" } }, - "node_modules/core-js-pure": { - "version": "3.35.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.35.1.tgz", - "integrity": "sha512-zcIdi/CL3MWbBJYo5YCeVAAx+Sy9yJE9I3/u9LkFABwbeaPhTMRWraM8mYFp9jW5Z50hOy7FVzCc8dCrpZqtIQ==", - "dev": true, - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -14797,12 +11819,6 @@ "node": ">=8" } }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "dev": true - }, "node_modules/detect-package-manager": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/detect-package-manager/-/detect-package-manager-2.0.1.tgz", @@ -15196,15 +12212,6 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "dev": true, - "dependencies": { - "stackframe": "^1.3.4" - } - }, "node_modules/es-abstract": { "version": "1.22.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", @@ -16873,36 +13880,6 @@ "node": ">=0.10.0" } }, - "node_modules/extract-zip": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", - "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", - "dev": true, - "dependencies": { - "concat-stream": "^1.6.2", - "debug": "^2.6.9", - "mkdirp": "^0.5.4", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - } - }, - "node_modules/extract-zip/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/extract-zip/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, "node_modules/fancy-log": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", @@ -17469,9 +14446,9 @@ "dev": true }, "node_modules/flow-parser": { - "version": "0.229.0", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.229.0.tgz", - "integrity": "sha512-mOYmMuvJwAo/CvnMFEq4SHftq7E5188hYMTTxJyQOXk2nh+sgslRdYMw3wTthH+FMcFaZLtmBPuMu6IwztdoUQ==", + "version": "0.231.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.231.0.tgz", + "integrity": "sha512-WVzuqwq7ZnvBceCG0DGeTQebZE+iIU0mlk5PmJgYj9DDrt+0isGC2m1ezW9vxL4V+HERJJo9ExppOnwKH2op6Q==", "dev": true, "engines": { "node": ">=0.4.0" @@ -17898,15 +14875,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/get-npm-tarball-url": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/get-npm-tarball-url/-/get-npm-tarball-url-2.1.0.tgz", @@ -17925,18 +14893,6 @@ "node": ">=8.0.0" } }, - "node_modules/get-port": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", - "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -18013,9 +14969,9 @@ "optional": true }, "node_modules/github-slugger": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", - "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", "dev": true }, "node_modules/glob": { @@ -19560,6 +16516,45 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-heading-rank": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-heading-rank/-/hast-util-heading-rank-3.0.0.tgz", + "integrity": "sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.0.tgz", + "integrity": "sha512-OGkAxX1Ua3cbcW6EJ5pT/tslVb90uViVkcJ4ZZIMW/R33DX/AkcJcRrPebPwJkHYwlDHXz4aIwvAAaAdtrACFA==", + "dev": true, + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -19638,9 +16633,9 @@ } }, "node_modules/html-entities": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", - "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", "dev": true, "funding": [ { @@ -20217,15 +17212,6 @@ "node": ">= 0.10" } }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", @@ -20264,12 +17250,15 @@ } }, "node_modules/is-absolute-url": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", - "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-4.0.1.tgz", + "integrity": "sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==", "dev": true, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-accessor-descriptor": { @@ -23051,9 +20040,9 @@ } }, "node_modules/jscodeshift": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.15.1.tgz", - "integrity": "sha512-hIJfxUy8Rt4HkJn/zZPU9ChKfKZM1342waJ1QC2e2YsPcWhM+3BJ4dcfQCzArTrk1jJeNLB341H+qOcEHRxJZg==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.15.2.tgz", + "integrity": "sha512-FquR7Okgmc4Sd0aEDwqho3rEiKR3BdvuG9jfdHjLJ6JQoWSMpavug3AoIfnfWhxFlf+5pzQh8qjqz0DWFrNQzA==", "dev": true, "dependencies": { "@babel/core": "^7.23.0", @@ -24263,9 +21252,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.6", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.6.tgz", - "integrity": "sha512-n62qCLbPjNjyo+owKtveQxZFZTBm+Ms6YoGD23Wew6Vw337PElFNifQpknPruVRQV57kVShPnLGo9vWxVhpPvA==", + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -24746,30 +21735,7 @@ "repeat-string": "^1.6.1" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mdast-util-definitions": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", - "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", - "dev": true, - "dependencies": { - "unist-util-visit": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz", - "integrity": "sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "node": ">=0.10.0" } }, "node_modules/mdurl": { @@ -25047,15 +22013,15 @@ } }, "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, "bin": { "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/mkdirp-classic": { @@ -25576,15 +22542,16 @@ "dev": true }, "node_modules/nypm": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.3.6.tgz", - "integrity": "sha512-2CATJh3pd6CyNfU5VZM7qSwFu0ieyabkEdnogE30Obn1czrmOYiZ8DOZLe1yBdLKWoyD3Mcy2maUs+0MR3yVjQ==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.3.8.tgz", + "integrity": "sha512-IGWlC6So2xv6V4cIDmoV0SwwWx7zLG086gyqkyumteH2fIgCAM4nDVFB2iDRszDvmdSVW9xb1N+2KjQ6C7d4og==", "dev": true, "dependencies": { - "citty": "^0.1.5", + "citty": "^0.1.6", + "consola": "^3.2.3", "execa": "^8.0.1", "pathe": "^1.1.2", - "ufo": "^1.3.2" + "ufo": "^1.4.0" }, "bin": { "nypm": "dist/cli.mjs" @@ -25676,9 +22643,9 @@ } }, "node_modules/nypm/node_modules/npm-run-path": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", - "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, "dependencies": { "path-key": "^4.0.0" @@ -27175,15 +24142,6 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/promise-polyfill": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz", @@ -27226,12 +24184,6 @@ "node": ">= 0.10" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true - }, "node_modules/pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", @@ -27291,102 +24243,6 @@ "node": ">=6" } }, - "node_modules/puppeteer-core": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-2.1.1.tgz", - "integrity": "sha512-n13AWriBMPYxnpbb6bnaY5YoY6rGj8vPLrz6CZF3o0qJNEwlcfJVxBzYZ0NJsQ21UbdJoijPCDrM++SUVEz7+w==", - "dev": true, - "dependencies": { - "@types/mime-types": "^2.1.0", - "debug": "^4.1.0", - "extract-zip": "^1.6.6", - "https-proxy-agent": "^4.0.0", - "mime": "^2.0.3", - "mime-types": "^2.1.25", - "progress": "^2.0.1", - "proxy-from-env": "^1.0.0", - "rimraf": "^2.6.1", - "ws": "^6.1.0" - }, - "engines": { - "node": ">=8.16.0" - } - }, - "node_modules/puppeteer-core/node_modules/agent-base": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", - "dev": true, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/puppeteer-core/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/puppeteer-core/node_modules/https-proxy-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", - "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", - "dev": true, - "dependencies": { - "agent-base": "5", - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/puppeteer-core/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/puppeteer-core/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/puppeteer-core/node_modules/ws": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", - "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", - "dev": true, - "dependencies": { - "async-limiter": "~1.0.0" - } - }, "node_modules/pure-rand": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", @@ -27626,85 +24482,6 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, - "node_modules/react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-remove-scroll": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", - "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", - "dev": true, - "dependencies": { - "react-remove-scroll-bar": "^2.3.3", - "react-style-singleton": "^2.2.1", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.4.tgz", - "integrity": "sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==", - "dev": true, - "dependencies": { - "react-style-singleton": "^2.2.1", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-style-singleton": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", - "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", - "dev": true, - "dependencies": { - "get-nonce": "^1.0.0", - "invariant": "^2.2.4", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", @@ -27908,15 +24685,15 @@ } }, "node_modules/recast": { - "version": "0.23.4", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.4.tgz", - "integrity": "sha512-qtEDqIZGVcSZCHniWwZWbRy79Dc6Wp3kT/UmDA2RJKBPg7+7k51aQBZirHmUGn5uvHf2rg8DkjizrN26k61ATw==", + "version": "0.23.6", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.6.tgz", + "integrity": "sha512-9FHoNjX1yjuesMwuthAmPKabxYQdOgihFYmT5ebXfYGBcnqXZf3WOVz+5foEZ8Y83P4ZY6yQD5GMmtV+pgCCAQ==", "dev": true, "dependencies": { - "assert": "^2.0.0", "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" }, "engines": { @@ -28090,47 +24867,50 @@ "jsesc": "bin/jsesc" } }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/remark-external-links": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/remark-external-links/-/remark-external-links-8.0.0.tgz", - "integrity": "sha512-5vPSX0kHoSsqtdftSHhIYofVINC8qmp0nctkeU9YoJwV3YfiBRiI6cbFRJ0oI/1F9xS+bopXG0m2KS8VFscuKA==", + "node_modules/rehype-external-links": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rehype-external-links/-/rehype-external-links-3.0.0.tgz", + "integrity": "sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw==", "dev": true, "dependencies": { - "extend": "^3.0.0", - "is-absolute-url": "^3.0.0", - "mdast-util-definitions": "^4.0.0", - "space-separated-tokens": "^1.0.0", - "unist-util-visit": "^2.0.0" + "@types/hast": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-is-element": "^3.0.0", + "is-absolute-url": "^4.0.0", + "space-separated-tokens": "^2.0.0", + "unist-util-visit": "^5.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, - "node_modules/remark-slug": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/remark-slug/-/remark-slug-6.1.0.tgz", - "integrity": "sha512-oGCxDF9deA8phWvxFuyr3oSJsdyUAxMFbA0mZ7Y1Sas+emILtO+e5WutF9564gDsEN4IXaQXm5pFo6MLH+YmwQ==", + "node_modules/rehype-slug": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rehype-slug/-/rehype-slug-6.0.0.tgz", + "integrity": "sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==", "dev": true, "dependencies": { - "github-slugger": "^1.0.0", - "mdast-util-to-string": "^1.0.0", - "unist-util-visit": "^2.0.0" + "@types/hast": "^3.0.0", + "github-slugger": "^2.0.0", + "hast-util-heading-rank": "^3.0.0", + "hast-util-to-string": "^3.0.0", + "unist-util-visit": "^5.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/remove-bom-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", @@ -29324,9 +26104,9 @@ "dev": true }, "node_modules/space-separated-tokens": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", - "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", "dev": true, "funding": { "type": "github", @@ -29427,12 +26207,6 @@ "node": ">=8" } }, - "node_modules/stackframe": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", - "dev": true - }, "node_modules/static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -29498,12 +26272,12 @@ "dev": true }, "node_modules/storybook": { - "version": "7.6.15", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-7.6.15.tgz", - "integrity": "sha512-Ybezq9JRk5CBhzjgzZ/oT7mnU45UwhyVSGKW+PUKZGGUG9VH2hCrTEES9f/zEF82kj/5COVPyqR/5vlXuuS39A==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.0.0.tgz", + "integrity": "sha512-ZWfFoKLsZ7kYgqcVgDeUZpN89cxzEx2Mw9afhfMNzwSnjhx9xRdzdNvK7DY1nDnfborxzBhkvwYf/oxRbifKuw==", "dev": true, "dependencies": { - "@storybook/cli": "7.6.15" + "@storybook/cli": "8.0.0" }, "bin": { "sb": "index.js", @@ -29986,28 +26760,12 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/swc-loader": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.2.4.tgz", - "integrity": "sha512-B0toRR3M8PuUV3f0hSoXHogtLCRW8bVYVZl0L1p1iSeTgVBkXa9mOQOYuQBbSJnSdnoQiM7ZCHIY47b0dKaehg==", - "dev": true, - "peerDependencies": { - "@swc/core": "^1.2.147", - "webpack": ">=2" - } - }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, - "node_modules/synchronous-promise": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/synchronous-promise/-/synchronous-promise-2.0.17.tgz", - "integrity": "sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g==", - "dev": true - }, "node_modules/synckit": { "version": "0.8.6", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.6.tgz", @@ -30122,18 +26880,6 @@ "node": ">=8" } }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/tar/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -30286,9 +27032,9 @@ } }, "node_modules/terser": { - "version": "5.27.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", - "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==", + "version": "5.29.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.2.tgz", + "integrity": "sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -30503,9 +27249,9 @@ } }, "node_modules/tiny-invariant": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", - "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", "dev": true }, "node_modules/titleize": { @@ -30640,9 +27386,9 @@ } }, "node_modules/tocbot": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/tocbot/-/tocbot-4.23.0.tgz", - "integrity": "sha512-5DWuSZXsqG894mkGb8ZsQt9myyQyVxE50AiGRZ0obV0BVUTVkaZmc9jbgpknaAAPUm4FIrzGkEseD6FuQJYJDQ==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/tocbot/-/tocbot-4.25.0.tgz", + "integrity": "sha512-kE5wyCQJ40hqUaRVkyQ4z5+4juzYsv/eK+aqD97N62YH0TxFhzJvo22RUQQZdO3YnXAk42ZOfOpjVdy+Z0YokA==", "dev": true }, "node_modules/toidentifier": { @@ -31479,24 +28225,27 @@ } }, "node_modules/unist-util-is": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", - "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", "dev": true, + "dependencies": { + "@types/unist": "^3.0.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, "node_modules/unist-util-visit": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", - "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", "dev": true, "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" }, "funding": { "type": "opencollective", @@ -31504,13 +28253,13 @@ } }, "node_modules/unist-util-visit-parents": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", - "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", "dev": true, "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0" + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" }, "funding": { "type": "opencollective", @@ -31540,21 +28289,24 @@ } }, "node_modules/unplugin": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.5.1.tgz", - "integrity": "sha512-0QkvG13z6RD+1L1FoibQqnvTwVBXvS4XSPwAyinVgoOCl2jAgwzdUKmEj05o4Lt8xwQI85Hb6mSyYkcAGwZPew==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.10.0.tgz", + "integrity": "sha512-CuZtvvO8ua2Wl+9q2jEaqH6m3DoQ38N7pvBYQbbaeNlWGvK2l6GHiKi29aIHDPoSxdUzQ7Unevf1/ugil5X6Pg==", "dev": true, "dependencies": { - "acorn": "^8.11.2", - "chokidar": "^3.5.3", + "acorn": "^8.11.3", + "chokidar": "^3.6.0", "webpack-sources": "^3.2.3", - "webpack-virtual-modules": "^0.6.0" + "webpack-virtual-modules": "^0.6.1" + }, + "engines": { + "node": ">=14.0.0" } }, "node_modules/unplugin/node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -31728,62 +28480,6 @@ "node": ">=0.10.0" } }, - "node_modules/use-callback-ref": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz", - "integrity": "sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==", - "dev": true, - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-resize-observer": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", - "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", - "dev": true, - "dependencies": { - "@juggle/resize-observer": "^3.3.1" - }, - "peerDependencies": { - "react": "16.8.0 - 18", - "react-dom": "16.8.0 - 18" - } - }, - "node_modules/use-sidecar": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", - "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", - "dev": true, - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", @@ -32193,9 +28889,9 @@ } }, "node_modules/webpack": { - "version": "5.90.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.0.tgz", - "integrity": "sha512-bdmyXRCXeeNIePv6R6tGPyy20aUobw4Zy8r0LUS2EWO+U+Ke/gYDgsCh7bl5rB6jPpr4r0SZa6dPxBxLooDT3w==", + "version": "5.90.3", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.3.tgz", + "integrity": "sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -32299,9 +28995,9 @@ } }, "node_modules/webpack-hot-middleware": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.26.0.tgz", - "integrity": "sha512-okzjec5sAEy4t+7rzdT8eRyxsk0FDSmBPN2KwX4Qd+6+oQCfe5Ve07+u7cJvofgB+B4w5/4dO4Pz0jhhHyyPLQ==", + "version": "2.26.1", + "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.26.1.tgz", + "integrity": "sha512-khZGfAeJx6I8K9zKohEWWYN6KDlVw2DHownoe+6Vtwj1LP9WFgegXnVMSkZ/dBEBtXFwrkkydsaPFlB7f8wU2A==", "dev": true, "dependencies": { "ansi-html-community": "0.0.8", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index b82af98a36c..efe691d67a2 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1995,17 +1995,17 @@ "@github/markdownlint-github": "^0.6.0", "@octokit/plugin-throttling": "^8.0.0", "@playwright/test": "^1.40.1", - "@storybook/addon-a11y": "^7.6.15", - "@storybook/addon-actions": "^7.1.0", - "@storybook/addon-essentials": "^7.1.0", - "@storybook/addon-interactions": "^7.1.0", - "@storybook/addon-links": "^7.1.0", - "@storybook/components": "^7.6.17", + "@storybook/addon-a11y": "^8.0.0", + "@storybook/addon-actions": "^8.0.0", + "@storybook/addon-essentials": "^8.0.0", + "@storybook/addon-interactions": "^8.0.0", + "@storybook/addon-links": "^8.0.0", + "@storybook/components": "^8.0.0", "@storybook/csf": "^0.1.1", - "@storybook/manager-api": "^7.6.7", - "@storybook/react": "^7.1.0", - "@storybook/react-webpack5": "^7.6.12", - "@storybook/theming": "^7.6.12", + "@storybook/manager-api": "^8.0.0", + "@storybook/react": "^8.0.0", + "@storybook/react-webpack5": "^8.0.0", + "@storybook/theming": "^8.0.0", "@testing-library/dom": "^9.3.4", "@testing-library/jest-dom": "^6.4.2", "@testing-library/react": "^14.2.1", @@ -2072,7 +2072,7 @@ "npm-run-all": "^4.1.5", "patch-package": "^8.0.0", "prettier": "^3.2.5", - "storybook": "^7.6.15", + "storybook": "^8.0.0", "tar-stream": "^3.1.7", "through2": "^4.0.2", "ts-jest": "^29.0.1", From 5427c169b968d9c1572dca475f6da916357c8306 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Thu, 14 Mar 2024 17:11:14 +0000 Subject: [PATCH 0154/1237] Show model packs in Model Alerts view (#3484) --- .../ql-vscode/src/common/interface-types.ts | 13 +++++++-- .../model-alerts/model-alerts-view.ts | 13 +++++++-- .../src/model-editor/model-evaluator.ts | 21 +++++++++++++- .../src/view/model-alerts/ModelAlerts.tsx | 28 +++++++++++++++++-- .../view/model-alerts/ModelAlertsHeader.tsx | 24 ++++++++++++++-- extensions/ql-vscode/src/view/vscode-api.ts | 4 ++- 6 files changed, 91 insertions(+), 12 deletions(-) diff --git a/extensions/ql-vscode/src/common/interface-types.ts b/extensions/ql-vscode/src/common/interface-types.ts index 525867d6c52..1c0a09ffcad 100644 --- a/extensions/ql-vscode/src/common/interface-types.ts +++ b/extensions/ql-vscode/src/common/interface-types.ts @@ -732,6 +732,15 @@ interface SetModelAlertsViewStateMessage { viewState: ModelAlertsViewState; } -export type ToModelAlertsMessage = SetModelAlertsViewStateMessage; +interface OpenModelPackMessage { + t: "openModelPack"; + path: string; +} + +export type ToModelAlertsMessage = + | SetModelAlertsViewStateMessage + | SetVariantAnalysisMessage; -export type FromModelAlertsMessage = CommonFromViewMessages; +export type FromModelAlertsMessage = + | CommonFromViewMessages + | OpenModelPackMessage; diff --git a/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts b/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts index 5acf6bc500d..1ac96ff459e 100644 --- a/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts @@ -1,4 +1,4 @@ -import { ViewColumn } from "vscode"; +import { Uri, ViewColumn } from "vscode"; import type { WebviewPanelConfig } from "../../common/vscode/abstract-webview"; import { AbstractWebview } from "../../common/vscode/abstract-webview"; import { assertNever } from "../../common/helpers-pure"; @@ -15,6 +15,7 @@ import type { ModelingEvents } from "../modeling-events"; import type { ModelingStore } from "../modeling-store"; import type { DatabaseItem } from "../../databases/local-databases"; import type { ExtensionPack } from "../shared/extension-pack"; +import type { VariantAnalysis } from "../../variant-analysis/shared/variant-analysis"; export class ModelAlertsView extends AbstractWebview< ToModelAlertsMessage, @@ -34,12 +35,17 @@ export class ModelAlertsView extends AbstractWebview< this.registerToModelingEvents(); } - public async showView() { + public async showView(variantAnalysis: VariantAnalysis) { const panel = await this.getPanel(); panel.reveal(undefined, true); await this.waitForPanelLoaded(); await this.setViewState(); + + await this.postMessage({ + t: "setVariantAnalysis", + variantAnalysis, + }); } protected async getPanelConfig(): Promise { @@ -73,6 +79,9 @@ export class ModelAlertsView extends AbstractWebview< )`Unhandled error in model alerts view: ${msg.error.message}`, ); break; + case "openModelPack": + await this.app.commands.execute("revealInExplorer", Uri.file(msg.path)); + break; default: assertNever(msg); } diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts index f8bf7c045fe..8b998733070 100644 --- a/extensions/ql-vscode/src/model-editor/model-evaluator.ts +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -124,7 +124,26 @@ export class ModelEvaluator extends DisposableObject { this.dbItem, this.extensionPack, ); - await view.showView(); + + // There should be a variant analysis available at this point, as the + // view can only opened when the variant analysis is complete. So we + // send this to the view. This is temporary until we have logic to + // listen to variant analysis updates and update the view accordingly. + const evaluationRun = this.modelingStore.getModelEvaluationRun( + this.dbItem, + ); + if (!evaluationRun) { + throw new Error("No evaluation run available"); + } + + const variantAnalysis = + await this.getVariantAnalysisForRun(evaluationRun); + + if (!variantAnalysis) { + throw new Error("No variant analysis available"); + } + + await view.showView(variantAnalysis); } } diff --git a/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx b/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx index b128676f28e..2b2eb1a1dd8 100644 --- a/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx +++ b/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx @@ -1,17 +1,30 @@ -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { ModelAlertsHeader } from "./ModelAlertsHeader"; import type { ModelAlertsViewState } from "../../model-editor/shared/view-state"; import type { ToModelAlertsMessage } from "../../common/interface-types"; +import type { VariantAnalysis } from "../../variant-analysis/shared/variant-analysis"; +import { vscode } from "../vscode-api"; type Props = { initialViewState?: ModelAlertsViewState; }; export function ModelAlerts({ initialViewState }: Props): React.JSX.Element { + const onOpenModelPackClick = useCallback((path: string) => { + vscode.postMessage({ + t: "openModelPack", + path, + }); + }, []); + const [viewState, setViewState] = useState( initialViewState, ); + const [variantAnalysis, setVariantAnalysis] = useState< + VariantAnalysis | undefined + >(undefined); + useEffect(() => { const listener = (evt: MessageEvent) => { if (evt.origin === window.origin) { @@ -21,6 +34,9 @@ export function ModelAlerts({ initialViewState }: Props): React.JSX.Element { setViewState(msg.viewState); break; } + case "setVariantAnalysis": { + setVariantAnalysis(msg.variantAnalysis); + } } } else { // sanitize origin @@ -35,9 +51,15 @@ export function ModelAlerts({ initialViewState }: Props): React.JSX.Element { }; }, []); - if (viewState === undefined) { + if (viewState === undefined || variantAnalysis === undefined) { return <>; } - return ; + return ( + + ); } diff --git a/extensions/ql-vscode/src/view/model-alerts/ModelAlertsHeader.tsx b/extensions/ql-vscode/src/view/model-alerts/ModelAlertsHeader.tsx index 5add201d89e..4dad006b003 100644 --- a/extensions/ql-vscode/src/view/model-alerts/ModelAlertsHeader.tsx +++ b/extensions/ql-vscode/src/view/model-alerts/ModelAlertsHeader.tsx @@ -1,8 +1,26 @@ import type { ModelAlertsViewState } from "../../model-editor/shared/view-state"; +import type { VariantAnalysis } from "../../variant-analysis/shared/variant-analysis"; import { ViewTitle } from "../common"; +import { ModelPacks } from "./ModelPacks"; -type Props = { viewState: ModelAlertsViewState }; +type Props = { + viewState: ModelAlertsViewState; + variantAnalysis: VariantAnalysis; + openModelPackClick: (path: string) => void; +}; -export const ModelAlertsHeader = ({ viewState }: Props) => { - return Model evaluation results for {viewState.title}; +export const ModelAlertsHeader = ({ + viewState, + variantAnalysis, + openModelPackClick, +}: Props) => { + return ( + <> + Model evaluation results for {viewState.title} + + + ); }; diff --git a/extensions/ql-vscode/src/view/vscode-api.ts b/extensions/ql-vscode/src/view/vscode-api.ts index a798be503ec..ab7e930cca4 100644 --- a/extensions/ql-vscode/src/view/vscode-api.ts +++ b/extensions/ql-vscode/src/view/vscode-api.ts @@ -1,6 +1,7 @@ import type { FromCompareViewMessage, FromMethodModelingMessage, + FromModelAlertsMessage, FromModelEditorMessage, FromResultsViewMsg, FromVariantAnalysisMessage, @@ -17,7 +18,8 @@ export interface VsCodeApi { | FromCompareViewMessage | FromVariantAnalysisMessage | FromModelEditorMessage - | FromMethodModelingMessage, + | FromMethodModelingMessage + | FromModelAlertsMessage, ): void; /** From f5c350220254564b407df6d737c1aa8a2239f7d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 10:43:59 -0700 Subject: [PATCH 0155/1237] Bump @babel/preset-env from 7.23.9 to 7.24.0 in /extensions/ql-vscode (#3475) Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.23.9 to 7.24.0. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.24.0/packages/babel-preset-env) --- updated-dependencies: - dependency-name: "@babel/preset-env" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/ql-vscode/package-lock.json | 40 ++++++++++---------------- extensions/ql-vscode/package.json | 2 +- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index ecc9eb09941..7484457397b 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -47,7 +47,7 @@ "devDependencies": { "@babel/core": "^7.24.0", "@babel/plugin-transform-modules-commonjs": "^7.18.6", - "@babel/preset-env": "^7.23.9", + "@babel/preset-env": "^7.24.0", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.4", "@faker-js/faker": "^8.4.1", @@ -90,7 +90,6 @@ "@types/tar-stream": "^3.1.3", "@types/through2": "^2.0.36", "@types/tmp": "^0.2.6", - "@types/unzipper": "^0.10.1", "@types/vscode": "^1.82.0", "@types/yauzl": "^2.10.3", "@typescript-eslint/eslint-plugin": "^6.19.0", @@ -624,9 +623,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", + "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", "dev": true, "engines": { "node": ">=6.9.0" @@ -1622,14 +1621,14 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", - "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.0.tgz", + "integrity": "sha512-y/yKMm7buHpFFXfxVFS4Vk1ToRJDilIa6fKRioB9Vjichv58TDGXTvqV0dN7plobAmTW5eSEGXDngE+Mm+uO+w==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.23.3", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/compat-data": "^7.23.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-transform-parameters": "^7.23.3" }, @@ -2007,14 +2006,14 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.9.tgz", - "integrity": "sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.0.tgz", + "integrity": "sha512-ZxPEzV9IgvGn73iK0E6VB9/95Nd7aMFpbE0l8KQFDG70cOV9IxRP7Y2FUPmlK0v6ImlLqYX50iuZ3ZTVhOF2lA==", "dev": true, "dependencies": { "@babel/compat-data": "^7.23.5", "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-validator-option": "^7.23.5", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", @@ -2067,7 +2066,7 @@ "@babel/plugin-transform-new-target": "^7.23.3", "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", "@babel/plugin-transform-numeric-separator": "^7.23.4", - "@babel/plugin-transform-object-rest-spread": "^7.23.4", + "@babel/plugin-transform-object-rest-spread": "^7.24.0", "@babel/plugin-transform-object-super": "^7.23.3", "@babel/plugin-transform-optional-catch-binding": "^7.23.4", "@babel/plugin-transform-optional-chaining": "^7.23.4", @@ -9825,15 +9824,6 @@ "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==", "dev": true }, - "node_modules/@types/unzipper": { - "version": "0.10.9", - "resolved": "https://registry.npmjs.org/@types/unzipper/-/unzipper-0.10.9.tgz", - "integrity": "sha512-vHbmFZAw8emNAOVkHVbS3qBnbr0x/qHQZ+ei1HE7Oy6Tyrptl+jpqnOX+BF5owcu/HZLOV0nJK+K9sjs1Ox2JA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/uuid": { "version": "9.0.7", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index b82af98a36c..2b7da037dcb 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1988,7 +1988,7 @@ "devDependencies": { "@babel/core": "^7.24.0", "@babel/plugin-transform-modules-commonjs": "^7.18.6", - "@babel/preset-env": "^7.23.9", + "@babel/preset-env": "^7.24.0", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.4", "@faker-js/faker": "^8.4.1", From c447548db3e27b63a0194a28dcd3b0367ff319d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 10:45:11 -0700 Subject: [PATCH 0156/1237] Bump the typescript-eslint group in /extensions/ql-vscode with 1 update (#3482) Bumps the typescript-eslint group in /extensions/ql-vscode with 1 update: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin). Updates `@typescript-eslint/eslint-plugin` from 6.19.0 to 7.2.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v7.2.0/packages/eslint-plugin) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-major dependency-group: typescript-eslint ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/ql-vscode/package-lock.json | 372 +++++++++++++++---------- extensions/ql-vscode/package.json | 2 +- 2 files changed, 228 insertions(+), 146 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 7484457397b..bc5437c2ebc 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -92,7 +92,7 @@ "@types/tmp": "^0.2.6", "@types/vscode": "^1.82.0", "@types/yauzl": "^2.10.3", - "@typescript-eslint/eslint-plugin": "^6.19.0", + "@typescript-eslint/eslint-plugin": "^7.2.0", "@typescript-eslint/parser": "^6.21.0", "@vscode/test-electron": "^2.3.9", "@vscode/vsce": "^2.24.0", @@ -9882,16 +9882,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.19.0.tgz", - "integrity": "sha512-DUCUkQNklCQYnrBSSikjVChdc84/vMPDQSgJTHBZ64G9bA9w0Crc0rd2diujKbTdp6w2J47qkeHQLoi0rpLCdg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.2.0.tgz", + "integrity": "sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.19.0", - "@typescript-eslint/type-utils": "6.19.0", - "@typescript-eslint/utils": "6.19.0", - "@typescript-eslint/visitor-keys": "6.19.0", + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/type-utils": "7.2.0", + "@typescript-eslint/utils": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -9907,8 +9907,8 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -9917,13 +9917,13 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz", - "integrity": "sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz", + "integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.19.0", - "@typescript-eslint/visitor-keys": "6.19.0" + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -9934,25 +9934,78 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", - "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz", + "integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz", + "integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.2.0.tgz", + "integrity": "sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==", "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/typescript-estree": "7.2.0", + "semver": "^7.5.4" + }, "engines": { "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", - "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz", + "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/types": "7.2.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -9963,6 +10016,30 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/experimental-utils": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz", @@ -10150,13 +10227,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.19.0.tgz", - "integrity": "sha512-mcvS6WSWbjiSxKCwBcXtOM5pRkPQ6kcDds/juxcy/727IQr3xMEcwr/YLHW2A2+Fp5ql6khjbKBzOyjuPqGi/w==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.2.0.tgz", + "integrity": "sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.19.0", - "@typescript-eslint/utils": "6.19.0", + "@typescript-eslint/typescript-estree": "7.2.0", + "@typescript-eslint/utils": "7.2.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -10168,7 +10245,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -10176,10 +10253,27 @@ } } }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz", + "integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", - "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz", + "integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -10190,13 +10284,13 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz", - "integrity": "sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz", + "integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.19.0", - "@typescript-eslint/visitor-keys": "6.19.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -10217,13 +10311,38 @@ } } }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.2.0.tgz", + "integrity": "sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/typescript-estree": "7.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", - "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz", + "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/types": "7.2.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -10324,17 +10443,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.19.0.tgz", - "integrity": "sha512-QR41YXySiuN++/dC9UArYOg4X86OAYP83OWTewpVx5ct1IZhjjgTLocj7QNxGhWoTqknsgpl7L+hGygCO+sdYw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.19.0", - "@typescript-eslint/types": "6.19.0", - "@typescript-eslint/typescript-estree": "6.19.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", "semver": "^7.5.4" }, "engines": { @@ -10348,105 +10467,6 @@ "eslint": "^7.0.0 || ^8.0.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz", - "integrity": "sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.19.0", - "@typescript-eslint/visitor-keys": "6.19.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", - "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz", - "integrity": "sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.19.0", - "@typescript-eslint/visitor-keys": "6.19.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", - "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.19.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/visitor-keys": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", @@ -15755,6 +15775,68 @@ "eslint": "^8.0.1" } }, + "node_modules/eslint-plugin-github/node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-github/node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/eslint-plugin-github/node_modules/aria-query": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 2b7da037dcb..0620c836c9b 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -2033,7 +2033,7 @@ "@types/tmp": "^0.2.6", "@types/vscode": "^1.82.0", "@types/yauzl": "^2.10.3", - "@typescript-eslint/eslint-plugin": "^6.19.0", + "@typescript-eslint/eslint-plugin": "^7.2.0", "@typescript-eslint/parser": "^6.21.0", "@vscode/test-electron": "^2.3.9", "@vscode/vsce": "^2.24.0", From ec31b97de81076ec94715f6f0ce38b46b0c023c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 10:45:42 -0700 Subject: [PATCH 0157/1237] Bump @types/jest from 29.5.11 to 29.5.12 in /extensions/ql-vscode (#3483) Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 29.5.11 to 29.5.12. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest) --- updated-dependencies: - dependency-name: "@types/jest" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/ql-vscode/package-lock.json | 8 ++++---- extensions/ql-vscode/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index bc5437c2ebc..d58b1adb58e 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -76,7 +76,7 @@ "@types/fs-extra": "^11.0.1", "@types/gulp": "^4.0.9", "@types/gulp-replace": "^1.1.0", - "@types/jest": "^29.0.2", + "@types/jest": "^29.5.12", "@types/js-yaml": "^4.0.6", "@types/nanoid": "^3.0.0", "@types/node": "18.17.*", @@ -9469,9 +9469,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.11", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", - "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", "dev": true, "dependencies": { "expect": "^29.0.0", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 0620c836c9b..3320605da44 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -2017,7 +2017,7 @@ "@types/fs-extra": "^11.0.1", "@types/gulp": "^4.0.9", "@types/gulp-replace": "^1.1.0", - "@types/jest": "^29.0.2", + "@types/jest": "^29.5.12", "@types/js-yaml": "^4.0.6", "@types/nanoid": "^3.0.0", "@types/node": "18.17.*", From 752ec044006762df4f283cf32cb123c5e17de9de Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Fri, 15 Mar 2024 12:27:26 +0000 Subject: [PATCH 0158/1237] Added error handling on webview message processing (#3470) --- .../src/common/vscode/abstract-webview.ts | 9 ++- .../ql-vscode/src/common/vscode/commands.ts | 57 +------------ .../src/common/vscode/error-handling.ts | 81 +++++++++++++++++++ 3 files changed, 93 insertions(+), 54 deletions(-) create mode 100644 extensions/ql-vscode/src/common/vscode/error-handling.ts diff --git a/extensions/ql-vscode/src/common/vscode/abstract-webview.ts b/extensions/ql-vscode/src/common/vscode/abstract-webview.ts index a1ac1c7cec0..c38590e4feb 100644 --- a/extensions/ql-vscode/src/common/vscode/abstract-webview.ts +++ b/extensions/ql-vscode/src/common/vscode/abstract-webview.ts @@ -13,6 +13,8 @@ import { tmpDir } from "../../tmp-dir"; import type { WebviewMessage, WebviewKind } from "./webview-html"; import { getHtmlForWebview } from "./webview-html"; import type { DeepReadonly } from "../readonly"; +import { runWithErrorHandling } from "./error-handling"; +import { telemetryListener } from "./telemetry"; export type WebviewPanelConfig = { viewId: string; @@ -117,7 +119,12 @@ export abstract class AbstractWebview< ); this.push( panel.webview.onDidReceiveMessage( - async (e) => this.onMessage(e), + async (e) => + runWithErrorHandling( + () => this.onMessage(e), + this.app.logger, + telemetryListener, + ), undefined, ), ); diff --git a/extensions/ql-vscode/src/common/vscode/commands.ts b/extensions/ql-vscode/src/common/vscode/commands.ts index 1c71aac5c00..be6d57ca3c9 100644 --- a/extensions/ql-vscode/src/common/vscode/commands.ts +++ b/extensions/ql-vscode/src/common/vscode/commands.ts @@ -3,18 +3,10 @@ import { commands } from "vscode"; import type { CommandFunction } from "../../packages/commands"; import { CommandManager } from "../../packages/commands"; import type { NotificationLogger } from "../logging"; -import { - showAndLogWarningMessage, - showAndLogExceptionWithTelemetry, -} from "../logging"; import { extLogger } from "../logging/vscode"; -import { asError, getErrorMessage } from "../../common/helpers-pure"; -import { redactableError } from "../../common/errors"; -import { UserCancellationException } from "./progress"; import { telemetryListener } from "./telemetry"; import type { AppTelemetry } from "../telemetry"; -import { CliError } from "../../codeql-cli/cli-errors"; -import { EOL } from "os"; +import { runWithErrorHandling } from "./error-handling"; /** * Create a command manager for VSCode, wrapping registerCommandWithErrorHandling @@ -48,50 +40,9 @@ export function registerCommandWithErrorHandling< logger: NotificationLogger = extLogger, telemetry: AppTelemetry | undefined = telemetryListener, ): Disposable { - return commands.registerCommand(commandId, async (...args: Parameters) => { - const startTime = Date.now(); - let error: Error | undefined; - - try { - return await task(...args); - } catch (e) { - error = asError(e); - const errorMessage = redactableError(error)`${ - getErrorMessage(e) || e - } (${commandId})`; - if (e instanceof UserCancellationException) { - // User has cancelled this action manually - if (e.silent) { - void logger.log(errorMessage.fullMessage); - } else { - void showAndLogWarningMessage(logger, errorMessage.fullMessage); - } - } else if (e instanceof CliError) { - const fullMessage = `${e.commandDescription} failed with args:${EOL} ${e.commandArgs.join(" ")}${EOL}${ - e.stderr ?? e.cause - }`; - void showAndLogExceptionWithTelemetry(logger, telemetry, errorMessage, { - fullMessage, - extraTelemetryProperties: { - command: commandId, - }, - }); - } else { - // Include the full stack in the error log only. - const fullMessage = errorMessage.fullMessageWithStack; - void showAndLogExceptionWithTelemetry(logger, telemetry, errorMessage, { - fullMessage, - extraTelemetryProperties: { - command: commandId, - }, - }); - } - return undefined; - } finally { - const executionTime = Date.now() - startTime; - telemetryListener?.sendCommandUsage(commandId, executionTime, error); - } - }); + return commands.registerCommand(commandId, async (...args: Parameters) => + runWithErrorHandling(task, logger, telemetry, commandId, ...args), + ); } /** diff --git a/extensions/ql-vscode/src/common/vscode/error-handling.ts b/extensions/ql-vscode/src/common/vscode/error-handling.ts new file mode 100644 index 00000000000..9074244742c --- /dev/null +++ b/extensions/ql-vscode/src/common/vscode/error-handling.ts @@ -0,0 +1,81 @@ +import { + showAndLogWarningMessage, + showAndLogExceptionWithTelemetry, +} from "../logging"; +import type { NotificationLogger } from "../logging"; +import { extLogger } from "../logging/vscode"; +import type { AppTelemetry } from "../telemetry"; +import { telemetryListener } from "./telemetry"; +import { asError, getErrorMessage } from "../helpers-pure"; +import { redactableError } from "../errors"; +import { UserCancellationException } from "./progress"; +import { CliError } from "../../codeql-cli/cli-errors"; +import { EOL } from "os"; + +/** + * Executes a task with error handling. It provides a uniform way to handle errors. + * + * @template T - A function type that takes an unknown number of arguments and returns a Promise. + * @param {T} task - The task to be executed. + * @param {NotificationLogger} [logger=extLogger] - The logger to use for error reporting. + * @param {AppTelemetry | undefined} [telemetry=telemetryListener] - The telemetry listener to use for error reporting. + * @param {string} [commandId] - The optional command id associated with the task. + * @param {...unknown} args - The arguments to be passed to the task. + * @returns {Promise} The result of the task, or undefined if an error occurred. + * @throws {Error} If an error occurs during the execution of the task. + */ +export async function runWithErrorHandling< + T extends (...args: unknown[]) => Promise, +>( + task: T, + logger: NotificationLogger = extLogger, + telemetry: AppTelemetry | undefined = telemetryListener, + commandId?: string, + ...args: unknown[] +): Promise { + const startTime = Date.now(); + let error: Error | undefined; + + try { + return await task(...args); + } catch (e) { + error = asError(e); + const errorMessage = redactableError(error)`${ + getErrorMessage(e) || e + }${commandId ? ` (${commandId})` : ""}`; + + const extraTelemetryProperties = commandId + ? { command: commandId } + : undefined; + + if (e instanceof UserCancellationException) { + // User has cancelled this action manually + if (e.silent) { + void logger.log(errorMessage.fullMessage); + } else { + void showAndLogWarningMessage(logger, errorMessage.fullMessage); + } + } else if (e instanceof CliError) { + const fullMessage = `${e.commandDescription} failed with args:${EOL} ${e.commandArgs.join(" ")}${EOL}${ + e.stderr ?? e.cause + }`; + void showAndLogExceptionWithTelemetry(logger, telemetry, errorMessage, { + fullMessage, + extraTelemetryProperties, + }); + } else { + // Include the full stack in the error log only. + const fullMessage = errorMessage.fullMessageWithStack; + void showAndLogExceptionWithTelemetry(logger, telemetry, errorMessage, { + fullMessage, + extraTelemetryProperties, + }); + } + return undefined; + } finally { + if (commandId) { + const executionTime = Date.now() - startTime; + telemetryListener?.sendCommandUsage(commandId, executionTime, error); + } + } +} From d57255152c77fb46b6680fdd51e2bb9b8c79057f Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Sun, 17 Mar 2024 11:25:05 -0700 Subject: [PATCH 0159/1237] Safely check for out of date databases When checking to re-import test databases, if the target database is missing or otherwise unavailable, avoid the check and just continue. This avoids a bug where a user would delete a test database and there would be an error when trying to run a query. --- .../src/databases/local-databases/database-manager.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/databases/local-databases/database-manager.ts b/extensions/ql-vscode/src/databases/local-databases/database-manager.ts index a1a7dc28be4..13e975a8743 100644 --- a/extensions/ql-vscode/src/databases/local-databases/database-manager.ts +++ b/extensions/ql-vscode/src/databases/local-databases/database-manager.ts @@ -227,8 +227,16 @@ export class DatabaseManager extends DisposableObject { "codeql-database.yml", ); + let originStat; + try { + originStat = await stat(originDbYml); + } catch (e) { + // if there is an error here, assume that the origin database + // is no longer available. Safely ignore and do not try to re-import. + return false; + } + try { - const originStat = await stat(originDbYml); const importedStat = await stat(importedDbYml); return originStat.mtimeMs > importedStat.mtimeMs; } catch (e) { From fe957db17aaeb3679ae3e29cb435d93c9674dd3d Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Sun, 17 Mar 2024 11:27:35 -0700 Subject: [PATCH 0160/1237] When importing from a directory, check for testproj Use the testproj style import when using the folder icon to import a database. This means that if the database directory ends with `testproj` copy the database into workspace storage and make it available for re-importing when the origin database changes. --- extensions/ql-vscode/src/databases/local-databases-ui.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 81edbda186e..3b6f8127162 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -995,14 +995,15 @@ export class DatabaseUI extends DisposableObject { return undefined; } - if (byFolder) { + if (byFolder && !uri.fsPath.endsWith("testproj")) { const fixedUri = await this.fixDbUri(uri); // we are selecting a database folder return await this.databaseManager.openDatabase(fixedUri, { type: "folder", }); } else { - // we are selecting a database archive. Must unzip into a workspace-controlled area + // we are selecting a database archive or a testproj. + // Unzip archives (if an archive) and copy into a workspace-controlled area // before importing. return await importLocalDatabase( this.app.commands, From 31cfaebb598afb144b833beba85fc7ddc9d81dea Mon Sep 17 00:00:00 2001 From: Andrew Eisenberg Date: Sun, 17 Mar 2024 11:51:58 -0700 Subject: [PATCH 0161/1237] Ensure database sources are zipped on re-import --- extensions/ql-vscode/src/databases/database-fetcher.ts | 4 +++- .../src/databases/local-databases/database-manager.ts | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/databases/database-fetcher.ts b/extensions/ql-vscode/src/databases/database-fetcher.ts index 995786dffa0..13f64e4f7fe 100644 --- a/extensions/ql-vscode/src/databases/database-fetcher.ts +++ b/extensions/ql-vscode/src/databases/database-fetcher.ts @@ -666,7 +666,9 @@ function isFile(databaseUrl: string) { * * @param databasePath The full path to the unzipped database */ -async function ensureZippedSourceLocation(databasePath: string): Promise { +export async function ensureZippedSourceLocation( + databasePath: string, +): Promise { const srcFolderPath = join(databasePath, "src"); const srcZipPath = `${srcFolderPath}.zip`; diff --git a/extensions/ql-vscode/src/databases/local-databases/database-manager.ts b/extensions/ql-vscode/src/databases/local-databases/database-manager.ts index 13e975a8743..f512e5f5df8 100644 --- a/extensions/ql-vscode/src/databases/local-databases/database-manager.ts +++ b/extensions/ql-vscode/src/databases/local-databases/database-manager.ts @@ -43,6 +43,7 @@ import { DatabaseResolver } from "./database-resolver"; import { telemetryListener } from "../../common/vscode/telemetry"; import type { LanguageContextStore } from "../../language-context-store"; import type { DatabaseOrigin } from "./database-origin"; +import { ensureZippedSourceLocation } from "../database-fetcher"; /** * The name of the key in the workspaceState dictionary in which we @@ -260,6 +261,7 @@ export class DatabaseManager extends DisposableObject { await this.removeDatabaseItem(dbItem); await copy(dbItem.origin.path, databaseUri.fsPath); + await ensureZippedSourceLocation(databaseUri.fsPath); const newDbItem = new DatabaseItemImpl(databaseUri, dbItem.contents, { dateAdded: Date.now(), language: dbItem.language, From 79d4e8cb78f5eb0692ebc6ef68b1a382f9c5d24e Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Mon, 18 Mar 2024 11:35:45 +0000 Subject: [PATCH 0162/1237] Wire up variant analysis updates to Model Alerts view (#3485) --- .../ql-vscode/src/common/interface-types.ts | 4 +- .../model-alerts/model-alerts-view.ts | 52 ++++++++++++++--- .../src/model-editor/model-evaluator.ts | 41 +++++++++---- .../variant-analysis-manager.ts | 19 +++++++ .../src/view/model-alerts/ModelAlerts.tsx | 57 +++++++++++++++++-- 5 files changed, 149 insertions(+), 24 deletions(-) diff --git a/extensions/ql-vscode/src/common/interface-types.ts b/extensions/ql-vscode/src/common/interface-types.ts index 1c0a09ffcad..4ce4653f6b9 100644 --- a/extensions/ql-vscode/src/common/interface-types.ts +++ b/extensions/ql-vscode/src/common/interface-types.ts @@ -739,7 +739,9 @@ interface OpenModelPackMessage { export type ToModelAlertsMessage = | SetModelAlertsViewStateMessage - | SetVariantAnalysisMessage; + | SetVariantAnalysisMessage + | SetRepoResultsMessage + | SetRepoStatesMessage; export type FromModelAlertsMessage = | CommonFromViewMessages diff --git a/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts b/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts index 1ac96ff459e..e7b8f41f1cf 100644 --- a/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts @@ -15,7 +15,11 @@ import type { ModelingEvents } from "../modeling-events"; import type { ModelingStore } from "../modeling-store"; import type { DatabaseItem } from "../../databases/local-databases"; import type { ExtensionPack } from "../shared/extension-pack"; -import type { VariantAnalysis } from "../../variant-analysis/shared/variant-analysis"; +import type { + VariantAnalysis, + VariantAnalysisScannedRepositoryResult, + VariantAnalysisScannedRepositoryState, +} from "../../variant-analysis/shared/variant-analysis"; export class ModelAlertsView extends AbstractWebview< ToModelAlertsMessage, @@ -35,17 +39,12 @@ export class ModelAlertsView extends AbstractWebview< this.registerToModelingEvents(); } - public async showView(variantAnalysis: VariantAnalysis) { + public async showView() { const panel = await this.getPanel(); panel.reveal(undefined, true); await this.waitForPanelLoaded(); await this.setViewState(); - - await this.postMessage({ - t: "setVariantAnalysis", - variantAnalysis, - }); } protected async getPanelConfig(): Promise { @@ -96,6 +95,45 @@ export class ModelAlertsView extends AbstractWebview< }); } + public async updateVariantAnalysis( + variantAnalysis: VariantAnalysis, + ): Promise { + if (!this.isShowingPanel) { + return; + } + + await this.postMessage({ + t: "setVariantAnalysis", + variantAnalysis, + }); + } + + public async updateRepoState( + repoState: VariantAnalysisScannedRepositoryState, + ): Promise { + if (!this.isShowingPanel) { + return; + } + + await this.postMessage({ + t: "setRepoStates", + repoStates: [repoState], + }); + } + + public async updateRepoResults( + repositoryResult: VariantAnalysisScannedRepositoryResult, + ): Promise { + if (!this.isShowingPanel) { + return; + } + + await this.postMessage({ + t: "setRepoResults", + repoResults: [repositoryResult], + }); + } + public async focusView(): Promise { this.panel?.reveal(); } diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts index 8b998733070..8e8761c5a5f 100644 --- a/extensions/ql-vscode/src/model-editor/model-evaluator.ts +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -27,6 +27,8 @@ export class ModelEvaluator extends DisposableObject { // submitted, we use the variant analysis manager's cancellation support. private cancellationSource: CancellationTokenSource; + private modelAlertsView: ModelAlertsView | undefined; + public constructor( private readonly app: App, private readonly cliServer: CodeQLCliServer, @@ -36,7 +38,7 @@ export class ModelEvaluator extends DisposableObject { private readonly dbItem: DatabaseItem, private readonly language: QueryLanguage, private readonly extensionPack: ExtensionPack, - private readonly updateView: ( + private readonly updateModelEditorView: ( run: ModelEvaluationRunState, ) => Promise, ) { @@ -117,18 +119,17 @@ export class ModelEvaluator extends DisposableObject { return; } else { this.modelingStore.updateIsModelAlertsViewOpen(this.dbItem, true); - const view = new ModelAlertsView( + this.modelAlertsView = new ModelAlertsView( this.app, this.modelingEvents, this.modelingStore, this.dbItem, this.extensionPack, ); + await this.modelAlertsView.showView(); // There should be a variant analysis available at this point, as the - // view can only opened when the variant analysis is complete. So we - // send this to the view. This is temporary until we have logic to - // listen to variant analysis updates and update the view accordingly. + // view can only opened when the variant analysis is submitted. const evaluationRun = this.modelingStore.getModelEvaluationRun( this.dbItem, ); @@ -143,7 +144,7 @@ export class ModelEvaluator extends DisposableObject { throw new Error("No variant analysis available"); } - await view.showView(variantAnalysis); + await this.modelAlertsView.updateVariantAnalysis(variantAnalysis); } } @@ -152,7 +153,7 @@ export class ModelEvaluator extends DisposableObject { this.modelingEvents.onModelEvaluationRunChanged(async (event) => { if (event.dbUri === this.dbItem.databaseUri.toString()) { if (!event.evaluationRun) { - await this.updateView({ + await this.updateModelEditorView({ isPreparing: false, variantAnalysis: undefined, }); @@ -164,7 +165,7 @@ export class ModelEvaluator extends DisposableObject { isPreparing: event.evaluationRun.isPreparing, variantAnalysis, }; - await this.updateView(run); + await this.updateModelEditorView(run); } } }), @@ -241,14 +242,34 @@ export class ModelEvaluator extends DisposableObject { this.variantAnalysisManager.onVariantAnalysisStatusUpdated( async (variantAnalysis) => { // Make sure it's the variant analysis we're interested in - if (variantAnalysisId === variantAnalysis.id) { - await this.updateView({ + if (variantAnalysis.id === variantAnalysisId) { + // Update model editor view + await this.updateModelEditorView({ isPreparing: false, variantAnalysis, }); + + // Update model alerts view + await this.modelAlertsView?.updateVariantAnalysis(variantAnalysis); } }, ), ); + + this.push( + this.variantAnalysisManager.onRepoStatesUpdated(async (e) => { + if (e.variantAnalysisId === variantAnalysisId) { + await this.modelAlertsView?.updateRepoState(e.repoState); + } + }), + ); + + this.push( + this.variantAnalysisManager.onRepoResultsLoaded(async (e) => { + if (e.variantAnalysisId === variantAnalysisId) { + await this.modelAlertsView?.updateRepoResults(e); + } + }), + ); } } diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 989e94344f9..184079d7f18 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -122,6 +122,21 @@ export class VariantAnalysisManager public readonly onVariantAnalysisRemoved = this._onVariantAnalysisRemoved.event; + private readonly _onRepoStateUpdated = this.push( + new EventEmitter<{ + variantAnalysisId: number; + repoState: VariantAnalysisScannedRepositoryState; + }>(), + ); + + public readonly onRepoStatesUpdated = this._onRepoStateUpdated.event; + + private readonly _onRepoResultsLoaded = this.push( + new EventEmitter(), + ); + + public readonly onRepoResultsLoaded = this._onRepoResultsLoaded.event; + private readonly variantAnalysisMonitor: VariantAnalysisMonitor; private readonly variantAnalyses = new Map(); private readonly views = new Map(); @@ -685,6 +700,8 @@ export class VariantAnalysisManager await this.getView( repositoryResult.variantAnalysisId, )?.sendRepositoryResults([repositoryResult]); + + this._onRepoResultsLoaded.fire(repositoryResult); } private async onRepoStateUpdated( @@ -700,6 +717,8 @@ export class VariantAnalysisManager } repoStates[repoState.repositoryId] = repoState; + + this._onRepoStateUpdated.fire({ variantAnalysisId, repoState }); } private async onDidChangeSessions( diff --git a/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx b/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx index 2b2eb1a1dd8..672f27a41fd 100644 --- a/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx +++ b/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx @@ -2,7 +2,11 @@ import { useCallback, useEffect, useState } from "react"; import { ModelAlertsHeader } from "./ModelAlertsHeader"; import type { ModelAlertsViewState } from "../../model-editor/shared/view-state"; import type { ToModelAlertsMessage } from "../../common/interface-types"; -import type { VariantAnalysis } from "../../variant-analysis/shared/variant-analysis"; +import type { + VariantAnalysis, + VariantAnalysisScannedRepositoryResult, + VariantAnalysisScannedRepositoryState, +} from "../../variant-analysis/shared/variant-analysis"; import { vscode } from "../vscode-api"; type Props = { @@ -24,6 +28,12 @@ export function ModelAlerts({ initialViewState }: Props): React.JSX.Element { const [variantAnalysis, setVariantAnalysis] = useState< VariantAnalysis | undefined >(undefined); + const [repoStates, setRepoStates] = useState< + VariantAnalysisScannedRepositoryState[] + >([]); + const [repoResults, setRepoResults] = useState< + VariantAnalysisScannedRepositoryResult[] + >([]); useEffect(() => { const listener = (evt: MessageEvent) => { @@ -36,6 +46,31 @@ export function ModelAlerts({ initialViewState }: Props): React.JSX.Element { } case "setVariantAnalysis": { setVariantAnalysis(msg.variantAnalysis); + break; + } + case "setRepoStates": { + setRepoStates((oldRepoStates) => { + const newRepoIds = msg.repoStates.map((r) => r.repositoryId); + return [ + ...oldRepoStates.filter( + (v) => !newRepoIds.includes(v.repositoryId), + ), + ...msg.repoStates, + ]; + }); + break; + } + case "setRepoResults": { + setRepoResults((oldRepoResults) => { + const newRepoIds = msg.repoResults.map((r) => r.repositoryId); + return [ + ...oldRepoResults.filter( + (v) => !newRepoIds.includes(v.repositoryId), + ), + ...msg.repoResults, + ]; + }); + break; } } } else { @@ -56,10 +91,20 @@ export function ModelAlerts({ initialViewState }: Props): React.JSX.Element { } return ( - + <> + +
    +

    Repo states

    +

    {JSON.stringify(repoStates, null, 2)}

    +
    +
    +

    Repo results

    +

    {JSON.stringify(repoResults, null, 2)}

    +
    + ); } From 46efb7c9716706bda2a72a7857ea163513a84ac8 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Mon, 18 Mar 2024 13:56:51 +0000 Subject: [PATCH 0163/1237] Center align model evaluation run link (#3488) --- .../src/view/model-editor/ModelEvaluation.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx index 2dd25cfe77d..de87fc663eb 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelEvaluation.tsx @@ -1,3 +1,4 @@ +import { styled } from "styled-components"; import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"; import type { ModeledMethod } from "../../model-editor/modeled-method"; import type { ModelEditorViewState } from "../../model-editor/shared/view-state"; @@ -16,6 +17,11 @@ export type Props = { evaluationRun: ModelEvaluationRunState | undefined; }; +const RunLink = styled(VSCodeLink)` + display: flex; + align-items: center; +`; + export const ModelEvaluation = ({ viewState, modeledMethods, @@ -61,12 +67,12 @@ export const ModelEvaluation = ({
    )} {shouldShowEvaluationRunLink && ( - + Evaluation run - + )} ); From 2d1c2349f869ef05cf26632e0ba3024b6f59bf38 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Mon, 18 Mar 2024 15:41:33 +0000 Subject: [PATCH 0164/1237] Add 'stop evaluation' action to Model Alerts view (#3487) --- .../ql-vscode/src/common/interface-types.ts | 7 ++- .../model-alerts/model-alerts-view.ts | 17 ++++++ .../src/model-editor/model-evaluator.ts | 4 ++ .../model-alerts/ModelAlerts.stories.tsx | 2 +- .../ModelAlertsHeader.stories.tsx | 52 +++++++++++++++++++ .../src/view/model-alerts/ModelAlerts.tsx | 7 +++ .../view/model-alerts/ModelAlertsActions.tsx | 39 ++++++++++++++ .../view/model-alerts/ModelAlertsHeader.tsx | 34 ++++++++++-- .../src/view/model-alerts/ModelPacks.tsx | 8 ++- .../shared/variant-analysis.ts | 4 ++ 10 files changed, 165 insertions(+), 9 deletions(-) create mode 100644 extensions/ql-vscode/src/stories/model-alerts/ModelAlertsHeader.stories.tsx create mode 100644 extensions/ql-vscode/src/view/model-alerts/ModelAlertsActions.tsx diff --git a/extensions/ql-vscode/src/common/interface-types.ts b/extensions/ql-vscode/src/common/interface-types.ts index 4ce4653f6b9..87e7e785ea3 100644 --- a/extensions/ql-vscode/src/common/interface-types.ts +++ b/extensions/ql-vscode/src/common/interface-types.ts @@ -737,6 +737,10 @@ interface OpenModelPackMessage { path: string; } +interface StopEvaluationRunMessage { + t: "stopEvaluationRun"; +} + export type ToModelAlertsMessage = | SetModelAlertsViewStateMessage | SetVariantAnalysisMessage @@ -745,4 +749,5 @@ export type ToModelAlertsMessage = export type FromModelAlertsMessage = | CommonFromViewMessages - | OpenModelPackMessage; + | OpenModelPackMessage + | StopEvaluationRunMessage; diff --git a/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts b/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts index e7b8f41f1cf..57e5c7decd5 100644 --- a/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts @@ -20,6 +20,7 @@ import type { VariantAnalysisScannedRepositoryResult, VariantAnalysisScannedRepositoryState, } from "../../variant-analysis/shared/variant-analysis"; +import type { AppEvent, AppEventEmitter } from "../../common/events"; export class ModelAlertsView extends AbstractWebview< ToModelAlertsMessage, @@ -27,6 +28,9 @@ export class ModelAlertsView extends AbstractWebview< > { public static readonly viewType = "codeQL.modelAlerts"; + public readonly onEvaluationRunStopClicked: AppEvent; + private readonly onEvaluationRunStopClickedEventEmitter: AppEventEmitter; + public constructor( app: App, private readonly modelingEvents: ModelingEvents, @@ -37,6 +41,12 @@ export class ModelAlertsView extends AbstractWebview< super(app); this.registerToModelingEvents(); + + this.onEvaluationRunStopClickedEventEmitter = this.push( + app.createEventEmitter(), + ); + this.onEvaluationRunStopClicked = + this.onEvaluationRunStopClickedEventEmitter.event; } public async showView() { @@ -81,6 +91,9 @@ export class ModelAlertsView extends AbstractWebview< case "openModelPack": await this.app.commands.execute("revealInExplorer", Uri.file(msg.path)); break; + case "stopEvaluationRun": + await this.stopEvaluationRun(); + break; default: assertNever(msg); } @@ -155,4 +168,8 @@ export class ModelAlertsView extends AbstractWebview< }), ); } + + private async stopEvaluationRun() { + this.onEvaluationRunStopClickedEventEmitter.fire(); + } } diff --git a/extensions/ql-vscode/src/model-editor/model-evaluator.ts b/extensions/ql-vscode/src/model-editor/model-evaluator.ts index 8e8761c5a5f..21e07a11e76 100644 --- a/extensions/ql-vscode/src/model-editor/model-evaluator.ts +++ b/extensions/ql-vscode/src/model-editor/model-evaluator.ts @@ -128,6 +128,10 @@ export class ModelEvaluator extends DisposableObject { ); await this.modelAlertsView.showView(); + this.modelAlertsView.onEvaluationRunStopClicked(async () => { + await this.stopEvaluation(); + }); + // There should be a variant analysis available at this point, as the // view can only opened when the variant analysis is submitted. const evaluationRun = this.modelingStore.getModelEvaluationRun( diff --git a/extensions/ql-vscode/src/stories/model-alerts/ModelAlerts.stories.tsx b/extensions/ql-vscode/src/stories/model-alerts/ModelAlerts.stories.tsx index b52c47d701b..6bdee3e1ba3 100644 --- a/extensions/ql-vscode/src/stories/model-alerts/ModelAlerts.stories.tsx +++ b/extensions/ql-vscode/src/stories/model-alerts/ModelAlerts.stories.tsx @@ -3,7 +3,7 @@ import type { Meta, StoryFn } from "@storybook/react"; import { ModelAlerts as ModelAlertsComponent } from "../../view/model-alerts/ModelAlerts"; export default { - title: "CodeQL Model Alerts/CodeQL Model Alerts", + title: "Model Alerts/Model Alerts", component: ModelAlertsComponent, } as Meta; diff --git a/extensions/ql-vscode/src/stories/model-alerts/ModelAlertsHeader.stories.tsx b/extensions/ql-vscode/src/stories/model-alerts/ModelAlertsHeader.stories.tsx new file mode 100644 index 00000000000..d110ed16d42 --- /dev/null +++ b/extensions/ql-vscode/src/stories/model-alerts/ModelAlertsHeader.stories.tsx @@ -0,0 +1,52 @@ +import type { Meta, StoryFn } from "@storybook/react"; + +import { ModelAlertsHeader as ModelAlertsHeaderComponent } from "../../view/model-alerts/ModelAlertsHeader"; +import { createMockVariantAnalysis } from "../../../test/factories/variant-analysis/shared/variant-analysis"; + +export default { + title: "Model Alerts/Model Alerts Header", + component: ModelAlertsHeaderComponent, + argTypes: { + openModelPackClick: { + action: "open-model-pack-clicked", + table: { + disable: true, + }, + }, + stopRunClick: { + action: "stop-run-clicked", + table: { + disable: true, + }, + }, + }, +} as Meta; + +const Template: StoryFn = (args) => ( + +); + +export const ModelAlertsHeader = Template.bind({}); +ModelAlertsHeader.args = { + viewState: { title: "codeql/sql2o-models" }, + variantAnalysis: createMockVariantAnalysis({ + modelPacks: [ + { + name: "Model pack 1", + path: "/path/to/model-pack-1", + }, + { + name: "Model pack 2", + path: "/path/to/model-pack-2", + }, + { + name: "Model pack 3", + path: "/path/to/model-pack-3", + }, + { + name: "Model pack 4", + path: "/path/to/model-pack-4", + }, + ], + }), +}; diff --git a/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx b/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx index 672f27a41fd..5d833387811 100644 --- a/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx +++ b/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx @@ -21,6 +21,12 @@ export function ModelAlerts({ initialViewState }: Props): React.JSX.Element { }); }, []); + const onStopRunClick = useCallback(() => { + vscode.postMessage({ + t: "stopEvaluationRun", + }); + }, []); + const [viewState, setViewState] = useState( initialViewState, ); @@ -96,6 +102,7 @@ export function ModelAlerts({ initialViewState }: Props): React.JSX.Element { viewState={viewState} variantAnalysis={variantAnalysis} openModelPackClick={onOpenModelPackClick} + stopRunClick={onStopRunClick} >

    Repo states

    diff --git a/extensions/ql-vscode/src/view/model-alerts/ModelAlertsActions.tsx b/extensions/ql-vscode/src/view/model-alerts/ModelAlertsActions.tsx new file mode 100644 index 00000000000..e99689634d2 --- /dev/null +++ b/extensions/ql-vscode/src/view/model-alerts/ModelAlertsActions.tsx @@ -0,0 +1,39 @@ +import { styled } from "styled-components"; +import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"; +import { VariantAnalysisStatus } from "../../variant-analysis/shared/variant-analysis"; + +type ModelAlertsActionsProps = { + variantAnalysisStatus: VariantAnalysisStatus; + + onStopRunClick: () => void; +}; + +const Container = styled.div` + margin-left: auto; + display: flex; + gap: 1em; +`; + +const Button = styled(VSCodeButton)` + white-space: nowrap; +`; + +export const ModelAlertsActions = ({ + variantAnalysisStatus, + onStopRunClick, +}: ModelAlertsActionsProps) => { + return ( + + {variantAnalysisStatus === VariantAnalysisStatus.InProgress && ( + + )} + {variantAnalysisStatus === VariantAnalysisStatus.Canceling && ( + + )} + + ); +}; diff --git a/extensions/ql-vscode/src/view/model-alerts/ModelAlertsHeader.tsx b/extensions/ql-vscode/src/view/model-alerts/ModelAlertsHeader.tsx index 4dad006b003..097ba05e814 100644 --- a/extensions/ql-vscode/src/view/model-alerts/ModelAlertsHeader.tsx +++ b/extensions/ql-vscode/src/view/model-alerts/ModelAlertsHeader.tsx @@ -1,26 +1,50 @@ +import { styled } from "styled-components"; import type { ModelAlertsViewState } from "../../model-editor/shared/view-state"; import type { VariantAnalysis } from "../../variant-analysis/shared/variant-analysis"; import { ViewTitle } from "../common"; +import { ModelAlertsActions } from "./ModelAlertsActions"; import { ModelPacks } from "./ModelPacks"; type Props = { viewState: ModelAlertsViewState; variantAnalysis: VariantAnalysis; openModelPackClick: (path: string) => void; + stopRunClick: () => void; }; +const Container = styled.div` + display: flex; + flex-direction: column; +`; + +const Row = styled.div` + display: flex; + align-items: flex-start; +`; + export const ModelAlertsHeader = ({ viewState, variantAnalysis, openModelPackClick, + stopRunClick, }: Props) => { return ( <> - Model evaluation results for {viewState.title} - + + + Model evaluation results for {viewState.title} + + + + + + ); }; diff --git a/extensions/ql-vscode/src/view/model-alerts/ModelPacks.tsx b/extensions/ql-vscode/src/view/model-alerts/ModelPacks.tsx index 89e65664b86..13a8c0d0505 100644 --- a/extensions/ql-vscode/src/view/model-alerts/ModelPacks.tsx +++ b/extensions/ql-vscode/src/view/model-alerts/ModelPacks.tsx @@ -2,6 +2,10 @@ import { styled } from "styled-components"; import { LinkIconButton } from "../common/LinkIconButton"; import type { ModelPackDetails } from "../../common/model-pack-details"; +const Container = styled.div` + display: block; +`; + const Title = styled.h3` font-size: medium; font-weight: 500; @@ -26,7 +30,7 @@ export const ModelPacks = ({ } return ( - <> + Model packs {modelPacks.map((modelPack) => ( @@ -38,6 +42,6 @@ export const ModelPacks = ({ ))} - + ); }; diff --git a/extensions/ql-vscode/test/factories/variant-analysis/shared/variant-analysis.ts b/extensions/ql-vscode/test/factories/variant-analysis/shared/variant-analysis.ts index 4ccde9eac83..82e4da11305 100644 --- a/extensions/ql-vscode/test/factories/variant-analysis/shared/variant-analysis.ts +++ b/extensions/ql-vscode/test/factories/variant-analysis/shared/variant-analysis.ts @@ -9,6 +9,7 @@ import { createMockScannedRepos } from "./scanned-repositories"; import { createMockSkippedRepos } from "./skipped-repositories"; import { createMockRepository } from "./repository"; import { QueryLanguage } from "../../../../src/common/query-language"; +import type { ModelPackDetails } from "../../../../src/common/model-pack-details"; export function createMockVariantAnalysis({ status = VariantAnalysisStatus.InProgress, @@ -16,12 +17,14 @@ export function createMockVariantAnalysis({ skippedRepos = createMockSkippedRepos(), executionStartTime = faker.number.int(), language = QueryLanguage.Javascript, + modelPacks = undefined, }: { status?: VariantAnalysisStatus; scannedRepos?: VariantAnalysisScannedRepository[]; skippedRepos?: VariantAnalysisSkippedRepositories; executionStartTime?: number | undefined; language?: QueryLanguage; + modelPacks?: ModelPackDetails[] | undefined; }): VariantAnalysis { return { id: faker.number.int(), @@ -37,6 +40,7 @@ export function createMockVariantAnalysis({ filePath: "a-query-file-path", text: "a-query-text", }, + modelPacks, databases: { repositories: ["1", "2", "3"], }, From 3f817b3c0f202367e1a20b4d4cbf9bcd05903535 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Mon, 18 Mar 2024 15:57:21 +0000 Subject: [PATCH 0165/1237] Don't show notification after local query cancellation (#3489) * Don't show notification after local query cancellation * Update CHANGELOG --- extensions/ql-vscode/CHANGELOG.md | 1 + .../ql-vscode/src/local-queries/local-queries.ts | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 1e00852a730..680dbb3f20c 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -2,6 +2,7 @@ ## [UNRELEASED] +- Don't show notification after local query cancellation. [#3489](https://github.com/github/vscode-codeql/pull/3489) - Databases created from [CodeQL test cases](https://docs.github.com/en/code-security/codeql-cli/using-the-advanced-functionality-of-the-codeql-cli/testing-custom-queries) are now copied into a shared VS Code storage location. This avoids a bug where re-running test cases would fail if the test's database is already imported into the workspace. [#3433](https://github.com/github/vscode-codeql/pull/3433) ## 1.12.3 - 29 February 2024 diff --git a/extensions/ql-vscode/src/local-queries/local-queries.ts b/extensions/ql-vscode/src/local-queries/local-queries.ts index c1a5dac785c..54dd4beffe3 100644 --- a/extensions/ql-vscode/src/local-queries/local-queries.ts +++ b/extensions/ql-vscode/src/local-queries/local-queries.ts @@ -2,7 +2,10 @@ import type { ProgressCallback, ProgressUpdate, } from "../common/vscode/progress"; -import { withProgress } from "../common/vscode/progress"; +import { + UserCancellationException, + withProgress, +} from "../common/vscode/progress"; import type { CancellationToken, Range, TabInputText } from "vscode"; import { CancellationTokenSource, Uri, window } from "vscode"; import { @@ -492,7 +495,12 @@ export class LocalQueries extends DisposableObject { // to unify both error handling paths. const err = asError(e); await localQueryRun.fail(err); - throw e; + + if (token.isCancellationRequested) { + throw new UserCancellationException(err.message, true); + } else { + throw e; + } } } finally { source.dispose(); From 724370fb40ba949a1983f0ddff9e82c1e39115b5 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Tue, 19 Mar 2024 16:07:39 +0000 Subject: [PATCH 0166/1237] Fix ModelAlerts view stories (#3490) --- .../model-alerts/ModelAlerts.stories.tsx | 13 +++++++++++ .../src/view/model-alerts/ModelAlerts.tsx | 22 ++++++++++++------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/extensions/ql-vscode/src/stories/model-alerts/ModelAlerts.stories.tsx b/extensions/ql-vscode/src/stories/model-alerts/ModelAlerts.stories.tsx index 6bdee3e1ba3..a732e65a464 100644 --- a/extensions/ql-vscode/src/stories/model-alerts/ModelAlerts.stories.tsx +++ b/extensions/ql-vscode/src/stories/model-alerts/ModelAlerts.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryFn } from "@storybook/react"; import { ModelAlerts as ModelAlertsComponent } from "../../view/model-alerts/ModelAlerts"; +import { createMockVariantAnalysis } from "../../../test/factories/variant-analysis/shared/variant-analysis"; export default { title: "Model Alerts/Model Alerts", @@ -14,4 +15,16 @@ const Template: StoryFn = (args) => ( export const ModelAlerts = Template.bind({}); ModelAlerts.args = { initialViewState: { title: "codeql/sql2o-models" }, + variantAnalysis: createMockVariantAnalysis({ + modelPacks: [ + { + name: "Model pack 1", + path: "/path/to/model-pack-1", + }, + { + name: "Model pack 2", + path: "/path/to/model-pack-2", + }, + ], + }), }; diff --git a/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx b/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx index 5d833387811..c7ee32c82f4 100644 --- a/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx +++ b/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx @@ -11,9 +11,17 @@ import { vscode } from "../vscode-api"; type Props = { initialViewState?: ModelAlertsViewState; + variantAnalysis?: VariantAnalysis; + repoStates?: VariantAnalysisScannedRepositoryState[]; + repoResults?: VariantAnalysisScannedRepositoryResult[]; }; -export function ModelAlerts({ initialViewState }: Props): React.JSX.Element { +export function ModelAlerts({ + initialViewState, + variantAnalysis: initialVariantAnalysis, + repoStates: initialRepoStates = [], + repoResults: initialRepoResults = [], +}: Props): React.JSX.Element { const onOpenModelPackClick = useCallback((path: string) => { vscode.postMessage({ t: "openModelPack", @@ -33,13 +41,11 @@ export function ModelAlerts({ initialViewState }: Props): React.JSX.Element { const [variantAnalysis, setVariantAnalysis] = useState< VariantAnalysis | undefined - >(undefined); - const [repoStates, setRepoStates] = useState< - VariantAnalysisScannedRepositoryState[] - >([]); - const [repoResults, setRepoResults] = useState< - VariantAnalysisScannedRepositoryResult[] - >([]); + >(initialVariantAnalysis); + const [repoStates, setRepoStates] = + useState(initialRepoStates); + const [repoResults, setRepoResults] = + useState(initialRepoResults); useEffect(() => { const listener = (evt: MessageEvent) => { From 3519139e68e42d7ea7795cc5eddb658d91842ace Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Wed, 20 Mar 2024 09:03:13 +0000 Subject: [PATCH 0167/1237] Update MethodName component to receive specific props (#3491) --- .../src/view/model-editor/MethodName.tsx | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/extensions/ql-vscode/src/view/model-editor/MethodName.tsx b/extensions/ql-vscode/src/view/model-editor/MethodName.tsx index bf189239472..76e0ef51479 100644 --- a/extensions/ql-vscode/src/view/model-editor/MethodName.tsx +++ b/extensions/ql-vscode/src/view/model-editor/MethodName.tsx @@ -1,33 +1,48 @@ import { styled } from "styled-components"; -import type { Method } from "../../model-editor/method"; const Name = styled.span` font-family: var(--vscode-editor-font-family); word-break: break-all; `; -const TypeMethodName = (method: Method) => { - if (!method.typeName) { - return <>{method.methodName}; +const TypeMethodName = ({ + typeName, + methodName, +}: { + typeName?: string; + methodName?: string; +}) => { + if (!typeName) { + return <>{methodName}; } - if (!method.methodName) { - return <>{method.typeName}; + if (!methodName) { + return <>{typeName}; } return ( <> - {method.typeName}.{method.methodName} + {typeName}.{methodName} ); }; -export const MethodName = (method: Method): React.JSX.Element => { +export const MethodName = ({ + packageName, + typeName, + methodName, + methodParameters, +}: { + packageName: string; + typeName?: string; + methodName?: string; + methodParameters?: string; +}): React.JSX.Element => { return ( - {method.packageName && <>{method.packageName}.} - - {method.methodParameters} + {packageName && <>{packageName}.} + + {methodParameters} ); }; From 876af669cb6410463e898f880de241709d170bd0 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 20 Mar 2024 10:28:24 +0100 Subject: [PATCH 0168/1237] v1.12.4 --- extensions/ql-vscode/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 680dbb3f20c..6a7e2cce376 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -1,6 +1,6 @@ # CodeQL for Visual Studio Code: Changelog -## [UNRELEASED] +## 1.12.4 - 20 March 2024 - Don't show notification after local query cancellation. [#3489](https://github.com/github/vscode-codeql/pull/3489) - Databases created from [CodeQL test cases](https://docs.github.com/en/code-security/codeql-cli/using-the-advanced-functionality-of-the-codeql-cli/testing-custom-queries) are now copied into a shared VS Code storage location. This avoids a bug where re-running test cases would fail if the test's database is already imported into the workspace. [#3433](https://github.com/github/vscode-codeql/pull/3433) From 36236680a4b580ba3758a85143d1382b53c279ec Mon Sep 17 00:00:00 2001 From: Shati Patel <42641846+shati-patel@users.noreply.github.com> Date: Wed, 20 Mar 2024 10:14:39 +0000 Subject: [PATCH 0169/1237] Add model alerts stats to the view (#3481) --- .../ql-vscode/src/common/interface-types.ts | 6 +++ .../model-alerts/model-alerts-view.ts | 6 +++ .../ModelAlertsHeader.stories.tsx | 6 +++ .../src/view/model-alerts/ModelAlerts.tsx | 13 +++++- .../view/model-alerts/ModelAlertsHeader.tsx | 44 +++++++++++++++++++ 5 files changed, 74 insertions(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/common/interface-types.ts b/extensions/ql-vscode/src/common/interface-types.ts index 87e7e785ea3..30d11c7b0f7 100644 --- a/extensions/ql-vscode/src/common/interface-types.ts +++ b/extensions/ql-vscode/src/common/interface-types.ts @@ -737,6 +737,11 @@ interface OpenModelPackMessage { path: string; } +interface OpenActionsLogsMessage { + t: "openActionsLogs"; + variantAnalysisId: number; +} + interface StopEvaluationRunMessage { t: "stopEvaluationRun"; } @@ -750,4 +755,5 @@ export type ToModelAlertsMessage = export type FromModelAlertsMessage = | CommonFromViewMessages | OpenModelPackMessage + | OpenActionsLogsMessage | StopEvaluationRunMessage; diff --git a/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts b/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts index 57e5c7decd5..098bf4b263a 100644 --- a/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts +++ b/extensions/ql-vscode/src/model-editor/model-alerts/model-alerts-view.ts @@ -91,6 +91,12 @@ export class ModelAlertsView extends AbstractWebview< case "openModelPack": await this.app.commands.execute("revealInExplorer", Uri.file(msg.path)); break; + case "openActionsLogs": + await this.app.commands.execute( + "codeQL.openVariantAnalysisLogs", + msg.variantAnalysisId, + ); + break; case "stopEvaluationRun": await this.stopEvaluationRun(); break; diff --git a/extensions/ql-vscode/src/stories/model-alerts/ModelAlertsHeader.stories.tsx b/extensions/ql-vscode/src/stories/model-alerts/ModelAlertsHeader.stories.tsx index d110ed16d42..1dbd2bb0e88 100644 --- a/extensions/ql-vscode/src/stories/model-alerts/ModelAlertsHeader.stories.tsx +++ b/extensions/ql-vscode/src/stories/model-alerts/ModelAlertsHeader.stories.tsx @@ -13,6 +13,12 @@ export default { disable: true, }, }, + onViewLogsClick: { + action: "view-logs-clicked", + table: { + disable: true, + }, + }, stopRunClick: { action: "stop-run-clicked", table: { diff --git a/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx b/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx index c7ee32c82f4..b327b76974b 100644 --- a/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx +++ b/extensions/ql-vscode/src/view/model-alerts/ModelAlerts.tsx @@ -1,5 +1,4 @@ import { useCallback, useEffect, useState } from "react"; -import { ModelAlertsHeader } from "./ModelAlertsHeader"; import type { ModelAlertsViewState } from "../../model-editor/shared/view-state"; import type { ToModelAlertsMessage } from "../../common/interface-types"; import type { @@ -8,6 +7,7 @@ import type { VariantAnalysisScannedRepositoryState, } from "../../variant-analysis/shared/variant-analysis"; import { vscode } from "../vscode-api"; +import { ModelAlertsHeader } from "./ModelAlertsHeader"; type Props = { initialViewState?: ModelAlertsViewState; @@ -102,12 +102,23 @@ export function ModelAlerts({ return <>; } + const openLogs = () => { + vscode.postMessage({ + t: "openActionsLogs", + variantAnalysisId: variantAnalysis.id, + }); + }; + + const onViewLogsClick = + variantAnalysis.actionsWorkflowRunId === undefined ? undefined : openLogs; + return ( <>
    diff --git a/extensions/ql-vscode/src/view/model-alerts/ModelAlertsHeader.tsx b/extensions/ql-vscode/src/view/model-alerts/ModelAlertsHeader.tsx index 097ba05e814..0e36431ed04 100644 --- a/extensions/ql-vscode/src/view/model-alerts/ModelAlertsHeader.tsx +++ b/extensions/ql-vscode/src/view/model-alerts/ModelAlertsHeader.tsx @@ -1,14 +1,24 @@ +import { useMemo } from "react"; +import { parseDate } from "../../common/date"; import { styled } from "styled-components"; import type { ModelAlertsViewState } from "../../model-editor/shared/view-state"; +import { + getSkippedRepoCount, + getTotalResultCount, + hasRepoScanCompleted, + isRepoScanSuccessful, +} from "../../variant-analysis/shared/variant-analysis"; import type { VariantAnalysis } from "../../variant-analysis/shared/variant-analysis"; import { ViewTitle } from "../common"; import { ModelAlertsActions } from "./ModelAlertsActions"; import { ModelPacks } from "./ModelPacks"; +import { VariantAnalysisStats } from "../variant-analysis/VariantAnalysisStats"; type Props = { viewState: ModelAlertsViewState; variantAnalysis: VariantAnalysis; openModelPackClick: (path: string) => void; + onViewLogsClick?: () => void; stopRunClick: () => void; }; @@ -26,8 +36,31 @@ export const ModelAlertsHeader = ({ viewState, variantAnalysis, openModelPackClick, + onViewLogsClick, stopRunClick, }: Props) => { + const totalScannedRepositoryCount = useMemo(() => { + return variantAnalysis.scannedRepos?.length ?? 0; + }, [variantAnalysis.scannedRepos]); + const completedRepositoryCount = useMemo(() => { + return ( + variantAnalysis.scannedRepos?.filter((repo) => hasRepoScanCompleted(repo)) + ?.length ?? 0 + ); + }, [variantAnalysis.scannedRepos]); + const successfulRepositoryCount = useMemo(() => { + return ( + variantAnalysis.scannedRepos?.filter((repo) => isRepoScanSuccessful(repo)) + ?.length ?? 0 + ); + }, [variantAnalysis.scannedRepos]); + const resultCount = useMemo(() => { + return getTotalResultCount(variantAnalysis.scannedRepos); + }, [variantAnalysis.scannedRepos]); + const skippedRepositoryCount = useMemo(() => { + return getSkippedRepoCount(variantAnalysis.skippedRepos); + }, [variantAnalysis.skippedRepos]); + return ( <> @@ -44,6 +77,17 @@ export const ModelAlertsHeader = ({ onStopRunClick={stopRunClick} /> + ); From 1982ad45b86e1261a2a9bd22c7f06f882c6ef3ff Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 20 Mar 2024 10:54:17 +0000 Subject: [PATCH 0170/1237] Bump version to v1.12.5 --- extensions/ql-vscode/CHANGELOG.md | 2 ++ extensions/ql-vscode/package-lock.json | 4 ++-- extensions/ql-vscode/package.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 6a7e2cce376..ef082262b42 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -1,5 +1,7 @@ # CodeQL for Visual Studio Code: Changelog +## [UNRELEASED] + ## 1.12.4 - 20 March 2024 - Don't show notification after local query cancellation. [#3489](https://github.com/github/vscode-codeql/pull/3489) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index d58b1adb58e..b670122056c 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-codeql", - "version": "1.12.4", + "version": "1.12.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscode-codeql", - "version": "1.12.4", + "version": "1.12.5", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 3320605da44..6acbcaad2ed 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -4,7 +4,7 @@ "description": "CodeQL for Visual Studio Code", "author": "GitHub", "private": true, - "version": "1.12.4", + "version": "1.12.5", "publisher": "GitHub", "license": "MIT", "icon": "media/VS-marketplace-CodeQL-icon.png", From ee4b13c9e95ebec9df2d950ff25b06a2e9957509 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 20 Mar 2024 12:06:08 +0100 Subject: [PATCH 0171/1237] Fix deprecation warning --- .../ql-vscode/.storybook/vscode-theme-addon/ThemeSelector.tsx | 4 ++-- extensions/ql-vscode/package-lock.json | 2 +- extensions/ql-vscode/package.json | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/extensions/ql-vscode/.storybook/vscode-theme-addon/ThemeSelector.tsx b/extensions/ql-vscode/.storybook/vscode-theme-addon/ThemeSelector.tsx index d37bb4c2e0e..71df8b27100 100644 --- a/extensions/ql-vscode/.storybook/vscode-theme-addon/ThemeSelector.tsx +++ b/extensions/ql-vscode/.storybook/vscode-theme-addon/ThemeSelector.tsx @@ -5,10 +5,10 @@ import { useCallback } from "react"; import { useGlobals } from "@storybook/manager-api"; import { IconButton, - Icons, TooltipLinkList, WithTooltip, } from "@storybook/components"; +import { DashboardIcon } from "@storybook/icons"; import { themeNames, VSCodeTheme } from "./theme"; @@ -53,7 +53,7 @@ export const ThemeSelector: FunctionComponent = () => { title="Change the theme of the preview" active={vscodeTheme !== VSCodeTheme.Dark} > - + ); diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 1a374bffaf8..3708cffdea5 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -13,6 +13,7 @@ "@floating-ui/react": "^0.26.9", "@octokit/plugin-retry": "^6.0.1", "@octokit/rest": "^20.0.2", + "@storybook/icons": "^1.2.9", "@vscode/codicons": "^0.0.35", "@vscode/debugadapter": "^1.59.0", "@vscode/debugprotocol": "^1.59.0", @@ -5480,7 +5481,6 @@ "version": "1.2.9", "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-1.2.9.tgz", "integrity": "sha512-cOmylsz25SYXaJL/gvTk/dl3pyk7yBFRfeXTsHvTA3dfhoU/LWSq0NKL9nM7WBasJyn6XPSGnLS4RtKXLw5EUg==", - "dev": true, "engines": { "node": ">=14.0.0" }, diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index efe691d67a2..01a9ac97f2c 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1954,6 +1954,7 @@ "@floating-ui/react": "^0.26.9", "@octokit/plugin-retry": "^6.0.1", "@octokit/rest": "^20.0.2", + "@storybook/icons": "^1.2.9", "@vscode/codicons": "^0.0.35", "@vscode/debugadapter": "^1.59.0", "@vscode/debugprotocol": "^1.59.0", From c553b1cb1d3336e4a41084a650339d113e8b5643 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 20 Mar 2024 12:08:36 +0100 Subject: [PATCH 0172/1237] Run storybook migrate mdx-to-csf --- extensions/ql-vscode/.storybook/main.ts | 2 +- extensions/ql-vscode/package-lock.json | 470 +++++++++++++++++- extensions/ql-vscode/package.json | 1 + .../{Overview.stories.mdx => Overview.mdx} | 14 +- 4 files changed, 470 insertions(+), 17 deletions(-) rename extensions/ql-vscode/src/stories/{Overview.stories.mdx => Overview.mdx} (92%) diff --git a/extensions/ql-vscode/.storybook/main.ts b/extensions/ql-vscode/.storybook/main.ts index 9cceeeb5e22..9b7cf72cfa6 100644 --- a/extensions/ql-vscode/.storybook/main.ts +++ b/extensions/ql-vscode/.storybook/main.ts @@ -1,7 +1,7 @@ import type { StorybookConfig } from "@storybook/react-webpack5"; const config: StorybookConfig = { - stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], + stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], addons: [ "@storybook/addon-links", "@storybook/addon-essentials", diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 3708cffdea5..7ac19f51c2d 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -60,6 +60,7 @@ "@storybook/addon-essentials": "^8.0.0", "@storybook/addon-interactions": "^8.0.0", "@storybook/addon-links": "^8.0.0", + "@storybook/blocks": "^8.0.2", "@storybook/components": "^8.0.0", "@storybook/csf": "^0.1.1", "@storybook/manager-api": "^8.0.0", @@ -4587,6 +4588,54 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/addon-controls/node_modules/@storybook/blocks": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.0.0.tgz", + "integrity": "sha512-Sxy7pOa6B3ci/XhfKca6u97Kz6pGZV5ieQBUWRYByUZTjiOp12RVLFptexxrJHyNBA00BHJPek4fvFSJfn6nOQ==", + "dev": true, + "dependencies": { + "@storybook/channels": "8.0.0", + "@storybook/client-logger": "8.0.0", + "@storybook/components": "8.0.0", + "@storybook/core-events": "8.0.0", + "@storybook/csf": "^0.1.2", + "@storybook/docs-tools": "8.0.0", + "@storybook/global": "^5.0.0", + "@storybook/icons": "^1.2.5", + "@storybook/manager-api": "8.0.0", + "@storybook/preview-api": "8.0.0", + "@storybook/theming": "8.0.0", + "@storybook/types": "8.0.0", + "@types/lodash": "^4.14.167", + "color-convert": "^2.0.1", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "markdown-to-jsx": "7.3.2", + "memoizerific": "^1.11.3", + "polished": "^4.2.2", + "react-colorful": "^5.1.2", + "telejson": "^7.2.0", + "tocbot": "^4.20.1", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/@storybook/addon-docs": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.0.0.tgz", @@ -4619,6 +4668,54 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/addon-docs/node_modules/@storybook/blocks": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.0.0.tgz", + "integrity": "sha512-Sxy7pOa6B3ci/XhfKca6u97Kz6pGZV5ieQBUWRYByUZTjiOp12RVLFptexxrJHyNBA00BHJPek4fvFSJfn6nOQ==", + "dev": true, + "dependencies": { + "@storybook/channels": "8.0.0", + "@storybook/client-logger": "8.0.0", + "@storybook/components": "8.0.0", + "@storybook/core-events": "8.0.0", + "@storybook/csf": "^0.1.2", + "@storybook/docs-tools": "8.0.0", + "@storybook/global": "^5.0.0", + "@storybook/icons": "^1.2.5", + "@storybook/manager-api": "8.0.0", + "@storybook/preview-api": "8.0.0", + "@storybook/theming": "8.0.0", + "@storybook/types": "8.0.0", + "@types/lodash": "^4.14.167", + "color-convert": "^2.0.1", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "markdown-to-jsx": "7.3.2", + "memoizerific": "^1.11.3", + "polished": "^4.2.2", + "react-colorful": "^5.1.2", + "telejson": "^7.2.0", + "tocbot": "^4.20.1", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/@storybook/addon-essentials": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.0.0.tgz", @@ -4750,23 +4847,23 @@ } }, "node_modules/@storybook/blocks": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.0.0.tgz", - "integrity": "sha512-Sxy7pOa6B3ci/XhfKca6u97Kz6pGZV5ieQBUWRYByUZTjiOp12RVLFptexxrJHyNBA00BHJPek4fvFSJfn6nOQ==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.0.2.tgz", + "integrity": "sha512-nVOyQV/d+MpfAXLadbTuxkyUa9rco6EjMW3eb49JrfmenViNlZ+YYcO1J0zHXvM3GYYNP5yBXXhiGVg0uM8trA==", "dev": true, "dependencies": { - "@storybook/channels": "8.0.0", - "@storybook/client-logger": "8.0.0", - "@storybook/components": "8.0.0", - "@storybook/core-events": "8.0.0", + "@storybook/channels": "8.0.2", + "@storybook/client-logger": "8.0.2", + "@storybook/components": "8.0.2", + "@storybook/core-events": "8.0.2", "@storybook/csf": "^0.1.2", - "@storybook/docs-tools": "8.0.0", + "@storybook/docs-tools": "8.0.2", "@storybook/global": "^5.0.0", "@storybook/icons": "^1.2.5", - "@storybook/manager-api": "8.0.0", - "@storybook/preview-api": "8.0.0", - "@storybook/theming": "8.0.0", - "@storybook/types": "8.0.0", + "@storybook/manager-api": "8.0.2", + "@storybook/preview-api": "8.0.2", + "@storybook/theming": "8.0.2", + "@storybook/types": "8.0.2", "@types/lodash": "^4.14.167", "color-convert": "^2.0.1", "dequal": "^2.0.2", @@ -4797,6 +4894,355 @@ } } }, + "node_modules/@storybook/blocks/node_modules/@storybook/channels": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-8.0.2.tgz", + "integrity": "sha512-r7TMUlALWc8sTXzyRZ1wSngvDWGhRLfhU9VJ0ouMyk2oSNEgcKBGvq7FkMmHINKHr3gte9+Ab0iG7TAoQ7pPsg==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "8.0.2", + "@storybook/core-events": "8.0.2", + "@storybook/global": "^5.0.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/blocks/node_modules/@storybook/client-logger": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-8.0.2.tgz", + "integrity": "sha512-/GvjkCHk5LyiJ0EzoJ3kV+tqCGVarxYSnhD8ciszbWBUH4ZX104So+uZjwwGKCEZxh17HLppQa5bzOayGcdRDg==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/blocks/node_modules/@storybook/components": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.0.2.tgz", + "integrity": "sha512-U/mm/cVL9NSM0pFYiZv7BS9U8KpZ0e9RkB45nKOIKzrtDBfec3cv9U3zIvYeIh3jQXusVZtjt9X9qhIoJkWl+w==", + "dev": true, + "dependencies": { + "@radix-ui/react-slot": "^1.0.2", + "@storybook/client-logger": "8.0.2", + "@storybook/csf": "^0.1.2", + "@storybook/global": "^5.0.0", + "@storybook/icons": "^1.2.5", + "@storybook/theming": "8.0.2", + "@storybook/types": "8.0.2", + "memoizerific": "^1.11.3", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/blocks/node_modules/@storybook/core-common": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-8.0.2.tgz", + "integrity": "sha512-0LkQn2dCVzFepLqqlt82ouIuc11UCsDzPtRVHp4p18JA0xs2dmD6d8vJUfEAYAgoeEaH3bFjb57IhMbYT5adhw==", + "dev": true, + "dependencies": { + "@storybook/core-events": "8.0.2", + "@storybook/csf-tools": "8.0.2", + "@storybook/node-logger": "8.0.2", + "@storybook/types": "8.0.2", + "@yarnpkg/fslib": "2.10.3", + "@yarnpkg/libzip": "2.3.0", + "chalk": "^4.1.0", + "cross-spawn": "^7.0.3", + "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0", + "esbuild-register": "^3.5.0", + "execa": "^5.0.0", + "file-system-cache": "2.3.0", + "find-cache-dir": "^3.0.0", + "find-up": "^5.0.0", + "fs-extra": "^11.1.0", + "glob": "^10.0.0", + "handlebars": "^4.7.7", + "lazy-universal-dotenv": "^4.0.0", + "node-fetch": "^2.0.0", + "picomatch": "^2.3.0", + "pkg-dir": "^5.0.0", + "pretty-hrtime": "^1.0.3", + "resolve-from": "^5.0.0", + "semver": "^7.3.7", + "tempy": "^1.0.1", + "tiny-invariant": "^1.3.1", + "ts-dedent": "^2.0.0", + "util": "^0.12.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/blocks/node_modules/@storybook/core-events": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-8.0.2.tgz", + "integrity": "sha512-1rtecdU3eyWGMT3U27ldF6ApdakvmmcS8E+1PqLGd5K9v5T0W82n+QyXft3kb434N8KYSwNFf08NfrU0VZeC4w==", + "dev": true, + "dependencies": { + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/blocks/node_modules/@storybook/csf-tools": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-8.0.2.tgz", + "integrity": "sha512-NZ7aYPslaCxciq2lKA5q4YsQYtIb7AeYdYqgjuVPdlwkqBuyeiym1OP7wF1X0iFwZVG3/UogqBCALnKQmROo2A==", + "dev": true, + "dependencies": { + "@babel/generator": "^7.23.0", + "@babel/parser": "^7.23.0", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0", + "@storybook/csf": "^0.1.2", + "@storybook/types": "8.0.2", + "fs-extra": "^11.1.0", + "recast": "^0.23.5", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/blocks/node_modules/@storybook/docs-tools": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-8.0.2.tgz", + "integrity": "sha512-N49fnqqqzW+/GMoQ23DQ4a2DaN2jarVBtweN8gWPocLkDq3oEm4ufa13lYMBNrrMJuNe0F/MOK1OIRcUrA79sA==", + "dev": true, + "dependencies": { + "@storybook/core-common": "8.0.2", + "@storybook/preview-api": "8.0.2", + "@storybook/types": "8.0.2", + "@types/doctrine": "^0.0.3", + "assert": "^2.1.0", + "doctrine": "^3.0.0", + "lodash": "^4.17.21" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/blocks/node_modules/@storybook/manager-api": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.0.2.tgz", + "integrity": "sha512-Bq+idvePWtaGIGgv6kBniVAjxRQU+TaqLqbxPG8j2HI8xi+Hc10dTaOfYQ9WVp6uRAum4BeoLsAqFDEcBt3kew==", + "dev": true, + "dependencies": { + "@storybook/channels": "8.0.2", + "@storybook/client-logger": "8.0.2", + "@storybook/core-events": "8.0.2", + "@storybook/csf": "^0.1.2", + "@storybook/global": "^5.0.0", + "@storybook/router": "8.0.2", + "@storybook/theming": "8.0.2", + "@storybook/types": "8.0.2", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "store2": "^2.14.2", + "telejson": "^7.2.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/blocks/node_modules/@storybook/node-logger": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-8.0.2.tgz", + "integrity": "sha512-UG6v5PCXYblNCZUlbC+D+NisvSn1caC+q3yNSVAW3Z2MDfWmrkThFVzI7LDj1c9DAkbMr2v9beMHdD+suSQe4g==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/blocks/node_modules/@storybook/preview-api": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.0.2.tgz", + "integrity": "sha512-b321QTjSw6k50eKTPYeB1rlCso9frHADMeudpcQcRdf8ezYQzd/mUZx9DcJnmTS+WuW9LJ435GvJ7b5O1oA6kg==", + "dev": true, + "dependencies": { + "@storybook/channels": "8.0.2", + "@storybook/client-logger": "8.0.2", + "@storybook/core-events": "8.0.2", + "@storybook/csf": "^0.1.2", + "@storybook/global": "^5.0.0", + "@storybook/types": "8.0.2", + "@types/qs": "^6.9.5", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "qs": "^6.10.0", + "tiny-invariant": "^1.3.1", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/blocks/node_modules/@storybook/router": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-8.0.2.tgz", + "integrity": "sha512-6RB+BaiQ6asVWvBsED4I2fjEv8rfKQv0CBJxIq24B9PY2YcQzO6O6eBG6FMSlDvFPX+y9+Ut0dAseACwKuDaTg==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "8.0.2", + "memoizerific": "^1.11.3", + "qs": "^6.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/blocks/node_modules/@storybook/theming": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.0.2.tgz", + "integrity": "sha512-llF6Pht11aJrWWuoBa3yyTFgKgA0lyRilfqhx7oWnjgImrl99tzuJNNAyunMMkepYbfvsWevpNegXu3TkqkJxQ==", + "dev": true, + "dependencies": { + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@storybook/client-logger": "8.0.2", + "@storybook/global": "^5.0.0", + "memoizerific": "^1.11.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/blocks/node_modules/@storybook/types": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-8.0.2.tgz", + "integrity": "sha512-vVBNUZFf8v8qxm/FYtg06K5T6dEqHtGZjm4DH/fPc59XvqGpAIl6XEkOwgfTqv30QqXDV2PAaaPDO/21VtXjrQ==", + "dev": true, + "dependencies": { + "@storybook/channels": "8.0.2", + "@types/express": "^4.7.0", + "file-system-cache": "2.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/blocks/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@storybook/blocks/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@storybook/blocks/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@storybook/blocks/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/blocks/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/blocks/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@storybook/builder-manager": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@storybook/builder-manager/-/builder-manager-8.0.0.tgz", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 01a9ac97f2c..02aa990b5e1 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -2001,6 +2001,7 @@ "@storybook/addon-essentials": "^8.0.0", "@storybook/addon-interactions": "^8.0.0", "@storybook/addon-links": "^8.0.0", + "@storybook/blocks": "^8.0.2", "@storybook/components": "^8.0.0", "@storybook/csf": "^0.1.1", "@storybook/manager-api": "^8.0.0", diff --git a/extensions/ql-vscode/src/stories/Overview.stories.mdx b/extensions/ql-vscode/src/stories/Overview.mdx similarity index 92% rename from extensions/ql-vscode/src/stories/Overview.stories.mdx rename to extensions/ql-vscode/src/stories/Overview.mdx index 6400457dfa9..cc06ee5cfca 100644 --- a/extensions/ql-vscode/src/stories/Overview.stories.mdx +++ b/extensions/ql-vscode/src/stories/Overview.mdx @@ -1,4 +1,4 @@ -import { Canvas, Meta, Story } from '@storybook/addon-docs'; +import { Canvas, Meta, Story } from '@storybook/blocks'; import { VSCodeButton } from '@vscode/webview-ui-toolkit/react'; @@ -39,23 +39,29 @@ updated manually if new variables are added to VSCode. It can also be updated if these variables, follow these steps: 1. Make sure you have selected the correct theme. If you want to use a variable which is currently not available and will be committed, please -select the **Dark+** theme. You can use **Preferences: Color Theme** in the *Command Palette* to select the theme. + select the **Dark+** theme. You can use **Preferences: Color Theme** in the *Command Palette* to select the theme. + 2. Open a WebView in VSCode (for example the results of a query) + 3. Open the *Command Palette* (Ctrl/Cmd+Shift+P) + 4. Select **Developer: Open WebView Developer Tools** + 5. Now, you will need to find the `` element in the lowest-level `