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/mykeywordtracker.com/www/node_modules/bare-fs/
Upload File :
Current File : //home/mykeywordtracker.com/www/node_modules/bare-fs/index.js
/* global Bare */
const EventEmitter = require('bare-events')
const path = require('bare-path')
const { Readable, Writable } = require('bare-stream')
const binding = require('./binding')

const isWindows = Bare.platform === 'win32'

const constants = (exports.constants = {
  O_RDWR: binding.O_RDWR,
  O_RDONLY: binding.O_RDONLY,
  O_WRONLY: binding.O_WRONLY,
  O_CREAT: binding.O_CREAT,
  O_TRUNC: binding.O_TRUNC,
  O_APPEND: binding.O_APPEND,

  F_OK: binding.F_OK || 0,
  R_OK: binding.R_OK || 0,
  W_OK: binding.W_OK || 0,
  X_OK: binding.X_OK || 0,

  S_IFMT: binding.S_IFMT,
  S_IFREG: binding.S_IFREG,
  S_IFDIR: binding.S_IFDIR,
  S_IFCHR: binding.S_IFCHR,
  S_IFLNK: binding.S_IFLNK,
  S_IFBLK: binding.S_IFBLK || 0,
  S_IFIFO: binding.S_IFIFO || 0,
  S_IFSOCK: binding.S_IFSOCK || 0,

  S_IRUSR: binding.S_IRUSR || 0,
  S_IWUSR: binding.S_IWUSR || 0,
  S_IXUSR: binding.S_IXUSR || 0,
  S_IRGRP: binding.S_IRGRP || 0,
  S_IWGRP: binding.S_IWGRP || 0,
  S_IXGRP: binding.S_IXGRP || 0,
  S_IROTH: binding.S_IROTH || 0,
  S_IWOTH: binding.S_IWOTH || 0,
  S_IXOTH: binding.S_IXOTH || 0,

  UV_DIRENT_UNKNOWN: binding.UV_DIRENT_UNKNOWN,
  UV_DIRENT_FILE: binding.UV_DIRENT_FILE,
  UV_DIRENT_DIR: binding.UV_DIRENT_DIR,
  UV_DIRENT_LINK: binding.UV_DIRENT_LINK,
  UV_DIRENT_FIFO: binding.UV_DIRENT_FIFO,
  UV_DIRENT_SOCKET: binding.UV_DIRENT_SOCKET,
  UV_DIRENT_CHAR: binding.UV_DIRENT_CHAR,
  UV_DIRENT_BLOCK: binding.UV_DIRENT_BLOCK,

  COPYFILE_EXCL: binding.UV_FS_COPYFILE_EXCL,
  COPYFILE_FICLONE: binding.UV_FS_COPYFILE_FICLONE,
  COPYFILE_FICLONE_FORCE: binding.UV_FS_COPYFILE_FICLONE_FORCE,
  UV_FS_SYMLINK_DIR: binding.UV_FS_SYMLINK_DIR,
  UV_FS_SYMLINK_JUNCTION: binding.UV_FS_SYMLINK_JUNCTION
})

// Lightly-modified from the Node FS internal utils.
function flagsToNumber(flags) {
  switch (flags) {
    case 'r':
      return constants.O_RDONLY
    case 'rs': // Fall through.
    case 'sr':
      return constants.O_RDONLY | constants.O_SYNC
    case 'r+':
      return constants.O_RDWR
    case 'rs+': // Fall through.
    case 'sr+':
      return constants.O_RDWR | constants.O_SYNC

    case 'w':
      return constants.O_TRUNC | constants.O_CREAT | constants.O_WRONLY
    case 'wx': // Fall through.
    case 'xw':
      return (
        constants.O_TRUNC |
        constants.O_CREAT |
        constants.O_WRONLY |
        constants.O_EXCL
      )

    case 'w+':
      return constants.O_TRUNC | constants.O_CREAT | constants.O_RDWR
    case 'wx+': // Fall through.
    case 'xw+':
      return (
        constants.O_TRUNC |
        constants.O_CREAT |
        constants.O_RDWR |
        constants.O_EXCL
      )

    case 'a':
      return constants.O_APPEND | constants.O_CREAT | constants.O_WRONLY
    case 'ax': // Fall through.
    case 'xa':
      return (
        constants.O_APPEND |
        constants.O_CREAT |
        constants.O_WRONLY |
        constants.O_EXCL
      )
    case 'as': // Fall through.
    case 'sa':
      return (
        constants.O_APPEND |
        constants.O_CREAT |
        constants.O_WRONLY |
        constants.O_SYNC
      )

    case 'a+':
      return constants.O_APPEND | constants.O_CREAT | constants.O_RDWR
    case 'ax+': // Fall through.
    case 'xa+':
      return (
        constants.O_APPEND |
        constants.O_CREAT |
        constants.O_RDWR |
        constants.O_EXCL
      )
    case 'as+': // Fall through.
    case 'sa+':
      return (
        constants.O_APPEND |
        constants.O_CREAT |
        constants.O_RDWR |
        constants.O_SYNC
      )
  }

  throw typeError('ERR_INVALID_ARG_VALUE', `Invalid value in flags: ${flags}`)
}

function modeToNumber(mode) {
  mode = parseInt(mode, 8)
  if (isNaN(mode))
    throw typeError(
      'ERR_INVALID_ARG_VALUE',
      'Mode must be a number or octal string'
    )
  return mode
}

const free = []

function alloc() {
  const req = { handle: null, callback: null }
  req.handle = binding.init(req, onresponse)
  return req
}

function getReq() {
  return free.length ? free.pop() : alloc()
}

function onresponse(err, result) {
  const req = this
  const cb = req.callback
  req.callback = null
  free.push(req)
  cb(err, result)
}

function open(filepath, flags, mode, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof cb !== 'function') {
    if (typeof flags === 'function') {
      cb = flags
      flags = 'r'
      mode = 0o666
    } else if (typeof mode === 'function') {
      cb = mode
      mode = 0o666
    } else {
      throw typeError(
        'ERR_INVALID_ARG_TYPE',
        'Callback must be a function. Received type ' +
          typeof cb +
          ' (' +
          cb +
          ')'
      )
    }
  }

  if (typeof flags === 'string') flags = flagsToNumber(flags)
  if (typeof mode === 'string') mode = modeToNumber(mode)

  const req = getReq()
  req.callback = cb
  binding.open(req.handle, filepath, flags, mode)
}

