From 63f6dd355c640016b34484e0f659c00c80256826 Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Thu, 17 Oct 2024 08:05:11 -0700 Subject: [PATCH 01/14] fix(jupyter): update to the new logo (#26353) Follow-up to #26084 Update to the new logo found here: https://github.com/denoland/docs/blob/main/static/img/logo.svg --- .../jupyter/resources/deno-logo-32x32.png | Bin 1029 -> 1386 bytes .../jupyter/resources/deno-logo-64x64.png | Bin 2066 -> 2913 bytes cli/tools/jupyter/resources/deno-logo-svg.svg | 18 +++++++++++++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/cli/tools/jupyter/resources/deno-logo-32x32.png b/cli/tools/jupyter/resources/deno-logo-32x32.png index 97871a02ee768c20879cadd36be22e7a6c3d3227..d59f251a2430183712d75b75aa60073119b55e36 100644 GIT binary patch delta 1367 zcmV-d1*rOk2$r%PhQU*F`C)VoynxUBw2e2(c)OLVw~<9W0WVgn&&7X@c$A z`54DGPKamT>B64Yi9eap9O*7*?tAx~``(>5_nAn-Aoc>x%XR>H z0KWjZ0^oVw=zk<@nVIBelIuc3XEqthu>)IS>d0ND7h@BwLLNaFR@FnwD_6T#vU)yxGp2 zIWwVYT9V`ok{@p+;9Zh`6$*t|dwYBQ|B~z0)zuYMRev=`GEw5FDquIskgBSQ&d$zg zy?~vaopXbOgTtXv$U8bZ>bZFF;;1A^TD>_P9UXI}mGG46t3tqcB*m#yrzYy`YdRDP zc}c4Id_F%kG!$H1TuhPV>gwu@%jFuYC*u71^W&wO_N*Q!Jx-#wXW2PRk_kt0XKLWoy7 z%iP>ttlXbUr7~qAsH$q*?RNXCZHwJ*Un03m@<(03zEbvqYUHi0tqYo_EtC_a)9Doe zs{m*=o1>bh306TamwVdV+gq_Mfk41dGE1@vB!9ml$z#WkReW+LlS$0a&j+gozjf=@ z_}Q~(?@dllzNqFe$DKQO?w7|sdi3aY$<+&z*Hu-G1E@IR+-|pjtKdd);lhQW9#@uS zRS1zGc?~2V&d$zO6rU{1>e|}c^PLhfHa6xhj~|Q0#z_Vx0FIfNnK#&k{{H?47K^2A z^MCudMMFb_RQ?=^L{b3WmjE;`EG$&~65Ve1?wukB0I%0uD1S~Q5+(r65&)B?X&VOb z-@iYlD9R^0$X~d6_3DPz)HF>7V3Gi&Z8n>!+}YRH7Xh$qhsf1SmoAMyd-lvx{!&pC z6M$vC2sfMow{PFRZ=|q<5U(CRdgLn<3V$!FxYcAbIo8wDGX+$hFQp=UU#nM@{YQ&W>{v)SIs=kse$r!(H(-oCQ5w6te>dfL9cynL|6JlgGc1;DQ$ zd5$Cx9Xb@z)uq$vDWjiPCX;z$Hk%98vaDr2Jv~oL)4$e_;=I@E_3Hz&+3eFgoPQfI zFficR4mki04-elbnPcfF2FagEt}2R>0>E4@7cnZ}1)YTt_$@_RF%Y)*|oj?tZMQ>fbDO#v9u6T5b94c15I8smar)PfzGwkR(aki4!Lt zkB^UgSX}l54YcLDDI`PN6+x0hi0u0M`aFxDnGO$&t84gX1QxGTS6IAWy-+RQu-F0r z+lFsPfNpIhuW!m|E96LCBl+Q0@r~S$buI}04x)HHx0QTJ051WI0l0_a^*n1N Z=s#HLK delta 1007 zcmV@2T^ooDMyZtshFT=7659KBqmbbfaX7-yOJ1-;x(trH?d{PdZlGBnij%`iJ zeCEpxm98h`SJ+9=7$|M?MV`D><%jKc{rBX>OEiFM-L(6u@fVQ$h&-Hp; z2|qbG@%OyFy(wQO5{W>ryt=wl@sdpXA+#mq;$jE^?Wa>7XxVI5jhtYN?W9C_2S$=x z3Wb6~htVmIi5b4YVTT~o*Pv0#K|4D;YK%ssYGheKG=Hac{qPaczRB2v%@J5mqW9ps z5tN~LAQu#2@$>Vuf)4*N!g_v!?d@$97uW}wiQDbAI668~sC7CWadUGspbfIHZ*Omp zPZyO7bcIVoIKhSyfkuK52en!a(QG{q=E0Bw#10P+hvLFQNu^u<9H3x4K0aCt$^QPn z=OXMsK7T&;`u+aEJf2aUNCt`)2t~}0e*gn+S5{WkhO@J?fen~9g>%Z~a%x|tQW;vK zN;$4dA`3DEtr<8dBIFhu0dg0_2u7^;dcDzA5|-@l?s_ua24Dk^I(-lV3P2Gf!4F+D zk7r@R0$2eT#%T~Ka>YZ~1}j`LJ3A|;K~xv`Gk-Gd>FLRS8Sw&S=JRKWmvV{~E;nq; zXIKX-kvLOejp7#{3l-9}k0gdwQ?UonEJDhP7GEF+rVB9Taf?txe=CKeVJ{F4I> zT&LA)RT)L=lTx?a75De|N-<=y1N{!^QvBe`bJTeA3MMEKs8}CWD=401R@s)m&!Bsi zMt`(<(`j};W-xLeLb#*WJ2QcrR_LZ6tWJ8h;?zKb(rJ=x;5wB7x^-@vJK!tcj+ySc zyuAEWHb57(>pTtflKTVqrR?L#_tc`9_dw8EL7AX+=CW^7i&N}ZbMUNiI!NGC>MLIKV5z7Z$IJXjZ;rat-KsePQfTx2w{fj&+{LDT4e deV+dlU;w$NTAD^@LGb_p002ovPDHLkV1o4L*#!Uq diff --git a/cli/tools/jupyter/resources/deno-logo-64x64.png b/cli/tools/jupyter/resources/deno-logo-64x64.png index 1b9444ef6370d846118422f31981f9595f142347..37e98abafdf5dbf2b957cab54c4f0e05117cbc54 100644 GIT binary patch delta 2906 zcmV-g3#Igu5aAY(B!3BTNLh0L00Jcd00Jce!R(iT0000PbVXQnQ*UN;cVTj60C#tH zE@^ISb7Ns}WiD@WXPfRk8UO$a?ny*JRCt`tTWw5KR~mlqy~FS^Ao8)LYD}@LO9fY~ zg{Fa6V=Wt-&2Hs~AB|fuYByb5x7%(vso7*VYa1nP*On%$ZGQ}j8`8LIsZnD6(NI3* zOGK)uxONZ~D4-xS!ySfUn7Q}eA9F8zyfDn2%M9$3oG^!T-gDmfx$imm+;iT;5fLJc zB>?i2v`Wb@2N18RX9qB-q)q_sN*aiya$E!m-~c=epa{Sg0GV@a?yWtx;3$B90uT-A`3S;6Kmve20r-RR`w_^p zJp98CKiq0+YLaSdYU0}3+8!Sq987h&Tybo@n3$M}%*@Qe?CfmE^Upu;C@d_DOHWUK zlIQuK%+RM3z$XAcR(=}?5gAQ95xq`C4@d)_&)0wY^ndA^_3PJP5Kt!#g)4UMy8zOSDy*r)GjxAfZTnV?)fl^UXQKQS{x~U<-Nkp$ik^r8F4zWGT zvOId^$bXUVc%Jvpm+`De96o%wUY2E%RY}oN_mI$Qd=wFV#Tq|4I(j8NJ-v6n4cDg3 z%*<|46t8K#;VVVwun-VMM5o!-TUuJGqoShR3vIlbOeT}Nxw*MsL&Dd3NYLZrMzOsM+{^mV<_OxbYW!()Y3FYPG>?>L(q8*_U@FX#>y88P1IUApx zoNPaF;zX_8Zof`MKGMMB@${CLm)9*`ym&MmBs4WOU0|9YC88(v62KGD)P!QQ*{+#P zCV#^Vo7B|Qk#E2KwoaDiN7fF3Aj`7-i!Z*YGn>txxeyQ?9qkz%9lgOc{T&f;dI)%f zQO1#xkuhLYiTB=nuTheuhgu^$J3H^KTeq$uJ3G6%qN3vanTQx4AGdDUu%UAXGD{rVlA=ZDz(rKP1U0UW}6{`~p6 z*x1-{qa>7-m9csZ5>fnY1RPXrilTUv=Xp8Q4igg-#Q^JdA|gslOS`O<5d>jSmgPVq zG%+!8XY=OG*NhOr^SmUAVkgtwUjh-3NJLZ1%9mbxDde?%Qc{vb6h-YCeF|M&U4Qoi z$#!>l4+Lbu52>!Mu0A0lVJuX=wr$(i&a~qo#tT>iey`RT8XCH4#O;TNhp*0N!HE+m zS_8>jt=9e^1o&Byq~T9L{j@&RNpfUlgk3Jbp(TJLqFZWdadB~5s04I&b~Xl6zPh?P zkQ;kkE|*&m0V*aZCwmVZIMB%Rynm!ezumicw=&{46`Q9B$X81|9#0R)aUqTUk3RZH zyAGcbk|aq{QBgfw`KOwq#0+n69d^w59F699{u4L%;?c0lkHEuGQd^>jRSbsG~9V3dKo}QRM zWdj2POPK639>8NtW^!U;V%pJ`7CU$Dydnrf+8lL^D4Ls_1A4O4>5Kv3RkA4{A{P;n zEXz*lnEh|xz8&b(_5T8Hx4S!-4YDlTmFiCRM0PS5v$3(U6B!v9KM(DB%r7b{D|>ZR zGb?|2(Es-S#t~!@^ndEBuU=Z92dfLRERX2fQzW8EMa0PiuqkZB#>dCIXQOTJ-o3My zFEHfd;^I3Fhhx!fm11LKT>zq#>;n){v&!Jgl`CgG>T)`r0XI7q7@h+M4m8fDyPDRl zS;N}Uzys)4*~!bx8w|vp#bR;A$H%WU^lTysx7*!SSXgL1e1G_Gfu8zn*RCCAvI9K) zVJ859O`A3abn)EW+#UcX!3zP`RoIXO9ZjvYHzfA{X)i$0&PAO0s543f=e>v;Fw zcbgLv6Vt1!t5*jX@#BRTUhp#68zum4YA!!NKWe&&AGx`?4!xb|#q#CLpFMNt%*Fit z{Oo=E_N4-TPk&pxc5UD4)vHIJdg>`pdV0ESHk&yB=j zN{SFE#ol@69sA_uq&axax#8`%-)4vLSpZPC#GX}s!sqk#3xY5$>#sgIz_%F0@8Uy(?fy3%0)uK zoSdA9%q=Po9Xix#u~^g{tos0tJW_#ZT7blF)qx_*@|gO8R8mqBk+*=|ZZ~x7ttVw> zX7*?TDT4+`s0XDrs)WsE>oA+mlS`H?8KTHQTCrk9f4Ge|nM@wL-Oio}SCh^V84yuA zF@LcFt*xyM0Lbh0>N>&>6;)MLVSf{}w6w6NRv`sRsc8o>@pj*Q^G%)C>-{kj1Q3xs zJ3ISsxQze&^UrIUwq+vP8pzJs2mtv{Q0E1rDBes=Ocdv0ILC4FsZ*zFHBBF!&BkB^ z@I>_Q`5=JEKi?P$fdN2iX=x98dU{$lO@E%G8OA+@Kmf=ea9lYL_7f42$K$#C;)^d{ z*K2r8OpLp%tgKF!Wt*m{Q$%D6W}hAcR1)UR6NpIe?d`o(Qc`j$E-uccHQH=8d$w)c z*526IST9M^v@1JA^e@sGE+5LJDIUPz0Q?#Lt8w!Mx7+RRcQ_o5=;&y(#bQZ_j(?6` z0$`@&F9PrZfWIoggTlzIvF!kkV}=`I3ye_!uL1Zf6d|M6=AHmh4q%fJ;Rv7_z#ag4 zPK6$U(J!$c0EGZv2k^jP8{xnpfV}{I0RzSZ;kh^&58#hVny+Eufd8xR69BHcG!)LO zvk3rRf&c1kqz+KK0Q>{~;WrkJ22&$-c{&&Vi_^ab6M|z9H{jpEJOlr2@~|o#2?Df8 zgFg`K8TiRq3V&FRHo(dO|1n)Zfd9b1PuT`w7!kyO0ZS}opL7BL0000007*qoM6N<$ Ef+|pZC;$Ke delta 2052 zcmV+f2>bWp7LpKx7J=~pOlxd)YaAX zvJbQ@YmKb68@d!(yJ78|wWEiJhodCtmr6k4TK4gswQs>^98zHIH$Is`9zUf4f|dT0 zwef^Om1k|08KM}+U|-@;Jd?@f*w?>x@E#c%@dgG4yt%nK|M%tPrS!V)?ryKEtIJEJ zQr`Xjz3dX)V}Fv}6Zie+<>lq3A_xeezGAcDH8nNf_4W1p?}dd0Z+(4TdhOoco^Jri z>FKFAI5=p7h#z5HBOOwC`1Z9e-240cOMXK!m7z#^$jQlx&G4ARbug#o4Zv;$xbLu5 zYug8~CV6^#@-8keq}R2zwOJWTBAClgFgGWua)kV$MSmD86Mj_>4i1#p!8rVPO9Ygl zfz1>1`)cbFRY@z4Rc&Z!u!1#^F*-Wx_t6~7Y+S(G403AA<5fCtHyNzG--$mgk z6xINMIr4~Nu~-%l69nt534-$-I1B`KJE&C zjDbEd9KN@IguHV?$@b7_$AcRDcUZ9|?(u7dK+8bIn zn}2;HnV+A3ll@)%pV+3Rrry|u-@?e_T z0XvTK#S4CQcnh2SA2A#88VC~k!iD)U2aGd0IqAoFAkh>_#H`c61+_c}lM_@UQ2ld<7@mn z3^_yRd>I1xuwz4VQIyB{LqkJ8#7)T*?gO_{-6uqN{tp3STy%tyc2YW_y+0r0;XWpT zv9b9Q4JOXpWKha`(tPNHA_RY%qyt1H*86Pb`T4ozw~deUNYJIr4!MbMfGqt&{C{*= zeSSmaD)sgCB~isCV!-IU&x?H>((^1xuu-h3w8&o;k!`GurfZ)kzajX z(N!XiRs5cx2ErgFEAnI+#Ry?hgjGj>#Cu+0S(G3H^I0a<3R1|7L|?MmyYmk)DkaEC zC@d3y$&ibLiqP8A(^F!wv{+t}?SI{gc+kjidA_KW@B|E{WXg-xD>0k3DvK{IEtU8v z;pWO+JvQ9YfqD5n)+fPb5?^*_X6 z)%LW~Za@)&MB%bGFu-{fA3{j1v=Kz2mWcuZ=U+0JjOQsz;WINc?@0s(kSPiy<@0&} zJWpq5r&nEFT@fQ(U0s!&JA#X%VLq7m_V%`{+hSUG4v!}}73(8A#2y_6!af~tQ3-OC z@c`z6Ge#<|g6^rHFtFoE&VTd)R;bBAA=#lO+!V@_P@au`v}L7DOVWbqgavRAoH-mO z1Hc8G&)2v-B&8MNp9_$I;}UVBV~`1EfIRI53SkW3Qih@kB1RG_fyWgM91Ku+PlSTA zG9h(bc{)GDXM(??#NM#a?Nld5lh_z>vRal32Y=DvD9k1#%2QjA zS6op8sI)-A5r58D+zTv*v9{~#W1=8W^8F7+!J@Jt0u-=_koPRl|AzH{n7GvFbjed3JRsSc7XuxX zqAiODe1HLPSi08M|5Qrp>I+c}0QbRTc3}+o`>^T6xUnV}41dsMg9K9nMfc)FSR$lt zxS%}d+QjC5W9o19Tp%!l(H%@BG+pxKiU1f8kHx?-0)hIlw|!y4`jr_B$XbUkI`IB& z$_{0lAX_7#HbKP~3EOwLyg=pr+T6>_%kNAkgF50LtW}F}#A?70xm?aWJ3A|bYfDRu z?;5MCt7T*N^?mhu&CSiq6-S@3u`$2@>+7rN?;}ic=Q4F-ug!t+U1PggV4=tKK#L~n z0wP1dHW&HL(cp{`j-U3%k;j84kQjj!4K9J4elhC1#c{qmi-hSf?_*w_{T0_s)&IXd io$zXX`q$_G6<`3epvB#igj8w(0000 + + + + + + + + + + + From 33ceae4ce5c9f95bde52d5453a6c41488fa36846 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Thu, 17 Oct 2024 17:19:43 +0200 Subject: [PATCH 02/14] fix(runtime): send ws ping frames from inspector server (#26352) Every 30 seconds the websocket server will now send a ping frame, so that the TCP socket stays alive. --- runtime/inspector_server.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/runtime/inspector_server.rs b/runtime/inspector_server.rs index 1f8cd5e714..33b2ab8727 100644 --- a/runtime/inspector_server.rs +++ b/runtime/inspector_server.rs @@ -23,6 +23,7 @@ use deno_core::InspectorSessionProxy; use deno_core::JsRuntime; use fastwebsockets::Frame; use fastwebsockets::OpCode; +use fastwebsockets::Payload; use fastwebsockets::WebSocket; use hyper::body::Bytes; use hyper_util::rt::TokioIo; @@ -33,6 +34,7 @@ use std::pin::pin; use std::process; use std::rc::Rc; use std::thread; +use std::time::Duration; use tokio::net::TcpListener; use tokio::sync::broadcast; use uuid::Uuid; @@ -393,8 +395,13 @@ async fn pump_websocket_messages( inbound_tx: UnboundedSender, mut outbound_rx: UnboundedReceiver, ) { + let mut ticker = tokio::time::interval(Duration::from_secs(30)); + 'pump: loop { tokio::select! { + _ = ticker.tick() => { + let _ = websocket.write_frame(Frame::new(true, OpCode::Ping, None, Payload::Borrowed(&[]))).await; + } Some(msg) = outbound_rx.next() => { let msg = Frame::text(msg.content.into_bytes().into()); let _ = websocket.write_frame(msg).await; From 3b62e05062805d45fd626ac8e413e9b7aee7c3a1 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira <118899497+lucasfernog-crabnebula@users.noreply.github.com> Date: Thu, 17 Oct 2024 13:39:18 -0300 Subject: [PATCH 03/14] feat(permissions): expose PermissionPrompter and set_prompter function (#26327) when defining a custom runtime, it might be useful to define a custom prompter - for instance when you are not relying on the terminal and want a GUI prompter instead --- runtime/permissions/lib.rs | 2 ++ runtime/permissions/prompter.rs | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs index 2904242dae..0cd489b179 100644 --- a/runtime/permissions/lib.rs +++ b/runtime/permissions/lib.rs @@ -39,6 +39,8 @@ use prompter::PromptResponse; use prompter::PERMISSION_EMOJI; pub use prompter::set_prompt_callbacks; +pub use prompter::set_prompter; +pub use prompter::PermissionPrompter; pub use prompter::PromptCallback; /// Fast exit from permission check routines if this permission diff --git a/runtime/permissions/prompter.rs b/runtime/permissions/prompter.rs index 3d7536928e..316911edc1 100644 --- a/runtime/permissions/prompter.rs +++ b/runtime/permissions/prompter.rs @@ -80,6 +80,10 @@ pub fn set_prompt_callbacks( *MAYBE_AFTER_PROMPT_CALLBACK.lock() = Some(after_callback); } +pub fn set_prompter(prompter: Box) { + *PERMISSION_PROMPTER.lock() = prompter; +} + pub type PromptCallback = Box; pub trait PermissionPrompter: Send + Sync { @@ -476,8 +480,4 @@ pub mod tests { STUB_PROMPT_VALUE.store(value, Ordering::SeqCst); } } - - pub fn set_prompter(prompter: Box) { - *PERMISSION_PROMPTER.lock() = prompter; - } } From ed13efc4ac3ed5262d025bd7228785553fecff1c Mon Sep 17 00:00:00 2001 From: Leo Kettmeir Date: Thu, 17 Oct 2024 09:43:04 -0700 Subject: [PATCH 04/14] refactor(ext/net): use concrete error type (#26227) --- Cargo.lock | 13 +-- Cargo.toml | 2 +- ext/cache/lib.rs | 2 +- ext/net/Cargo.toml | 1 + ext/net/io.rs | 45 +++++---- ext/net/ops.rs | 218 +++++++++++++++++++++++++--------------- ext/net/ops_tls.rs | 107 ++++++++++---------- ext/net/ops_unix.rs | 67 ++++++------ ext/net/resolve_addr.rs | 5 +- runtime/errors.rs | 48 +++++++++ 10 files changed, 317 insertions(+), 191 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c8807b65d5..043bcc2035 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1423,9 +1423,9 @@ dependencies = [ [[package]] name = "deno_core" -version = "0.313.0" +version = "0.314.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f36be738d78e39b6603a6b07f1cf91e28baf3681f87205f07482999e0d0bc2" +checksum = "1fcd11ab87426c611b7170138a768dad7170c8fb66d8095b773d25e58fd254ea" dependencies = [ "anyhow", "bincode", @@ -1797,6 +1797,7 @@ dependencies = [ "rustls-tokio-stream", "serde", "socket2", + "thiserror", "tokio", "trust-dns-proto", "trust-dns-resolver", @@ -1913,9 +1914,9 @@ dependencies = [ [[package]] name = "deno_ops" -version = "0.189.0" +version = "0.190.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8f998ad1d5b36064109367ffe67b1088385eb3d8025efc95e445bc013a147a2" +checksum = "a48a3e06cace18a2c49e148da067678c6af80e70757a8c3991301397cf6b9919" dependencies = [ "proc-macro-rules", "proc-macro2", @@ -6211,9 +6212,9 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.222.0" +version = "0.223.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27130b5cd87f6f06228940a1f3a7ecc988ea13d1bede1398a48d74cb59dabc9a" +checksum = "c127bb9f2024433d06789b242477c808fd7f7dc4c3278576dd5bc99c4e5c75ff" dependencies = [ "num-bigint", "serde", diff --git a/Cargo.toml b/Cargo.toml index e8919eade7..b625643510 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ repository = "https://github.com/denoland/deno" [workspace.dependencies] deno_ast = { version = "=0.42.2", features = ["transpiling"] } -deno_core = { version = "0.313.0" } +deno_core = { version = "0.314.1" } deno_bench_util = { version = "0.166.0", path = "./bench_util" } deno_lockfile = "=0.23.1" diff --git a/ext/cache/lib.rs b/ext/cache/lib.rs index 08661c3493..b9cc5427c2 100644 --- a/ext/cache/lib.rs +++ b/ext/cache/lib.rs @@ -28,7 +28,7 @@ pub enum CacheError { Resource(deno_core::error::AnyError), #[error(transparent)] Other(deno_core::error::AnyError), - #[error(transparent)] + #[error("{0}")] Io(#[from] std::io::Error), } diff --git a/ext/net/Cargo.toml b/ext/net/Cargo.toml index 4ba4fb315e..a578620721 100644 --- a/ext/net/Cargo.toml +++ b/ext/net/Cargo.toml @@ -21,6 +21,7 @@ pin-project.workspace = true rustls-tokio-stream.workspace = true serde.workspace = true socket2.workspace = true +thiserror.workspace = true tokio.workspace = true trust-dns-proto = "0.23" trust-dns-resolver = { version = "0.23", features = ["tokio-runtime", "serde-config"] } diff --git a/ext/net/io.rs b/ext/net/io.rs index f3aed3fcbd..2907fa398b 100644 --- a/ext/net/io.rs +++ b/ext/net/io.rs @@ -1,7 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::error::generic_error; -use deno_core::error::AnyError; +use deno_core::futures::TryFutureExt; use deno_core::AsyncMutFuture; use deno_core::AsyncRefCell; use deno_core::AsyncResult; @@ -69,25 +68,36 @@ where pub async fn read( self: Rc, data: &mut [u8], - ) -> Result { + ) -> Result { let mut rd = self.rd_borrow_mut().await; let nread = rd.read(data).try_or_cancel(self.cancel_handle()).await?; Ok(nread) } - pub async fn write(self: Rc, data: &[u8]) -> Result { + pub async fn write( + self: Rc, + data: &[u8], + ) -> Result { let mut wr = self.wr_borrow_mut().await; let nwritten = wr.write(data).await?; Ok(nwritten) } - pub async fn shutdown(self: Rc) -> Result<(), AnyError> { + pub async fn shutdown(self: Rc) -> Result<(), std::io::Error> { let mut wr = self.wr_borrow_mut().await; wr.shutdown().await?; Ok(()) } } +#[derive(Debug, thiserror::Error)] +pub enum MapError { + #[error("{0}")] + Io(std::io::Error), + #[error("Unable to get resources")] + NoResources, +} + pub type TcpStreamResource = FullDuplexResource; @@ -100,7 +110,7 @@ impl Resource for TcpStreamResource { } fn shutdown(self: Rc) -> AsyncResult<()> { - Box::pin(self.shutdown()) + Box::pin(self.shutdown().map_err(Into::into)) } fn close(self: Rc) { @@ -109,31 +119,30 @@ impl Resource for TcpStreamResource { } impl TcpStreamResource { - pub fn set_nodelay(self: Rc, nodelay: bool) -> Result<(), AnyError> { - self.map_socket(Box::new(move |socket| Ok(socket.set_nodelay(nodelay)?))) + pub fn set_nodelay(self: Rc, nodelay: bool) -> Result<(), MapError> { + self.map_socket(Box::new(move |socket| socket.set_nodelay(nodelay))) } pub fn set_keepalive( self: Rc, keepalive: bool, - ) -> Result<(), AnyError> { - self - .map_socket(Box::new(move |socket| Ok(socket.set_keepalive(keepalive)?))) + ) -> Result<(), MapError> { + self.map_socket(Box::new(move |socket| socket.set_keepalive(keepalive))) } #[allow(clippy::type_complexity)] fn map_socket( self: Rc, - map: Box Result<(), AnyError>>, - ) -> Result<(), AnyError> { + map: Box Result<(), std::io::Error>>, + ) -> Result<(), MapError> { if let Some(wr) = RcRef::map(self, |r| &r.wr).try_borrow() { let stream = wr.as_ref().as_ref(); let socket = socket2::SockRef::from(stream); - return map(socket); + return map(socket).map_err(MapError::Io); } - Err(generic_error("Unable to get resources")) + Err(MapError::NoResources) } } @@ -153,7 +162,9 @@ impl UnixStreamResource { unreachable!() } #[allow(clippy::unused_async)] - pub async fn shutdown(self: Rc) -> Result<(), AnyError> { + pub async fn shutdown( + self: Rc, + ) -> Result<(), deno_core::error::AnyError> { unreachable!() } pub fn cancel_read_ops(&self) { @@ -170,7 +181,7 @@ impl Resource for UnixStreamResource { } fn shutdown(self: Rc) -> AsyncResult<()> { - Box::pin(self.shutdown()) + Box::pin(self.shutdown().map_err(Into::into)) } fn close(self: Rc) { diff --git a/ext/net/ops.rs b/ext/net/ops.rs index 5248493f41..0f92dead0c 100644 --- a/ext/net/ops.rs +++ b/ext/net/ops.rs @@ -6,10 +6,6 @@ use crate::resolve_addr::resolve_addr; use crate::resolve_addr::resolve_addr_sync; use crate::tcp::TcpListener; use crate::NetPermissions; -use deno_core::error::bad_resource; -use deno_core::error::custom_error; -use deno_core::error::generic_error; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::CancelFuture; @@ -43,6 +39,7 @@ use trust_dns_proto::rr::record_type::RecordType; use trust_dns_resolver::config::NameServerConfigGroup; use trust_dns_resolver::config::ResolverConfig; use trust_dns_resolver::config::ResolverOpts; +use trust_dns_resolver::error::ResolveError; use trust_dns_resolver::error::ResolveErrorKind; use trust_dns_resolver::system_conf; use trust_dns_resolver::AsyncResolver; @@ -68,11 +65,69 @@ impl From for IpAddr { } } -pub(crate) fn accept_err(e: std::io::Error) -> AnyError { +#[derive(Debug, thiserror::Error)] +pub enum NetError { + #[error("Listener has been closed")] + ListenerClosed, + #[error("Listener already in use")] + ListenerBusy, + #[error("Socket has been closed")] + SocketClosed, + #[error("Socket has been closed")] + SocketClosedNotConnected, + #[error("Socket already in use")] + SocketBusy, + #[error("{0}")] + Io(#[from] std::io::Error), + #[error("Another accept task is ongoing")] + AcceptTaskOngoing, + #[error("{0}")] + Permission(deno_core::error::AnyError), + #[error("{0}")] + Resource(deno_core::error::AnyError), + #[error("No resolved address found")] + NoResolvedAddress, + #[error("{0}")] + AddrParse(#[from] std::net::AddrParseError), + #[error("{0}")] + Map(crate::io::MapError), + #[error("{0}")] + Canceled(#[from] deno_core::Canceled), + #[error("{0}")] + DnsNotFound(ResolveError), + #[error("{0}")] + DnsNotConnected(ResolveError), + #[error("{0}")] + DnsTimedOut(ResolveError), + #[error("{0}")] + Dns(#[from] ResolveError), + #[error("Provided record type is not supported")] + UnsupportedRecordType, + #[error("File name or path {0:?} is not valid UTF-8")] + InvalidUtf8(std::ffi::OsString), + #[error("unexpected key type")] + UnexpectedKeyType, + #[error("Invalid hostname: '{0}'")] + InvalidHostname(String), // TypeError + #[error("TCP stream is currently in use")] + TcpStreamBusy, + #[error("{0}")] + Rustls(#[from] deno_tls::rustls::Error), + #[error("{0}")] + Tls(#[from] deno_tls::TlsError), + #[error("Error creating TLS certificate: Deno.listenTls requires a key")] + ListenTlsRequiresKey, // InvalidData + #[error("{0}")] + RootCertStore(deno_core::anyhow::Error), + #[error("{0}")] + Reunite(tokio::net::tcp::ReuniteError), +} + +pub(crate) fn accept_err(e: std::io::Error) -> NetError { if let std::io::ErrorKind::Interrupted = e.kind() { - bad_resource("Listener has been closed") + NetError::ListenerClosed } else { - e.into() + NetError::Io(e) } } @@ -81,15 +136,15 @@ pub(crate) fn accept_err(e: std::io::Error) -> AnyError { pub async fn op_net_accept_tcp( state: Rc>, #[smi] rid: ResourceId, -) -> Result<(ResourceId, IpAddr, IpAddr), AnyError> { +) -> Result<(ResourceId, IpAddr, IpAddr), NetError> { let resource = state .borrow() .resource_table .get::>(rid) - .map_err(|_| bad_resource("Listener has been closed"))?; + .map_err(|_| NetError::ListenerClosed)?; let listener = RcRef::map(&resource, |r| &r.listener) .try_borrow_mut() - .ok_or_else(|| custom_error("Busy", "Another accept task is ongoing"))?; + .ok_or_else(|| NetError::AcceptTaskOngoing)?; let cancel = RcRef::map(resource, |r| &r.cancel); let (tcp_stream, _socket_addr) = listener .accept() @@ -112,12 +167,12 @@ pub async fn op_net_recv_udp( state: Rc>, #[smi] rid: ResourceId, #[buffer] mut buf: JsBuffer, -) -> Result<(usize, IpAddr), AnyError> { +) -> Result<(usize, IpAddr), NetError> { let resource = state .borrow_mut() .resource_table .get::(rid) - .map_err(|_| bad_resource("Socket has been closed"))?; + .map_err(|_| NetError::SocketClosed)?; let socket = RcRef::map(&resource, |r| &r.socket).borrow().await; let cancel_handle = RcRef::map(&resource, |r| &r.cancel); let (nread, remote_addr) = socket @@ -134,27 +189,29 @@ pub async fn op_net_send_udp( #[smi] rid: ResourceId, #[serde] addr: IpAddr, #[buffer] zero_copy: JsBuffer, -) -> Result +) -> Result where NP: NetPermissions + 'static, { { let mut s = state.borrow_mut(); - s.borrow_mut::().check_net( - &(&addr.hostname, Some(addr.port)), - "Deno.DatagramConn.send()", - )?; + s.borrow_mut::() + .check_net( + &(&addr.hostname, Some(addr.port)), + "Deno.DatagramConn.send()", + ) + .map_err(NetError::Permission)?; } let addr = resolve_addr(&addr.hostname, addr.port) .await? .next() - .ok_or_else(|| generic_error("No resolved address found"))?; + .ok_or(NetError::NoResolvedAddress)?; let resource = state .borrow_mut() .resource_table .get::(rid) - .map_err(|_| bad_resource("Socket has been closed"))?; + .map_err(|_| NetError::SocketClosed)?; let socket = RcRef::map(&resource, |r| &r.socket).borrow().await; let nwritten = socket.send_to(&zero_copy, &addr).await?; @@ -167,12 +224,12 @@ pub async fn op_net_join_multi_v4_udp( #[smi] rid: ResourceId, #[string] address: String, #[string] multi_interface: String, -) -> Result<(), AnyError> { +) -> Result<(), NetError> { let resource = state .borrow_mut() .resource_table .get::(rid) - .map_err(|_| bad_resource("Socket has been closed"))?; + .map_err(|_| NetError::SocketClosed)?; let socket = RcRef::map(&resource, |r| &r.socket).borrow().await; let addr = Ipv4Addr::from_str(address.as_str())?; @@ -189,12 +246,12 @@ pub async fn op_net_join_multi_v6_udp( #[smi] rid: ResourceId, #[string] address: String, #[smi] multi_interface: u32, -) -> Result<(), AnyError> { +) -> Result<(), NetError> { let resource = state .borrow_mut() .resource_table .get::(rid) - .map_err(|_| bad_resource("Socket has been closed"))?; + .map_err(|_| NetError::SocketClosed)?; let socket = RcRef::map(&resource, |r| &r.socket).borrow().await; let addr = Ipv6Addr::from_str(address.as_str())?; @@ -210,12 +267,12 @@ pub async fn op_net_leave_multi_v4_udp( #[smi] rid: ResourceId, #[string] address: String, #[string] multi_interface: String, -) -> Result<(), AnyError> { +) -> Result<(), NetError> { let resource = state .borrow_mut() .resource_table .get::(rid) - .map_err(|_| bad_resource("Socket has been closed"))?; + .map_err(|_| NetError::SocketClosed)?; let socket = RcRef::map(&resource, |r| &r.socket).borrow().await; let addr = Ipv4Addr::from_str(address.as_str())?; @@ -232,12 +289,12 @@ pub async fn op_net_leave_multi_v6_udp( #[smi] rid: ResourceId, #[string] address: String, #[smi] multi_interface: u32, -) -> Result<(), AnyError> { +) -> Result<(), NetError> { let resource = state .borrow_mut() .resource_table .get::(rid) - .map_err(|_| bad_resource("Socket has been closed"))?; + .map_err(|_| NetError::SocketClosed)?; let socket = RcRef::map(&resource, |r| &r.socket).borrow().await; let addr = Ipv6Addr::from_str(address.as_str())?; @@ -253,16 +310,16 @@ pub async fn op_net_set_multi_loopback_udp( #[smi] rid: ResourceId, is_v4_membership: bool, loopback: bool, -) -> Result<(), AnyError> { +) -> Result<(), NetError> { let resource = state .borrow_mut() .resource_table .get::(rid) - .map_err(|_| bad_resource("Socket has been closed"))?; + .map_err(|_| NetError::SocketClosed)?; let socket = RcRef::map(&resource, |r| &r.socket).borrow().await; if is_v4_membership { - socket.set_multicast_loop_v4(loopback)? + socket.set_multicast_loop_v4(loopback)?; } else { socket.set_multicast_loop_v6(loopback)?; } @@ -275,12 +332,12 @@ pub async fn op_net_set_multi_ttl_udp( state: Rc>, #[smi] rid: ResourceId, #[smi] ttl: u32, -) -> Result<(), AnyError> { +) -> Result<(), NetError> { let resource = state .borrow_mut() .resource_table .get::(rid) - .map_err(|_| bad_resource("Socket has been closed"))?; + .map_err(|_| NetError::SocketClosed)?; let socket = RcRef::map(&resource, |r| &r.socket).borrow().await; socket.set_multicast_ttl_v4(ttl)?; @@ -293,7 +350,7 @@ pub async fn op_net_set_multi_ttl_udp( pub async fn op_net_connect_tcp( state: Rc>, #[serde] addr: IpAddr, -) -> Result<(ResourceId, IpAddr, IpAddr), AnyError> +) -> Result<(ResourceId, IpAddr, IpAddr), NetError> where NP: NetPermissions + 'static, { @@ -304,7 +361,7 @@ where pub async fn op_net_connect_tcp_inner( state: Rc>, addr: IpAddr, -) -> Result<(ResourceId, IpAddr, IpAddr), AnyError> +) -> Result<(ResourceId, IpAddr, IpAddr), NetError> where NP: NetPermissions + 'static, { @@ -312,13 +369,14 @@ where let mut state_ = state.borrow_mut(); state_ .borrow_mut::() - .check_net(&(&addr.hostname, Some(addr.port)), "Deno.connect()")?; + .check_net(&(&addr.hostname, Some(addr.port)), "Deno.connect()") + .map_err(NetError::Permission)?; } let addr = resolve_addr(&addr.hostname, addr.port) .await? .next() - .ok_or_else(|| generic_error("No resolved address found"))?; + .ok_or_else(|| NetError::NoResolvedAddress)?; let tcp_stream = TcpStream::connect(&addr).await?; let local_addr = tcp_stream.local_addr()?; let remote_addr = tcp_stream.peer_addr()?; @@ -353,7 +411,7 @@ pub fn op_net_listen_tcp( #[serde] addr: IpAddr, reuse_port: bool, load_balanced: bool, -) -> Result<(ResourceId, IpAddr), AnyError> +) -> Result<(ResourceId, IpAddr), NetError> where NP: NetPermissions + 'static, { @@ -362,10 +420,11 @@ where } state .borrow_mut::() - .check_net(&(&addr.hostname, Some(addr.port)), "Deno.listen()")?; + .check_net(&(&addr.hostname, Some(addr.port)), "Deno.listen()") + .map_err(NetError::Permission)?; let addr = resolve_addr_sync(&addr.hostname, addr.port)? .next() - .ok_or_else(|| generic_error("No resolved address found"))?; + .ok_or_else(|| NetError::NoResolvedAddress)?; let listener = if load_balanced { TcpListener::bind_load_balanced(addr) @@ -384,16 +443,17 @@ fn net_listen_udp( addr: IpAddr, reuse_address: bool, loopback: bool, -) -> Result<(ResourceId, IpAddr), AnyError> +) -> Result<(ResourceId, IpAddr), NetError> where NP: NetPermissions + 'static, { state .borrow_mut::() - .check_net(&(&addr.hostname, Some(addr.port)), "Deno.listenDatagram()")?; + .check_net(&(&addr.hostname, Some(addr.port)), "Deno.listenDatagram()") + .map_err(NetError::Permission)?; let addr = resolve_addr_sync(&addr.hostname, addr.port)? .next() - .ok_or_else(|| generic_error("No resolved address found"))?; + .ok_or_else(|| NetError::NoResolvedAddress)?; let domain = if addr.is_ipv4() { Domain::IPV4 @@ -453,7 +513,7 @@ pub fn op_net_listen_udp( #[serde] addr: IpAddr, reuse_address: bool, loopback: bool, -) -> Result<(ResourceId, IpAddr), AnyError> +) -> Result<(ResourceId, IpAddr), NetError> where NP: NetPermissions + 'static, { @@ -468,7 +528,7 @@ pub fn op_node_unstable_net_listen_udp( #[serde] addr: IpAddr, reuse_address: bool, loopback: bool, -) -> Result<(ResourceId, IpAddr), AnyError> +) -> Result<(ResourceId, IpAddr), NetError> where NP: NetPermissions + 'static, { @@ -551,7 +611,7 @@ pub struct NameServer { pub async fn op_dns_resolve( state: Rc>, #[serde] args: ResolveAddrArgs, -) -> Result, AnyError> +) -> Result, NetError> where NP: NetPermissions + 'static, { @@ -587,7 +647,9 @@ where let socker_addr = &ns.socket_addr; let ip = socker_addr.ip().to_string(); let port = socker_addr.port(); - perm.check_net(&(ip, Some(port)), "Deno.resolveDns()")?; + perm + .check_net(&(ip, Some(port)), "Deno.resolveDns()") + .map_err(NetError::Permission)?; } } @@ -618,22 +680,17 @@ where }; lookup - .map_err(|e| { - let message = format!("{e}"); - match e.kind() { - ResolveErrorKind::NoRecordsFound { .. } => { - custom_error("NotFound", message) - } - ResolveErrorKind::Message("No connections available") => { - custom_error("NotConnected", message) - } - ResolveErrorKind::Timeout => custom_error("TimedOut", message), - _ => generic_error(message), + .map_err(|e| match e.kind() { + ResolveErrorKind::NoRecordsFound { .. } => NetError::DnsNotFound(e), + ResolveErrorKind::Message("No connections available") => { + NetError::DnsNotConnected(e) } + ResolveErrorKind::Timeout => NetError::DnsTimedOut(e), + _ => NetError::Dns(e), })? .iter() .filter_map(|rdata| rdata_to_return_record(record_type)(rdata).transpose()) - .collect::, AnyError>>() + .collect::, NetError>>() } #[op2(fast)] @@ -641,7 +698,7 @@ pub fn op_set_nodelay( state: &mut OpState, #[smi] rid: ResourceId, nodelay: bool, -) -> Result<(), AnyError> { +) -> Result<(), NetError> { op_set_nodelay_inner(state, rid, nodelay) } @@ -650,10 +707,12 @@ pub fn op_set_nodelay_inner( state: &mut OpState, rid: ResourceId, nodelay: bool, -) -> Result<(), AnyError> { - let resource: Rc = - state.resource_table.get::(rid)?; - resource.set_nodelay(nodelay) +) -> Result<(), NetError> { + let resource: Rc = state + .resource_table + .get::(rid) + .map_err(NetError::Resource)?; + resource.set_nodelay(nodelay).map_err(NetError::Map) } #[op2(fast)] @@ -661,7 +720,7 @@ pub fn op_set_keepalive( state: &mut OpState, #[smi] rid: ResourceId, keepalive: bool, -) -> Result<(), AnyError> { +) -> Result<(), NetError> { op_set_keepalive_inner(state, rid, keepalive) } @@ -670,17 +729,19 @@ pub fn op_set_keepalive_inner( state: &mut OpState, rid: ResourceId, keepalive: bool, -) -> Result<(), AnyError> { - let resource: Rc = - state.resource_table.get::(rid)?; - resource.set_keepalive(keepalive) +) -> Result<(), NetError> { + let resource: Rc = state + .resource_table + .get::(rid) + .map_err(NetError::Resource)?; + resource.set_keepalive(keepalive).map_err(NetError::Map) } fn rdata_to_return_record( ty: RecordType, -) -> impl Fn(&RData) -> Result, AnyError> { +) -> impl Fn(&RData) -> Result, NetError> { use RecordType::*; - move |r: &RData| -> Result, AnyError> { + move |r: &RData| -> Result, NetError> { let record = match ty { A => r.as_a().map(ToString::to_string).map(DnsReturnRecord::A), AAAA => r @@ -761,12 +822,7 @@ fn rdata_to_return_record( .collect(); DnsReturnRecord::Txt(texts) }), - _ => { - return Err(custom_error( - "NotSupported", - "Provided record type is not supported", - )) - } + _ => return Err(NetError::UnsupportedRecordType), }; Ok(record) } @@ -985,7 +1041,7 @@ mod tests { &mut self, _host: &(T, Option), _api_name: &str, - ) -> Result<(), AnyError> { + ) -> Result<(), deno_core::error::AnyError> { Ok(()) } @@ -993,7 +1049,7 @@ mod tests { &mut self, p: &str, _api_name: &str, - ) -> Result { + ) -> Result { Ok(PathBuf::from(p)) } @@ -1001,7 +1057,7 @@ mod tests { &mut self, p: &str, _api_name: &str, - ) -> Result { + ) -> Result { Ok(PathBuf::from(p)) } @@ -1009,7 +1065,7 @@ mod tests { &mut self, p: &'a Path, _api_name: &str, - ) -> Result, AnyError> { + ) -> Result, deno_core::error::AnyError> { Ok(Cow::Borrowed(p)) } } @@ -1091,7 +1147,7 @@ mod tests { let vals = result.unwrap(); rid = rid.or(Some(vals.0)); } - }; + } let rid = rid.unwrap(); let state = runtime.op_state(); diff --git a/ext/net/ops_tls.rs b/ext/net/ops_tls.rs index a68d144b57..c7d65dd85e 100644 --- a/ext/net/ops_tls.rs +++ b/ext/net/ops_tls.rs @@ -2,6 +2,7 @@ use crate::io::TcpStreamResource; use crate::ops::IpAddr; +use crate::ops::NetError; use crate::ops::TlsHandshakeInfo; use crate::raw::NetworkListenerResource; use crate::resolve_addr::resolve_addr; @@ -10,13 +11,7 @@ use crate::tcp::TcpListener; use crate::DefaultTlsOptions; use crate::NetPermissions; use crate::UnsafelyIgnoreCertificateErrors; -use deno_core::anyhow::anyhow; -use deno_core::anyhow::bail; -use deno_core::error::bad_resource; -use deno_core::error::custom_error; -use deno_core::error::generic_error; -use deno_core::error::invalid_hostname; -use deno_core::error::AnyError; +use deno_core::futures::TryFutureExt; use deno_core::op2; use deno_core::v8; use deno_core::AsyncRefCell; @@ -118,20 +113,23 @@ impl TlsStreamResource { pub async fn read( self: Rc, data: &mut [u8], - ) -> Result { + ) -> Result { let mut rd = RcRef::map(&self, |r| &r.rd).borrow_mut().await; let cancel_handle = RcRef::map(&self, |r| &r.cancel_handle); - Ok(rd.read(data).try_or_cancel(cancel_handle).await?) + rd.read(data).try_or_cancel(cancel_handle).await } - pub async fn write(self: Rc, data: &[u8]) -> Result { + pub async fn write( + self: Rc, + data: &[u8], + ) -> Result { let mut wr = RcRef::map(self, |r| &r.wr).borrow_mut().await; let nwritten = wr.write(data).await?; wr.flush().await?; Ok(nwritten) } - pub async fn shutdown(self: Rc) -> Result<(), AnyError> { + pub async fn shutdown(self: Rc) -> Result<(), std::io::Error> { let mut wr = RcRef::map(self, |r| &r.wr).borrow_mut().await; wr.shutdown().await?; Ok(()) @@ -139,7 +137,7 @@ impl TlsStreamResource { pub async fn handshake( self: &Rc, - ) -> Result { + ) -> Result { if let Some(tls_info) = &*self.handshake_info.borrow() { return Ok(tls_info.clone()); } @@ -164,7 +162,7 @@ impl Resource for TlsStreamResource { } fn shutdown(self: Rc) -> AsyncResult<()> { - Box::pin(self.shutdown()) + Box::pin(self.shutdown().map_err(Into::into)) } fn close(self: Rc) { @@ -201,7 +199,7 @@ pub fn op_tls_key_null() -> TlsKeysHolder { pub fn op_tls_key_static( #[string] cert: &str, #[string] key: &str, -) -> Result { +) -> Result { let cert = load_certs(&mut BufReader::new(cert.as_bytes()))?; let key = load_private_keys(key.as_bytes())? .into_iter() @@ -236,9 +234,9 @@ pub fn op_tls_cert_resolver_resolve( #[cppgc] lookup: &TlsKeyLookup, #[string] sni: String, #[cppgc] key: &TlsKeysHolder, -) -> Result<(), AnyError> { +) -> Result<(), NetError> { let TlsKeys::Static(key) = key.take() else { - bail!("unexpected key type"); + return Err(NetError::UnexpectedKeyType); }; lookup.resolve(sni, Ok(key)); Ok(()) @@ -258,7 +256,7 @@ pub fn op_tls_cert_resolver_resolve_error( pub fn op_tls_start( state: Rc>, #[serde] args: StartTlsArgs, -) -> Result<(ResourceId, IpAddr, IpAddr), AnyError> +) -> Result<(ResourceId, IpAddr, IpAddr), NetError> where NP: NetPermissions + 'static, { @@ -271,7 +269,9 @@ where { let mut s = state.borrow_mut(); let permissions = s.borrow_mut::(); - permissions.check_net(&(&hostname, Some(0)), "Deno.startTls()")?; + permissions + .check_net(&(&hostname, Some(0)), "Deno.startTls()") + .map_err(NetError::Permission)?; } let ca_certs = args @@ -281,7 +281,7 @@ where .collect::>(); let hostname_dns = ServerName::try_from(hostname.to_string()) - .map_err(|_| invalid_hostname(&hostname))?; + .map_err(|_| NetError::InvalidHostname(hostname))?; let unsafely_ignore_certificate_errors = state .borrow() @@ -291,19 +291,21 @@ where let root_cert_store = state .borrow() .borrow::() - .root_cert_store()?; + .root_cert_store() + .map_err(NetError::RootCertStore)?; let resource_rc = state .borrow_mut() .resource_table - .take::(rid)?; + .take::(rid) + .map_err(NetError::Resource)?; // This TCP connection might be used somewhere else. If it's the case, we cannot proceed with the // process of starting a TLS connection on top of this TCP connection, so we just return a Busy error. // See also: https://github.com/denoland/deno/pull/16242 - let resource = Rc::try_unwrap(resource_rc) - .map_err(|_| custom_error("Busy", "TCP stream is currently in use"))?; + let resource = + Rc::try_unwrap(resource_rc).map_err(|_| NetError::TcpStreamBusy)?; let (read_half, write_half) = resource.into_inner(); - let tcp_stream = read_half.reunite(write_half)?; + let tcp_stream = read_half.reunite(write_half).map_err(NetError::Reunite)?; let local_addr = tcp_stream.local_addr()?; let remote_addr = tcp_stream.peer_addr()?; @@ -345,7 +347,7 @@ pub async fn op_net_connect_tls( #[serde] addr: IpAddr, #[serde] args: ConnectTlsArgs, #[cppgc] key_pair: &TlsKeysHolder, -) -> Result<(ResourceId, IpAddr, IpAddr), AnyError> +) -> Result<(ResourceId, IpAddr, IpAddr), NetError> where NP: NetPermissions + 'static, { @@ -359,9 +361,14 @@ where let mut s = state.borrow_mut(); let permissions = s.borrow_mut::(); permissions - .check_net(&(&addr.hostname, Some(addr.port)), "Deno.connectTls()")?; + .check_net(&(&addr.hostname, Some(addr.port)), "Deno.connectTls()") + .map_err(NetError::Permission)?; if let Some(path) = cert_file { - Some(permissions.check_read(path, "Deno.connectTls()")?) + Some( + permissions + .check_read(path, "Deno.connectTls()") + .map_err(NetError::Permission)?, + ) } else { None } @@ -382,17 +389,18 @@ where let root_cert_store = state .borrow() .borrow::() - .root_cert_store()?; + .root_cert_store() + .map_err(NetError::RootCertStore)?; let hostname_dns = if let Some(server_name) = args.server_name { ServerName::try_from(server_name) } else { ServerName::try_from(addr.hostname.clone()) } - .map_err(|_| invalid_hostname(&addr.hostname))?; + .map_err(|_| NetError::InvalidHostname(addr.hostname.clone()))?; let connect_addr = resolve_addr(&addr.hostname, addr.port) .await? .next() - .ok_or_else(|| generic_error("No resolved address found"))?; + .ok_or_else(|| NetError::NoResolvedAddress)?; let tcp_stream = TcpStream::connect(connect_addr).await?; let local_addr = tcp_stream.local_addr()?; let remote_addr = tcp_stream.peer_addr()?; @@ -444,7 +452,7 @@ pub fn op_net_listen_tls( #[serde] addr: IpAddr, #[serde] args: ListenTlsArgs, #[cppgc] keys: &TlsKeysHolder, -) -> Result<(ResourceId, IpAddr), AnyError> +) -> Result<(ResourceId, IpAddr), NetError> where NP: NetPermissions + 'static, { @@ -455,12 +463,13 @@ where { let permissions = state.borrow_mut::(); permissions - .check_net(&(&addr.hostname, Some(addr.port)), "Deno.listenTls()")?; + .check_net(&(&addr.hostname, Some(addr.port)), "Deno.listenTls()") + .map_err(NetError::Permission)?; } let bind_addr = resolve_addr_sync(&addr.hostname, addr.port)? .next() - .ok_or_else(|| generic_error("No resolved address found"))?; + .ok_or(NetError::NoResolvedAddress)?; let tcp_listener = if args.load_balanced { TcpListener::bind_load_balanced(bind_addr) @@ -475,28 +484,24 @@ where .map(|s| s.into_bytes()) .collect(); let listener = match keys.take() { - TlsKeys::Null => Err(anyhow!("Deno.listenTls requires a key")), + TlsKeys::Null => return Err(NetError::ListenTlsRequiresKey), TlsKeys::Static(TlsKey(cert, key)) => { let mut tls_config = ServerConfig::builder() .with_no_client_auth() - .with_single_cert(cert, key) - .map_err(|e| anyhow!(e))?; + .with_single_cert(cert, key)?; tls_config.alpn_protocols = alpn; - Ok(TlsListener { + TlsListener { tcp_listener, tls_config: Some(tls_config.into()), server_config_provider: None, - }) + } } - TlsKeys::Resolver(resolver) => Ok(TlsListener { + TlsKeys::Resolver(resolver) => TlsListener { tcp_listener, tls_config: None, server_config_provider: Some(resolver.into_server_config_provider(alpn)), - }), - } - .map_err(|e| { - custom_error("InvalidData", "Error creating TLS certificate").context(e) - })?; + }, + }; let tls_listener_resource = NetworkListenerResource::new(listener); @@ -510,23 +515,23 @@ where pub async fn op_net_accept_tls( state: Rc>, #[smi] rid: ResourceId, -) -> Result<(ResourceId, IpAddr, IpAddr), AnyError> { +) -> Result<(ResourceId, IpAddr, IpAddr), NetError> { let resource = state .borrow() .resource_table .get::>(rid) - .map_err(|_| bad_resource("Listener has been closed"))?; + .map_err(|_| NetError::ListenerClosed)?; let cancel_handle = RcRef::map(&resource, |r| &r.cancel); let listener = RcRef::map(&resource, |r| &r.listener) .try_borrow_mut() - .ok_or_else(|| custom_error("Busy", "Another accept task is ongoing"))?; + .ok_or_else(|| NetError::AcceptTaskOngoing)?; let (tls_stream, remote_addr) = match listener.accept().try_or_cancel(&cancel_handle).await { Ok(tuple) => tuple, Err(err) if err.kind() == ErrorKind::Interrupted => { - return Err(bad_resource("Listener has been closed")); + return Err(NetError::ListenerClosed); } Err(err) => return Err(err.into()), }; @@ -547,11 +552,11 @@ pub async fn op_net_accept_tls( pub async fn op_tls_handshake( state: Rc>, #[smi] rid: ResourceId, -) -> Result { +) -> Result { let resource = state .borrow() .resource_table .get::(rid) - .map_err(|_| bad_resource("Listener has been closed"))?; - resource.handshake().await + .map_err(|_| NetError::ListenerClosed)?; + resource.handshake().await.map_err(Into::into) } diff --git a/ext/net/ops_unix.rs b/ext/net/ops_unix.rs index 95293284f0..04ae84906f 100644 --- a/ext/net/ops_unix.rs +++ b/ext/net/ops_unix.rs @@ -1,11 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use crate::io::UnixStreamResource; +use crate::ops::NetError; use crate::raw::NetworkListenerResource; use crate::NetPermissions; -use deno_core::error::bad_resource; -use deno_core::error::custom_error; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::AsyncRefCell; use deno_core::CancelHandle; @@ -26,11 +24,8 @@ use tokio::net::UnixListener; pub use tokio::net::UnixStream; /// A utility function to map OsStrings to Strings -pub fn into_string(s: std::ffi::OsString) -> Result { - s.into_string().map_err(|s| { - let message = format!("File name or path {s:?} is not valid UTF-8"); - custom_error("InvalidData", message) - }) +pub fn into_string(s: std::ffi::OsString) -> Result { + s.into_string().map_err(NetError::InvalidUtf8) } pub struct UnixDatagramResource { @@ -63,15 +58,15 @@ pub struct UnixListenArgs { pub async fn op_net_accept_unix( state: Rc>, #[smi] rid: ResourceId, -) -> Result<(ResourceId, Option, Option), AnyError> { +) -> Result<(ResourceId, Option, Option), NetError> { let resource = state .borrow() .resource_table .get::>(rid) - .map_err(|_| bad_resource("Listener has been closed"))?; + .map_err(|_| NetError::ListenerClosed)?; let listener = RcRef::map(&resource, |r| &r.listener) .try_borrow_mut() - .ok_or_else(|| custom_error("Busy", "Listener already in use"))?; + .ok_or(NetError::ListenerBusy)?; let cancel = RcRef::map(resource, |r| &r.cancel); let (unix_stream, _socket_addr) = listener .accept() @@ -95,7 +90,7 @@ pub async fn op_net_accept_unix( pub async fn op_net_connect_unix( state: Rc>, #[string] address_path: String, -) -> Result<(ResourceId, Option, Option), AnyError> +) -> Result<(ResourceId, Option, Option), NetError> where NP: NetPermissions + 'static, { @@ -103,10 +98,12 @@ where let mut state_ = state.borrow_mut(); let address_path = state_ .borrow_mut::() - .check_read(&address_path, "Deno.connect()")?; + .check_read(&address_path, "Deno.connect()") + .map_err(NetError::Permission)?; _ = state_ .borrow_mut::() - .check_write_path(&address_path, "Deno.connect()")?; + .check_write_path(&address_path, "Deno.connect()") + .map_err(NetError::Permission)?; address_path }; let unix_stream = UnixStream::connect(&address_path).await?; @@ -127,15 +124,15 @@ pub async fn op_net_recv_unixpacket( state: Rc>, #[smi] rid: ResourceId, #[buffer] mut buf: JsBuffer, -) -> Result<(usize, Option), AnyError> { +) -> Result<(usize, Option), NetError> { let resource = state .borrow() .resource_table .get::(rid) - .map_err(|_| bad_resource("Socket has been closed"))?; + .map_err(|_| NetError::SocketClosed)?; let socket = RcRef::map(&resource, |r| &r.socket) .try_borrow_mut() - .ok_or_else(|| custom_error("Busy", "Socket already in use"))?; + .ok_or(NetError::SocketBusy)?; let cancel = RcRef::map(resource, |r| &r.cancel); let (nread, remote_addr) = socket.recv_from(&mut buf).try_or_cancel(cancel).await?; @@ -150,24 +147,25 @@ pub async fn op_net_send_unixpacket( #[smi] rid: ResourceId, #[string] address_path: String, #[buffer] zero_copy: JsBuffer, -) -> Result +) -> Result where NP: NetPermissions + 'static, { let address_path = { let mut s = state.borrow_mut(); s.borrow_mut::() - .check_write(&address_path, "Deno.DatagramConn.send()")? + .check_write(&address_path, "Deno.DatagramConn.send()") + .map_err(NetError::Permission)? }; let resource = state .borrow() .resource_table .get::(rid) - .map_err(|_| custom_error("NotConnected", "Socket has been closed"))?; + .map_err(|_| NetError::SocketClosedNotConnected)?; let socket = RcRef::map(&resource, |r| &r.socket) .try_borrow_mut() - .ok_or_else(|| custom_error("Busy", "Socket already in use"))?; + .ok_or(NetError::SocketBusy)?; let nwritten = socket.send_to(&zero_copy, address_path).await?; Ok(nwritten) @@ -179,14 +177,18 @@ pub fn op_net_listen_unix( state: &mut OpState, #[string] address_path: String, #[string] api_name: String, -) -> Result<(ResourceId, Option), AnyError> +) -> Result<(ResourceId, Option), NetError> where NP: NetPermissions + 'static, { let permissions = state.borrow_mut::(); let api_call_expr = format!("{}()", api_name); - let address_path = permissions.check_read(&address_path, &api_call_expr)?; - _ = permissions.check_write_path(&address_path, &api_call_expr)?; + let address_path = permissions + .check_read(&address_path, &api_call_expr) + .map_err(NetError::Permission)?; + _ = permissions + .check_write_path(&address_path, &api_call_expr) + .map_err(NetError::Permission)?; let listener = UnixListener::bind(address_path)?; let local_addr = listener.local_addr()?; let pathname = local_addr.as_pathname().map(pathstring).transpose()?; @@ -198,14 +200,17 @@ where pub fn net_listen_unixpacket( state: &mut OpState, address_path: String, -) -> Result<(ResourceId, Option), AnyError> +) -> Result<(ResourceId, Option), NetError> where NP: NetPermissions + 'static, { let permissions = state.borrow_mut::(); - let address_path = - permissions.check_read(&address_path, "Deno.listenDatagram()")?; - _ = permissions.check_write_path(&address_path, "Deno.listenDatagram()")?; + let address_path = permissions + .check_read(&address_path, "Deno.listenDatagram()") + .map_err(NetError::Permission)?; + _ = permissions + .check_write_path(&address_path, "Deno.listenDatagram()") + .map_err(NetError::Permission)?; let socket = UnixDatagram::bind(address_path)?; let local_addr = socket.local_addr()?; let pathname = local_addr.as_pathname().map(pathstring).transpose()?; @@ -222,7 +227,7 @@ where pub fn op_net_listen_unixpacket( state: &mut OpState, #[string] path: String, -) -> Result<(ResourceId, Option), AnyError> +) -> Result<(ResourceId, Option), NetError> where NP: NetPermissions + 'static, { @@ -235,13 +240,13 @@ where pub fn op_node_unstable_net_listen_unixpacket( state: &mut OpState, #[string] path: String, -) -> Result<(ResourceId, Option), AnyError> +) -> Result<(ResourceId, Option), NetError> where NP: NetPermissions + 'static, { net_listen_unixpacket::(state, path) } -pub fn pathstring(pathname: &Path) -> Result { +pub fn pathstring(pathname: &Path) -> Result { into_string(pathname.into()) } diff --git a/ext/net/resolve_addr.rs b/ext/net/resolve_addr.rs index 8bbdd5192c..3a97081eac 100644 --- a/ext/net/resolve_addr.rs +++ b/ext/net/resolve_addr.rs @@ -1,6 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::error::AnyError; use std::net::SocketAddr; use std::net::ToSocketAddrs; use tokio::net::lookup_host; @@ -9,7 +8,7 @@ use tokio::net::lookup_host; pub async fn resolve_addr( hostname: &str, port: u16, -) -> Result + '_, AnyError> { +) -> Result + '_, std::io::Error> { let addr_port_pair = make_addr_port_pair(hostname, port); let result = lookup_host(addr_port_pair).await?; Ok(result) @@ -19,7 +18,7 @@ pub async fn resolve_addr( pub fn resolve_addr_sync( hostname: &str, port: u16, -) -> Result, AnyError> { +) -> Result, std::io::Error> { let addr_port_pair = make_addr_port_pair(hostname, port); let result = addr_port_pair.to_socket_addrs()?; Ok(result) diff --git a/runtime/errors.rs b/runtime/errors.rs index 612a66b6e2..4539a8ff4b 100644 --- a/runtime/errors.rs +++ b/runtime/errors.rs @@ -23,6 +23,7 @@ use deno_ffi::DlfcnError; use deno_ffi::IRError; use deno_ffi::ReprError; use deno_ffi::StaticError; +use deno_net::ops::NetError; use deno_tls::TlsError; use deno_webstorage::WebStorageError; use std::env; @@ -292,6 +293,48 @@ fn get_broadcast_channel_error(error: &BroadcastChannelError) -> &'static str { } } +fn get_net_error(error: &NetError) -> &'static str { + match error { + NetError::ListenerClosed => "BadResource", + NetError::ListenerBusy => "Busy", + NetError::SocketClosed => "BadResource", + NetError::SocketClosedNotConnected => "NotConnected", + NetError::SocketBusy => "Busy", + NetError::Io(e) => get_io_error_class(e), + NetError::AcceptTaskOngoing => "Busy", + NetError::RootCertStore(e) + | NetError::Permission(e) + | NetError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), + NetError::NoResolvedAddress => "Error", + NetError::AddrParse(_) => "Error", + NetError::Map(e) => get_net_map_error(e), + NetError::Canceled(e) => { + let io_err: io::Error = e.to_owned().into(); + get_io_error_class(&io_err) + } + NetError::DnsNotFound(_) => "NotFound", + NetError::DnsNotConnected(_) => "NotConnected", + NetError::DnsTimedOut(_) => "TimedOut", + NetError::Dns(_) => "Error", + NetError::UnsupportedRecordType => "NotSupported", + NetError::InvalidUtf8(_) => "InvalidData", + NetError::UnexpectedKeyType => "Error", + NetError::InvalidHostname(_) => "TypeError", + NetError::TcpStreamBusy => "Busy", + NetError::Rustls(_) => "Error", + NetError::Tls(e) => get_tls_error_class(e), + NetError::ListenTlsRequiresKey => "InvalidData", + NetError::Reunite(_) => "Error", + } +} + +fn get_net_map_error(error: &deno_net::io::MapError) -> &'static str { + match error { + deno_net::io::MapError::Io(e) => get_io_error_class(e), + deno_net::io::MapError::NoResources => "Error", + } +} + pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> { deno_core::error::get_custom_error_class(e) .or_else(|| deno_webgpu::error::get_error_class_name(e)) @@ -316,6 +359,11 @@ pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> { .or_else(|| e.downcast_ref::().map(get_cron_error_class)) .or_else(|| e.downcast_ref::().map(get_canvas_error)) .or_else(|| e.downcast_ref::().map(get_cache_error)) + .or_else(|| e.downcast_ref::().map(get_net_error)) + .or_else(|| { + e.downcast_ref::() + .map(get_net_map_error) + }) .or_else(|| { e.downcast_ref::() .map(get_broadcast_channel_error) From 7c790da8260db24af12e700f191af551f4307476 Mon Sep 17 00:00:00 2001 From: Leo Kettmeir Date: Thu, 17 Oct 2024 10:59:02 -0700 Subject: [PATCH 05/14] refactor(ext/kv): use concrete error type (#26239) --- Cargo.lock | 1 + ext/kv/Cargo.toml | 1 + ext/kv/lib.rs | 288 ++++++++++++++++++++++++++++------------------ runtime/errors.rs | 45 ++++++++ 4 files changed, 220 insertions(+), 115 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 043bcc2035..7863f31fa9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1720,6 +1720,7 @@ dependencies = [ "rand", "rusqlite", "serde", + "thiserror", "url", ] diff --git a/ext/kv/Cargo.toml b/ext/kv/Cargo.toml index 8aee80c575..98c14dc316 100644 --- a/ext/kv/Cargo.toml +++ b/ext/kv/Cargo.toml @@ -36,6 +36,7 @@ prost.workspace = true rand.workspace = true rusqlite.workspace = true serde.workspace = true +thiserror.workspace = true url.workspace = true [build-dependencies] diff --git a/ext/kv/lib.rs b/ext/kv/lib.rs index 13e4f1662f..a4ccfe3d63 100644 --- a/ext/kv/lib.rs +++ b/ext/kv/lib.rs @@ -12,15 +12,11 @@ use std::num::NonZeroU32; use std::rc::Rc; use std::time::Duration; -use anyhow::bail; use base64::prelude::BASE64_URL_SAFE; use base64::Engine; use chrono::DateTime; use chrono::Utc; -use deno_core::anyhow::Context; use deno_core::error::get_custom_error_class; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::futures::StreamExt; use deno_core::op2; use deno_core::serde_v8::AnyValue; @@ -118,12 +114,72 @@ impl Resource for DatabaseWatcherResource { } } +#[derive(Debug, thiserror::Error)] +pub enum KvError { + #[error(transparent)] + DatabaseHandler(deno_core::error::AnyError), + #[error(transparent)] + Resource(deno_core::error::AnyError), + #[error("Too many ranges (max {0})")] + TooManyRanges(usize), + #[error("Too many entries (max {0})")] + TooManyEntries(usize), + #[error("Too many checks (max {0})")] + TooManyChecks(usize), + #[error("Too many mutations (max {0})")] + TooManyMutations(usize), + #[error("Too many keys (max {0})")] + TooManyKeys(usize), + #[error("limit must be greater than 0")] + InvalidLimit, + #[error("Invalid boundary key")] + InvalidBoundaryKey, + #[error("Key too large for read (max {0} bytes)")] + KeyTooLargeToRead(usize), + #[error("Key too large for write (max {0} bytes)")] + KeyTooLargeToWrite(usize), + #[error("Total mutation size too large (max {0} bytes)")] + TotalMutationTooLarge(usize), + #[error("Total key size too large (max {0} bytes)")] + TotalKeyTooLarge(usize), + #[error(transparent)] + Kv(deno_core::error::AnyError), + #[error(transparent)] + Io(#[from] std::io::Error), + #[error("Queue message not found")] + QueueMessageNotFound, + #[error("Start key is not in the keyspace defined by prefix")] + StartKeyNotInKeyspace, + #[error("End key is not in the keyspace defined by prefix")] + EndKeyNotInKeyspace, + #[error("Start key is greater than end key")] + StartKeyGreaterThanEndKey, + #[error("Invalid check")] + InvalidCheck(#[source] KvCheckError), + #[error("Invalid mutation")] + InvalidMutation(#[source] KvMutationError), + #[error("Invalid enqueue")] + InvalidEnqueue(#[source] std::io::Error), + #[error("key cannot be empty")] + EmptyKey, // TypeError + #[error("Value too large (max {0} bytes)")] + ValueTooLarge(usize), // TypeError + #[error("enqueue payload too large (max {0} bytes)")] + EnqueuePayloadTooLarge(usize), // TypeError + #[error("invalid cursor")] + InvalidCursor, + #[error("cursor out of bounds")] + CursorOutOfBounds, + #[error("Invalid range")] + InvalidRange, +} + #[op2(async)] #[smi] async fn op_kv_database_open( state: Rc>, #[string] path: Option, -) -> Result +) -> Result where DBH: DatabaseHandler + 'static, { @@ -134,7 +190,10 @@ where .check_or_exit(UNSTABLE_FEATURE_NAME, "Deno.openKv"); state.borrow::>().clone() }; - let db = handler.open(state.clone(), path).await?; + let db = handler + .open(state.clone(), path) + .await + .map_err(KvError::DatabaseHandler)?; let rid = state.borrow_mut().resource_table.add(DatabaseResource { db, cancel_handle: CancelHandle::new_rc(), @@ -184,8 +243,8 @@ enum ToV8Value { } impl TryFrom for KvValue { - type Error = AnyError; - fn try_from(value: FromV8Value) -> Result { + type Error = num_bigint::TryFromBigIntError; + fn try_from(value: FromV8Value) -> Result { Ok(match value { FromV8Value::V8(buf) => KvValue::V8(buf.to_vec()), FromV8Value::Bytes(buf) => KvValue::Bytes(buf.to_vec()), @@ -214,8 +273,8 @@ struct ToV8KvEntry { } impl TryFrom for ToV8KvEntry { - type Error = AnyError; - fn try_from(entry: KvEntry) -> Result { + type Error = std::io::Error; + fn try_from(entry: KvEntry) -> Result { Ok(ToV8KvEntry { key: decode_key(&entry.key)? .0 @@ -261,14 +320,16 @@ async fn op_kv_snapshot_read( #[smi] rid: ResourceId, #[serde] ranges: Vec, #[serde] consistency: V8Consistency, -) -> Result>, AnyError> +) -> Result>, KvError> where DBH: DatabaseHandler + 'static, { let db = { let state = state.borrow(); - let resource = - state.resource_table.get::>(rid)?; + let resource = state + .resource_table + .get::>(rid) + .map_err(KvError::Resource)?; resource.db.clone() }; @@ -278,10 +339,7 @@ where }; if ranges.len() > config.max_read_ranges { - return Err(type_error(format!( - "Too many ranges (max {})", - config.max_read_ranges - ))); + return Err(KvError::TooManyRanges(config.max_read_ranges)); } let mut total_entries = 0usize; @@ -300,33 +358,32 @@ where Ok(ReadRange { start, end, - limit: NonZeroU32::new(limit) - .with_context(|| "limit must be greater than 0")?, + limit: NonZeroU32::new(limit).ok_or(KvError::InvalidLimit)?, reverse, }) }) - .collect::, AnyError>>()?; + .collect::, KvError>>()?; if total_entries > config.max_read_entries { - return Err(type_error(format!( - "Too many entries (max {})", - config.max_read_entries - ))); + return Err(KvError::TooManyEntries(config.max_read_entries)); } let opts = SnapshotReadOptions { consistency: consistency.into(), }; - let output_ranges = db.snapshot_read(read_ranges, opts).await?; + let output_ranges = db + .snapshot_read(read_ranges, opts) + .await + .map_err(KvError::Kv)?; let output_ranges = output_ranges .into_iter() .map(|x| { x.entries .into_iter() .map(TryInto::try_into) - .collect::, AnyError>>() + .collect::, std::io::Error>>() }) - .collect::, AnyError>>()?; + .collect::, std::io::Error>>()?; Ok(output_ranges) } @@ -345,7 +402,7 @@ impl Resource for QueueMessageResource { async fn op_kv_dequeue_next_message( state: Rc>, #[smi] rid: ResourceId, -) -> Result, AnyError> +) -> Result, KvError> where DBH: DatabaseHandler + 'static, { @@ -358,17 +415,19 @@ where if get_custom_error_class(&err) == Some("BadResource") { return Ok(None); } else { - return Err(err); + return Err(KvError::Resource(err)); } } }; resource.db.clone() }; - let Some(mut handle) = db.dequeue_next_message().await? else { + let Some(mut handle) = + db.dequeue_next_message().await.map_err(KvError::Kv)? + else { return Ok(None); }; - let payload = handle.take_payload().await?.into(); + let payload = handle.take_payload().await.map_err(KvError::Kv)?.into(); let handle_rid = { let mut state = state.borrow_mut(); state.resource_table.add(QueueMessageResource { handle }) @@ -382,18 +441,18 @@ fn op_kv_watch( state: &mut OpState, #[smi] rid: ResourceId, #[serde] keys: Vec, -) -> Result +) -> Result where DBH: DatabaseHandler + 'static, { - let resource = state.resource_table.get::>(rid)?; + let resource = state + .resource_table + .get::>(rid) + .map_err(KvError::Resource)?; let config = state.borrow::>().clone(); if keys.len() > config.max_watched_keys { - return Err(type_error(format!( - "Too many keys (max {})", - config.max_watched_keys - ))); + return Err(KvError::TooManyKeys(config.max_watched_keys)); } let keys: Vec> = keys @@ -428,10 +487,13 @@ enum WatchEntry { async fn op_kv_watch_next( state: Rc>, #[smi] rid: ResourceId, -) -> Result>, AnyError> { +) -> Result>, KvError> { let resource = { let state = state.borrow(); - let resource = state.resource_table.get::(rid)?; + let resource = state + .resource_table + .get::(rid) + .map_err(KvError::Resource)?; resource.clone() }; @@ -457,7 +519,7 @@ async fn op_kv_watch_next( return Ok(None); }; - let entries = res?; + let entries = res.map_err(KvError::Kv)?; let entries = entries .into_iter() .map(|entry| { @@ -468,7 +530,7 @@ async fn op_kv_watch_next( WatchKeyOutput::Unchanged => WatchEntry::Unchanged, }) }) - .collect::>()?; + .collect::>()?; Ok(Some(entries)) } @@ -478,7 +540,7 @@ async fn op_kv_finish_dequeued_message( state: Rc>, #[smi] handle_rid: ResourceId, success: bool, -) -> Result<(), AnyError> +) -> Result<(), KvError> where DBH: DatabaseHandler + 'static, { @@ -487,9 +549,9 @@ where let handle = state .resource_table .take::::DB as Database>::QMH>>(handle_rid) - .map_err(|_| type_error("Queue message not found"))?; + .map_err(|_| KvError::QueueMessageNotFound)?; Rc::try_unwrap(handle) - .map_err(|_| type_error("Queue message not found"))? + .map_err(|_| KvError::QueueMessageNotFound)? .handle }; // if we fail to finish the message, there is not much we can do and the @@ -500,32 +562,52 @@ where Ok(()) } +#[derive(Debug, thiserror::Error)] +pub enum KvCheckError { + #[error("invalid versionstamp")] + InvalidVersionstamp, + #[error(transparent)] + Io(std::io::Error), +} + type V8KvCheck = (KvKey, Option); -fn check_from_v8(value: V8KvCheck) -> Result { +fn check_from_v8(value: V8KvCheck) -> Result { let versionstamp = match value.1 { Some(data) => { let mut out = [0u8; 10]; if data.len() != out.len() * 2 { - bail!(type_error("invalid versionstamp")); + return Err(KvCheckError::InvalidVersionstamp); } faster_hex::hex_decode(&data, &mut out) - .map_err(|_| type_error("invalid versionstamp"))?; + .map_err(|_| KvCheckError::InvalidVersionstamp)?; Some(out) } None => None, }; Ok(Check { - key: encode_v8_key(value.0)?, + key: encode_v8_key(value.0).map_err(KvCheckError::Io)?, versionstamp, }) } +#[derive(Debug, thiserror::Error)] +pub enum KvMutationError { + #[error(transparent)] + BigInt(#[from] num_bigint::TryFromBigIntError), + #[error(transparent)] + Io(#[from] std::io::Error), + #[error("Invalid mutation '{0}' with value")] + InvalidMutationWithValue(String), + #[error("Invalid mutation '{0}' without value")] + InvalidMutationWithoutValue(String), +} + type V8KvMutation = (KvKey, String, Option, Option); fn mutation_from_v8( (value, current_timstamp): (V8KvMutation, DateTime), -) -> Result { +) -> Result { let key = encode_v8_key(value.0)?; let kind = match (value.1.as_str(), value.2) { ("set", Some(value)) => MutationKind::Set(value.try_into()?), @@ -542,10 +624,10 @@ fn mutation_from_v8( MutationKind::SetSuffixVersionstampedKey(value.try_into()?) } (op, Some(_)) => { - return Err(type_error(format!("Invalid mutation '{op}' with value"))) + return Err(KvMutationError::InvalidMutationWithValue(op.to_string())) } (op, None) => { - return Err(type_error(format!("Invalid mutation '{op}' without value"))) + return Err(KvMutationError::InvalidMutationWithoutValue(op.to_string())) } }; Ok(Mutation { @@ -562,7 +644,7 @@ type V8Enqueue = (JsBuffer, u64, Vec, Option>); fn enqueue_from_v8( value: V8Enqueue, current_timestamp: DateTime, -) -> Result { +) -> Result { Ok(Enqueue { payload: value.0.to_vec(), deadline: current_timestamp @@ -597,7 +679,7 @@ impl RawSelector { prefix: Option, start: Option, end: Option, - ) -> Result { + ) -> Result { let prefix = prefix.map(encode_v8_key).transpose()?; let start = start.map(encode_v8_key).transpose()?; let end = end.map(encode_v8_key).transpose()?; @@ -610,9 +692,7 @@ impl RawSelector { }), (Some(prefix), Some(start), None) => { if !start.starts_with(&prefix) || start.len() == prefix.len() { - return Err(type_error( - "Start key is not in the keyspace defined by prefix", - )); + return Err(KvError::StartKeyNotInKeyspace); } Ok(Self::Prefixed { prefix, @@ -622,9 +702,7 @@ impl RawSelector { } (Some(prefix), None, Some(end)) => { if !end.starts_with(&prefix) || end.len() == prefix.len() { - return Err(type_error( - "End key is not in the keyspace defined by prefix", - )); + return Err(KvError::EndKeyNotInKeyspace); } Ok(Self::Prefixed { prefix, @@ -634,7 +712,7 @@ impl RawSelector { } (None, Some(start), Some(end)) => { if start > end { - return Err(type_error("Start key is greater than end key")); + return Err(KvError::StartKeyGreaterThanEndKey); } Ok(Self::Range { start, end }) } @@ -642,7 +720,7 @@ impl RawSelector { let end = start.iter().copied().chain(Some(0)).collect(); Ok(Self::Range { start, end }) } - _ => Err(type_error("Invalid range")), + _ => Err(KvError::InvalidRange), } } @@ -701,10 +779,10 @@ fn common_prefix_for_bytes<'a>(a: &'a [u8], b: &'a [u8]) -> &'a [u8] { fn encode_cursor( selector: &RawSelector, boundary_key: &[u8], -) -> Result { +) -> Result { let common_prefix = selector.common_prefix(); if !boundary_key.starts_with(common_prefix) { - return Err(type_error("Invalid boundary key")); + return Err(KvError::InvalidBoundaryKey); } Ok(BASE64_URL_SAFE.encode(&boundary_key[common_prefix.len()..])) } @@ -713,7 +791,7 @@ fn decode_selector_and_cursor( selector: &RawSelector, reverse: bool, cursor: Option<&ByteString>, -) -> Result<(Vec, Vec), AnyError> { +) -> Result<(Vec, Vec), KvError> { let Some(cursor) = cursor else { return Ok((selector.range_start_key(), selector.range_end_key())); }; @@ -721,7 +799,7 @@ fn decode_selector_and_cursor( let common_prefix = selector.common_prefix(); let cursor = BASE64_URL_SAFE .decode(cursor) - .map_err(|_| type_error("invalid cursor"))?; + .map_err(|_| KvError::InvalidCursor)?; let first_key: Vec; let last_key: Vec; @@ -746,13 +824,13 @@ fn decode_selector_and_cursor( // Defend against out-of-bounds reading if let Some(start) = selector.start() { if &first_key[..] < start { - return Err(type_error("cursor out of bounds")); + return Err(KvError::CursorOutOfBounds); } } if let Some(end) = selector.end() { if &last_key[..] > end { - return Err(type_error("cursor out of bounds")); + return Err(KvError::CursorOutOfBounds); } } @@ -767,15 +845,17 @@ async fn op_kv_atomic_write( #[serde] checks: Vec, #[serde] mutations: Vec, #[serde] enqueues: Vec, -) -> Result, AnyError> +) -> Result, KvError> where DBH: DatabaseHandler + 'static, { let current_timestamp = chrono::Utc::now(); let db = { let state = state.borrow(); - let resource = - state.resource_table.get::>(rid)?; + let resource = state + .resource_table + .get::>(rid) + .map_err(KvError::Resource)?; resource.db.clone() }; @@ -785,34 +865,28 @@ where }; if checks.len() > config.max_checks { - return Err(type_error(format!( - "Too many checks (max {})", - config.max_checks - ))); + return Err(KvError::TooManyChecks(config.max_checks)); } if mutations.len() + enqueues.len() > config.max_mutations { - return Err(type_error(format!( - "Too many mutations (max {})", - config.max_mutations - ))); + return Err(KvError::TooManyMutations(config.max_mutations)); } let checks = checks .into_iter() .map(check_from_v8) - .collect::, AnyError>>() - .with_context(|| "invalid check")?; + .collect::, KvCheckError>>() + .map_err(KvError::InvalidCheck)?; let mutations = mutations .into_iter() .map(|mutation| mutation_from_v8((mutation, current_timestamp))) - .collect::, AnyError>>() - .with_context(|| "Invalid mutation")?; + .collect::, KvMutationError>>() + .map_err(KvError::InvalidMutation)?; let enqueues = enqueues .into_iter() .map(|e| enqueue_from_v8(e, current_timestamp)) - .collect::, AnyError>>() - .with_context(|| "invalid enqueue")?; + .collect::, std::io::Error>>() + .map_err(KvError::InvalidEnqueue)?; let mut total_payload_size = 0usize; let mut total_key_size = 0usize; @@ -823,7 +897,7 @@ where .chain(mutations.iter().map(|m| &m.key)) { if key.is_empty() { - return Err(type_error("key cannot be empty")); + return Err(KvError::EmptyKey); } total_payload_size += check_write_key_size(key, &config)?; @@ -847,17 +921,13 @@ where } if total_payload_size > config.max_total_mutation_size_bytes { - return Err(type_error(format!( - "Total mutation size too large (max {} bytes)", - config.max_total_mutation_size_bytes - ))); + return Err(KvError::TotalMutationTooLarge( + config.max_total_mutation_size_bytes, + )); } if total_key_size > config.max_total_key_size_bytes { - return Err(type_error(format!( - "Total key size too large (max {} bytes)", - config.max_total_key_size_bytes - ))); + return Err(KvError::TotalKeyTooLarge(config.max_total_key_size_bytes)); } let atomic_write = AtomicWrite { @@ -866,7 +936,7 @@ where enqueues, }; - let result = db.atomic_write(atomic_write).await?; + let result = db.atomic_write(atomic_write).await.map_err(KvError::Kv)?; Ok(result.map(|res| faster_hex::hex_string(&res.versionstamp))) } @@ -879,19 +949,16 @@ type EncodeCursorRangeSelector = (Option, Option, Option); fn op_kv_encode_cursor( #[serde] (prefix, start, end): EncodeCursorRangeSelector, #[serde] boundary_key: KvKey, -) -> Result { +) -> Result { let selector = RawSelector::from_tuple(prefix, start, end)?; let boundary_key = encode_v8_key(boundary_key)?; let cursor = encode_cursor(&selector, &boundary_key)?; Ok(cursor) } -fn check_read_key_size(key: &[u8], config: &KvConfig) -> Result<(), AnyError> { +fn check_read_key_size(key: &[u8], config: &KvConfig) -> Result<(), KvError> { if key.len() > config.max_read_key_size_bytes { - Err(type_error(format!( - "Key too large for read (max {} bytes)", - config.max_read_key_size_bytes - ))) + Err(KvError::KeyTooLargeToRead(config.max_read_key_size_bytes)) } else { Ok(()) } @@ -900,12 +967,9 @@ fn check_read_key_size(key: &[u8], config: &KvConfig) -> Result<(), AnyError> { fn check_write_key_size( key: &[u8], config: &KvConfig, -) -> Result { +) -> Result { if key.len() > config.max_write_key_size_bytes { - Err(type_error(format!( - "Key too large for write (max {} bytes)", - config.max_write_key_size_bytes - ))) + Err(KvError::KeyTooLargeToWrite(config.max_write_key_size_bytes)) } else { Ok(key.len()) } @@ -914,7 +978,7 @@ fn check_write_key_size( fn check_value_size( value: &KvValue, config: &KvConfig, -) -> Result { +) -> Result { let payload = match value { KvValue::Bytes(x) => x, KvValue::V8(x) => x, @@ -922,10 +986,7 @@ fn check_value_size( }; if payload.len() > config.max_value_size_bytes { - Err(type_error(format!( - "Value too large (max {} bytes)", - config.max_value_size_bytes - ))) + Err(KvError::ValueTooLarge(config.max_value_size_bytes)) } else { Ok(payload.len()) } @@ -934,12 +995,9 @@ fn check_value_size( fn check_enqueue_payload_size( payload: &[u8], config: &KvConfig, -) -> Result { +) -> Result { if payload.len() > config.max_value_size_bytes { - Err(type_error(format!( - "enqueue payload too large (max {} bytes)", - config.max_value_size_bytes - ))) + Err(KvError::EnqueuePayloadTooLarge(config.max_value_size_bytes)) } else { Ok(payload.len()) } diff --git a/runtime/errors.rs b/runtime/errors.rs index 4539a8ff4b..ffcc1c1191 100644 --- a/runtime/errors.rs +++ b/runtime/errors.rs @@ -23,6 +23,9 @@ use deno_ffi::DlfcnError; use deno_ffi::IRError; use deno_ffi::ReprError; use deno_ffi::StaticError; +use deno_kv::KvCheckError; +use deno_kv::KvError; +use deno_kv::KvMutationError; use deno_net::ops::NetError; use deno_tls::TlsError; use deno_webstorage::WebStorageError; @@ -293,6 +296,47 @@ fn get_broadcast_channel_error(error: &BroadcastChannelError) -> &'static str { } } +fn get_kv_error(error: &KvError) -> &'static str { + match error { + KvError::DatabaseHandler(e) | KvError::Resource(e) | KvError::Kv(e) => { + get_error_class_name(e).unwrap_or("Error") + } + KvError::TooManyRanges(_) => "TypeError", + KvError::TooManyEntries(_) => "TypeError", + KvError::TooManyChecks(_) => "TypeError", + KvError::TooManyMutations(_) => "TypeError", + KvError::TooManyKeys(_) => "TypeError", + KvError::InvalidLimit => "TypeError", + KvError::InvalidBoundaryKey => "TypeError", + KvError::KeyTooLargeToRead(_) => "TypeError", + KvError::KeyTooLargeToWrite(_) => "TypeError", + KvError::TotalMutationTooLarge(_) => "TypeError", + KvError::TotalKeyTooLarge(_) => "TypeError", + KvError::Io(e) => get_io_error_class(e), + KvError::QueueMessageNotFound => "TypeError", + KvError::StartKeyNotInKeyspace => "TypeError", + KvError::EndKeyNotInKeyspace => "TypeError", + KvError::StartKeyGreaterThanEndKey => "TypeError", + KvError::InvalidCheck(e) => match e { + KvCheckError::InvalidVersionstamp => "TypeError", + KvCheckError::Io(e) => get_io_error_class(e), + }, + KvError::InvalidMutation(e) => match e { + KvMutationError::BigInt(_) => "Error", + KvMutationError::Io(e) => get_io_error_class(e), + KvMutationError::InvalidMutationWithValue(_) => "TypeError", + KvMutationError::InvalidMutationWithoutValue(_) => "TypeError", + }, + KvError::InvalidEnqueue(e) => get_io_error_class(e), + KvError::EmptyKey => "TypeError", + KvError::ValueTooLarge(_) => "TypeError", + KvError::EnqueuePayloadTooLarge(_) => "TypeError", + KvError::InvalidCursor => "TypeError", + KvError::CursorOutOfBounds => "TypeError", + KvError::InvalidRange => "TypeError", + } +} + fn get_net_error(error: &NetError) -> &'static str { match error { NetError::ListenerClosed => "BadResource", @@ -359,6 +403,7 @@ pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> { .or_else(|| e.downcast_ref::().map(get_cron_error_class)) .or_else(|| e.downcast_ref::().map(get_canvas_error)) .or_else(|| e.downcast_ref::().map(get_cache_error)) + .or_else(|| e.downcast_ref::().map(get_kv_error)) .or_else(|| e.downcast_ref::().map(get_net_error)) .or_else(|| { e.downcast_ref::() From 40b4b9aaa318dcbd1e639542b7cb18df6d96db68 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira <118899497+lucasfernog-crabnebula@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:36:34 -0300 Subject: [PATCH 06/14] feat(permissions): expose PromptResponse (#26358) follow-up for #26327 --- runtime/permissions/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs index 0cd489b179..1e1321bb2f 100644 --- a/runtime/permissions/lib.rs +++ b/runtime/permissions/lib.rs @@ -35,13 +35,13 @@ use std::sync::Arc; pub mod prompter; use prompter::permission_prompt; -use prompter::PromptResponse; use prompter::PERMISSION_EMOJI; pub use prompter::set_prompt_callbacks; pub use prompter::set_prompter; pub use prompter::PermissionPrompter; pub use prompter::PromptCallback; +pub use prompter::PromptResponse; /// Fast exit from permission check routines if this permission /// is in the "fully-granted" state. From eca83fc9b45ab1e5a73bd7b13b05ee42ab1a4dcc Mon Sep 17 00:00:00 2001 From: Leo Kettmeir Date: Thu, 17 Oct 2024 12:05:38 -0700 Subject: [PATCH 07/14] refactor(ext/web): use concrete error types (#26185) --- Cargo.lock | 1 + cli/file_fetcher.rs | 2 +- ext/crypto/lib.rs | 5 +- ext/web/Cargo.toml | 1 + ext/web/blob.rs | 60 ++++++----- ext/web/compression.rs | 64 +++++++----- ext/web/lib.rs | 146 +++++++++------------------ ext/web/message_port.rs | 46 ++++++--- ext/web/stream_resource.rs | 36 +++++-- runtime/errors.rs | 75 +++++++++++++- runtime/ops/web_worker.rs | 1 + runtime/ops/web_worker/sync_fetch.rs | 2 +- runtime/ops/worker_host.rs | 2 +- 13 files changed, 257 insertions(+), 184 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7863f31fa9..205804abe4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2180,6 +2180,7 @@ dependencies = [ "flate2", "futures", "serde", + "thiserror", "tokio", "uuid", ] diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index 69daf14954..e92aca5420 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -333,7 +333,7 @@ impl FileFetcher { ) })?; - let bytes = blob.read_all().await?; + let bytes = blob.read_all().await; let headers = HashMap::from([("content-type".to_string(), blob.media_type.clone())]); diff --git a/ext/crypto/lib.rs b/ext/crypto/lib.rs index c96029bf4a..b5bafc580b 100644 --- a/ext/crypto/lib.rs +++ b/ext/crypto/lib.rs @@ -149,10 +149,7 @@ pub fn op_crypto_get_random_values( #[buffer] out: &mut [u8], ) -> Result<(), AnyError> { if out.len() > 65536 { - return Err( - deno_web::DomExceptionQuotaExceededError::new(&format!("The ArrayBufferView's byte length ({}) exceeds the number of bytes of entropy available via this API (65536)", out.len())) - .into(), - ); + return Err(custom_error("DOMExceptionQuotaExceededError", format!("The ArrayBufferView's byte length ({}) exceeds the number of bytes of entropy available via this API (65536)", out.len()))); } let maybe_seeded_rng = state.try_borrow_mut::(); diff --git a/ext/web/Cargo.toml b/ext/web/Cargo.toml index 3d01e573d5..ad44f53202 100644 --- a/ext/web/Cargo.toml +++ b/ext/web/Cargo.toml @@ -23,6 +23,7 @@ encoding_rs.workspace = true flate2 = { workspace = true, features = ["default"] } futures.workspace = true serde = "1.0.149" +thiserror.workspace = true tokio.workspace = true uuid = { workspace = true, features = ["serde"] } diff --git a/ext/web/blob.rs b/ext/web/blob.rs index 392f36acb8..bc64a0f27e 100644 --- a/ext/web/blob.rs +++ b/ext/web/blob.rs @@ -7,8 +7,6 @@ use std::rc::Rc; use std::sync::Arc; use async_trait::async_trait; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::parking_lot::Mutex; use deno_core::url::Url; @@ -19,6 +17,18 @@ use serde::Deserialize; use serde::Serialize; use uuid::Uuid; +#[derive(Debug, thiserror::Error)] +pub enum BlobError { + #[error("Blob part not found")] + BlobPartNotFound, + #[error("start + len can not be larger than blob part size")] + SizeLargerThanBlobPart, + #[error("Blob URLs are not supported in this context")] + BlobURLsNotSupported, + #[error(transparent)] + Url(#[from] deno_core::url::ParseError), +} + use crate::Location; pub type PartMap = HashMap>; @@ -96,18 +106,18 @@ pub struct Blob { impl Blob { // TODO(lucacsonato): this should be a stream! - pub async fn read_all(&self) -> Result, AnyError> { + pub async fn read_all(&self) -> Vec { let size = self.size(); let mut bytes = Vec::with_capacity(size); for part in &self.parts { - let chunk = part.read().await?; + let chunk = part.read().await; bytes.extend_from_slice(chunk); } assert_eq!(bytes.len(), size); - Ok(bytes) + bytes } fn size(&self) -> usize { @@ -122,7 +132,7 @@ impl Blob { #[async_trait] pub trait BlobPart: Debug { // TODO(lucacsonato): this should be a stream! - async fn read(&self) -> Result<&[u8], AnyError>; + async fn read(&self) -> &[u8]; fn size(&self) -> usize; } @@ -137,8 +147,8 @@ impl From> for InMemoryBlobPart { #[async_trait] impl BlobPart for InMemoryBlobPart { - async fn read(&self) -> Result<&[u8], AnyError> { - Ok(&self.0) + async fn read(&self) -> &[u8] { + &self.0 } fn size(&self) -> usize { @@ -155,9 +165,9 @@ pub struct SlicedBlobPart { #[async_trait] impl BlobPart for SlicedBlobPart { - async fn read(&self) -> Result<&[u8], AnyError> { - let original = self.part.read().await?; - Ok(&original[self.start..self.start + self.len]) + async fn read(&self) -> &[u8] { + let original = self.part.read().await; + &original[self.start..self.start + self.len] } fn size(&self) -> usize { @@ -189,19 +199,17 @@ pub fn op_blob_slice_part( state: &mut OpState, #[serde] id: Uuid, #[serde] options: SliceOptions, -) -> Result { +) -> Result { let blob_store = state.borrow::>(); let part = blob_store .get_part(&id) - .ok_or_else(|| type_error("Blob part not found"))?; + .ok_or(BlobError::BlobPartNotFound)?; let SliceOptions { start, len } = options; let size = part.size(); if start + len > size { - return Err(type_error( - "start + len can not be larger than blob part size", - )); + return Err(BlobError::SizeLargerThanBlobPart); } let sliced_part = SlicedBlobPart { part, start, len }; @@ -215,14 +223,14 @@ pub fn op_blob_slice_part( pub async fn op_blob_read_part( state: Rc>, #[serde] id: Uuid, -) -> Result { +) -> Result { let part = { let state = state.borrow(); let blob_store = state.borrow::>(); blob_store.get_part(&id) } - .ok_or_else(|| type_error("Blob part not found"))?; - let buf = part.read().await?; + .ok_or(BlobError::BlobPartNotFound)?; + let buf = part.read().await; Ok(ToJsBuffer::from(buf.to_vec())) } @@ -238,13 +246,13 @@ pub fn op_blob_create_object_url( state: &mut OpState, #[string] media_type: String, #[serde] part_ids: Vec, -) -> Result { +) -> Result { let mut parts = Vec::with_capacity(part_ids.len()); let blob_store = state.borrow::>(); for part_id in part_ids { let part = blob_store .get_part(&part_id) - .ok_or_else(|| type_error("Blob part not found"))?; + .ok_or(BlobError::BlobPartNotFound)?; parts.push(part); } @@ -263,7 +271,7 @@ pub fn op_blob_create_object_url( pub fn op_blob_revoke_object_url( state: &mut OpState, #[string] url: &str, -) -> Result<(), AnyError> { +) -> Result<(), BlobError> { let url = Url::parse(url)?; let blob_store = state.borrow::>(); blob_store.remove_object_url(&url); @@ -287,15 +295,15 @@ pub struct ReturnBlobPart { pub fn op_blob_from_object_url( state: &mut OpState, #[string] url: String, -) -> Result, AnyError> { +) -> Result, BlobError> { let url = Url::parse(&url)?; if url.scheme() != "blob" { return Ok(None); } - let blob_store = state.try_borrow::>().ok_or_else(|| { - type_error("Blob URLs are not supported in this context.") - })?; + let blob_store = state + .try_borrow::>() + .ok_or(BlobError::BlobURLsNotSupported)?; if let Some(blob) = blob_store.get_object_url(url) { let parts = blob .parts diff --git a/ext/web/compression.rs b/ext/web/compression.rs index b9ae12ef17..6967009915 100644 --- a/ext/web/compression.rs +++ b/ext/web/compression.rs @@ -1,7 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::op2; use flate2::write::DeflateDecoder; use flate2::write::DeflateEncoder; @@ -13,6 +11,18 @@ use flate2::Compression; use std::cell::RefCell; use std::io::Write; +#[derive(Debug, thiserror::Error)] +pub enum CompressionError { + #[error("Unsupported format")] + UnsupportedFormat, + #[error("resource is closed")] + ResourceClosed, + #[error(transparent)] + IoTypeError(std::io::Error), + #[error(transparent)] + Io(std::io::Error), +} + #[derive(Debug)] struct CompressionResource(RefCell>); @@ -34,7 +44,7 @@ enum Inner { pub fn op_compression_new( #[string] format: &str, is_decoder: bool, -) -> Result { +) -> Result { let w = Vec::new(); let inner = match (format, is_decoder) { ("deflate", true) => Inner::DeflateDecoder(ZlibDecoder::new(w)), @@ -49,7 +59,7 @@ pub fn op_compression_new( ("gzip", false) => { Inner::GzEncoder(GzEncoder::new(w, Compression::default())) } - _ => return Err(type_error("Unsupported format")), + _ => return Err(CompressionError::UnsupportedFormat), }; Ok(CompressionResource(RefCell::new(Some(inner)))) } @@ -59,40 +69,38 @@ pub fn op_compression_new( pub fn op_compression_write( #[cppgc] resource: &CompressionResource, #[anybuffer] input: &[u8], -) -> Result, AnyError> { +) -> Result, CompressionError> { let mut inner = resource.0.borrow_mut(); - let inner = inner - .as_mut() - .ok_or_else(|| type_error("resource is closed"))?; + let inner = inner.as_mut().ok_or(CompressionError::ResourceClosed)?; let out: Vec = match &mut *inner { Inner::DeflateDecoder(d) => { - d.write_all(input).map_err(|e| type_error(e.to_string()))?; - d.flush()?; + d.write_all(input).map_err(CompressionError::IoTypeError)?; + d.flush().map_err(CompressionError::Io)?; d.get_mut().drain(..) } Inner::DeflateEncoder(d) => { - d.write_all(input).map_err(|e| type_error(e.to_string()))?; - d.flush()?; + d.write_all(input).map_err(CompressionError::IoTypeError)?; + d.flush().map_err(CompressionError::Io)?; d.get_mut().drain(..) } Inner::DeflateRawDecoder(d) => { - d.write_all(input).map_err(|e| type_error(e.to_string()))?; - d.flush()?; + d.write_all(input).map_err(CompressionError::IoTypeError)?; + d.flush().map_err(CompressionError::Io)?; d.get_mut().drain(..) } Inner::DeflateRawEncoder(d) => { - d.write_all(input).map_err(|e| type_error(e.to_string()))?; - d.flush()?; + d.write_all(input).map_err(CompressionError::IoTypeError)?; + d.flush().map_err(CompressionError::Io)?; d.get_mut().drain(..) } Inner::GzDecoder(d) => { - d.write_all(input).map_err(|e| type_error(e.to_string()))?; - d.flush()?; + d.write_all(input).map_err(CompressionError::IoTypeError)?; + d.flush().map_err(CompressionError::Io)?; d.get_mut().drain(..) } Inner::GzEncoder(d) => { - d.write_all(input).map_err(|e| type_error(e.to_string()))?; - d.flush()?; + d.write_all(input).map_err(CompressionError::IoTypeError)?; + d.flush().map_err(CompressionError::Io)?; d.get_mut().drain(..) } } @@ -105,27 +113,27 @@ pub fn op_compression_write( pub fn op_compression_finish( #[cppgc] resource: &CompressionResource, report_errors: bool, -) -> Result, AnyError> { +) -> Result, CompressionError> { let inner = resource .0 .borrow_mut() .take() - .ok_or_else(|| type_error("resource is closed"))?; + .ok_or(CompressionError::ResourceClosed)?; let out = match inner { Inner::DeflateDecoder(d) => { - d.finish().map_err(|e| type_error(e.to_string())) + d.finish().map_err(CompressionError::IoTypeError) } Inner::DeflateEncoder(d) => { - d.finish().map_err(|e| type_error(e.to_string())) + d.finish().map_err(CompressionError::IoTypeError) } Inner::DeflateRawDecoder(d) => { - d.finish().map_err(|e| type_error(e.to_string())) + d.finish().map_err(CompressionError::IoTypeError) } Inner::DeflateRawEncoder(d) => { - d.finish().map_err(|e| type_error(e.to_string())) + d.finish().map_err(CompressionError::IoTypeError) } - Inner::GzDecoder(d) => d.finish().map_err(|e| type_error(e.to_string())), - Inner::GzEncoder(d) => d.finish().map_err(|e| type_error(e.to_string())), + Inner::GzDecoder(d) => d.finish().map_err(CompressionError::IoTypeError), + Inner::GzEncoder(d) => d.finish().map_err(CompressionError::IoTypeError), }; match out { Err(err) => { diff --git a/ext/web/lib.rs b/ext/web/lib.rs index 3977379a5d..4935af5bd3 100644 --- a/ext/web/lib.rs +++ b/ext/web/lib.rs @@ -6,9 +6,6 @@ mod message_port; mod stream_resource; mod timers; -use deno_core::error::range_error; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::url::Url; use deno_core::v8; @@ -22,10 +19,14 @@ use encoding_rs::DecoderResult; use encoding_rs::Encoding; use std::borrow::Cow; use std::cell::RefCell; -use std::fmt; use std::path::PathBuf; use std::sync::Arc; +pub use blob::BlobError; +pub use compression::CompressionError; +pub use message_port::MessagePortError; +pub use stream_resource::StreamResourceError; + use crate::blob::op_blob_create_object_url; use crate::blob::op_blob_create_part; use crate::blob::op_blob_from_object_url; @@ -126,9 +127,27 @@ deno_core::extension!(deno_web, } ); +#[derive(Debug, thiserror::Error)] +pub enum WebError { + #[error("Failed to decode base64")] + Base64Decode, + #[error("The encoding label provided ('{0}') is invalid.")] + InvalidEncodingLabel(String), + #[error("buffer exceeds maximum length")] + BufferTooLong, + #[error("Value too large to decode")] + ValueTooLarge, + #[error("Provided buffer too small")] + BufferTooSmall, + #[error("The encoded data is not valid")] + DataInvalid, + #[error(transparent)] + DataError(#[from] v8::DataError), +} + #[op2] #[serde] -fn op_base64_decode(#[string] input: String) -> Result { +fn op_base64_decode(#[string] input: String) -> Result { let mut s = input.into_bytes(); let decoded_len = forgiving_base64_decode_inplace(&mut s)?; s.truncate(decoded_len); @@ -137,7 +156,7 @@ fn op_base64_decode(#[string] input: String) -> Result { #[op2] #[serde] -fn op_base64_atob(#[serde] mut s: ByteString) -> Result { +fn op_base64_atob(#[serde] mut s: ByteString) -> Result { let decoded_len = forgiving_base64_decode_inplace(&mut s)?; s.truncate(decoded_len); Ok(s) @@ -147,11 +166,9 @@ fn op_base64_atob(#[serde] mut s: ByteString) -> Result { #[inline] fn forgiving_base64_decode_inplace( input: &mut [u8], -) -> Result { - let error = - || DomExceptionInvalidCharacterError::new("Failed to decode base64"); - let decoded = - base64_simd::forgiving_decode_inplace(input).map_err(|_| error())?; +) -> Result { + let decoded = base64_simd::forgiving_decode_inplace(input) + .map_err(|_| WebError::Base64Decode)?; Ok(decoded.len()) } @@ -177,13 +194,9 @@ fn forgiving_base64_encode(s: &[u8]) -> String { #[string] fn op_encoding_normalize_label( #[string] label: String, -) -> Result { +) -> Result { let encoding = Encoding::for_label_no_replacement(label.as_bytes()) - .ok_or_else(|| { - range_error(format!( - "The encoding label provided ('{label}') is invalid." - )) - })?; + .ok_or(WebError::InvalidEncodingLabel(label))?; Ok(encoding.name().to_lowercase()) } @@ -192,7 +205,7 @@ fn op_encoding_decode_utf8<'a>( scope: &mut v8::HandleScope<'a>, #[anybuffer] zero_copy: &[u8], ignore_bom: bool, -) -> Result, AnyError> { +) -> Result, WebError> { let buf = &zero_copy; let buf = if !ignore_bom @@ -216,7 +229,7 @@ fn op_encoding_decode_utf8<'a>( // - https://github.com/v8/v8/blob/d68fb4733e39525f9ff0a9222107c02c28096e2a/include/v8.h#L3277-L3278 match v8::String::new_from_utf8(scope, buf, v8::NewStringType::Normal) { Some(text) => Ok(text), - None => Err(type_error("buffer exceeds maximum length")), + None => Err(WebError::BufferTooLong), } } @@ -227,12 +240,9 @@ fn op_encoding_decode_single( #[string] label: String, fatal: bool, ignore_bom: bool, -) -> Result { - let encoding = Encoding::for_label(label.as_bytes()).ok_or_else(|| { - range_error(format!( - "The encoding label provided ('{label}') is invalid." - )) - })?; +) -> Result { + let encoding = Encoding::for_label(label.as_bytes()) + .ok_or(WebError::InvalidEncodingLabel(label))?; let mut decoder = if ignore_bom { encoding.new_decoder_without_bom_handling() @@ -242,7 +252,7 @@ fn op_encoding_decode_single( let max_buffer_length = decoder .max_utf16_buffer_length(data.len()) - .ok_or_else(|| range_error("Value too large to decode."))?; + .ok_or(WebError::ValueTooLarge)?; let mut output = vec![0; max_buffer_length]; @@ -254,12 +264,8 @@ fn op_encoding_decode_single( output.truncate(written); Ok(output.into()) } - DecoderResult::OutputFull => { - Err(range_error("Provided buffer too small.")) - } - DecoderResult::Malformed(_, _) => { - Err(type_error("The encoded data is not valid.")) - } + DecoderResult::OutputFull => Err(WebError::BufferTooSmall), + DecoderResult::Malformed(_, _) => Err(WebError::DataInvalid), } } else { let (result, _, written, _) = @@ -269,7 +275,7 @@ fn op_encoding_decode_single( output.truncate(written); Ok(output.into()) } - CoderResult::OutputFull => Err(range_error("Provided buffer too small.")), + CoderResult::OutputFull => Err(WebError::BufferTooSmall), } } } @@ -280,12 +286,9 @@ fn op_encoding_new_decoder( #[string] label: &str, fatal: bool, ignore_bom: bool, -) -> Result { - let encoding = Encoding::for_label(label.as_bytes()).ok_or_else(|| { - range_error(format!( - "The encoding label provided ('{label}') is invalid." - )) - })?; +) -> Result { + let encoding = Encoding::for_label(label.as_bytes()) + .ok_or_else(|| WebError::InvalidEncodingLabel(label.to_string()))?; let decoder = if ignore_bom { encoding.new_decoder_without_bom_handling() @@ -305,13 +308,13 @@ fn op_encoding_decode( #[anybuffer] data: &[u8], #[cppgc] resource: &TextDecoderResource, stream: bool, -) -> Result { +) -> Result { let mut decoder = resource.decoder.borrow_mut(); let fatal = resource.fatal; let max_buffer_length = decoder .max_utf16_buffer_length(data.len()) - .ok_or_else(|| range_error("Value too large to decode."))?; + .ok_or(WebError::ValueTooLarge)?; let mut output = vec![0; max_buffer_length]; @@ -323,12 +326,8 @@ fn op_encoding_decode( output.truncate(written); Ok(output.into()) } - DecoderResult::OutputFull => { - Err(range_error("Provided buffer too small.")) - } - DecoderResult::Malformed(_, _) => { - Err(type_error("The encoded data is not valid.")) - } + DecoderResult::OutputFull => Err(WebError::BufferTooSmall), + DecoderResult::Malformed(_, _) => Err(WebError::DataInvalid), } } else { let (result, _, written, _) = @@ -338,7 +337,7 @@ fn op_encoding_decode( output.truncate(written); Ok(output.into()) } - CoderResult::OutputFull => Err(range_error("Provided buffer too small.")), + CoderResult::OutputFull => Err(WebError::BufferTooSmall), } } } @@ -356,7 +355,7 @@ fn op_encoding_encode_into( input: v8::Local, #[buffer] buffer: &mut [u8], #[buffer] out_buf: &mut [u32], -) -> Result<(), AnyError> { +) -> Result<(), WebError> { let s = v8::Local::::try_from(input)?; let mut nchars = 0; @@ -414,53 +413,4 @@ pub fn get_declaration() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_web.d.ts") } -#[derive(Debug)] -pub struct DomExceptionQuotaExceededError { - pub msg: String, -} - -impl DomExceptionQuotaExceededError { - pub fn new(msg: &str) -> Self { - DomExceptionQuotaExceededError { - msg: msg.to_string(), - } - } -} - -#[derive(Debug)] -pub struct DomExceptionInvalidCharacterError { - pub msg: String, -} - -impl DomExceptionInvalidCharacterError { - pub fn new(msg: &str) -> Self { - DomExceptionInvalidCharacterError { - msg: msg.to_string(), - } - } -} - -impl fmt::Display for DomExceptionQuotaExceededError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.pad(&self.msg) - } -} -impl fmt::Display for DomExceptionInvalidCharacterError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.pad(&self.msg) - } -} - -impl std::error::Error for DomExceptionQuotaExceededError {} - -impl std::error::Error for DomExceptionInvalidCharacterError {} - -pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> { - e.downcast_ref::() - .map(|_| "DOMExceptionQuotaExceededError") - .or_else(|| { - e.downcast_ref::() - .map(|_| "DOMExceptionInvalidCharacterError") - }) -} pub struct Location(pub Url); diff --git a/ext/web/message_port.rs b/ext/web/message_port.rs index fa299475d9..1a4a09073d 100644 --- a/ext/web/message_port.rs +++ b/ext/web/message_port.rs @@ -4,8 +4,6 @@ use std::borrow::Cow; use std::cell::RefCell; use std::rc::Rc; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::CancelFuture; @@ -23,6 +21,20 @@ use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::mpsc::UnboundedSender; +#[derive(Debug, thiserror::Error)] +pub enum MessagePortError { + #[error("Invalid message port transfer")] + InvalidTransfer, + #[error("Message port is not ready for transfer")] + NotReady, + #[error("Can not transfer self message port")] + TransferSelf, + #[error(transparent)] + Canceled(#[from] deno_core::Canceled), + #[error(transparent)] + Resource(deno_core::error::AnyError), +} + pub enum Transferable { MessagePort(MessagePort), ArrayBuffer(u32), @@ -40,7 +52,7 @@ impl MessagePort { &self, state: &mut OpState, data: JsMessageData, - ) -> Result<(), AnyError> { + ) -> Result<(), MessagePortError> { let transferables = deserialize_js_transferables(state, data.transferables)?; @@ -56,7 +68,7 @@ impl MessagePort { pub async fn recv( &self, state: Rc>, - ) -> Result, AnyError> { + ) -> Result, MessagePortError> { let rx = &self.rx; let maybe_data = poll_fn(|cx| { @@ -147,7 +159,7 @@ pub enum JsTransferable { pub fn deserialize_js_transferables( state: &mut OpState, js_transferables: Vec, -) -> Result, AnyError> { +) -> Result, MessagePortError> { let mut transferables = Vec::with_capacity(js_transferables.len()); for js_transferable in js_transferables { match js_transferable { @@ -155,10 +167,10 @@ pub fn deserialize_js_transferables( let resource = state .resource_table .take::(id) - .map_err(|_| type_error("Invalid message port transfer"))?; + .map_err(|_| MessagePortError::InvalidTransfer)?; resource.cancel.cancel(); - let resource = Rc::try_unwrap(resource) - .map_err(|_| type_error("Message port is not ready for transfer"))?; + let resource = + Rc::try_unwrap(resource).map_err(|_| MessagePortError::NotReady)?; transferables.push(Transferable::MessagePort(resource.port)); } JsTransferable::ArrayBuffer(id) => { @@ -202,16 +214,19 @@ pub fn op_message_port_post_message( state: &mut OpState, #[smi] rid: ResourceId, #[serde] data: JsMessageData, -) -> Result<(), AnyError> { +) -> Result<(), MessagePortError> { for js_transferable in &data.transferables { if let JsTransferable::MessagePort(id) = js_transferable { if *id == rid { - return Err(type_error("Can not transfer self message port")); + return Err(MessagePortError::TransferSelf); } } } - let resource = state.resource_table.get::(rid)?; + let resource = state + .resource_table + .get::(rid) + .map_err(MessagePortError::Resource)?; resource.port.send(state, data) } @@ -220,7 +235,7 @@ pub fn op_message_port_post_message( pub async fn op_message_port_recv_message( state: Rc>, #[smi] rid: ResourceId, -) -> Result, AnyError> { +) -> Result, MessagePortError> { let resource = { let state = state.borrow(); match state.resource_table.get::(rid) { @@ -237,8 +252,11 @@ pub async fn op_message_port_recv_message( pub fn op_message_port_recv_message_sync( state: &mut OpState, // Rc>, #[smi] rid: ResourceId, -) -> Result, AnyError> { - let resource = state.resource_table.get::(rid)?; +) -> Result, MessagePortError> { + let resource = state + .resource_table + .get::(rid) + .map_err(MessagePortError::Resource)?; let mut rx = resource.port.rx.borrow_mut(); match rx.try_recv() { diff --git a/ext/web/stream_resource.rs b/ext/web/stream_resource.rs index 78487883b6..c44a385ea9 100644 --- a/ext/web/stream_resource.rs +++ b/ext/web/stream_resource.rs @@ -1,7 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use bytes::BytesMut; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::external; use deno_core::op2; use deno_core::serde_v8::V8Slice; @@ -18,6 +16,7 @@ use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; use futures::future::poll_fn; +use futures::TryFutureExt; use std::borrow::Cow; use std::cell::RefCell; use std::cell::RefMut; @@ -31,6 +30,14 @@ use std::task::Context; use std::task::Poll; use std::task::Waker; +#[derive(Debug, thiserror::Error)] +pub enum StreamResourceError { + #[error(transparent)] + Canceled(#[from] deno_core::Canceled), + #[error("{0}")] + Js(String), +} + // How many buffers we'll allow in the channel before we stop allowing writes. const BUFFER_CHANNEL_SIZE: u16 = 1024; @@ -48,7 +55,7 @@ struct BoundedBufferChannelInner { buffers: [MaybeUninit>; BUFFER_CHANNEL_SIZE as _], ring_producer: u16, ring_consumer: u16, - error: Option, + error: Option, current_size: usize, // TODO(mmastrac): we can math this field instead of accounting for it len: usize, @@ -141,7 +148,10 @@ impl BoundedBufferChannelInner { self.len = 0; } - pub fn read(&mut self, limit: usize) -> Result, AnyError> { + pub fn read( + &mut self, + limit: usize, + ) -> Result, StreamResourceError> { // Empty buffers will return the error, if one exists, or None if self.len == 0 { if let Some(error) = self.error.take() { @@ -230,7 +240,7 @@ impl BoundedBufferChannelInner { Ok(()) } - pub fn write_error(&mut self, error: AnyError) { + pub fn write_error(&mut self, error: StreamResourceError) { self.error = Some(error); if let Some(waker) = self.read_waker.take() { waker.wake(); @@ -306,7 +316,10 @@ impl BoundedBufferChannel { self.inner.borrow_mut() } - pub fn read(&self, limit: usize) -> Result, AnyError> { + pub fn read( + &self, + limit: usize, + ) -> Result, StreamResourceError> { self.inner().read(limit) } @@ -314,7 +327,7 @@ impl BoundedBufferChannel { self.inner().write(buffer) } - pub fn write_error(&self, error: AnyError) { + pub fn write_error(&self, error: StreamResourceError) { self.inner().write_error(error) } @@ -358,7 +371,10 @@ impl ReadableStreamResource { RcRef::map(self, |s| &s.cancel_handle).clone() } - async fn read(self: Rc, limit: usize) -> Result { + async fn read( + self: Rc, + limit: usize, + ) -> Result { let cancel_handle = self.cancel_handle(); // Serialize all the reads using a task queue. let _read_permit = self.read_queue.acquire().await; @@ -387,7 +403,7 @@ impl Resource for ReadableStreamResource { } fn read(self: Rc, limit: usize) -> AsyncResult { - Box::pin(ReadableStreamResource::read(self, limit)) + Box::pin(ReadableStreamResource::read(self, limit).map_err(|e| e.into())) } fn close(self: Rc) { @@ -550,7 +566,7 @@ pub fn op_readable_stream_resource_write_error( ) -> bool { let sender = get_sender(sender); // We can always write an error, no polling required - sender.write_error(type_error(Cow::Owned(error))); + sender.write_error(StreamResourceError::Js(error)); !sender.closed() } diff --git a/runtime/errors.rs b/runtime/errors.rs index ffcc1c1191..99de6beb04 100644 --- a/runtime/errors.rs +++ b/runtime/errors.rs @@ -28,6 +28,11 @@ use deno_kv::KvError; use deno_kv::KvMutationError; use deno_net::ops::NetError; use deno_tls::TlsError; +use deno_web::BlobError; +use deno_web::CompressionError; +use deno_web::MessagePortError; +use deno_web::StreamResourceError; +use deno_web::WebError; use deno_webstorage::WebStorageError; use std::env; use std::error::Error; @@ -169,6 +174,61 @@ pub fn get_nix_error_class(error: &nix::Error) -> &'static str { } } +fn get_web_error_class(e: &WebError) -> &'static str { + match e { + WebError::Base64Decode => "DOMExceptionInvalidCharacterError", + WebError::InvalidEncodingLabel(_) => "RangeError", + WebError::BufferTooLong => "TypeError", + WebError::ValueTooLarge => "RangeError", + WebError::BufferTooSmall => "RangeError", + WebError::DataInvalid => "TypeError", + WebError::DataError(_) => "Error", + } +} + +fn get_web_compression_error_class(e: &CompressionError) -> &'static str { + match e { + CompressionError::UnsupportedFormat => "TypeError", + CompressionError::ResourceClosed => "TypeError", + CompressionError::IoTypeError(_) => "TypeError", + CompressionError::Io(e) => get_io_error_class(e), + } +} + +fn get_web_message_port_error_class(e: &MessagePortError) -> &'static str { + match e { + MessagePortError::InvalidTransfer => "TypeError", + MessagePortError::NotReady => "TypeError", + MessagePortError::TransferSelf => "TypeError", + MessagePortError::Canceled(e) => { + let io_err: io::Error = e.to_owned().into(); + get_io_error_class(&io_err) + } + MessagePortError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), + } +} + +fn get_web_stream_resource_error_class( + e: &StreamResourceError, +) -> &'static str { + match e { + StreamResourceError::Canceled(e) => { + let io_err: io::Error = e.to_owned().into(); + get_io_error_class(&io_err) + } + StreamResourceError::Js(_) => "TypeError", + } +} + +fn get_web_blob_error_class(e: &BlobError) -> &'static str { + match e { + BlobError::BlobPartNotFound => "TypeError", + BlobError::SizeLargerThanBlobPart => "TypeError", + BlobError::BlobURLsNotSupported => "TypeError", + BlobError::Url(_) => "Error", + } +} + fn get_ffi_repr_error_class(e: &ReprError) -> &'static str { match e { ReprError::InvalidOffset => "TypeError", @@ -382,8 +442,21 @@ fn get_net_map_error(error: &deno_net::io::MapError) -> &'static str { pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> { deno_core::error::get_custom_error_class(e) .or_else(|| deno_webgpu::error::get_error_class_name(e)) - .or_else(|| deno_web::get_error_class_name(e)) .or_else(|| deno_websocket::get_network_error_class_name(e)) + .or_else(|| e.downcast_ref::().map(get_web_error_class)) + .or_else(|| { + e.downcast_ref::() + .map(get_web_compression_error_class) + }) + .or_else(|| { + e.downcast_ref::() + .map(get_web_message_port_error_class) + }) + .or_else(|| { + e.downcast_ref::() + .map(get_web_stream_resource_error_class) + }) + .or_else(|| e.downcast_ref::().map(get_web_blob_error_class)) .or_else(|| e.downcast_ref::().map(|_| "TypeError")) .or_else(|| e.downcast_ref::().map(get_ffi_repr_error_class)) .or_else(|| { diff --git a/runtime/ops/web_worker.rs b/runtime/ops/web_worker.rs index 0ed76ebd50..e28bf2192f 100644 --- a/runtime/ops/web_worker.rs +++ b/runtime/ops/web_worker.rs @@ -50,6 +50,7 @@ async fn op_worker_recv_message( .recv(state.clone()) .or_cancel(handle.cancel) .await? + .map_err(|e| e.into()) } #[op2(fast)] diff --git a/runtime/ops/web_worker/sync_fetch.rs b/runtime/ops/web_worker/sync_fetch.rs index cdb151a86f..87fc558405 100644 --- a/runtime/ops/web_worker/sync_fetch.rs +++ b/runtime/ops/web_worker/sync_fetch.rs @@ -134,7 +134,7 @@ pub fn op_worker_sync_fetch( let mime_type = mime_type_essence(&blob.media_type); - let body = blob.read_all().await?; + let body = blob.read_all().await; (Bytes::from(body), Some(mime_type), script) } diff --git a/runtime/ops/worker_host.rs b/runtime/ops/worker_host.rs index 61e5ef3e0a..d85541d51d 100644 --- a/runtime/ops/worker_host.rs +++ b/runtime/ops/worker_host.rs @@ -359,7 +359,7 @@ async fn op_host_recv_message( } Ok(ret) } - Ok(Err(err)) => Err(err), + Ok(Err(err)) => Err(err.into()), Err(_) => { // The worker was terminated. Ok(None) From 9fde5cb5e045551fe344b3f60370744eea30ccb4 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Thu, 17 Oct 2024 12:51:15 -0700 Subject: [PATCH 08/14] fix(node/fs): copyFile with `COPYFILE_EXCL` should not throw if the destination doesn't exist (#26360) Fixes #26313. We were checking for the NotFound error, but still calling the callback with the error / throwing. --- ext/node/polyfills/_fs/_fs_copy.ts | 6 ++++-- tests/unit_node/fs_test.ts | 21 ++++++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/ext/node/polyfills/_fs/_fs_copy.ts b/ext/node/polyfills/_fs/_fs_copy.ts index 2f8ddf4fcd..0434bff4d1 100644 --- a/ext/node/polyfills/_fs/_fs_copy.ts +++ b/ext/node/polyfills/_fs/_fs_copy.ts @@ -53,8 +53,9 @@ export function copyFile( }, (e) => { if (e instanceof Deno.errors.NotFound) { Deno.copyFile(srcStr, destStr).then(() => cb(null), cb); + } else { + cb(e); } - cb(e); }); } else { Deno.copyFile(srcStr, destStr).then(() => cb(null), cb); @@ -83,8 +84,9 @@ export function copyFileSync( } catch (e) { if (e instanceof Deno.errors.NotFound) { Deno.copyFileSync(srcStr, destStr); + } else { + throw e; } - throw e; } } else { Deno.copyFileSync(srcStr, destStr); diff --git a/tests/unit_node/fs_test.ts b/tests/unit_node/fs_test.ts index 2d1465aec6..17da45dcff 100644 --- a/tests/unit_node/fs_test.ts +++ b/tests/unit_node/fs_test.ts @@ -1,11 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals, assertThrows } from "@std/assert"; +/// +import { assert, assertEquals, assertRejects, assertThrows } from "@std/assert"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { closeSync, constants, + copyFileSync, createWriteStream, existsSync, lstatSync, @@ -20,6 +22,7 @@ import { } from "node:fs"; import { constants as fsPromiseConstants, + copyFile, cp, FileHandle, open, @@ -212,3 +215,19 @@ Deno.test("[node/fs] readSync works", () => { assertEquals(bytesRead, 12); closeSync(fd!); }); + +Deno.test("[node/fs] copyFile COPYFILE_EXCL works", async () => { + const dir = mkdtempSync(join(tmpdir(), "foo-")); + const src = join(dir, "src.txt"); + const dest = join(dir, "dest.txt"); + await writeFile(src, ""); + await copyFile(src, dest, fsPromiseConstants.COPYFILE_EXCL); + assert(existsSync(dest)); + assertRejects(() => copyFile(src, dest, fsPromiseConstants.COPYFILE_EXCL)); + const dest2 = join(dir, "dest2.txt"); + copyFileSync(src, dest2, fsPromiseConstants.COPYFILE_EXCL); + assert(existsSync(dest2)); + assertThrows(() => + copyFileSync(src, dest2, fsPromiseConstants.COPYFILE_EXCL) + ); +}); From 8cfd9968fa9ef79777ac2e7dfcef8f1ed618b78b Mon Sep 17 00:00:00 2001 From: Leo Kettmeir Date: Thu, 17 Oct 2024 12:51:33 -0700 Subject: [PATCH 09/14] refactor(ext/napi): use concrete error types (#26186) --- Cargo.lock | 1 + ext/napi/Cargo.toml | 1 + ext/napi/lib.rs | 45 ++++++++++++++++++++++++++------------------- runtime/errors.rs | 11 +++++++++++ 4 files changed, 39 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 205804abe4..ae4501c8dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1772,6 +1772,7 @@ dependencies = [ "deno_core", "deno_permissions", "libloading 0.7.4", + "thiserror", ] [[package]] diff --git a/ext/napi/Cargo.toml b/ext/napi/Cargo.toml index 5405d6280c..68d281ccc6 100644 --- a/ext/napi/Cargo.toml +++ b/ext/napi/Cargo.toml @@ -17,3 +17,4 @@ path = "lib.rs" deno_core.workspace = true deno_permissions.workspace = true libloading = { version = "0.7" } +thiserror.workspace = true diff --git a/ext/napi/lib.rs b/ext/napi/lib.rs index 4500c66fd4..0b2b3eb5e7 100644 --- a/ext/napi/lib.rs +++ b/ext/napi/lib.rs @@ -6,8 +6,6 @@ #![deny(clippy::missing_safety_doc)] use core::ptr::NonNull; -use deno_core::error::type_error; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::parking_lot::RwLock; use deno_core::url::Url; @@ -20,6 +18,18 @@ use std::path::PathBuf; use std::rc::Rc; use std::thread_local; +#[derive(Debug, thiserror::Error)] +pub enum NApiError { + #[error("Invalid path")] + InvalidPath, + #[error(transparent)] + LibLoading(#[from] libloading::Error), + #[error("Unable to find register Node-API module at {}", .0.display())] + ModuleNotFound(PathBuf), + #[error(transparent)] + Permission(deno_core::error::AnyError), +} + #[cfg(unix)] use libloading::os::unix::*; @@ -482,14 +492,20 @@ deno_core::extension!(deno_napi, pub trait NapiPermissions { #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] - fn check(&mut self, path: &str) -> std::result::Result; + fn check( + &mut self, + path: &str, + ) -> Result; } // NOTE(bartlomieju): for now, NAPI uses `--allow-ffi` flag, but that might // change in the future. impl NapiPermissions for deno_permissions::PermissionsContainer { #[inline(always)] - fn check(&mut self, path: &str) -> Result { + fn check( + &mut self, + path: &str, + ) -> Result { deno_permissions::PermissionsContainer::check_ffi(self, path) } } @@ -512,7 +528,7 @@ fn op_napi_open( global: v8::Local<'scope, v8::Object>, buffer_constructor: v8::Local<'scope, v8::Function>, report_error: v8::Local<'scope, v8::Function>, -) -> std::result::Result, AnyError> +) -> Result, NApiError> where NP: NapiPermissions + 'static, { @@ -521,7 +537,7 @@ where let (async_work_sender, cleanup_hooks, external_ops_tracker, path) = { let mut op_state = op_state.borrow_mut(); let permissions = op_state.borrow_mut::(); - let path = permissions.check(&path)?; + let path = permissions.check(&path).map_err(NApiError::Permission)?; let napi_state = op_state.borrow::(); ( op_state.borrow::().clone(), @@ -540,7 +556,7 @@ where let type_tag = v8::Global::new(scope, type_tag); let url_filename = - Url::from_file_path(&path).map_err(|_| type_error("Invalid path"))?; + Url::from_file_path(&path).map_err(|_| NApiError::InvalidPath)?; let env_shared = EnvShared::new(napi_wrap, type_tag, format!("{url_filename}\0")); @@ -565,17 +581,11 @@ where // SAFETY: opening a DLL calls dlopen #[cfg(unix)] - let library = match unsafe { Library::open(Some(&path), flags) } { - Ok(lib) => lib, - Err(e) => return Err(type_error(e.to_string())), - }; + let library = unsafe { Library::open(Some(&path), flags) }?; // SAFETY: opening a DLL calls dlopen #[cfg(not(unix))] - let library = match unsafe { Library::load_with_flags(&path, flags) } { - Ok(lib) => lib, - Err(e) => return Err(type_error(e.to_string())), - }; + let library = unsafe { Library::load_with_flags(&path, flags) }?; let maybe_module = MODULE_TO_REGISTER.with(|cell| { let mut slot = cell.borrow_mut(); @@ -610,10 +620,7 @@ where // SAFETY: we are going blind, calling the register function on the other side. unsafe { init(env_ptr, exports.into()) } } else { - return Err(type_error(format!( - "Unable to find register Node-API module at {}", - path.display() - ))); + return Err(NApiError::ModuleNotFound(path)); }; let exports = maybe_exports.unwrap_or(exports.into()); diff --git a/runtime/errors.rs b/runtime/errors.rs index 99de6beb04..935f62d264 100644 --- a/runtime/errors.rs +++ b/runtime/errors.rs @@ -26,6 +26,7 @@ use deno_ffi::StaticError; use deno_kv::KvCheckError; use deno_kv::KvError; use deno_kv::KvMutationError; +use deno_napi::NApiError; use deno_net::ops::NetError; use deno_tls::TlsError; use deno_web::BlobError; @@ -174,6 +175,15 @@ pub fn get_nix_error_class(error: &nix::Error) -> &'static str { } } +fn get_napi_error_class(e: &NApiError) -> &'static str { + match e { + NApiError::InvalidPath + | NApiError::LibLoading(_) + | NApiError::ModuleNotFound(_) => "TypeError", + NApiError::Permission(e) => get_error_class_name(e).unwrap_or("Error"), + } +} + fn get_web_error_class(e: &WebError) -> &'static str { match e { WebError::Base64Decode => "DOMExceptionInvalidCharacterError", @@ -443,6 +453,7 @@ pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> { deno_core::error::get_custom_error_class(e) .or_else(|| deno_webgpu::error::get_error_class_name(e)) .or_else(|| deno_websocket::get_network_error_class_name(e)) + .or_else(|| e.downcast_ref::().map(get_napi_error_class)) .or_else(|| e.downcast_ref::().map(get_web_error_class)) .or_else(|| { e.downcast_ref::() From 2435a361c64fc9bac4aee7b268b4c0a42eee4471 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:19:37 -0700 Subject: [PATCH 10/14] chore: fix flaky COPYFILE_EXCL test (#26370) It was missing an await --- tests/unit_node/fs_test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/unit_node/fs_test.ts b/tests/unit_node/fs_test.ts index 17da45dcff..ac9c4a53e7 100644 --- a/tests/unit_node/fs_test.ts +++ b/tests/unit_node/fs_test.ts @@ -223,7 +223,9 @@ Deno.test("[node/fs] copyFile COPYFILE_EXCL works", async () => { await writeFile(src, ""); await copyFile(src, dest, fsPromiseConstants.COPYFILE_EXCL); assert(existsSync(dest)); - assertRejects(() => copyFile(src, dest, fsPromiseConstants.COPYFILE_EXCL)); + await assertRejects(() => + copyFile(src, dest, fsPromiseConstants.COPYFILE_EXCL) + ); const dest2 = join(dir, "dest2.txt"); copyFileSync(src, dest2, fsPromiseConstants.COPYFILE_EXCL); assert(existsSync(dest2)); From 50724d014ad6923e228e488648d40ce6f00297e9 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Thu, 17 Oct 2024 15:25:22 -0700 Subject: [PATCH 11/14] fix(install): don't attempt to cache specifiers that point to directories (#26369) Fixes https://github.com/denoland/deno/issues/26162 --- cli/tools/registry/pm/cache_deps.rs | 7 +++++++ tests/specs/install/import_map_with_dir/__test__.jsonc | 9 +++++++++ tests/specs/install/import_map_with_dir/deno.json | 5 +++++ 3 files changed, 21 insertions(+) create mode 100644 tests/specs/install/import_map_with_dir/__test__.jsonc create mode 100644 tests/specs/install/import_map_with_dir/deno.json diff --git a/cli/tools/registry/pm/cache_deps.rs b/cli/tools/registry/pm/cache_deps.rs index c8258e6009..b4cd1c2532 100644 --- a/cli/tools/registry/pm/cache_deps.rs +++ b/cli/tools/registry/pm/cache_deps.rs @@ -75,6 +75,13 @@ pub async fn cache_top_level_deps( if entry.key.ends_with('/') && specifier.as_str().ends_with('/') { continue; } + if specifier.scheme() == "file" { + if let Ok(path) = specifier.to_file_path() { + if !path.is_file() { + continue; + } + } + } roots.push(specifier.clone()); } } diff --git a/tests/specs/install/import_map_with_dir/__test__.jsonc b/tests/specs/install/import_map_with_dir/__test__.jsonc new file mode 100644 index 0000000000..cbbc7cc2e1 --- /dev/null +++ b/tests/specs/install/import_map_with_dir/__test__.jsonc @@ -0,0 +1,9 @@ +{ + "tempDir": true, + "steps": [ + { + "args": "install", + "output": "" + } + ] +} diff --git a/tests/specs/install/import_map_with_dir/deno.json b/tests/specs/install/import_map_with_dir/deno.json new file mode 100644 index 0000000000..5c3224b071 --- /dev/null +++ b/tests/specs/install/import_map_with_dir/deno.json @@ -0,0 +1,5 @@ +{ + "imports": { + "@assets": "./src/assets/" + } +} From 02e5a7a012d0c53a61f5cb38f15b92dd0db4f841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Thu, 17 Oct 2024 23:44:14 +0100 Subject: [PATCH 12/14] fix(jupyter): fix panics for overslow subtraction (#26371) I don't have a reliable reproduction for it, but it makes it painful to use the Jupyter kernel with semi-frequent random panics. The completions don't always work correctly anyway, so I think it's better to just not panic here for the time being. Fixes https://github.com/denoland/deno/issues/26340 --- cli/tools/jupyter/server.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/cli/tools/jupyter/server.rs b/cli/tools/jupyter/server.rs index 0cd80f7ddd..5680ed4c13 100644 --- a/cli/tools/jupyter/server.rs +++ b/cli/tools/jupyter/server.rs @@ -329,7 +329,12 @@ impl JupyterServer { }) .collect(); - (candidates, cursor_pos - prop_name.len()) + if prop_name.len() > cursor_pos { + // TODO(bartlomieju): most likely not correct, but better than panicking because of sub with overflow + (candidates, cursor_pos) + } else { + (candidates, cursor_pos - prop_name.len()) + } } else { // combine results of declarations and globalThis properties let mut candidates = get_expression_property_names( @@ -349,7 +354,12 @@ impl JupyterServer { candidates.sort(); candidates.dedup(); // make sure to sort first - (candidates, cursor_pos - expr.len()) + if expr.len() > cursor_pos { + // TODO(bartlomieju): most likely not correct, but better than panicking because of sub with overflow + (candidates, cursor_pos) + } else { + (candidates, cursor_pos - expr.len()) + } }; connection From 8f3eb9d0e7bbbfa67058c67c5e5c1484dee1ef9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Thu, 17 Oct 2024 23:57:05 +0100 Subject: [PATCH 13/14] fix(ext/node): add null check for kStreamBaseField (#26368) It's not guaranteed that `kStreamBaseField` is not undefined, so added a check for it. Closes https://github.com/denoland/deno/issues/26363 --- ext/node/polyfills/internal_binding/tcp_wrap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/node/polyfills/internal_binding/tcp_wrap.ts b/ext/node/polyfills/internal_binding/tcp_wrap.ts index 2856f808a1..d9f1c5356a 100644 --- a/ext/node/polyfills/internal_binding/tcp_wrap.ts +++ b/ext/node/polyfills/internal_binding/tcp_wrap.ts @@ -300,7 +300,7 @@ export class TCP extends ConnectionWrap { * @return An error status code. */ setNoDelay(noDelay: boolean): number { - if ("setNoDelay" in this[kStreamBaseField]) { + if (this[kStreamBaseField] && "setNoDelay" in this[kStreamBaseField]) { this[kStreamBaseField].setNoDelay(noDelay); } return 0; From 3ae10a01e0c8b9c425276a33b98f661c1473cd59 Mon Sep 17 00:00:00 2001 From: denobot <33910674+denobot@users.noreply.github.com> Date: Thu, 17 Oct 2024 21:12:49 -0400 Subject: [PATCH 14/14] chore: forward v2.0.2 release commit to main (#26376) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the release commit being forwarded back to main for 2.0.2 Co-authored-by: bartlomieju Co-authored-by: Bartek IwaƄczuk --- .github/workflows/ci.generate.ts | 2 +- .github/workflows/ci.yml | 8 ++--- Cargo.lock | 58 ++++++++++++++++---------------- Cargo.toml | 56 +++++++++++++++--------------- Releases.md | 15 +++++++++ bench_util/Cargo.toml | 2 +- cli/Cargo.toml | 2 +- cli/napi/sym/Cargo.toml | 2 +- ext/broadcast_channel/Cargo.toml | 2 +- ext/cache/Cargo.toml | 2 +- ext/canvas/Cargo.toml | 2 +- ext/console/Cargo.toml | 2 +- ext/cron/Cargo.toml | 2 +- ext/crypto/Cargo.toml | 2 +- ext/fetch/Cargo.toml | 2 +- ext/ffi/Cargo.toml | 2 +- ext/fs/Cargo.toml | 2 +- ext/http/Cargo.toml | 2 +- ext/io/Cargo.toml | 2 +- ext/kv/Cargo.toml | 2 +- ext/napi/Cargo.toml | 2 +- ext/net/Cargo.toml | 2 +- ext/node/Cargo.toml | 2 +- ext/tls/Cargo.toml | 2 +- ext/url/Cargo.toml | 2 +- ext/web/Cargo.toml | 2 +- ext/webgpu/Cargo.toml | 2 +- ext/webidl/Cargo.toml | 2 +- ext/websocket/Cargo.toml | 2 +- ext/webstorage/Cargo.toml | 2 +- resolvers/deno/Cargo.toml | 2 +- resolvers/node/Cargo.toml | 2 +- runtime/Cargo.toml | 2 +- runtime/permissions/Cargo.toml | 2 +- 34 files changed, 106 insertions(+), 91 deletions(-) diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index d18ec6e1a8..98272abe4e 100755 --- a/.github/workflows/ci.generate.ts +++ b/.github/workflows/ci.generate.ts @@ -5,7 +5,7 @@ import { stringify } from "jsr:@std/yaml@^0.221/stringify"; // Bump this number when you want to purge the cache. // Note: the tools/release/01_bump_crate_versions.ts script will update this version // automatically via regex, so ensure that this line maintains this format. -const cacheVersion = 20; +const cacheVersion = 21; const ubuntuX86Runner = "ubuntu-22.04"; const ubuntuX86XlRunner = "ubuntu-22.04-xl"; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1353bf4a6f..161b8719c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -361,8 +361,8 @@ jobs: path: |- ~/.cargo/registry/index ~/.cargo/registry/cache - key: '20-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' - restore-keys: '20-cargo-home-${{ matrix.os }}-${{ matrix.arch }}' + key: '21-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' + restore-keys: '21-cargo-home-${{ matrix.os }}-${{ matrix.arch }}' if: '!(matrix.skip)' - name: Restore cache build output (PR) uses: actions/cache/restore@v4 @@ -375,7 +375,7 @@ jobs: !./target/*/*.zip !./target/*/*.tar.gz key: never_saved - restore-keys: '20-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-' + restore-keys: '21-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-' - name: Apply and update mtime cache if: '!(matrix.skip) && (!startsWith(github.ref, ''refs/tags/''))' uses: ./.github/mtime_cache @@ -685,7 +685,7 @@ jobs: !./target/*/*.zip !./target/*/*.sha256sum !./target/*/*.tar.gz - key: '20-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' + key: '21-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' publish-canary: name: publish canary runs-on: ubuntu-22.04 diff --git a/Cargo.lock b/Cargo.lock index ae4501c8dc..bcbbaaae03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1154,7 +1154,7 @@ dependencies = [ [[package]] name = "deno" -version = "2.0.1" +version = "2.0.2" dependencies = [ "anstream", "async-trait", @@ -1328,7 +1328,7 @@ dependencies = [ [[package]] name = "deno_bench_util" -version = "0.166.0" +version = "0.167.0" dependencies = [ "bencher", "deno_core", @@ -1337,7 +1337,7 @@ dependencies = [ [[package]] name = "deno_broadcast_channel" -version = "0.166.0" +version = "0.167.0" dependencies = [ "async-trait", "deno_core", @@ -1348,7 +1348,7 @@ dependencies = [ [[package]] name = "deno_cache" -version = "0.104.0" +version = "0.105.0" dependencies = [ "async-trait", "deno_core", @@ -1381,7 +1381,7 @@ dependencies = [ [[package]] name = "deno_canvas" -version = "0.41.0" +version = "0.42.0" dependencies = [ "deno_core", "deno_webgpu", @@ -1416,7 +1416,7 @@ dependencies = [ [[package]] name = "deno_console" -version = "0.172.0" +version = "0.173.0" dependencies = [ "deno_core", ] @@ -1461,7 +1461,7 @@ checksum = "a13951ea98c0a4c372f162d669193b4c9d991512de9f2381dd161027f34b26b1" [[package]] name = "deno_cron" -version = "0.52.0" +version = "0.53.0" dependencies = [ "anyhow", "async-trait", @@ -1474,7 +1474,7 @@ dependencies = [ [[package]] name = "deno_crypto" -version = "0.186.0" +version = "0.187.0" dependencies = [ "aes", "aes-gcm", @@ -1534,7 +1534,7 @@ dependencies = [ [[package]] name = "deno_fetch" -version = "0.196.0" +version = "0.197.0" dependencies = [ "base64 0.21.7", "bytes", @@ -1566,7 +1566,7 @@ dependencies = [ [[package]] name = "deno_ffi" -version = "0.159.0" +version = "0.160.0" dependencies = [ "deno_core", "deno_permissions", @@ -1585,7 +1585,7 @@ dependencies = [ [[package]] name = "deno_fs" -version = "0.82.0" +version = "0.83.0" dependencies = [ "async-trait", "base32", @@ -1635,7 +1635,7 @@ dependencies = [ [[package]] name = "deno_http" -version = "0.170.0" +version = "0.171.0" dependencies = [ "async-compression", "async-trait", @@ -1674,7 +1674,7 @@ dependencies = [ [[package]] name = "deno_io" -version = "0.82.0" +version = "0.83.0" dependencies = [ "async-trait", "deno_core", @@ -1695,7 +1695,7 @@ dependencies = [ [[package]] name = "deno_kv" -version = "0.80.0" +version = "0.81.0" dependencies = [ "anyhow", "async-trait", @@ -1767,7 +1767,7 @@ dependencies = [ [[package]] name = "deno_napi" -version = "0.103.0" +version = "0.104.0" dependencies = [ "deno_core", "deno_permissions", @@ -1790,7 +1790,7 @@ dependencies = [ [[package]] name = "deno_net" -version = "0.164.0" +version = "0.165.0" dependencies = [ "deno_core", "deno_permissions", @@ -1807,7 +1807,7 @@ dependencies = [ [[package]] name = "deno_node" -version = "0.109.0" +version = "0.110.0" dependencies = [ "aead-gcm-stream", "aes", @@ -1956,7 +1956,7 @@ dependencies = [ [[package]] name = "deno_permissions" -version = "0.32.0" +version = "0.33.0" dependencies = [ "deno_core", "deno_path_util", @@ -1973,7 +1973,7 @@ dependencies = [ [[package]] name = "deno_resolver" -version = "0.4.0" +version = "0.5.0" dependencies = [ "anyhow", "base32", @@ -1989,7 +1989,7 @@ dependencies = [ [[package]] name = "deno_runtime" -version = "0.181.0" +version = "0.182.0" dependencies = [ "color-print", "deno_ast", @@ -2105,7 +2105,7 @@ dependencies = [ [[package]] name = "deno_tls" -version = "0.159.0" +version = "0.160.0" dependencies = [ "deno_core", "deno_native_certs", @@ -2154,7 +2154,7 @@ dependencies = [ [[package]] name = "deno_url" -version = "0.172.0" +version = "0.173.0" dependencies = [ "deno_bench_util", "deno_console", @@ -2166,7 +2166,7 @@ dependencies = [ [[package]] name = "deno_web" -version = "0.203.0" +version = "0.204.0" dependencies = [ "async-trait", "base64-simd 0.8.0", @@ -2188,7 +2188,7 @@ dependencies = [ [[package]] name = "deno_webgpu" -version = "0.139.0" +version = "0.140.0" dependencies = [ "deno_core", "raw-window-handle", @@ -2200,7 +2200,7 @@ dependencies = [ [[package]] name = "deno_webidl" -version = "0.172.0" +version = "0.173.0" dependencies = [ "deno_bench_util", "deno_core", @@ -2208,7 +2208,7 @@ dependencies = [ [[package]] name = "deno_websocket" -version = "0.177.0" +version = "0.178.0" dependencies = [ "bytes", "deno_core", @@ -2229,7 +2229,7 @@ dependencies = [ [[package]] name = "deno_webstorage" -version = "0.167.0" +version = "0.168.0" dependencies = [ "deno_core", "deno_web", @@ -4515,7 +4515,7 @@ dependencies = [ [[package]] name = "napi_sym" -version = "0.102.0" +version = "0.103.0" dependencies = [ "quote", "serde", @@ -4584,7 +4584,7 @@ dependencies = [ [[package]] name = "node_resolver" -version = "0.11.0" +version = "0.12.0" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index b625643510..3bf5342b8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,16 +48,16 @@ repository = "https://github.com/denoland/deno" deno_ast = { version = "=0.42.2", features = ["transpiling"] } deno_core = { version = "0.314.1" } -deno_bench_util = { version = "0.166.0", path = "./bench_util" } +deno_bench_util = { version = "0.167.0", path = "./bench_util" } deno_lockfile = "=0.23.1" deno_media_type = { version = "0.1.4", features = ["module_specifier"] } deno_npm = "=0.25.4" deno_path_util = "=0.2.1" -deno_permissions = { version = "0.32.0", path = "./runtime/permissions" } -deno_runtime = { version = "0.181.0", path = "./runtime" } +deno_permissions = { version = "0.33.0", path = "./runtime/permissions" } +deno_runtime = { version = "0.182.0", path = "./runtime" } deno_semver = "=0.5.14" deno_terminal = "0.2.0" -napi_sym = { version = "0.102.0", path = "./cli/napi/sym" } +napi_sym = { version = "0.103.0", path = "./cli/napi/sym" } test_util = { package = "test_server", path = "./tests/util/server" } denokv_proto = "0.8.1" @@ -66,32 +66,32 @@ denokv_remote = "0.8.1" denokv_sqlite = { default-features = false, version = "0.8.2" } # exts -deno_broadcast_channel = { version = "0.166.0", path = "./ext/broadcast_channel" } -deno_cache = { version = "0.104.0", path = "./ext/cache" } -deno_canvas = { version = "0.41.0", path = "./ext/canvas" } -deno_console = { version = "0.172.0", path = "./ext/console" } -deno_cron = { version = "0.52.0", path = "./ext/cron" } -deno_crypto = { version = "0.186.0", path = "./ext/crypto" } -deno_fetch = { version = "0.196.0", path = "./ext/fetch" } -deno_ffi = { version = "0.159.0", path = "./ext/ffi" } -deno_fs = { version = "0.82.0", path = "./ext/fs" } -deno_http = { version = "0.170.0", path = "./ext/http" } -deno_io = { version = "0.82.0", path = "./ext/io" } -deno_kv = { version = "0.80.0", path = "./ext/kv" } -deno_napi = { version = "0.103.0", path = "./ext/napi" } -deno_net = { version = "0.164.0", path = "./ext/net" } -deno_node = { version = "0.109.0", path = "./ext/node" } -deno_tls = { version = "0.159.0", path = "./ext/tls" } -deno_url = { version = "0.172.0", path = "./ext/url" } -deno_web = { version = "0.203.0", path = "./ext/web" } -deno_webgpu = { version = "0.139.0", path = "./ext/webgpu" } -deno_webidl = { version = "0.172.0", path = "./ext/webidl" } -deno_websocket = { version = "0.177.0", path = "./ext/websocket" } -deno_webstorage = { version = "0.167.0", path = "./ext/webstorage" } +deno_broadcast_channel = { version = "0.167.0", path = "./ext/broadcast_channel" } +deno_cache = { version = "0.105.0", path = "./ext/cache" } +deno_canvas = { version = "0.42.0", path = "./ext/canvas" } +deno_console = { version = "0.173.0", path = "./ext/console" } +deno_cron = { version = "0.53.0", path = "./ext/cron" } +deno_crypto = { version = "0.187.0", path = "./ext/crypto" } +deno_fetch = { version = "0.197.0", path = "./ext/fetch" } +deno_ffi = { version = "0.160.0", path = "./ext/ffi" } +deno_fs = { version = "0.83.0", path = "./ext/fs" } +deno_http = { version = "0.171.0", path = "./ext/http" } +deno_io = { version = "0.83.0", path = "./ext/io" } +deno_kv = { version = "0.81.0", path = "./ext/kv" } +deno_napi = { version = "0.104.0", path = "./ext/napi" } +deno_net = { version = "0.165.0", path = "./ext/net" } +deno_node = { version = "0.110.0", path = "./ext/node" } +deno_tls = { version = "0.160.0", path = "./ext/tls" } +deno_url = { version = "0.173.0", path = "./ext/url" } +deno_web = { version = "0.204.0", path = "./ext/web" } +deno_webgpu = { version = "0.140.0", path = "./ext/webgpu" } +deno_webidl = { version = "0.173.0", path = "./ext/webidl" } +deno_websocket = { version = "0.178.0", path = "./ext/websocket" } +deno_webstorage = { version = "0.168.0", path = "./ext/webstorage" } # resolvers -deno_resolver = { version = "0.4.0", path = "./resolvers/deno" } -node_resolver = { version = "0.11.0", path = "./resolvers/node" } +deno_resolver = { version = "0.5.0", path = "./resolvers/deno" } +node_resolver = { version = "0.12.0", path = "./resolvers/node" } aes = "=0.8.3" anyhow = "1.0.57" diff --git a/Releases.md b/Releases.md index 207e18e26d..08562134a1 100644 --- a/Releases.md +++ b/Releases.md @@ -6,6 +6,21 @@ https://github.com/denoland/deno/releases We also have one-line install commands at: https://github.com/denoland/deno_install +### 2.0.2 / 2024.10.17 + +- fix(cli): set napi object property properly (#26344) +- fix(ext/node): add null check for kStreamBaseField (#26368) +- fix(install): don't attempt to cache specifiers that point to directories + (#26369) +- fix(jupyter): fix panics for overslow subtraction (#26371) +- fix(jupyter): update to the new logo (#26353) +- fix(net): don't try to set nodelay on upgrade streams (#26342) +- fix(node/fs): copyFile with `COPYFILE_EXCL` should not throw if the + destination doesn't exist (#26360) +- fix(node/http): normalize header names in `ServerResponse` (#26339) +- fix(runtime): send ws ping frames from inspector server (#26352) +- fix: don't warn on ignored signals on windows (#26332) + ### 2.0.1 / 2024.10.16 - feat(lsp): "deno/didRefreshDenoConfigurationTree" notifications (#26215) diff --git a/bench_util/Cargo.toml b/bench_util/Cargo.toml index a233e42118..fbf30b9fcf 100644 --- a/bench_util/Cargo.toml +++ b/bench_util/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_bench_util" -version = "0.166.0" +version = "0.167.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 04546cc9f1..7d6fc71622 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno" -version = "2.0.1" +version = "2.0.2" authors.workspace = true default-run = "deno" edition.workspace = true diff --git a/cli/napi/sym/Cargo.toml b/cli/napi/sym/Cargo.toml index 287688e44f..7c9bf208c7 100644 --- a/cli/napi/sym/Cargo.toml +++ b/cli/napi/sym/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "napi_sym" -version = "0.102.0" +version = "0.103.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/broadcast_channel/Cargo.toml b/ext/broadcast_channel/Cargo.toml index 95d480d248..7ca058a44a 100644 --- a/ext/broadcast_channel/Cargo.toml +++ b/ext/broadcast_channel/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_broadcast_channel" -version = "0.166.0" +version = "0.167.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/cache/Cargo.toml b/ext/cache/Cargo.toml index d0f260d9c3..dee4d7274d 100644 --- a/ext/cache/Cargo.toml +++ b/ext/cache/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_cache" -version = "0.104.0" +version = "0.105.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/canvas/Cargo.toml b/ext/canvas/Cargo.toml index e88567ef13..6ca3d76c05 100644 --- a/ext/canvas/Cargo.toml +++ b/ext/canvas/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_canvas" -version = "0.41.0" +version = "0.42.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/console/Cargo.toml b/ext/console/Cargo.toml index d83afb1786..f83f7138d8 100644 --- a/ext/console/Cargo.toml +++ b/ext/console/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_console" -version = "0.172.0" +version = "0.173.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/cron/Cargo.toml b/ext/cron/Cargo.toml index 9aafa0b48c..ccd81de0cc 100644 --- a/ext/cron/Cargo.toml +++ b/ext/cron/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_cron" -version = "0.52.0" +version = "0.53.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml index 143f4b0098..e3b59ed469 100644 --- a/ext/crypto/Cargo.toml +++ b/ext/crypto/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_crypto" -version = "0.186.0" +version = "0.187.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/fetch/Cargo.toml b/ext/fetch/Cargo.toml index c8e2c858b6..ddb58a3f3f 100644 --- a/ext/fetch/Cargo.toml +++ b/ext/fetch/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_fetch" -version = "0.196.0" +version = "0.197.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/ffi/Cargo.toml b/ext/ffi/Cargo.toml index 9bca9d98f2..80b0180d0b 100644 --- a/ext/ffi/Cargo.toml +++ b/ext/ffi/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_ffi" -version = "0.159.0" +version = "0.160.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/fs/Cargo.toml b/ext/fs/Cargo.toml index 904483c798..a33347f9c7 100644 --- a/ext/fs/Cargo.toml +++ b/ext/fs/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_fs" -version = "0.82.0" +version = "0.83.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/http/Cargo.toml b/ext/http/Cargo.toml index f8e3bb38a2..6fa7598cb5 100644 --- a/ext/http/Cargo.toml +++ b/ext/http/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_http" -version = "0.170.0" +version = "0.171.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/io/Cargo.toml b/ext/io/Cargo.toml index 5a87977e05..8f407c820c 100644 --- a/ext/io/Cargo.toml +++ b/ext/io/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_io" -version = "0.82.0" +version = "0.83.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/kv/Cargo.toml b/ext/kv/Cargo.toml index 98c14dc316..f7b28af675 100644 --- a/ext/kv/Cargo.toml +++ b/ext/kv/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_kv" -version = "0.80.0" +version = "0.81.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/napi/Cargo.toml b/ext/napi/Cargo.toml index 68d281ccc6..ef2e41d57e 100644 --- a/ext/napi/Cargo.toml +++ b/ext/napi/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_napi" -version = "0.103.0" +version = "0.104.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/net/Cargo.toml b/ext/net/Cargo.toml index a578620721..634dd7dda4 100644 --- a/ext/net/Cargo.toml +++ b/ext/net/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_net" -version = "0.164.0" +version = "0.165.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index a6b778c1ae..55d53d0ce0 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_node" -version = "0.109.0" +version = "0.110.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/tls/Cargo.toml b/ext/tls/Cargo.toml index 9c3d5b8047..b060f28298 100644 --- a/ext/tls/Cargo.toml +++ b/ext/tls/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_tls" -version = "0.159.0" +version = "0.160.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/url/Cargo.toml b/ext/url/Cargo.toml index f2e81becf0..ceef18f45a 100644 --- a/ext/url/Cargo.toml +++ b/ext/url/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_url" -version = "0.172.0" +version = "0.173.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/web/Cargo.toml b/ext/web/Cargo.toml index ad44f53202..395ea182f9 100644 --- a/ext/web/Cargo.toml +++ b/ext/web/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_web" -version = "0.203.0" +version = "0.204.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/webgpu/Cargo.toml b/ext/webgpu/Cargo.toml index 262351b817..8f22ca7647 100644 --- a/ext/webgpu/Cargo.toml +++ b/ext/webgpu/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webgpu" -version = "0.139.0" +version = "0.140.0" authors = ["the Deno authors"] edition.workspace = true license = "MIT" diff --git a/ext/webidl/Cargo.toml b/ext/webidl/Cargo.toml index d5d4d9278b..d069b32125 100644 --- a/ext/webidl/Cargo.toml +++ b/ext/webidl/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webidl" -version = "0.172.0" +version = "0.173.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/websocket/Cargo.toml b/ext/websocket/Cargo.toml index 5bf217f13b..b265ba78a2 100644 --- a/ext/websocket/Cargo.toml +++ b/ext/websocket/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_websocket" -version = "0.177.0" +version = "0.178.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/webstorage/Cargo.toml b/ext/webstorage/Cargo.toml index 2707ccfe91..0d6fcd6a0d 100644 --- a/ext/webstorage/Cargo.toml +++ b/ext/webstorage/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webstorage" -version = "0.167.0" +version = "0.168.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/resolvers/deno/Cargo.toml b/resolvers/deno/Cargo.toml index 8cc93e79b3..e75a9a1014 100644 --- a/resolvers/deno/Cargo.toml +++ b/resolvers/deno/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_resolver" -version = "0.4.0" +version = "0.5.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/resolvers/node/Cargo.toml b/resolvers/node/Cargo.toml index 5395253a4b..44549bec19 100644 --- a/resolvers/node/Cargo.toml +++ b/resolvers/node/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "node_resolver" -version = "0.11.0" +version = "0.12.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 71b0c84c0f..dcb6b1ade6 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_runtime" -version = "0.181.0" +version = "0.182.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/runtime/permissions/Cargo.toml b/runtime/permissions/Cargo.toml index 16d4896c4c..d7f2364a56 100644 --- a/runtime/permissions/Cargo.toml +++ b/runtime/permissions/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_permissions" -version = "0.32.0" +version = "0.33.0" authors.workspace = true edition.workspace = true license.workspace = true