PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB`Mini Shell

HOME


Mini Shell 1.0
DIR:/home/htlwork.com/www/himsaral/node_modules/webpack/lib/schemes/
Upload File :
Current File : /home/htlwork.com/www/himsaral/node_modules/webpack/lib/schemes/HttpUriPlugin.js
/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

const EventEmitter = require("events");
const { extname, basename } = require("path");
const { URL } = require("url");
const { createGunzip, createBrotliDecompress, createInflate } = require("zlib");
const NormalModule = require("../NormalModule");
const createSchemaValidation = require("../util/create-schema-validation");
const createHash = require("../util/createHash");
const { mkdirp, dirname, join } = require("../util/fs");
const memoize = require("../util/memoize");

/** @typedef {import("../../declarations/plugins/schemes/HttpUriPlugin").HttpUriPluginOptions} HttpUriPluginOptions */
/** @typedef {import("../Compiler")} Compiler */

const getHttp = memoize(() => require("http"));
const getHttps = memoize(() => require("https"));
const proxyFetch = (request, proxy) => (url, options, callback) => {
	const eventEmitter = new EventEmitter();
	const doRequest = socket =>
		request
			.get(url, { ...options, ...(socket && { socket }) }, callback)
			.on("error", eventEmitter.emit.bind(eventEmitter, "error"));

	if (proxy) {
		const { hostname: host, port } = new URL(proxy);

		getHttp()
			.request({
				host, // IP address of proxy server
				port, // port of proxy server
				method: "CONNECT",
				path: url.host
			})
			.on("connect", (res, socket) => {
				if (res.statusCode === 200) {
					// connected to proxy server
					doRequest(socket);
				}
			})
			.on("error", err => {
				eventEmitter.emit(
					"error",
					new Error(
						`Failed to connect to proxy server "${proxy}": ${err.message}`
					)
				);
			})
			.end();
	} else {
		doRequest();
	}

	return eventEmitter;
};

/** @type {(() => void)[] | undefined} */
let inProgressWrite = undefined;

const validate = createSchemaValidation(
	require("../../schemas/plugins/schemes/HttpUriPlugin.check.js"),
	() => require("../../schemas/plugins/schemes/HttpUriPlugin.json"),
	{
		name: "Http Uri Plugin",
		baseDataPath: "options"
	}
);

const toSafePath = str =>
	str
		.replace(/^[^a-zA-Z0-9]+|[^a-zA-Z0-9]+$/g, "")
		.replace(/[^a-zA-Z0-9._-]+/g, "_");

const computeIntegrity = content => {
	const hash = createHash("sha512");
	hash.update(content);
	const integrity = "sha512-" + hash.digest("base64");
	return integrity;
};

const verifyIntegrity = (content, integrity) => {
	if (integrity === "ignore") return true;
	return computeIntegrity(content) === integrity;
};

/**
 * @param {string} str input
 * @returns {Record<string, string>} parsed
 */
const parseKeyValuePairs = str => {
	/** @type {Record<string, string>} */
	const result = {};
	for (const item of str.split(",")) {
		const i = item.indexOf("=");
		if (i >= 0) {
			const key = item.slice(0, i).trim();
			const value = item.slice(i + 1).trim();
			result[key] = value;
		} else {
			const key = item.trim();
			if (!key) continue;
			result[key] = key;
		}
	}
	return result;
};

const parseCacheControl = (cacheControl, requestTime) => {
	// When false resource is not stored in cache
	let storeCache = true;
	// When false resource is not stored in lockfile cache
	let storeLock = true;
	// Resource is only revalidated, after that timestamp and when upgrade is chosen
	let validUntil = 0;
	if (cacheControl) {
		const parsed = parseKeyValuePairs(cacheControl);
		if (parsed["no-cache"]) storeCache = storeLock = false;
		if (parsed["max-age"] && !isNaN(+parsed["max-age"])) {
			validUntil = requestTime + +parsed["max-age"] * 1000;
		}
		if (parsed["must-revalidate"]) validUntil = 0;
	}
	return {
		storeLock,
		storeCache,
		validUntil
	};
};

/**
 * @typedef {Object} LockfileEntry
 * @property {string} resolved
 * @property {string} integrity
 * @property {string} contentType
 */

const areLockfileEntriesEqual = (a, b) => {
	return (
		a.resolved === b.resolved &&
		a.integrity === b.integrity &&
		a.contentType === b.contentType
	);
};

const entryToString = entry => {
	return `resolved: ${entry.resolved}, integrity: ${entry.integrity}, contentType: ${entry.contentType}`;
};

class Lockfile {
	constructor() {
		this.version = 1;
		/** @type {Map<string, LockfileEntry | "ignore" | "no-cache">} */
		this.entries = new Map();
	}

	static parse(content) {
		// TODO handle merge conflicts
		const data = JSON.parse(content);
		if (data.version !== 1)
			throw new Error(`Unsupported lockfile version ${data.version}`);
		const lockfile = new Lockfile();
		for (const key of Object.keys(data)) {
			if (key === "version") continue;
			const entry = data[key];
			lockfile.entries.set(
				key,
				typeof entry === "string"
					? entry
					: {
							resolved: key,
							...entry
					  }
			);
		}
		return lockfile;
	}

	toString() {
		let str = "{\n";
		const entries = Array.from(this.entries).sort(([a], [b]) =>
			a < b ? -1 : 1
		);
		for (const [key, entry] of entries) {
			if (typeof entry === "string") {
				str += `  ${JSON.stringify(key)}: ${JSON.stringify(entry)},\n`;
			} else {
				str += `  ${JSON.stringify(key)}: { `;
				if (entry.resolved !== key)
					str += `"resolved": ${JSON.stringify(entry.resolved)}, `;
				str += `"integrity": ${JSON.stringify(
					entry.integrity
				)}, "contentType": ${JSON.stringify(entry.contentType)} },\n`;
			}
		}
		str += `  "version": ${this.version}\n}\n`;
		return str;
	}
}

/**
 * @template R
 * @param {function(function(Error=, R=): void): void} fn function
 * @returns {function(function((Error | null)=, R=): void): void} cached function
 */
const cachedWithoutKey = fn => {
	let inFlight = false;
	/** @type {Error | undefined} */
	let cachedError = undefined;
	/** @type {R | undefined} */
	let cachedResult = undefined;
	/** @type {(function(Error=, R=): void)[] | undefined} */
	let cachedCallbacks = undefined;
	return callback => {
		if (inFlight) {
			if (cachedResult !== undefined) return callback(null, cachedResult);
			if (cachedError !== undefined) return callback(cachedError);
			if (cachedCallbacks === undefined) cachedCallbacks = [callback];
			else cachedCallbacks.push(callback);
			return;
		}
		inFlight = true;
		fn((err, result) => {
			if (err) cachedError = err;
			else cachedResult = result;
			const callbacks = cachedCallbacks;
			cachedCallbacks = undefined;
			callback(err, result);
			if (callbacks !== undefined) for (const cb of callbacks) cb(err, result);
		});
	};
};

/**
 * @template T
 * @template R
 * @param {function(T, function(Error=, R=): void): void} fn function
 * @param {function(T, function(Error=, R=): void): void=} forceFn function for the second try
 * @returns {(function(T, function((Error | null)=, R=): void): void) & { force: function(T, function((Error | null)=, R=): void): void }} cached function
 */
const cachedWithKey = (fn, forceFn = fn) => {
	/** @typedef {{ result?: R, error?: Error, callbacks?: (function((Error | null)=, R=): void)[], force?: true }} CacheEntry */
	/** @type {Map<T, CacheEntry>} */
	const cache = new Map();
	const resultFn = (arg, callback) => {
		const cacheEntry = cache.get(arg);
		if (cacheEntry !== undefined) {
			if (cacheEntry.result !== undefined)
				return callback(null, cacheEntry.result);
			if (cacheEntry.error !== undefined) return callback(cacheEntry.error);
			if (cacheEntry.callbacks === undefined) cacheEntry.callbacks = [callback];
			else cacheEntry.callbacks.push(callback);
			return;
		}
		/** @type {CacheEntry} */
		const newCacheEntry = {
			result: undefined,
			error: undefined,
			callbacks: undefined
		};
		cache.set(arg, newCacheEntry);
		fn(arg, (err, result) => {
			if (err) newCacheEntry.error = err;
			else newCacheEntry.result = result;
			const callbacks = newCacheEntry.callbacks;
			newCacheEntry.callbacks = undefined;
			callback(err, result);
			if (callbacks !== undefined) for (const cb of callbacks) cb(err, result);
		});
	};
	resultFn.force = (arg, callback) => {
		const cacheEntry = cache.get(arg);
		if (cacheEntry !== undefined && cacheEntry.force) {
			if (cacheEntry.result !== undefined)
				return callback(null, cacheEntry.result);
			if (cacheEntry.error !== undefined) return callback(cacheEntry.error);
			if (cacheEntry.callbacks === undefined) cacheEntry.callbacks = [callback];
			else cacheEntry.callbacks.push(callback);
			return;
		}
		/** @type {CacheEntry} */
		const newCacheEntry = {
			result: undefined,
			error: undefined,
			callbacks: undefined,
			force: true
		};
		cache.set(arg, newCacheEntry);
		forceFn(arg, (err, result) => {
			if (err) newCacheEntry.error = err;
			else newCacheEntry.result = result;
			const callbacks = newCacheEntry.callbacks;
			newCacheEntry.callbacks = undefined;
			callback(err, result);
			if (callbacks !== undefined) for (const cb of callbacks) cb(err, result);
		});
	};
	return resultFn;
};

class HttpUriPlugin {
	/**
	 * @param {HttpUriPluginOptions} options options
	 */
	constructor(options) {
		validate(options);
		this._lockfileLocation = options.lockfileLocation;
		this._cacheLocation = options.cacheLocation;
		this._upgrade = options.upgrade;
		this._frozen = options.frozen;
		this._allowedUris = options.allowedUris;
		this._proxy = options.proxy;
	}

	/**
	 * Apply the plugin
	 * @param {Compiler} compiler the compiler instance
	 * @returns {void}
	 */
	apply(compiler) {
		const proxy =
			this._proxy || process.env["http_proxy"] || process.env["HTTP_PROXY"];
		const schemes = [
			{
				scheme: "http",
				fetch: proxyFetch(getHttp(), proxy)
			},
			{
				scheme: "https",
				fetch: proxyFetch(getHttps(), proxy)
			}
		];
		let lockfileCache;
		compiler.hooks.compilation.tap(
			"HttpUriPlugin",
			(compilation, { normalModuleFactory }) => {
				const intermediateFs = compiler.intermediateFileSystem;
				const fs = compilation.inputFileSystem;
				const cache = compilation.getCache("webpack.HttpUriPlugin");
				const logger = compilation.getLogger("webpack.HttpUriPlugin");
				const lockfileLocation =
					this._lockfileLocation ||
					join(
						intermediateFs,
						compiler.context,
						compiler.name
							? `${toSafePath(compiler.name)}.webpack.lock`
							: "webpack.lock"
					);
				const cacheLocation =
					this._cacheLocation !== undefined
						? this._cacheLocation
						: lockfileLocation + ".data";
				const upgrade = this._upgrade || false;
				const frozen = this._frozen || false;
				const hashFunction = "sha512";
				const hashDigest = "hex";
				const hashDigestLength = 20;
				const allowedUris = this._allowedUris;

				let warnedAboutEol = false;

				const cacheKeyCache = new Map();
				/**
				 * @param {string} url the url
				 * @returns {string} the key
				 */
				const getCacheKey = url => {
					const cachedResult = cacheKeyCache.get(url);
					if (cachedResult !== undefined) return cachedResult;
					const result = _getCacheKey(url);
					cacheKeyCache.set(url, result);
					return result;
				};

				/**
				 * @param {string} url the url
				 * @returns {string} the key
				 */
				const _getCacheKey = url => {
					const parsedUrl = new URL(url);
					const folder = toSafePath(parsedUrl.origin);
					const name = toSafePath(parsedUrl.pathname);
					const query = toSafePath(parsedUrl.search);
					let ext = extname(name);
					if (ext.length > 20) ext = "";
					const basename = ext ? name.slice(0, -ext.length) : name;
					const hash = createHash(hashFunction);
					hash.update(url);
					const digest = hash.digest(hashDigest).slice(0, hashDigestLength);
					return `${folder.slice(-50)}/${`${basename}${
						query ? `_${query}` : ""
					}`.slice(0, 150)}_${digest}${ext}`;
				};

				const getLockfile = cachedWithoutKey(
					/**
					 * @param {function((Error | null)=, Lockfile=): void} callback callback
					 * @returns {void}
					 */
					callback => {
						const readLockfile = () => {
							intermediateFs.readFile(lockfileLocation, (err, buffer) => {
								if (err && err.code !== "ENOENT") {
									compilation.missingDependencies.add(lockfileLocation);
									return callback(err);
								}
								compilation.fileDependencies.add(lockfileLocation);
								compilation.fileSystemInfo.createSnapshot(
									compiler.fsStartTime,
									buffer ? [lockfileLocation] : [],
									[],
									buffer ? [] : [lockfileLocation],
									{ timestamp: true },
									(err, snapshot) => {
										if (err) return callback(err);
										const lockfile = buffer
											? Lockfile.parse(buffer.toString("utf-8"))
											: new Lockfile();
										lockfileCache = {
											lockfile,
											snapshot
										};
										callback(null, lockfile);
									}
								);
							});
						};
						if (lockfileCache) {
							compilation.fileSystemInfo.checkSnapshotValid(
								lockfileCache.snapshot,
								(err, valid) => {
									if (err) return callback(err);
									if (!valid) return readLockfile();
									callback(null, lockfileCache.lockfile);
								}
							);
						} else {
							readLockfile();
						}
					}
				);

				/** @type {Map<string, LockfileEntry | "ignore" | "no-cache"> | undefined} */
				let lockfileUpdates = undefined;
				const storeLockEntry = (lockfile, url, entry) => {
					const oldEntry = lockfile.entries.get(url);
					if (lockfileUpdates === undefined) lockfileUpdates = new Map();
					lockfileUpdates.set(url, entry);
					lockfile.entries.set(url, entry);
					if (!oldEntry) {
						logger.log(`${url} added to lockfile`);
					} else if (typeof oldEntry === "string") {
						if (typeof entry === "string") {
							logger.log(`${url} updated in lockfile: ${oldEntry} -> ${entry}`);
						} else {
							logger.log(
								`${url} updated in lockfile: ${oldEntry} -> ${entry.resolved}`
							);
						}
					} else if (typeof entry === "string") {
						logger.log(
							`${url} updated in lockfile: ${oldEntry.resolved} -> ${entry}`
						);
					} else if (oldEntry.resolved !== entry.resolved) {
						logger.log(
							`${url} updated in lockfile: ${oldEntry.resolved} -> ${entry.resolved}`
						);
					} else if (oldEntry.integrity !== entry.integrity) {
						logger.log(`${url} updated in lockfile: content changed`);
					} else if (oldEntry.contentType !== entry.contentType) {
						logger.log(
							`${url} updated in lockfile: ${oldEntry.contentType} -> ${entry.contentType}`
						);
					} else {
						logger.log(`${url} updated in lockfile`);
					}
				};

				const storeResult = (lockfile, url, result, callback) => {
					if (result.storeLock) {
						storeLockEntry(lockfile, url, result.entry);
						if (!cacheLocation || !result.content)
							return callback(null, result);
						const key = getCacheKey(result.entry.resolved);
						const filePath = join(intermediateFs, cacheLocation, key);
						mkdirp(intermediateFs, dirname(intermediateFs, filePath), err => {
							if (err) return callback(err);
							intermediateFs.writeFile(filePath, result.content, err => {
								if (err) return callback(err);
								callback(null, result);
							});
						});
					} else {
						storeLockEntry(lockfile, url, "no-cache");
						callback(null, result);
					}
				};

				for (const { scheme, fetch } of schemes) {
					/**
					 *
					 * @param {string} url URL
					 * @param {string} integrity integrity
					 * @param {function((Error | null)=, { entry: LockfileEntry, content: Buffer, storeLock: boolean }=): void} callback callback
					 */
					const resolveContent = (url, integrity, callback) => {
						const handleResult = (err, result) => {
							if (err) return callback(err);
							if ("location" in result) {
								return resolveContent(
									result.location,
									integrity,
									(err, innerResult) => {
										if (err) return callback(err);
										callback(null, {
											entry: innerResult.entry,
											content: innerResult.content,
											storeLock: innerResult.storeLock && result.storeLock
										});
									}
								);
							} else {
								if (
									!result.fresh &&
									integrity &&
									result.entry.integrity !== integrity &&
									!verifyIntegrity(result.content, integrity)
								) {
									return fetchContent.force(url, handleResult);
								}
								return callback(null, {
									entry: result.entry,
									content: result.content,
									storeLock: result.storeLock
								});
							}
						};
						fetchContent(url, handleResult);
					};

					/** @typedef {{ storeCache: boolean, storeLock: boolean, validUntil: number, etag: string | undefined, fresh: boolean }} FetchResultMeta */
					/** @typedef {FetchResultMeta & { location: string }} RedirectFetchResult */
					/** @typedef {FetchResultMeta & { entry: LockfileEntry, content: Buffer }} ContentFetchResult */
					/** @typedef {RedirectFetchResult | ContentFetchResult} FetchResult */

					/**
					 * @param {string} url URL
					 * @param {FetchResult | RedirectFetchResult} cachedResult result from cache
					 * @param {function((Error | null)=, FetchResult=): void} callback callback
					 * @returns {void}
					 */
					const fetchContentRaw = (url, cachedResult, callback) => {
						const requestTime = Date.now();
						fetch(
							new URL(url),
							{
								headers: {
									"accept-encoding": "gzip, deflate, br",
									"user-agent": "webpack",
									"if-none-match": cachedResult
										? cachedResult.etag || null
										: null
								}
							},
							res => {
								const etag = res.headers["etag"];
								const location = res.headers["location"];
								const cacheControl = res.headers["cache-control"];
								const { storeLock, storeCache, validUntil } = parseCacheControl(
									cacheControl,
									requestTime
								);
								/**
								 * @param {Partial<Pick<FetchResultMeta, "fresh">> & (Pick<RedirectFetchResult, "location"> | Pick<ContentFetchResult, "content" | "entry">)} partialResult result
								 * @returns {void}
								 */
								const finishWith = partialResult => {
									if ("location" in partialResult) {
										logger.debug(
											`GET ${url} [${res.statusCode}] -> ${partialResult.location}`
										);
									} else {
										logger.debug(
											`GET ${url} [${res.statusCode}] ${Math.ceil(
												partialResult.content.length / 1024
											)} kB${!storeLock ? " no-cache" : ""}`
										);
									}
									const result = {
										...partialResult,
										fresh: true,
										storeLock,
										storeCache,
										validUntil,
										etag
									};
									if (!storeCache) {
										logger.log(
											`${url} can't be stored in cache, due to Cache-Control header: ${cacheControl}`
										);
										return callback(null, result);
									}
									cache.store(
										url,
										null,
										{
											...result,
											fresh: false
										},
										err => {
											if (err) {
												logger.warn(
													`${url} can't be stored in cache: ${err.message}`
												);
												logger.debug(err.stack);
											}
											callback(null, result);
										}
									);
								};
								if (res.statusCode === 304) {
									if (
										cachedResult.validUntil < validUntil ||
										cachedResult.storeLock !== storeLock ||
										cachedResult.storeCache !== storeCache ||
										cachedResult.etag !== etag
									) {
										return finishWith(cachedResult);
									} else {
										logger.debug(`GET ${url} [${res.statusCode}] (unchanged)`);
										return callback(null, {
											...cachedResult,
											fresh: true
										});
									}
								}
								if (
									location &&
									res.statusCode >= 301 &&
									res.statusCode <= 308
								) {
									const result = {
										location: new URL(location, url).href
									};
									if (
										!cachedResult ||
										!("location" in cachedResult) ||
										cachedResult.location !== result.location ||
										cachedResult.validUntil < validUntil ||
										cachedResult.storeLock !== storeLock ||
										cachedResult.storeCache !== storeCache ||
										cachedResult.etag !== etag
									) {
										return finishWith(result);
									} else {
										logger.debug(`GET ${url} [${res.statusCode}] (unchanged)`);
										return callback(null, {
											...result,
											fresh: true,
											storeLock,
											storeCache,
											validUntil,
											etag
										});
									}
								}
								const contentType = res.headers["content-type"] || "";
								const bufferArr = [];

								const contentEncoding = res.headers["content-encoding"];
								let stream = res;
								if (contentEncoding === "gzip") {
									stream = stream.pipe(createGunzip());
								} else if (contentEncoding === "br") {
									stream = stream.pipe(createBrotliDecompress());
								} else if (contentEncoding === "deflate") {
									stream = stream.pipe(createInflate());
								}

								stream.on("data", chunk => {
									bufferArr.push(chunk);
								});

								stream.on("end", () => {
									if (!res.complete) {
										logger.log(`GET ${url} [${res.statusCode}] (terminated)`);
										return callback(new Error(`${url} request was terminated`));
									}

									const content = Buffer.concat(bufferArr);

									if (res.statusCode !== 200) {
										logger.log(`GET ${url} [${res.statusCode}]`);
										return callback(
											new Error(
												`${url} request status code = ${
													res.statusCode
												}\n${content.toString("utf-8")}`
											)
										);
									}

									const integrity = computeIntegrity(content);
									const entry = { resolved: url, integrity, contentType };

									finishWith({
										entry,
										content
									});
								});
							}
						).on("error", err => {
							logger.log(`GET ${url} (error)`);
							err.message += `\nwhile fetching ${url}`;
							callback(err);
						});
					};

					const fetchContent = cachedWithKey(
						/**
						 * @param {string} url URL
						 * @param {function((Error | null)=, { validUntil: number, etag?: string, entry: LockfileEntry, content: Buffer, fresh: boolean } | { validUntil: number, etag?: string, location: string, fresh: boolean }=): void} callback callback
						 * @returns {void}
						 */ (url, callback) => {
							cache.get(url, null, (err, cachedResult) => {
								if (err) return callback(err);
								if (cachedResult) {
									const isValid = cachedResult.validUntil >= Date.now();
									if (isValid) return callback(null, cachedResult);
								}
								fetchContentRaw(url, cachedResult, callback);
							});
						},
						(url, callback) => fetchContentRaw(url, undefined, callback)
					);

					const isAllowed = uri => {
						for (const allowed of allowedUris) {
							if (typeof allowed === "string") {
								if (uri.startsWith(allowed)) return true;
							} else if (typeof allowed === "function") {
								if (allowed(uri)) return true;
							} else {
								if (allowed.test(uri)) return true;
							}
						}
						return false;
					};

					const getInfo = cachedWithKey(
						/**
						 * @param {string} url the url
						 * @param {function((Error | null)=, { entry: LockfileEntry, content: Buffer }=): void} callback callback
						 * @returns {void}
						 */
						(url, callback) => {
							if (!isAllowed(url)) {
								return callback(
									new Error(
										`${url} doesn't match the allowedUris policy. These URIs are allowed:\n${allowedUris
											.map(uri => ` - ${uri}`)
											.join("\n")}`
									)
								);
							}
							getLockfile((err, lockfile) => {
								if (err) return callback(err);
								const entryOrString = lockfile.entries.get(url);
								if (!entryOrString) {
									if (frozen) {
										return callback(
											new Error(
												`${url} has no lockfile entry and lockfile is frozen`
											)
										);
									}
									resolveContent(url, null, (err, result) => {
										if (err) return callback(err);
										storeResult(lockfile, url, result, callback);
									});
									return;
								}
								if (typeof entryOrString === "string") {
									const entryTag = entryOrString;
									resolveContent(url, null, (err, result) => {
										if (err) return callback(err);
										if (!result.storeLock || entryTag === "ignore")
											return callback(null, result);
										if (frozen) {
											return callback(
												new Error(
													`${url} used to have ${entryTag} lockfile entry and has content now, but lockfile is frozen`
												)
											);
										}
										if (!upgrade) {
											return callback(
												new Error(
													`${url} used to have ${entryTag} lockfile entry and has content now.
This should be reflected in the lockfile, so this lockfile entry must be upgraded, but upgrading is not enabled.
Remove this line from the lockfile to force upgrading.`
												)
											);
										}
										storeResult(lockfile, url, result, callback);
									});
									return;
								}
								let entry = entryOrString;
								const doFetch = lockedContent => {
									resolveContent(url, entry.integrity, (err, result) => {
										if (err) {
											if (lockedContent) {
												logger.warn(
													`Upgrade request to ${url} failed: ${err.message}`
												);
												logger.debug(err.stack);
												return callback(null, {
													entry,
													content: lockedContent
												});
											}
											return callback(err);
										}
										if (!result.storeLock) {
											// When the lockfile entry should be no-cache
											// we need to update the lockfile
											if (frozen) {
												return callback(
													new Error(
														`${url} has a lockfile entry and is no-cache now, but lockfile is frozen\nLockfile: ${entryToString(
															entry
														)}`
													)
												);
											}
											storeResult(lockfile, url, result, callback);
											return;
										}
										if (!areLockfileEntriesEqual(result.entry, entry)) {
											// When the lockfile entry is outdated
											// we need to update the lockfile
											if (frozen) {
												return callback(
													new Error(
														`${url} has an outdated lockfile entry, but lockfile is frozen\nLockfile: ${entryToString(
															entry
														)}\nExpected: ${entryToString(result.entry)}`
													)
												);
											}
											storeResult(lockfile, url, result, callback);
											return;
										}
										if (!lockedContent && cacheLocation) {
											// When the lockfile cache content is missing
											// we need to update the lockfile
											if (frozen) {
												return callback(
													new Error(
														`${url} is missing content in the lockfile cache, but lockfile is frozen\nLockfile: ${entryToString(
															entry
														)}`
													)
												);
											}
											storeResult(lockfile, url, result, callback);
											return;
										}
										return callback(null, result);
									});
								};
								if (cacheLocation) {
									// When there is a lockfile cache
									// we read the content from there
									const key = getCacheKey(entry.resolved);
									const filePath = join(intermediateFs, cacheLocation, key);
									fs.readFile(filePath, (err, result) => {
										const content = /** @type {Buffer} */ (result);
										if (err) {
											if (err.code === "ENOENT") return doFetch();
											return callback(err);
										}
										const continueWithCachedContent = result => {
											if (!upgrade) {
												// When not in upgrade mode, we accept the result from the lockfile cache
												return callback(null, { entry, content });
											}
											return doFetch(content);
										};
										if (!verifyIntegrity(content, entry.integrity)) {
											let contentWithChangedEol;
											let isEolChanged = false;
											try {
												contentWithChangedEol = Buffer.from(
													content.toString("utf-8").replace(/\r\n/g, "\n")
												);
												isEolChanged = verifyIntegrity(
													contentWithChangedEol,
													entry.integrity
												);
											} catch (e) {
												// ignore
											}
											if (isEolChanged) {
												if (!warnedAboutEol) {
													const explainer = `Incorrect end of line sequence was detected in the lockfile cache.
The lockfile cache is protected by integrity checks, so any external modification will lead to a corrupted lockfile cache.
When using git make sure to configure .gitattributes correctly for the lockfile cache:
  **/*webpack.lock.data/** -text
This will avoid that the end of line sequence is changed by git on Windows.`;
													if (frozen) {
														logger.error(explainer);
													} else {
														logger.warn(explainer);
														logger.info(
															"Lockfile cache will be automatically fixed now, but when lockfile is frozen this would result in an error."
														);
													}
													warnedAboutEol = true;
												}
												if (!frozen) {
													// "fix" the end of line sequence of the lockfile content
													logger.log(
														`${filePath} fixed end of line sequence (\\r\\n instead of \\n).`
													);
													intermediateFs.writeFile(
														filePath,
														contentWithChangedEol,
														err => {
															if (err) return callback(err);
															continueWithCachedContent(contentWithChangedEol);
														}
													);
													return;
												}
											}
											if (frozen) {
												return callback(
													new Error(
														`${
															entry.resolved
														} integrity mismatch, expected content with integrity ${
															entry.integrity
														} but got ${computeIntegrity(content)}.
Lockfile corrupted (${
															isEolChanged
																? "end of line sequence was unexpectedly changed"
																: "incorrectly merged? changed by other tools?"
														}).
Run build with un-frozen lockfile to automatically fix lockfile.`
													)
												);
											} else {
												// "fix" the lockfile entry to the correct integrity
												// the content has priority over the integrity value
												entry = {
													...entry,
													integrity: computeIntegrity(content)
												};
												storeLockEntry(lockfile, url, entry);
											}
										}
										continueWithCachedContent(result);
									});
								} else {
									doFetch();
								}
							});
						}
					);

					const respondWithUrlModule = (url, resourceData, callback) => {
						getInfo(url.href, (err, result) => {
							if (err) return callback(err);
							resourceData.resource = url.href;
							resourceData.path = url.origin + url.pathname;
							resourceData.query = url.search;
							resourceData.fragment = url.hash;
							resourceData.context = new URL(
								".",
								result.entry.resolved
							).href.slice(0, -1);
							resourceData.data.mimetype = result.entry.contentType;
							callback(null, true);
						});
					};
					normalModuleFactory.hooks.resolveForScheme
						.for(scheme)
						.tapAsync(
							"HttpUriPlugin",
							(resourceData, resolveData, callback) => {
								respondWithUrlModule(
									new URL(resourceData.resource),
									resourceData,
									callback
								);
							}
						);
					normalModuleFactory.hooks.resolveInScheme
						.for(scheme)
						.tapAsync("HttpUriPlugin", (resourceData, data, callback) => {
							// Only handle relative urls (./xxx, ../xxx, /xxx, //xxx)
							if (
								data.dependencyType !== "url" &&
								!/^\.{0,2}\//.test(resourceData.resource)
							) {
								return callback();
							}
							respondWithUrlModule(
								new URL(resourceData.resource, data.context + "/"),
								resourceData,
								callback
							);
						});
					const hooks = NormalModule.getCompilationHooks(compilation);
					hooks.readResourceForScheme
						.for(scheme)
						.tapAsync("HttpUriPlugin", (resource, module, callback) => {
							return getInfo(resource, (err, result) => {
								if (err) return callback(err);
								module.buildInfo.resourceIntegrity = result.entry.integrity;
								callback(null, result.content);
							});
						});
					hooks.needBuild.tapAsync(
						"HttpUriPlugin",
						(module, context, callback) => {
							if (
								module.resource &&
								module.resource.startsWith(`${scheme}://`)
							) {
								getInfo(module.resource, (err, result) => {
									if (err) return callback(err);
									if (
										result.entry.integrity !==
										module.buildInfo.resourceIntegrity
									) {
										return callback(null, true);
									}
									callback();
								});
							} else {
								return callback();
							}
						}
					);
				}
				compilation.hooks.finishModules.tapAsync(
					"HttpUriPlugin",
					(modules, callback) => {
						if (!lockfileUpdates) return callback();
						const ext = extname(lockfileLocation);
						const tempFile = join(
							intermediateFs,
							dirname(intermediateFs, lockfileLocation),
							`.${basename(lockfileLocation, ext)}.${
								(Math.random() * 10000) | 0
							}${ext}`
						);

						const writeDone = () => {
							const nextOperation = inProgressWrite.shift();
							if (nextOperation) {
								nextOperation();
							} else {
								inProgressWrite = undefined;
							}
						};
						const runWrite = () => {
							intermediateFs.readFile(lockfileLocation, (err, buffer) => {
								if (err && err.code !== "ENOENT") {
									writeDone();
									return callback(err);
								}
								const lockfile = buffer
									? Lockfile.parse(buffer.toString("utf-8"))
									: new Lockfile();
								for (const [key, value] of lockfileUpdates) {
									lockfile.entries.set(key, value);
								}
								intermediateFs.writeFile(tempFile, lockfile.toString(), err => {
									if (err) {
										writeDone();
										return intermediateFs.unlink(tempFile, () => callback(err));
									}
									intermediateFs.rename(tempFile, lockfileLocation, err => {
										if (err) {
											writeDone();
											return intermediateFs.unlink(tempFile, () =>
												callback(err)
											);
										}
										writeDone();
										callback();
									});
								});
							});
						};
						if (inProgressWrite) {
							inProgressWrite.push(runWrite);
						} else {
							inProgressWrite = [];
							runWrite();
						}
					}
				);
			}
		);
	}
}

module.exports = HttpUriPlugin;