function openSync(filepath, flags = 'r', mode = 0o666) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof flags === 'string') flags = flagsToNumber(flags)
  if (typeof mode === 'string') mode = modeToNumber(mode)

  return binding.openSync(filepath, flags, mode)
}

function close(fd, cb = noop) {
  if (typeof fd !== 'number') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'File descriptor must be a number. Received type ' +
        typeof fd +
        ' (' +
        fd +
        ')'
    )
  }

  if (fd < 0 || fd > 0x7fffffff) {
    throw typeError(
      'ERR_OUT_OF_RANGE',
      'File descriptor is out of range. It must be >= 0 && <= 2147483647. Received ' +
        fd
    )
  }

  if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  const req = getReq()
  req.callback = cb
  binding.close(req.handle, fd)
}

function closeSync(fd) {
  if (typeof fd !== 'number') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'File descriptor must be a number. Received type ' +
        typeof fd +
        ' (' +
        fd +
        ')'
    )
  }

  if (fd < 0 || fd > 0x7fffffff) {
    throw typeError(
      'ERR_OUT_OF_RANGE',
      'File descriptor is out of range. It must be >= 0 && <= 2147483647. Received ' +
        fd
    )
  }

  return binding.closeSync(fd)
}

function access(filepath, mode, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof cb !== 'function') {
    if (typeof mode === 'function') {
      cb = mode
      mode = constants.F_OK
    } else {
      throw typeError(
        'ERR_INVALID_ARG_TYPE',
        'Callback must be a function. Received type ' +
          typeof cb +
          ' (' +
          cb +
          ')'
      )
    }
  }

  const req = getReq()
  req.callback = cb
  binding.access(req.handle, filepath, mode)
}

function accessSync(filepath, mode = constants.F_OK) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  binding.accessSync(filepath, mode)
}

function exists(filepath, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  return access(filepath, (err) => cb(!!err))
}

function existsSync(filepath) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  try {
    accessSync(filepath)
    return true
  } catch {
    return false
  }
}

function read(fd, buffer, offset, len, pos, cb) {
  if (typeof fd !== 'number') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'File descriptor must be a number. Received type ' +
        typeof fd +
        ' (' +
        fd +
        ')'
    )
  }

  if (fd < 0 || fd > 0x7fffffff) {
    throw typeError(
      'ERR_OUT_OF_RANGE',
      'File descriptor is out of range. It must be >= 0 && <= 2147483647. Received ' +
        fd
    )
  }

  if (!Buffer.isBuffer(buffer) && !ArrayBuffer.isView(buffer)) {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Buffer must be a buffer. Received type ' +
        typeof buffer +
        ' (' +
        buffer +
        ')'
    )
  }

  if (typeof cb !== 'function') {
    if (typeof offset === 'function') {
      cb = offset
      offset = 0
      len = buffer.byteLength
      pos = -1
    } else if (typeof len === 'function') {
      cb = len
      len = buffer.byteLength - offset
      pos = -1
    } else if (typeof pos === 'function') {
      cb = pos
      pos = -1
    } else {
      throw typeError(
        'ERR_INVALID_ARG_TYPE',
        'Callback must be a function. Received type ' +
          typeof cb +
          ' (' +
          cb +
          ')'
      )
    }
  }

  if (typeof pos !== 'number') pos = -1

  const req = getReq()
  req.callback = cb
  binding.read(req.handle, fd, buffer, offset, len, pos)
}

function readSync(
  fd,
  buffer,
  offset = 0,
  len = buffer.byteLength - offset,
  pos = -1
) {
  if (typeof fd !== 'number') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'File descriptor must be a number. Received type ' +
        typeof fd +
        ' (' +
        fd +
        ')'
    )
  }

  if (fd < 0 || fd > 0x7fffffff) {
    throw typeError(
      'ERR_OUT_OF_RANGE',
      'File descriptor is out of range. It must be >= 0 && <= 2147483647. Received ' +
        fd
    )
  }

  if (!Buffer.isBuffer(buffer) && !ArrayBuffer.isView(buffer)) {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Buffer must be a buffer. Received type ' +
        typeof buffer +
        ' (' +
        buffer +
        ')'
    )
  }

  return binding.readSync(fd, buffer, offset, len, pos)
}

function readv(fd, buffers, pos, cb) {
  if (typeof fd !== 'number') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'File descriptor must be a number. Received type ' +
        typeof fd +
        ' (' +
        fd +
        ')'
    )
  }

  if (fd < 0 || fd > 0x7fffffff) {
    throw typeError(
      'ERR_OUT_OF_RANGE',
      'File descriptor is out of range. It must be >= 0 && <= 2147483647. Received ' +
        fd
    )
  }

  if (typeof pos === 'function') {
    cb = pos
    pos = -1
  } else if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  if (typeof pos !== 'number') pos = -1

  const req = getReq()
  req.callback = cb
  binding.readv(req.handle, fd, buffers, pos)
}

function write(fd, data, offset, len, pos, cb) {
  if (typeof fd !== 'number') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'File descriptor must be a number. Received type ' +
        typeof fd +
        ' (' +
        fd +
        ')'
    )
  }

  if (fd < 0 || fd > 0x7fffffff) {
    throw typeError(
      'ERR_OUT_OF_RANGE',
      'File descriptor is out of range. It must be >= 0 && <= 2147483647. Received ' +
        fd
    )
  }

  if (
    typeof data !== 'string' &&
    !Buffer.isBuffer(data) &&
    !ArrayBuffer.isView(data)
  ) {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Data must be a string or buffer. Received type ' + typeof data
    )
  }

  if (typeof data === 'string') {
    let encoding = len
    cb = pos
    pos = offset

    if (typeof cb !== 'function') {
      if (typeof pos === 'function') {
        cb = pos
        pos = -1
        encoding = 'utf8'
      } else if (typeof encoding === 'function') {
        cb = encoding
        encoding = 'utf8'
      } else {
        throw typeError(
          'ERR_INVALID_ARG_TYPE',
          'Callback must be a function. Received type ' +
            typeof cb +
            ' (' +
            cb +
            ')'
        )
      }
    }

    if (typeof pos === 'string') {
      encoding = pos
      pos = -1
    }

    data = Buffer.from(data, encoding)
    offset = 0
    len = data.byteLength
  } else if (typeof cb !== 'function') {
    if (typeof offset === 'function') {
      cb = offset
      offset = 0
      len = data.byteLength
      pos = -1
    } else if (typeof len === 'function') {
      cb = len
      len = data.byteLength - offset
      pos = -1
    } else if (typeof pos === 'function') {
      cb = pos
      pos = -1
    } else {
      throw typeError(
        'ERR_INVALID_ARG_TYPE',
        'Callback must be a function. Received type ' +
          typeof cb +
          ' (' +
          cb +
          ')'
      )
    }
  }

  if (typeof pos !== 'number') pos = -1

  const req = getReq()
  req.callback = cb
  binding.write(req.handle, fd, data, offset, len, pos)
}

