From d26addeca147e076d7a5e2c7fe14febdd658393f Mon Sep 17 00:00:00 2001 From: Dylan Conway <35280289+dylan-conway@users.noreply.github.com> Date: Fri, 15 Sep 2023 01:39:42 -0700 Subject: [PATCH] dup and close file descriptors (#5341) * track one shot fds * dup fd * skip for rearm on mac * dup if fd * cleanup * force unregister on close * deinitForceUnregister * test * add prompts --------- Co-authored-by: Jarred Sumner --- src/bun.js/base.zig | 31 +++++++++----- src/bun.js/webcore/streams.zig | 45 +++++++++----------- test/bun.lockb | Bin 157381 -> 163146 bytes test/js/third_party/prompts/prompts.js | 25 +++++++++++ test/js/third_party/prompts/prompts.test.ts | 28 ++++++++++++ test/package.json | 1 + 6 files changed, 95 insertions(+), 35 deletions(-) create mode 100644 test/js/third_party/prompts/prompts.js create mode 100644 test/js/third_party/prompts/prompts.test.ts diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig index a6df36c4fda30..26d5d74c36a5b 100644 --- a/src/bun.js/base.zig +++ b/src/bun.js/base.zig @@ -1787,12 +1787,19 @@ pub const FilePoll = struct { pub fn deinit(this: *FilePoll) void { var vm = JSC.VirtualMachine.get(); - this.deinitWithVM(vm); + var loop = vm.event_loop_handle.?; + this.deinitPossiblyDefer(vm, loop, vm.rareData().filePolls(vm), false); + } + + pub fn deinitForceUnregister(this: *FilePoll) void { + var vm = JSC.VirtualMachine.get(); + var loop = vm.event_loop_handle.?; + this.deinitPossiblyDefer(vm, loop, vm.rareData().filePolls(vm), true); } - fn deinitPossiblyDefer(this: *FilePoll, vm: *JSC.VirtualMachine, loop: *uws.Loop, polls: *JSC.FilePoll.Store) void { + fn deinitPossiblyDefer(this: *FilePoll, vm: *JSC.VirtualMachine, loop: *uws.Loop, polls: *JSC.FilePoll.Store, force_unregister: bool) void { if (this.isRegistered()) { - _ = this.unregister(loop); + _ = this.unregister(loop, force_unregister); } this.owner = Deactivated.owner; @@ -1804,7 +1811,7 @@ pub const FilePoll = struct { pub fn deinitWithVM(this: *FilePoll, vm: *JSC.VirtualMachine) void { var loop = vm.event_loop_handle.?; - this.deinitPossiblyDefer(vm, loop, vm.rareData().filePolls(vm)); + this.deinitPossiblyDefer(vm, loop, vm.rareData().filePolls(vm), false); } pub fn isRegistered(this: *const FilePoll) bool { @@ -2168,10 +2175,12 @@ pub const FilePoll = struct { var event = linux.epoll_event{ .events = flags, .data = .{ .u64 = @intFromPtr(Pollable.init(this).ptr()) } }; + var op: u32 = if (this.isRegistered() or this.flags.contains(.needs_rearm)) linux.EPOLL.CTL_MOD else linux.EPOLL.CTL_ADD; + const ctl = linux.epoll_ctl( watcher_fd, - if (this.isRegistered() or this.flags.contains(.needs_rearm)) linux.EPOLL.CTL_MOD else linux.EPOLL.CTL_ADD, - @as(std.os.fd_t, @intCast(fd)), + op, + @intCast(fd), &event, ); this.flags.insert(.was_ever_registered); @@ -2285,11 +2294,11 @@ pub const FilePoll = struct { const invalid_fd = bun.invalid_fd; - pub fn unregister(this: *FilePoll, loop: *uws.Loop) JSC.Maybe(void) { - return this.unregisterWithFd(loop, this.fd); + pub fn unregister(this: *FilePoll, loop: *uws.Loop, force_unregister: bool) JSC.Maybe(void) { + return this.unregisterWithFd(loop, this.fd, force_unregister); } - pub fn unregisterWithFd(this: *FilePoll, loop: *uws.Loop, fd: bun.UFileDescriptor) JSC.Maybe(void) { + pub fn unregisterWithFd(this: *FilePoll, loop: *uws.Loop, fd: bun.UFileDescriptor, force_unregister: bool) JSC.Maybe(void) { if (!(this.flags.contains(.poll_readable) or this.flags.contains(.poll_writable) or this.flags.contains(.poll_process) or this.flags.contains(.poll_machport))) { // no-op return JSC.Maybe(void).success; @@ -2310,7 +2319,7 @@ pub const FilePoll = struct { return JSC.Maybe(void).success; }; - if (this.flags.contains(.needs_rearm)) { + if (this.flags.contains(.needs_rearm) and !force_unregister) { log("unregister: {s} ({d}) skipped due to needs_rearm", .{ @tagName(flag), fd }); this.flags.remove(.poll_process); this.flags.remove(.poll_readable); @@ -2325,7 +2334,7 @@ pub const FilePoll = struct { const ctl = linux.epoll_ctl( watcher_fd, linux.EPOLL.CTL_DEL, - @as(std.os.fd_t, @intCast(fd)), + @intCast(fd), null, ); diff --git a/src/bun.js/webcore/streams.zig b/src/bun.js/webcore/streams.zig index a578313d74177..b96721e6e17ae 100644 --- a/src/bun.js/webcore/streams.zig +++ b/src/bun.js/webcore/streams.zig @@ -3788,7 +3788,6 @@ pub const FIFO = struct { }, signal: JSC.WebCore.Signal = .{}, is_first_read: bool = true, - auto_close: bool = true, has_adjusted_pipe_size_on_linux: bool = false, drained: bool = true, @@ -3807,7 +3806,12 @@ pub const FIFO = struct { pub fn close(this: *FIFO) void { if (this.poll_ref) |poll| { this.poll_ref = null; - poll.deinit(); + if (comptime Environment.isLinux) { + // force target fd to be removed from epoll + poll.deinitForceUnregister(); + } else { + poll.deinit(); + } } const fd = this.fd; @@ -3815,8 +3819,7 @@ pub const FIFO = struct { defer if (signal_close) this.signal.close(null); if (signal_close) { this.fd = bun.invalid_fd; - if (this.auto_close) - _ = bun.sys.close(fd); + _ = bun.sys.close(fd); } this.to_read = null; @@ -4198,10 +4201,15 @@ pub const File = struct { file: *Blob.FileStore, ) StreamStart { var file_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; - var auto_close = file.pathlike == .path; - var fd = if (!auto_close) - file.pathlike.fd + var fd = if (file.pathlike != .path) + // We will always need to close the file descriptor. + switch (Syscall.dup(@intCast(file.pathlike.fd))) { + .result => |_fd| _fd, + .err => |err| { + return .{ .err = err.withPath(file.pathlike.path.slice()) }; + }, + } else switch (Syscall.open(file.pathlike.path.sliceZ(&file_buf), std.os.O.RDONLY | std.os.O.NONBLOCK | std.os.O.CLOEXEC, 0)) { .result => |_fd| _fd, .err => |err| { @@ -4218,7 +4226,7 @@ pub const File = struct { } } - if (!auto_close and !(file.is_atty orelse false)) { + if (file.pathlike != .path and !(file.is_atty orelse false)) { if (comptime Environment.isWindows) { bun.todo(@src(), {}); } else { @@ -4229,7 +4237,6 @@ pub const File = struct { // if we do not, clone the descriptor and set non-blocking // it is important for us to clone it so we don't cause Weird Things to happen if ((flags & std.os.O.NONBLOCK) == 0) { - auto_close = true; fd = switch (Syscall.fcntl(fd, std.os.F.DUPFD, 0)) { .result => |_fd| @as(@TypeOf(fd), @intCast(_fd)), .err => |err| return .{ .err = err }, @@ -4249,24 +4256,18 @@ pub const File = struct { const stat: bun.Stat = switch (Syscall.fstat(fd)) { .result => |result| result, .err => |err| { - if (auto_close) { - _ = Syscall.close(fd); - } + _ = Syscall.close(fd); return .{ .err = err }; }, }; if (std.os.S.ISDIR(stat.mode)) { - if (auto_close) { - _ = Syscall.close(fd); - } + _ = Syscall.close(fd); return .{ .err = Syscall.Error.fromCode(.ISDIR, .fstat) }; } if (std.os.S.ISSOCK(stat.mode)) { - if (auto_close) { - _ = Syscall.close(fd); - } + _ = Syscall.close(fd); return .{ .err = Syscall.Error.fromCode(.INVAL, .fstat) }; } @@ -4292,9 +4293,7 @@ pub const File = struct { file.max_size = this.remaining_bytes; if (this.remaining_bytes == 0) { - if (auto_close) { - _ = Syscall.close(fd); - } + _ = Syscall.close(fd); return .{ .empty = {} }; } @@ -4303,7 +4302,6 @@ pub const File = struct { } this.fd = fd; - this.auto_close = auto_close; return StreamStart{ .ready = {} }; } @@ -4725,7 +4723,6 @@ pub const FileReader = struct { .readable = .{ .FIFO = .{ .fd = readable_file.fd, - .auto_close = readable_file.auto_close, .drained = this.buffered_data.len == 0, }, }, @@ -4885,7 +4882,7 @@ pub fn NewReadyWatcher( const fd = @as(c_int, @intCast(fd_)); std.debug.assert(@as(c_int, @intCast(this.poll_ref.?.fd)) == fd); std.debug.assert( - this.poll_ref.?.unregister(JSC.VirtualMachine.get().event_loop_handle.?) == .result, + this.poll_ref.?.unregister(JSC.VirtualMachine.get().event_loop_handle.?, false) == .result, ); } diff --git a/test/bun.lockb b/test/bun.lockb index 059054600ab35e3373a09bcdc2383ce93171816e..a6a467f5bb727592778d15f894ca23c7d1b6b38f 100755 GIT binary patch delta 19299 zcmeHPcU)B0wmxSBWl&Ti3Mh(TMFj-Wp$G~Zqk>`u8_EC!j8qvEG3tz6V)v*UjlBjF zjlDi&k0sHdQKLyL*kXyA*b=;!_pNjGkulesm%N+z?myW-KEA!y+GXu}&Y9VB4xi8- zf2N)3+Y3Bn?ByWaC&i#wLWWKUA_3SLghLue|)~|`Dgu>blNpiBT!g7E7n~z zcGbq={&wzJ8QHlep+#3guonbNTDoCq4pL<27)=>^LAWPvv~xE*brS>!ShG{I^(K=b zn6lH2x&BC%;+>nHZ7{X$E(ngW56?AZCLwoY$dtY&xH7mBXQVe~<^~E{lvhzM&pSIS zD_z)&JWi0GM=SXraNY_%ypp1|TJxdag3t_U3ONr3`#=ukvLo0V^3`5~;0rzgt_#k{ z%1p^hN)SHkD+skA&*`VM^gHl7kaKeaTA>;}lhYrtA?qb7=+W6&q&dK*on2P(2SNU10>RiB=Qyq8c2 zbXeHPR>X}fqI1NTsGzE zbBCG?rWnXnL1!@KHz|W8gmJkRPj4`ICmVB2xfvK?3K_->1Mw@&gNnAum}Zbh*f+Gl zMKZ+8dTFh_Pmcg(CqH$mO1phHH-w(5wFgtjK0-FCerRU4F&hPc1-UBZ#H_3|BjkhJ zzCkZFsn}4nLNE2Gc+(t}D+o1_%Ad0vxCZ1`aFT}eCYXln1m~?_7s!jj&fpKgG-^3q zkHM2iFCt%=9-f>voNo+Q$~^{l6NE&|YDDUxfJtC?a4P4{;5v}K!Q@CaFgf}pPtkWp zMb!L&(TXEMkgG%X0#mc!H!IyzY?f*|)Un@SQPfK;n|AzM^Sj<}(8m1bcSkhM z9q?#UZ0OjjaXTZAOP1=l;?GuayKYx>bxfqzHneg|$2>>Z!_1{P@Z8EKm!=HufD*uYY@Hix#ya8y>I~kN;5OJIlAgM_qlgV^;e9?#Z`1Z%(aITuwUb=~h#Ow;P6NGl>7|S8X{E51TV<+J|djbP)tB zAB#p3`{`^$(dgFFivc0pe8jLpSjtKJ8|t(>Aq7CPmB!T3X`e$1do2ZHtp_S8YwNT* zkgz$Ry!~!E@qm;+ppCW(6cNyglv|VDO?oyUOuH0HD$+&@Z=%zlfkcI9NV4sK?WVI7 z+9X6f6ESkbMjE5j*&cutA?;}%qP6KE2<_z}_j~HJ9l3-`Ax(uuO%kPWcjSQ-Chdt1 z(F#2kJ(}dEvuy>bo3y81h;|}kihGp%D3|22H1@ru5%FQ#5xoSVBZ@3XZfJkPPL=su z8W>|OeF-V&wJj{x+V%;g?AJE8K7x?@T6ziz>kd7sWE(4Y`o17a8q&k)-^h_#bKTZh;R*djyZSO{XW zPCErLtR2fc^3WcF6iO*H?+lWHlftw;v77qCj1le-Xa|96t{_i}W01mRlUP@$b&6Mp z2~z}K#Y1Wh8wNQ>r(MV;K?-lJ(>{jO7BRA5y@fNEOaE5DNhc~ zx&-MNln=O4ki>vMyTsR15!1&2sl7ZEix3N!bB<}K)7(jvf>Xn^wUU&oL}@>q>JNzu z$3WH6Y3D?~J_ z!Lf}n%98}^cPwJG%F#clYa1k5Mv!{xv@f|01qJDB{RdMX+In&j!wy3gbku3~4VHFg zg=t?wNp52FJ#`vgniQNJrnTVmOLM0j4YYO>qzJim+GZZ7NzbywY@4Rj_`S#uvBhnQ z&_RxEMXaM7d-2*Do}uar5L429g_x4J8kW0~qd#I%a^5wsbq^5hD(gbCRBHiZycEQg zyv|tsN_qVeQ*8fFSk#*Ysh*9Ta+6L*gGa%8n zXG4=ub2vu|&I{Apm|k}qW=d<5v@0*nHW}MaA8Aisi0x;uV>O1#+aYwxhz*pjhY*XA zV>O4Vv24V8DOSXK$gvuECO_tZfq@VWZ+H zuRy|9$fcn71pzY>Qn*K;9R%zT-1O=VY2Zjfz@EfyyCIboS!0wSVE^W}G)QGBk3ypP zE??#v4;?KC@YG@}2m)O`2^1osZvuVS#cm^8R=c}HT3XXZdmqY3IV(-{AhWtxiI}ZZ z&C;&XVcIKD(gq@LOIi<$vN>a0Y^2j9S)>u>Fzr4lX$<8Ft$AvZo|(fmfn%g#OPFTl z7-dKju0vXet-+m37|l%0pO1uthqci-2oc_-5{rn1ylGvrt&Zna(Z+o znu36c9Ei&S0~Jwb>Me|q+-vb%PfY0&0kS6nJ{urM=K(aqKLjWclYR*aQrtM_PLz5TNeIML z>PG;%Q3OyRCV4Fh2-ZyckAZU1x|z;ql0F59Hvkl@nQGevkbN^iftchi0NJ+z6o^T` zo%0UPJHZsJSqu39T4WmUDEn|wPJxFOFqLtL%fH9e@*_O`QJ$Wd@*m^!+nDUf0rLGz z_;7c;)=cq}00m+X-~m8&JmmZYOyO-zWj>XyZ!+nB;d){!?-`eg$^H*ozckB4NEN}A zNXwa`4){kIt8%?H*MPnu*Z=RCub=;Y0r28aIzqF$33t3nd)`8Ct{|qU50|Z(GWv6S z3vMU2llDw>md;33^`s2rmOsLjv<**BOnn^zt^n@D_0}v(T_!oZ(v*qf);Cz1Hp$r$ zMxhJ0$}BCK_B%;On}X(t=WWn!|A<}xvL@B}caCgPvH zbYiZvJQrt^3c;Ew_Z+S#rs!NQ6O)~B*_ugJ$n|ey+KZNPJJ>82yqqhDsTpg)r22^K z{|r->BA)N}n7U(w$k*Zqp5f0hO_yEBM?LXhoIm5`5L0w7*YD^0e`4B?$##JAL7tG9 zX2|DUCZ-v7oa;|;JuyX3a+#PMD(3oAoKJK98cczhq7BY)g*Cgw@QCY)HPXEg+|5*d zDI^DQd8&gmC9J^Zw=oT9C2s#WSeYpfydY~P2P#8PrmEcjdrTwm%+viIQ@JikR|#BS zgPB4-&=?9bc<}9Yy# zRIvaBYo@xb9!_L?fBC=ij6x3n70)JAzhLAA{~l9?=>T=%A9*IBA}JlE|D9(NxqdkT zwU{1CC=gS|*AFM8L?D^c{dLbKsQ-^VoQ$K|5#Gk+&;)>bXfi;7*bbNpko{jio6u6B z^|%E1`{CrxhZgG4BLK|=dMKelO!C_vO2|%nvj6>Xf=*~iJt4EQ%&Ziu;s1_lR{#BQ zf==khN5PtDPx$-c1fB5r!^z(dC-jiwj=Avn!-;(9`upMJ?}roRnWWa64<%S4^1}+< z7v*f8zu-*Ywz_IrFC(t(s2j3o_@RempQg4=|77k`u}EC4UD~wB7-|^Z@7n6bwNGEQ z+81Mb8U1S?rT>0rS zSzjbN+5~iQ+cdJ*^i_8syxXIh|6t&XM$FY*jQwOQ`lSr|m zWPB1U9ffoPl7r;>G*%k^i-R=kX{1<1ItHonuMU#;FOj07l>bYtbRN=0NYx~-Ut^`o zr4G`xUn9lp(gjF?&m5%C(n!%+DkzPWZb7;S$yEw^7AqD0!$Df~EK;l`-F_A;x=G>x zh!tx~^YOoq^fUguOP!v_igl%B`0pW=;=iZV{Y9);Pbzv5E7`qrkQ`n{c4=VUCHo^> zM(iK%(0bgEV_O!F35ph%KOVm*uC3>j(1q=$Mw?2UY-jB#+5TnB#l_hrI}=#VdsjAJ z8gFcvSy*pP$W-lpowV+ii@D$93v*`l%WChkwN;}X2d*sJTCrcv`W-?d?wxz#^G)@U zOD1g^`SFcA!O35@-JbLCy9pOM?VmcL=fav7cF*;Ce0kKmP^k5bqw%PKJhE|KQ6zKJ{KL zzrVND1rIx$A#>wI7jtZdxbnBloxJ~K*)RLT-QT}?dw<=*OS~3D{p0i4yY*K-9Oe=8 zL6COh{4YZC;%lAXTB~d6+Vb;0i5~aU$RAJ5P5I^i9V_p=ti59!vqp+Ha?)0c;+C&y&7kHzG)vbcGSTqU2CT4vg>x)bh0oxYEJvgE8+tE?q+UFE<8T@^dpbq zzRv=pkDZ-zX>gkgrMLT)POZ^3cxP+l`teroHMMr{uwTn+H*HH+&8W}joLJrQdO*sy zzAl%qMD!}$lICdKHvIEK`x>7|&2p_e**xI*<-3(xG9U?nZhJFwBT<6m)Dx(Cvcx5!+T2IGuQS zXY|(b#SfddENOXrcprz0KdiNK&)eF)ek+_`Iyt`x3&T3kJrL zgH6ubtZ(;`@7XR(2bFWpThe4pxhdDX2E6Y!Z2C{to*rNHuIJuQuTPr8GBj{6?$;Gx zY2~x{ z?I+VqTfJ-)y0O4)FzG)(T04C8)AGi4IT!mZdaM~+;#=plvm31nZf;$$!AD&4!K>s+ zYwuo*Y$&}vweaTYIU^6$>Ja)Ow%R*W>Q{C9GOm!GfnX<}8bpD0NEU-o9_%VE#i^DKD?$Dj}gFb@Y% z9Lpyf$W9RrVqTR&@oWr{o?ReHV189ViL8JqiIor;SWs0^GMhz|!fq3#vT#R`k z%zh?HW1ZdsrL$#38LX5jlXb5K%3?)C*-Uf-4PkwWa@a;96Vp})%Mluf<&?uHqG@6|vGBYn%kcEvQ8pAFSjb(l{ zLE~5f(RfxuG=T-x0!?JIh$gYyM3Y>oQIC;EW>Of-dcstuaTmJv;3r9{(N_d1{% ztO&#!*N3YP?r?P$>+25vY_^d|V%oZ(IZRJ9m+d5)#~eLCj2Vdv*?yw=%+(XLfDIw~ zkR2mh$UN$S7O{My#q1Q(66RGOw3Lk@TE;FAEoXiWKr2`Q(MncAw2B2a1g&PXh}N*% zL?5y6MxY`#zmeFpDi)?3XtAeu$2aEB8;RYVSk=~INAt&Bm2<9?Ks`17|EM!t{zDb> zddXefR ze_?@t(m}Le?bd&@C4aJFUN80kf23kwulX+?&p+D>=KpN>3(N(mS^`A`ETp&fz(9d1x%`YMHj z`n@_m0n-=O6f8U)&A95a24~E;j=rR>$#rA7jwT0o9|~q+99Pnp$8|}CFrMqE(vI94 z^@N2Mx^u9T19aMg%=W;Kh*Ox%^U@I{a)KNv;5z!Mhn%4B0a!yv4yhGXA%&?tA$`f1 zgE)CQjq9o)ZsO^ta~)j+yK~(PFgfK2oCByq^w}zz-vKTI6eK9f>1x0=TBsE02BD}kK<6eX%;!2f!AT9EmM`Es>`cNi#Hr!t z54n=YXgK23%7t7!eD%G0?+XU}!p z6rEY93*~cwx??*}=z(}CK)ta8I_fS@;Aen(Lp>c*4{`OIO*$!pKcPMl1yI<-bqx^T zCTj#iJwfu5eD(!X_Qy}z&G}+OX172bZ}vbcs);`R$p(f1^mR6UwN2ln(|7d!fd0S$ zAPyJ^37Wf9Z0DKEv1TF!Wfh#}>a1FQ)+ys6A zZUJ|Iy8x}7d%%6*0q`^M5cnFP12cyJ`jC7hunC~!KWl-H>73Cgh^zzF1D^sk9clW} z%%Yh@Glymh%?t+6RHvzJ1}p$ANZJHw4GsZljnSIw3Y^4Hd1{*OT-WG2U-9DKubUe;E+H4TtoO_7fEHS3 zAQJEeUcmMgSOSa##sdX(;9&|PQ-Nu~3;+k)1)Mvltx>Q5CLk9W3Je2i!DRs%Ks=xa z5&-&Sffim@;5f@)jJdfF(fz;%U?V`EjjRAx0&(zZATS6x348?{2fhRj0Q&%1)3k=y z02x3gkPM^%sXz!22803*Koy`n%I*dDQ|C5@!V7o^#ROmyFc!!Ik^lpc45R>k0Xold z2sjLU4jceB1Dk-4flq*Sz$#!hum(s%`FbD$NCZv+#lQ(V1NRvs*+3eQ4zvc)=kh7} z7;qEF^o9BnU<!Eubdg3b+8yKvkeJ-~iAK5Y3z#Ky|ZID#BTBhna z<&Ou*;Z!hnLX4W{&$zkklk=WF67@|XgT)+h6u+SpW z{oS(S|8?o(k^EnkJ_EMt>_w4S&%6-P4}k^1e4r3uz&v0sFb9x;*}yDdF<=MOM=O_s zm-F~a@ETw>uT6ThSl|F=Cz{h_JwQ3V$oIoO}zR$VvX3! zr@0SSAxry2T;!6Sqr4PA{SC549O&;8;N!o&{5sLnFj{>_fcleUm|Gxw3(Rm_5paiY zQGc`yiythwHs6D#3M|9cdkpQjt@%lf*wV+(hc4m|*s`xhSMfR9{k7OdqwU7*&xlcO z>Kg~vZhId6X;{-2HdKjQSRW z8(r@gonBmm>!oZ7z$)v`DxVdj!~?AVS<%()Dy|~9Gq7yloqA>K?qxe{L~0M+T{!k; zGtZ(D^(6xyelrU`*Yp})mO_0E!Gz6+i=xN;h);^uyoJ5lCFFhm;sVzVPU{YBdO5Z% zh5AMT^=H^}cfolqY2~E@g8D;kSojF3uQ5=6`VEV}4{W%S;j&g0tvGYWyvWuD*Cd{joY)6o}%GM}1X;`tx?`A7yOSmo}(B zbccny9Su@n>!AJ=UgJUD$)@a~nfZxXNd~X5gaTb<+c>%FFsE3ht)kmt^}P=b`ETVl zViUB4g8IS-^;hz|GR$}N6%gw0=E*^2=BO`$sFzVXXkvKhfmBzJ?8b&T*6f^^tO3tC zC;Dqf4`fHrVLsz?_Zr_oUm>22{zh~Oh>2Hv`&6H`#ho56xgpP9KOfBATd>e*mO6ND zt=@L&Pa5$&G&J`*b%=X5? zUrID0hM*-4T}%SY{uW*&C9u!FMYih+>>;t0S8cx&7g)>WC2RaWn!sIT1HKmnRR4Iz z7L8J}zb4JV5-y?!pH#Mi+Nn!r$1lEF14?vJ^ULjHNteV_jnZAoyc)F!IaJ-NW_ak}}I;6B~UUo73x8UaY&6{w%)tsSB7HU0=>A;mW9bWtND1mX5qmuV3h~zFoVjnyE81qRnVlcvB2^Q(x(^ z_sYPlU9#FHlx-o!W_JH3Cf*VYYyATz*ai!W`2iE}H?sXt{JK5!8GUevj|l`;_mD(RZnbn{r*nK6(P_1Zs!8Gg#}KBAUA+~RQz;7?b*xc-G+?M zek?!qIDTaCciOyt&y?GOO(VA-za`e|%I3#wYBk`OdtYx~9`ly3_CC2OBTPn9t|3RC zX)-d$2V$*SWinEi3EtTC*hYiKqndng0GrI-@@>KP+pRRG?aln212!J>mak}3OIuYl z*yL-{o6_-Gn`~8;n3b7nNX#{6WqO-(bBviO-a~WJtuu@;e@$)oGTD%WeAXf=Z7F-V LeRsIVzQKP3Mf(c? delta 15743 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 ZL0Q$yFRSh1tH;M${^VR8oosRL@Nb7$Y?%N6 diff --git a/test/js/third_party/prompts/prompts.js b/test/js/third_party/prompts/prompts.js new file mode 100644 index 0000000000000..bf96ba26e3c00 --- /dev/null +++ b/test/js/third_party/prompts/prompts.js @@ -0,0 +1,25 @@ +import prompt from "prompts"; + +const questions = [ + { + type: "text", + name: "twitter", + message: `What's your twitter handle?`, + format: v => `@${v}`, + }, + { + type: "number", + name: "age", + message: "How old are you?", + validate: value => (value < 18 ? `Sorry, you have to be 18` : true), + }, + { + type: "password", + name: "secret", + message: "Tell me a secret", + }, +]; + +const answers = await prompt(questions); + +console.log(answers); diff --git a/test/js/third_party/prompts/prompts.test.ts b/test/js/third_party/prompts/prompts.test.ts new file mode 100644 index 0000000000000..9c62456f5b605 --- /dev/null +++ b/test/js/third_party/prompts/prompts.test.ts @@ -0,0 +1,28 @@ +import path from "path"; +import { bunExe, bunEnv } from "harness"; + +test("works with prompts", async () => { + var child = Bun.spawn({ + cmd: [bunExe(), path.join(import.meta.dir, "prompts.js")], + env: bunEnv, + stdout: "pipe", + stdin: "pipe", + }); + + child.stdin.write("dylan\n"); + Bun.sleepSync(100); + child.stdin.write("999\n"); + Bun.sleepSync(100); + child.stdin.write("hi\n"); + + var out = ""; + for await (const chunk of child.stdout) { + out += new TextDecoder().decode(chunk); + } + + expect(await child.exited).toBe(0); + + expect(out).toContain('twitter: "@dylan"'); + expect(out).toContain("age: 999"); + expect(out).toContain('secret: "hi"'); +}); diff --git a/test/package.json b/test/package.json index bab0e72000752..d7ae63d11ada5 100644 --- a/test/package.json +++ b/test/package.json @@ -27,6 +27,7 @@ "pg-connection-string": "2.6.1", "postgres": "3.3.5", "prisma": "5.1.1", + "prompts": "^2.4.2", "socket.io": "4.7.1", "socket.io-client": "4.7.1", "supertest": "6.3.3",