function writeSync(fd, data, offset = 0, len, pos) {
  if (typeof fd !== 'number') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'File descriptor must be a number. Received type ' +
        typeof fd +
        ' (' +
        fd +
        ')'
    )
  }

  if (fd < 0 || fd > 0x7fffffff) {
    throw typeError(
      'ERR_OUT_OF_RANGE',
      'File descriptor is out of range. It must be >= 0 && <= 2147483647. Received ' +
        fd
    )
  }

  if (
    typeof data !== 'string' &&
    !Buffer.isBuffer(data) &&
    !ArrayBuffer.isView(data)
  ) {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Data must be a string or buffer. Received type ' + typeof data
    )
  }

  if (typeof data === 'string') data = Buffer.from(data)

  if (typeof len !== 'number') len = data.byteLength - offset

  if (typeof pos !== 'number') pos = -1

  return binding.writeSync(fd, data, offset, len, pos)
}

function writev(fd, buffers, pos, cb) {
  if (typeof fd !== 'number') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'File descriptor must be a number. Received type ' +
        typeof fd +
        ' (' +
        fd +
        ')'
    )
  }

  if (fd < 0 || fd > 0x7fffffff) {
    throw typeError(
      'ERR_OUT_OF_RANGE',
      'File descriptor is out of range. It must be >= 0 && <= 2147483647. Received ' +
        fd
    )
  }

  if (typeof pos === 'function') {
    cb = pos
    pos = -1
  } else if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  if (typeof pos !== 'number') pos = -1

  const req = getReq()
  req.callback = cb
  binding.writev(req.handle, fd, buffers, pos)
}

function stat(filepath, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  const data = new Array(Stats.length)

  const req = getReq()

  req.callback = function (err, _) {
    if (err) cb(err, null)
    else cb(null, new Stats(...data))
  }

  binding.stat(req.handle, filepath, data)
}

function statSync(filepath) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  return new Stats(...binding.statSync(filepath))
}

function lstat(filepath, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  const data = new Array(Stats.length)

  const req = getReq()

  req.callback = function (err, _) {
    if (err) cb(err, null)
    else cb(null, new Stats(...data))
  }

  binding.lstat(req.handle, filepath, data)
}

function lstatSync(filepath) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  return new Stats(...binding.lstatSync(filepath))
}

function fstat(fd, cb) {
  if (typeof fd !== 'number') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'File descriptor must be a number. Received type ' +
        typeof fd +
        ' (' +
        fd +
        ')'
    )
  }

  if (fd < 0 || fd > 0x7fffffff) {
    throw typeError(
      'ERR_OUT_OF_RANGE',
      'File descriptor is out of range. It must be >= 0 && <= 2147483647. Received ' +
        fd
    )
  }

  if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  const data = new Array(Stats.length)

  const req = getReq()

  req.callback = function (err, _) {
    if (err) cb(err, null)
    else cb(null, new Stats(...data))
  }

  binding.fstat(req.handle, fd, data)
}

function fstatSync(fd) {
  if (typeof fd !== 'number') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'File descriptor must be a number. Received type ' +
        typeof fd +
        ' (' +
        fd +
        ')'
    )
  }

  if (fd < 0 || fd > 0x7fffffff) {
    throw typeError(
      'ERR_OUT_OF_RANGE',
      'File descriptor is out of range. It must be >= 0 && <= 2147483647. Received ' +
        fd
    )
  }

  return new Stats(...binding.fstatSync(fd))
}

function ftruncate(fd, len, cb) {
  if (typeof fd !== 'number') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'File descriptor must be a number. Received type ' +
        typeof fd +
        ' (' +
        fd +
        ')'
    )
  }

  if (fd < 0 || fd > 0x7fffffff) {
    throw typeError(
      'ERR_OUT_OF_RANGE',
      'File descriptor is out of range. It must be >= 0 && <= 2147483647. Received ' +
        fd
    )
  }

  if (typeof len === 'function') {
    cb = len
    len = 0
  } else if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  const req = getReq()
  req.callback = cb
  binding.ftruncate(req.handle, fd, len)
}

function chmod(filepath, mode, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof mode === 'string') mode = modeToNumber(mode)

  const req = getReq()
  req.callback = cb
  binding.chmod(req.handle, filepath, mode)
}

function chmodSync(filepath, mode) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof mode === 'string') mode = modeToNumber(mode)

  binding.chmodSync(filepath, mode)
}

function fchmod(fd, mode, cb) {
  if (typeof fd !== 'number') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'File descriptor must be a number. Received type ' +
        typeof fd +
        ' (' +
        fd +
        ')'
    )
  }

  if (fd < 0 || fd > 0x7fffffff) {
    throw typeError(
      'ERR_OUT_OF_RANGE',
      'File descriptor is out of range. It must be >= 0 && <= 2147483647. Received ' +
        fd
    )
  }

  if (typeof mode === 'string') mode = modeToNumber(mode)

  const req = getReq()
  req.callback = cb
  binding.fchmod(req.handle, fd, mode)
}

function fchmodSync(fd, mode) {
  if (typeof fd !== 'number') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'File descriptor must be a number. Received type ' +
        typeof fd +
        ' (' +
        fd +
        ')'
    )
  }

  if (fd < 0 || fd > 0x7fffffff) {
    throw typeError(
      'ERR_OUT_OF_RANGE',
      'File descriptor is out of range. It must be >= 0 && <= 2147483647. Received ' +
        fd
    )
  }

  if (typeof mode === 'string') mode = modeToNumber(mode)

  binding.fchmodSync(fd, mode)
}

function mkdirRecursive(filepath, mode, cb) {
  mkdir(filepath, { mode }, function (err) {
    if (err === null) return cb(null, 0, null)

    if (err.code !== 'ENOENT') {
      stat(filepath, function (e, st) {
        if (e) return cb(e, e.errno, null)
        if (st.isDirectory()) return cb(null, 0, null)
        cb(err, err.errno, null)
      })
      return
    }

    while (filepath.endsWith(path.sep)) filepath = filepath.slice(0, -1)
    const i = filepath.lastIndexOf(path.sep)
    if (i <= 0) return cb(err, err.errno, null)

    mkdirRecursive(filepath.slice(0, i), mode, function (err) {
      if (err) return cb(err, err.errno, null)

      mkdir(filepath, { mode }, function (err) {
        if (err === null) return cb(null, 0, null)

        stat(filepath, function (e, st) {
          if (e) return cb(e, e.errno, null)
          if (st.isDirectory()) return cb(null, 0, null)
          cb(err, err.errno, null)
        })
      })
    })
  })
}

function mkdir(filepath, opts, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof opts === 'function') {
    cb = opts
    opts = { mode: 0o777 }
  } else if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  if (typeof opts === 'number') opts = { mode: opts }
  else if (!opts) opts = {}

  const mode = typeof opts.mode === 'number' ? opts.mode : 0o777

  if (opts.recursive)
    return mkdirRecursive(filepath.replace(/\//g, path.sep), mode, cb)

  const req = getReq()
  req.callback = cb
  binding.mkdir(req.handle, filepath, mode)
}

function mkdirRecursiveSync(filepath, mode) {
  try {
    mkdirSync(filepath, { mode })
  } catch (err) {
    if (err.code !== 'ENOENT' && statSync(filepath).isDirectory()) {
      return
    }

    while (filepath.endsWith(path.sep)) filepath = filepath.slice(0, -1)
    const i = filepath.lastIndexOf(path.sep)
    if (i <= 0) throw err

    mkdirRecursiveSync(filepath.slice(0, i), { mode })

    try {
      mkdirSync(filepath, { mode })
    } catch (err) {
      if (statSync(filepath).isDirectory()) {
        return
      }

      throw err
    }
  }
}

function mkdirSync(filepath, opts) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof opts === 'number') opts = { mode: opts }
  else if (!opts) opts = {}

  const mode = typeof opts.mode === 'number' ? opts.mode : 0o777

  if (opts.recursive)
    return mkdirRecursiveSync(filepath.replace(/\//g, path.sep), mode)

  binding.mkdirSync(filepath, mode)
}

function rmdir(filepath, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  const req = getReq()
  req.callback = cb
  binding.rmdir(req.handle, filepath)
}

function rmdirSync(filepath) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  binding.rmdirSync(filepath)
}

function rmRecursive(filepath, opts, cb) {
  rmdir(filepath, function (err) {
    if (err === null) return cb(null)

    if (err.code !== 'ENOTEMPTY') return cb(err)

    readdir(filepath, function (err, files) {
      if (err) return cb(err)

      if (files.length === 0) return rmdir(filepath, cb)

      let missing = files.length
      let done = false

      for (const file of files) {
        rm(filepath + path.sep + file, opts, function (err) {
          if (done) return

          if (err) {
            done = true
            return cb(err)
          }

          if (--missing === 0) rmdir(filepath, cb)
        })
      }
    })
  })
}

function rm(filepath, opts, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof opts === 'function') {
    cb = opts
    opts = {}
  } else if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  if (!opts) opts = {}

  lstat(filepath, function (err, st) {
    if (err) {
      return cb(err.code === 'ENOENT' && opts.force ? null : err)
    }

    if (st.isDirectory()) {
      if (opts.recursive) return rmRecursive(filepath, opts, cb)

      const err = new Error('is a directory')
      err.code = 'EISDIR'
      return cb(err)
    }

    unlink(filepath, cb)
  })
}

function rmRecursiveSync(filepath, opts) {
  try {
    rmdirSync(filepath)
  } catch (err) {
    if (err.code !== 'ENOTEMPTY') throw err

    const files = readdirSync(filepath)

    for (const file of files) {
      rmSync(filepath + path.sep + file, opts)
    }

    rmdirSync(filepath)
  }
}

function rmSync(filepath, opts) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (!opts) opts = {}

  try {
    const st = lstatSync(filepath)

    if (st.isDirectory()) {
      if (opts.recursive) return rmRecursiveSync(filepath, opts)

      const err = new Error('is a directory')
      err.code = 'EISDIR'
      throw err
    }

    unlinkSync(filepath)
  } catch (err) {
    if (err.code !== 'ENOENT' || !opts.force) throw err
  }
}

function unlink(filepath, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  const req = getReq()
  req.callback = cb
  binding.unlink(req.handle, filepath)
}

function unlinkSync(filepath) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  binding.unlinkSync(filepath)
}

function rename(src, dst, cb) {
  if (typeof src !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' + typeof src + ' (' + src + ')'
    )
  }

  if (typeof dst !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' + typeof dst + ' (' + dst + ')'
    )
  }

  if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  const req = getReq()
  req.callback = cb
  binding.rename(req.handle, src, dst)
}

function renameSync(src, dst) {
  if (typeof src !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' + typeof src + ' (' + src + ')'
    )
  }

  if (typeof dst !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' + typeof dst + ' (' + dst + ')'
    )
  }

  binding.renameSync(src, dst)
}

function copyFile(src, dst, mode, cb) {
  if (typeof mode === 'function') {
    cb = mode
    mode = 0
  }

  if (typeof src !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' + typeof src + ' (' + src + ')'
    )
  }

  if (typeof dst !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' + typeof dst + ' (' + dst + ')'
    )
  }

  const req = getReq()
  req.callback = cb
  binding.copyfile(req.handle, src, dst, mode)
}

function copyFileSync(src, dst, mode = 0) {
  if (typeof src !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' + typeof src + ' (' + src + ')'
    )
  }

  if (typeof dst !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' + typeof dst + ' (' + dst + ')'
    )
  }

  binding.copyfileSync(src, dst, mode)
}

function realpath(filepath, opts, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof opts === 'function') {
    cb = opts
    opts = {}
  } else if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  if (typeof opts === 'string') opts = { encoding: opts }
  else if (!opts) opts = {}

  const { encoding = 'utf8' } = opts

  const data = Buffer.allocUnsafe(binding.sizeofFSPath)

  const req = getReq()

  req.callback = function (err, _) {
    if (err) return cb(err, null)
    let path = data.subarray(0, data.indexOf(0))
    if (encoding !== 'buffer') path = path.toString(encoding)
    cb(null, path)
  }

  binding.realpath(req.handle, filepath, data)
}

function realpathSync(filepath, opts) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof opts === 'string') opts = { encoding: opts }
  else if (!opts) opts = {}

  const { encoding = 'utf8' } = opts

  const data = Buffer.allocUnsafe(binding.sizeofFSPath)

  binding.realpathSync(filepath, data)

  filepath = data.subarray(0, data.indexOf(0))
  if (encoding !== 'buffer') filepath = filepath.toString(encoding)
  return filepath
}

function readlink(filepath, opts, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof opts === 'function') {
    cb = opts
    opts = {}
  } else if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  if (typeof opts === 'string') opts = { encoding: opts }
  else if (!opts) opts = {}

  const { encoding = 'utf8' } = opts

  const data = Buffer.allocUnsafe(binding.sizeofFSPath)

  const req = getReq()

  req.callback = function (err, _) {
    if (err) return cb(err, null)
    let path = data.subarray(0, data.indexOf(0))
    if (encoding !== 'buffer') path = path.toString(encoding)
    cb(null, path)
  }

  binding.readlink(req.handle, filepath, data)
}

function readlinkSync(filepath, opts) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof opts === 'string') opts = { encoding: opts }
  else if (!opts) opts = {}

  const { encoding = 'utf8' } = opts

  const data = Buffer.allocUnsafe(binding.sizeofFSPath)

  binding.readlinkSync(filepath, data)

  filepath = data.subarray(0, data.indexOf(0))
  if (encoding !== 'buffer') filepath = filepath.toString(encoding)
  return filepath
}

function normalizeSymlinkTarget(target, type, filepath) {
  if (isWindows) {
    if (type === 'junction') target = path.resolve(filepath, '..', target)

    if (path.isAbsolute(target)) return path.toNamespacedPath(target)

    return target.replace(/\//g, path.sep)
  }

  return target
}

function symlink(target, filepath, type, cb) {
  if (typeof target !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Target must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof type === 'function') {
    cb = type
    type = null
  } else if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  if (typeof type === 'string') {
    switch (type) {
      case 'file':
        type = 0
        break
      case 'dir':
        type = constants.UV_FS_SYMLINK_DIR
        break
      case 'junction':
        type = constants.UV_FS_SYMLINK_JUNCTION
        break
      default:
        throw typeError(
          'ERR_FS_INVALID_SYMLINK_TYPE',
          'Symlink type must be one of "dir", "file", or "junction". Received "' +
            type +
            '"'
        )
    }
  } else if (typeof type !== 'number') {
    if (isWindows) {
      target = path.resolve(filepath, '..', target)

      stat(target, (err, st) => {
        type =
          err === null && st.isDirectory()
            ? constants.UV_FS_SYMLINK_DIR
            : constants.UV_FS_SYMLINK_JUNCTION

        symlink(target, filepath, type, cb)
      })

      return
    }

    type = 0
  }

  const req = getReq()
  req.callback = cb
  binding.symlink(
    req.handle,
    normalizeSymlinkTarget(target),
    path.toNamespacedPath(filepath),
    type
  )
}

function symlinkSync(target, filepath, type) {
  if (typeof target !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Target must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof type === 'string') {
    switch (type) {
      case 'file':
        type = 0
        break
      case 'dir':
        type = constants.UV_FS_SYMLINK_DIR
        break
      case 'junction':
        type = constants.UV_FS_SYMLINK_JUNCTION
        break
      default:
        throw typeError(
          'ERR_FS_INVALID_SYMLINK_TYPE',
          'Symlink type must be one of "dir", "file", or "junction". Received "' +
            type +
            '"'
        )
    }
  } else if (typeof type !== 'number') {
    if (isWindows) {
      target = path.resolve(filepath, '..', target)

      type = statSync(target).isDirectory()
        ? constants.UV_FS_SYMLINK_DIR
        : constants.UV_FS_SYMLINK_JUNCTION
    } else {
      type = 0
    }
  }

  binding.symlinkSync(
    normalizeSymlinkTarget(target),
    path.toNamespacedPath(filepath),
    type
  )
}

function opendir(filepath, opts, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof opts === 'function') {
    cb = opts
    opts = {}
  } else if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  if (typeof opts === 'string') opts = { encoding: opts }
  else if (!opts) opts = {}

  const data = Buffer.allocUnsafe(binding.sizeofFSDir)

  const req = getReq()

  req.callback = function (err, _) {
    if (err) return cb(err, null)
    cb(null, new Dir(filepath, data, opts))
  }

  binding.opendir(req.handle, filepath, data)
}

function opendirSync(filepath, opts) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof opts === 'string') opts = { encoding: opts }
  else if (!opts) opts = {}

  const data = Buffer.allocUnsafe(binding.sizeofFSDir)
  binding.opendirSync(filepath, data)
  return new Dir(filepath, data, opts)
}

function readdir(filepath, opts, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof opts === 'function') {
    cb = opts
    opts = {}
  } else if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  if (typeof opts === 'string') opts = { encoding: opts }
  else if (!opts) opts = {}

  const { withFileTypes = false } = opts

  opendir(filepath, opts, async (err, dir) => {
    if (err) return cb(err, null)
    const result = []
    for await (const entry of dir) {
      result.push(withFileTypes ? entry : entry.name)
    }
    cb(null, result)
  })
}

function readdirSync(filepath, opts) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof opts === 'string') opts = { encoding: opts }
  else if (!opts) opts = {}

  const { withFileTypes = false } = opts

  const dir = opendirSync(filepath, opts)
  const result = []

  while (true) {
    const entry = dir.readSync()
    if (entry === null) break
    result.push(withFileTypes ? entry : entry.name)
  }

  return result
}

function readFile(filepath, opts, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof opts === 'function') {
    cb = opts
    opts = {}
  } else if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  if (typeof opts === 'string') opts = { encoding: opts }
  else if (!opts) opts = {}

  const { encoding = 'buffer' } = opts

  open(filepath, opts.flag || 'r', function (err, fd) {
    if (err) return cb(err)

    fstat(fd, function (err, st) {
      if (err) return closeAndError(err)

      let buffer = Buffer.allocUnsafe(st.size)
      let len = 0

      read(fd, buffer, loop)

      function loop(err, r) {
        if (err) return closeAndError(err)
        len += r
        if (r === 0 || len === buffer.byteLength) return done()
        read(fd, buffer.subarray(len), loop)
      }

      function done() {
        if (len !== buffer.byteLength) buffer = buffer.subarray(0, len)
        close(fd, function (err) {
          if (err) return cb(err)
          if (encoding !== 'buffer') buffer = buffer.toString(encoding)
          cb(null, buffer)
        })
      }
    })

    function closeAndError(err) {
      close(fd, function () {
        cb(err)
      })
    }
  })
}

function readFileSync(filepath, opts) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof opts === 'string') opts = { encoding: opts }
  else if (!opts) opts = {}

  const { encoding = 'buffer' } = opts

  const fd = openSync(filepath, opts.flag || 'r')

  try {
    const st = fstatSync(fd)

    let buffer = Buffer.allocUnsafe(st.size)
    let len = 0

    while (true) {
      const r = readSync(fd, len ? buffer.subarray(len) : buffer)
      len += r
      if (r === 0 || len === buffer.byteLength) break
    }

    if (len !== buffer.byteLength) buffer = buffer.subarray(0, len)
    if (encoding !== 'buffer') buffer = buffer.toString(encoding)
    return buffer
  } finally {
    try {
      closeSync(fd)
    } catch {}
  }
}

function writeFile(filepath, data, opts, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (
    typeof data !== 'string' &&
    !Buffer.isBuffer(data) &&
    !ArrayBuffer.isView(data)
  ) {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Data must be a string or buffer. Received type ' + typeof data
    )
  }

  if (typeof opts === 'function') {
    cb = opts
    opts = {}
  } else if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  if (typeof opts === 'string') opts = { encoding: opts }
  else if (!opts) opts = {}

  if (typeof data === 'string') data = Buffer.from(data, opts.encoding)

  open(filepath, opts.flag || 'w', opts.mode || 0o666, function (err, fd) {
    if (err) return cb(err)

    write(fd, data, loop)

    function loop(err, w) {
      if (err) return closeAndError(err)
      if (w === data.byteLength) return done()
      write(fd, data.subarray(w), loop)
    }

    function done() {
      close(fd, function (err) {
        if (err) return cb(err)
        return cb(null)
      })
    }

    function closeAndError(err) {
      close(fd, function () {
        cb(err)
      })
    }
  })
}

function writeFileSync(filepath, data, opts) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (
    typeof data !== 'string' &&
    !Buffer.isBuffer(data) &&
    !ArrayBuffer.isView(data)
  ) {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Data must be a string or buffer. Received type ' + typeof data
    )
  }

  if (typeof opts === 'string') opts = { encoding: opts }
  else if (!opts) opts = {}

  if (typeof data === 'string') data = Buffer.from(data, opts.encoding)

  const fd = openSync(filepath, opts.flag || 'w', opts.mode)

  try {
    let len = 0

    while (true) {
      len += writeSync(fd, len ? data.subarray(len) : data)
      if (len === data.byteLength) break
    }
  } finally {
    try {
      closeSync(fd)
    } catch {}
  }
}

function appendFile(filepath, data, opts, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (
    typeof data !== 'string' &&
    !Buffer.isBuffer(data) &&
    !ArrayBuffer.isView(data)
  ) {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Data must be a string or buffer. Received type ' + typeof data
    )
  }

  if (typeof opts === 'function') {
    cb = opts
    opts = {}
  } else if (typeof cb !== 'function') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Callback must be a function. Received type ' +
        typeof cb +
        ' (' +
        cb +
        ')'
    )
  }

  if (typeof opts === 'string') opts = { encoding: opts }
  else if (!opts) opts = {}

  opts = { ...opts }

  if (!opts.flags) opts.flag = 'a'

  return writeFile(filepath, data, opts, cb)
}

function appendFileSync(filepath, data, opts) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (
    typeof data !== 'string' &&
    !Buffer.isBuffer(data) &&
    !ArrayBuffer.isView(data)
  ) {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Data must be a string or buffer. Received type ' + typeof data
    )
  }

  if (typeof opts === 'string') opts = { encoding: opts }
  else if (!opts) opts = {}

  opts = { ...opts }

  if (!opts.flags) opts.flag = 'a'

  return writeFileSync(filepath, data, opts)
}

function watch(filepath, opts, cb) {
  if (typeof filepath !== 'string') {
    throw typeError(
      'ERR_INVALID_ARG_TYPE',
      'Path must be a string. Received type ' +
        typeof filepath +
        ' (' +
        filepath +
        ')'
    )
  }

  if (typeof opts === 'function') {
    cb = opts
    opts = {}
  }

  if (typeof opts === 'string') opts = { encoding: opts }
  else if (!opts) opts = {}

  const watcher = new Watcher(filepath, opts)
  if (cb) watcher.on('change', cb)
  return watcher
}

class Stats {
  constructor(
    dev,
    mode,
    nlink,
    uid,
    gid,
    rdev,
    blksize,
    ino,
    size,
    blocks,
    atimeMs,
    mtimeMs,
    ctimeMs,
    birthtimeMs
  ) {
    this.dev = dev
    this.mode = mode
    this.nlink = nlink
    this.uid = uid
    this.gid = gid
    this.rdev = rdev
    this.blksize = blksize
    this.ino = ino
    this.size = size
    this.blocks = blocks
    this.atimeMs = atimeMs
    this.mtimeMs = mtimeMs
    this.ctimeMs = ctimeMs
    this.birthtimeMs = birthtimeMs
    this.atime = new Date(atimeMs)
    this.mtime = new Date(mtimeMs)
    this.ctime = new Date(ctimeMs)
    this.birthtime = new Date(birthtimeMs)
  }

  isDirectory() {
    return (this.mode & constants.S_IFMT) === constants.S_IFDIR
  }

  isFile() {
    return (this.mode & constants.S_IFMT) === constants.S_IFREG
  }

  isBlockDevice() {
    return (this.mode & constants.S_IFMT) === constants.S_IFBLK
  }

  isCharacterDevice() {
    return (this.mode & constants.S_IFCHR) === constants.S_IFCHR
  }

  isFIFO() {
    return (this.mode & constants.S_IFMT) === constants.S_IFIFO
  }

  isSymbolicLink() {
    return (this.mode & constants.S_IFMT) === constants.S_IFLNK
  }

  isSocket() {
    return (this.mode & constants.S_IFMT) === constants.S_IFSOCK
  }
}

class Dir {
  constructor(path, handle, opts = {}) {
    const { encoding = 'utf8', bufferSize = 32 } = opts

    this._handle = handle
    this._dirents = Buffer.allocUnsafe(binding.sizeofFSDirent * bufferSize)
    this._encoding = encoding
    this._buffer = []
    this._ended = false

    this.path = path
  }

  read(cb) {
    if (!cb) return promisify(this.read.bind(this))

    if (this._buffer.length)
      return queueMicrotask(() => cb(null, this._buffer.shift()))
    if (this._ended) return queueMicrotask(() => cb(null, null))

    const data = []
    const req = getReq()

    req.callback = (err, _) => {
      if (err) return cb(err, null)
      if (data.length === 0) this._ended = true
      else {
        for (const entry of data) {
          let name = Buffer.from(entry.name)
          if (this._encoding !== 'buffer') name = name.toString(this._encoding)
          this._buffer.push(new Dirent(this.path, name, entry.type))
        }
      }

      if (this._ended) return cb(null, null)
      cb(null, this._buffer.shift())
    }

    binding.readdir(req.handle, this._handle, this._dirents, data)
  }

  readSync() {
    if (this._buffer.length) return this._buffer.shift()
    if (this._ended) return null

    const data = []

    binding.readdirSync(this._handle, this._dirents, data)

    if (data.length === 0) this._ended = true
    else {
      for (const entry of data) {
        let name = Buffer.from(entry.name)
        if (this._encoding !== 'buffer') name = name.toString(this._encoding)
        this._buffer.push(new Dirent(this.path, name, entry.type))
      }
    }

    if (this._ended) return null
    return this._buffer.shift()
  }

  close(cb) {
    if (!cb) return promisify(this.close.bind(this))

    const req = getReq()

    req.callback = (err, _) => {
      this._handle = null
      cb(err)
    }

    binding.closedir(req.handle, this._handle)
  }

  closeSync() {
    binding.closedirSync(this._handle)
    this._handle = null
  }

  [Symbol.iterator]() {
    return {
      next: () => {
        if (this._buffer.length) {
          return { done: false, value: this._buffer.shift() }
        }

        if (this._ended) {
          return { done: true }
        }

        const entry = this.readSync()

        if (entry) {
          return { done: false, value: entry }
        }

        this.closeSync()

        return { done: true }
      }
    }
  }

  [Symbol.asyncIterator]() {
    return {
      next: () =>
        new Promise((resolve, reject) => {
          if (this._buffer.length) {
            return resolve({ done: false, value: this._buffer.shift() })
          }

          if (this._ended) {
            return resolve({ done: true })
          }

          this.read((err, entry) => {
            if (err) return reject(err)

            if (entry) {
              return resolve({ done: false, value: entry })
            }

            this.close((err) => (err ? reject(err) : resolve({ done: true })))
          })
        })
    }
  }
}

class Dirent {
  constructor(path, name, type) {
    this.type = type
    this.path = path
    this.name = name
  }

  isFile() {
    return this.type === constants.UV_DIRENT_FILE
  }

  isDirectory() {
    return this.type === constants.UV_DIRENT_DIR
  }

  isSymbolicLink() {
    return this.type === constants.UV_DIRENT_LINK
  }

  isFIFO() {
    return this.type === constants.UV_DIRENT_FIFO
  }

  isSocket() {
    return this.type === constants.UV_DIRENT_SOCKET
  }

  isCharacterDevice() {
    return this.type === constants.UV_DIRENT_CHAR
  }

  isBlockDevice() {
    return this.type === constants.UV_DIRENT_BLOCK
  }
}

class FileWriteStream extends Writable {
  constructor(path, opts = {}) {
    super({ map })

    this.path = path
    this.fd = 0
    this.flags = opts.flags || 'w'
    this.mode = opts.mode || 0o666
  }

  _open(cb) {
    open(this.path, this.flags, this.mode, (err, fd) => {
      if (err) return cb(err)
      this.fd = fd
      cb(null)
    })
  }

  _writev(batch, cb) {
    writev(
      this.fd,
      batch.map(({ chunk }) => chunk),
      cb
    )
  }

  _destroy(err, cb) {
    if (!this.fd) return cb(err)
    close(this.fd, () => cb(err))
  }
}

class FileReadStream extends Readable {
  constructor(path, opts = {}) {
    super()

    this.path = path
    this.fd = 0

    this._offset = opts.start || 0
    this._missing = 0

    if (opts.length) {
      this._missing = opts.length
    } else if (typeof opts.end === 'number') {
      this._missing = opts.end - this._offset + 1
    } else {
      this._missing = -1
    }
  }

  _open(cb) {
    open(this.path, constants.O_RDONLY, (err, fd) => {
      if (err) return cb(err)

      const onerror = (err) => close(fd, () => cb(err))

      fstat(fd, (err, st) => {
        if (err) return onerror(err)
        if (!st.isFile())
          return onerror(new Error(this.path + ' is not a file'))

        this.fd = fd
        if (this._missing === -1) this._missing = st.size

        if (st.size < this._offset) {
          this._offset = st.size
          this._missing = 0
          return cb(null)
        }
        if (st.size < this._offset + this._missing) {
          this._missing = st.size - this._offset
          return cb(null)
        }

        cb(null)
      })
    })
  }

  _read(size) {
    if (!this._missing) {
      this.push(null)
      return
    }

    const data = Buffer.allocUnsafe(Math.min(this._missing, size))

    read(this.fd, data, 0, data.byteLength, this._offset, (err, read) => {
      if (err) return this.destroy(err)

      if (!read) {
        this.push(null)
        return
      }

      if (this._missing < read) read = this._missing
      this.push(data.subarray(0, read))
      this._missing -= read
      this._offset += read
      if (!this._missing) this.push(null)
    })
  }

  _destroy(err, cb) {
    if (!this.fd) return cb(err)
    close(this.fd, () => cb(err))
  }
}

class Watcher extends EventEmitter {
  constructor(path, opts) {
    const { persistent = true, recursive = false, encoding = 'utf8' } = opts

    super()

    this._closed = false
    this._encoding = encoding
    this._handle = binding.watcherInit(
      path,
      recursive,
      this,
      this._onevent,
      this._onclose
    )

    if (!persistent) this.unref()
  }

  _onevent(err, events, filename) {
    if (err) {
      this.close()
      this.emit('error', err)
    } else {
      const path =
        this._encoding === 'buffer'
          ? Buffer.from(filename)
          : Buffer.from(filename).toString(this._encoding)

      if (events & binding.UV_RENAME) {
        this.emit('change', 'rename', path)
      }

      if (events & binding.UV_CHANGE) {
        this.emit('change', 'change', path)
      }
    }
  }

  _onclose() {
    this._handle = null

    this.emit('close')
  }

  close() {
    if (this._closed) return
    this._closed = true

    binding.watcherClose(this._handle)
  }

  ref() {
    if (this._handle) binding.watcherRef(this._handle)
    return this
  }

  unref() {
    if (this._handle) binding.watcherUnref(this._handle)
    return this
  }

  [Symbol.asyncIterator]() {
    const buffer = []
    let done = false
    let error = null
    let next = null

    this.on('change', (eventType, filename) => {
      if (next) {
        next.resolve({ done: false, value: { eventType, filename } })
        next = null
      } else {
        buffer.push({ eventType, filename })
      }
    })
      .on('error', (err) => {
        done = true
        error = err

        if (next) {
          next.reject(error)
          next = null
        }
      })
      .on('close', () => {
        done = true

        if (next) {
          next.resolve({ done })
          next = null
        }
      })

    return {
      next: () =>
        new Promise((resolve, reject) => {
          if (error) return reject(error)

          if (buffer.length)
            return resolve({ done: false, value: buffer.shift() })

          if (done) return resolve({ done })

          next = { resolve, reject }
        })
    }
  }
}

exports.promises = {}

function typeError(code, message) {
  const error = new TypeError(message)
  error.code = code
  return error
}

function noop() {}

exports.access = access
exports.appendFile = appendFile
exports.chmod = chmod
exports.close = close
exports.copyFile = copyFile
exports.exists = exists
exports.fchmod = fchmod
exports.fstat = fstat
exports.ftruncate = ftruncate
exports.lstat = lstat
exports.mkdir = mkdir
exports.open = open
exports.opendir = opendir
exports.read = read
exports.readFile = readFile
exports.readdir = readdir
exports.readlink = readlink
exports.readv = readv
exports.realpath = realpath
exports.rename = rename
exports.rm = rm
exports.rmdir = rmdir
exports.stat = stat
exports.symlink = symlink
exports.unlink = unlink
exports.watch = watch
exports.write = write
exports.writeFile = writeFile
exports.writev = writev

exports.accessSync = accessSync
exports.appendFileSync = appendFileSync
exports.chmodSync = chmodSync
exports.closeSync = closeSync
exports.copyFileSync = copyFileSync
exports.existsSync = existsSync
exports.fchmodSync = fchmodSync
exports.fstatSync = fstatSync
exports.lstatSync = lstatSync
exports.mkdirSync = mkdirSync
exports.openSync = openSync
exports.opendirSync = opendirSync
exports.readFileSync = readFileSync
exports.readSync = readSync
exports.readdirSync = readdirSync
exports.readlinkSync = readlinkSync
exports.realpathSync = realpathSync
exports.renameSync = renameSync
exports.rmSync = rmSync
exports.rmdirSync = rmdirSync
exports.statSync = statSync
exports.symlinkSync = symlinkSync
exports.unlinkSync = unlinkSync
exports.writeFileSync = writeFileSync
exports.writeSync = writeSync

exports.promises.access = promisify(access)
exports.promises.appendFile = promisify(appendFile)
exports.promises.chmod = promisify(chmod)
exports.promises.copyFile = promisify(copyFile)
exports.promises.lstat = promisify(lstat)
exports.promises.mkdir = promisify(mkdir)
exports.promises.opendir = promisify(opendir)
exports.promises.readFile = promisify(readFile)
exports.promises.readdir = promisify(readdir)
exports.promises.readlink = promisify(readlink)
exports.promises.realpath = promisify(realpath)
exports.promises.rename = promisify(rename)
exports.promises.rm = promisify(rm)
exports.promises.rmdir = promisify(rmdir)
exports.promises.stat = promisify(stat)
exports.promises.symlink = promisify(symlink)
exports.promises.unlink = promisify(unlink)
exports.promises.writeFile = promisify(writeFile)

exports.promises.watch = watch // Already async iterable

exports.Stats = Stats
exports.Dir = Dir
exports.Dirent = Dirent
exports.Watcher = Watcher

exports.ReadStream = FileReadStream
exports.createReadStream = function createReadStream(path, opts) {
  return new FileReadStream(path, opts)
}

exports.WriteStream = FileWriteStream
exports.createWriteStream = function createWriteStream(path, opts) {
  return new FileWriteStream(path, opts)
}

function promisify(fn) {
  return function (...args) {
    return new Promise((resolve, reject) => {
      fn(...args, function (err, res) {
        if (err) return reject(err)
        resolve(res)
      })
    })
  }
}

function map(data) {
  return typeof data === 'string' ? Buffer.from(data) : data
}