From 1950ba3a272feeffc7ec94fdb554aec507039e14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Mon, 20 Jan 2025 10:28:58 +0100 Subject: [PATCH] Properly handle event source errors (#687) * Properly handle event source errors * Changeset * Fix TS --- .changeset/heavy-olives-divide.md | 5 +++++ bun.lockb | Bin 377528 -> 377528 bytes packages/api/package.json | 2 +- packages/api/templates/http-client.ejs | 18 +++++++++++++----- 4 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 .changeset/heavy-olives-divide.md diff --git a/.changeset/heavy-olives-divide.md b/.changeset/heavy-olives-divide.md new file mode 100644 index 000000000..eebbb2860 --- /dev/null +++ b/.changeset/heavy-olives-divide.md @@ -0,0 +1,5 @@ +--- +'@gitbook/api': patch +--- + +Improve error handling for streamed API operations. diff --git a/bun.lockb b/bun.lockb index 2e8d85976fc4d6386bcc40e8042a897d06064b40..3251fbff9dbd92dae4d4da99bbf8df64a204c9da 100755 GIT binary patch delta 11855 zcmZXa37k{qxrb-SOhA#SAQ45QfCLl_Adp%uVUw^)0Fkg7StQ^>5XC4c8MP%=fd~xb zut=baQEN@5YCx;Ojkw3F*0frUU~THwD6Paj-2eOjzwv@MKc9KN^PDdy+sQd6w7XZF zxO>HkYdoiqe&j&kF5iy&<7rRdlJ0rv)Yng$@M>{XY3jkNFBty(4>JZlyE8CsjWu+6 ztG?^bYxBs9YgWHdUvCAc)=z;llzo~mg{Um$n65EQ1tZF{(&;mS~JqCCYo9#spQSd{K=_ zszUj%*O;QJRNw}UX{tsAmuk#V_GP*hqOz3ZMvY-AM>%iO7@_i%Yq`cKRiNB4jWMc7 zd73oFsS@SAS!04KQ@&d?CaDVLzg1(3s#1YpYfMu$D)<|X8OpvwmqJvQa>O-;sT}3J zO=E=0Q?B1?j8X;4eY?gORir$3XpB=O%DYlyf+|zKRT`62h4Qb~n4+pwAfYi$)u`Z| z8Z(rAjV^_#Eah0MF-+ws=Q@oMDo?qZHAbld+4; z7@_i%>j8~XszAAauQ5gyDbIr%<5Y?A{y}4cDpS6PG$yGE<%sEBUGMp?a>&e3Y7b4jWMc7 zd7jZ2r%IIfS&a#*O!=PEn4~I{|9Ooms!9d+YD`l#Dwx-pq3kc{Qi#e@ju$nCsT}2e zNn?b{Q?8daMyUejenn%9DpHg($2>uSf=;Az%@*Xl3C zC07M2rX99o2F5M7hVc^~o!PzX=2IpQAGrI~sSjL#YWrA^4xeoum%3odye?nAbWE4; zW?#5u*S=MClUK&i?sxw5J}U=)R#^U?_x{V6m`qf2S3Mg`x{ zn4#=%>QacxQjWJYhN&Fo{ENm2m8V>9Ym8C_%3ahLql%R0uNvc2iSqtUV}dGEzIQYx zsS4$PS7VB*Qh~o~Oj9)~_@2fLWq)6nLR6M=lr)B^9Oe8#V}#06u6-J#RDp8u*BGOU zl;=Z@ajHal|DiEKl_}px8k1Co^8Zs~imFn9vc@!3qk{j^n4#<+>r#lyQjSkFhN&Fo z{8VFv%2Te-G)Acc<^Eh_j4D!|e`}0WCCXdTn4rp(?+cAdszUj{)R>~GRN#QdG*zR5 z|IwJC?ElrJ5S67IUug_eIm&rZV}#06uByf;Re%ovR=(cyt&DxG(~6Ym8(oT1CCdA) z#spQSeBWtIQWeVoy~Y$(r2;=_Oj9)~_@l-QWv}T{h{{rqLmI~p8Wn7#F+JzA#aezxMwmK72Gl&w7F zI#IV4r3#e0lg1cTq&z2Sj8i4bd$Ps^Ri=EMH72PFJsM+Fk@ED>7^g~n z(wL&ERKTY(P1UI2V2v5dK17#7RF-lK)flF7lyjKI2$iQ?!!<^!0_DCyV~i?No)H@3 zREhHXH72Ms{{SLsrS%2JMn8pBkMaz->ps66Fb zq%lerDEHMGV^opyEY=vON|g5+jR~qu`L5NNq$-quiN+LFr2^M!Oj9)~7}c1e?APm3 zh{{rq8#IQg9OYc9F+$}j*D{S!szAAK)EJ|Rl;5K#f+8}sV3cGl?vRfOKGY`1#i)qq3pNnQi#e@j$dmGQ#s1{8;ubvPq|iT zj8X;49oHD6ij?Ozjd7|(d4H=hL6s@r?HZF*h4SB_F-29Wz)Fp2szwD@Y0Oae)w&d- zvXmpCF-+ws=baiORGxCJIr0-&-SS2kezxMymKCwJN8V#?%L>oBBUd!*3TykjtxmMQ ze63Z(u5Z@0I`^C7ZTRPiW5L%LG{U{Dp(|}21`Wfa{9DEGpjRvW!ELx{?J@3c+68Db z>;U?h=C{^j6HOz{^p03H2*_=eneN1R0B8fon0Y5++~2gZXfmf0@R~N;%sUCor(rC& zIj{^*1_kr*^El5@*BS9oW@6atw+hc`KG)TqidDzh@dvjBrnxXa#k9*!I}NR)X;+wb zI$CGbt~9L+n!~iKOzVo~G;JYTLtR}{T{pxNOk8Xxo`KfRv};W3j&`(Z*P_X5a|4@c z*P&s9bv?i#`P^_@YVO+;V>{-xgO{0hCdQK+#^Wz;H=1}B#us5L4)`WCd5~UUl4&tC zdA!~rX5QW3nERfMc8j%kwXMgX3(czA&8ogwH4;tsi91Z|hjC}L6X2DmorCdh*1^@b z^C#Yoq1@J(Rq~m4#aKS;wWj?7<8G#{gXQI)3y#BBZb>umml(fo-IBn^{a5qMHkwuE zW0gEhxotA-R~R>$w%Ih}5Z0deX#;u`tiXr_ki_CWASq?J@I$Xe+Q%cKD}Fn}YFpG!OiY zX;U$tiPi^x*0gCd269+?j;*O~Iu_2ug4~`r6K7!D9b?&__nI~nW4CE}(=I{#0uLg$ z7fcIbEH6NAFPe5K#`4k1?IqJ@VLZZ)&tGmYn>ZWejrg2~z^|amw&#Gw=Cgav%$tk$ zA2iu_3Z`9#@z-dwpS*7FI}hUzu@Tu%-Z1mR7<)06ed0~|TFDMFADn>CU3QAMOk05Q zr?|816Mr%7a*WGpvQNBi+7%e@HJ^JC4Xf*}1Pk!d$;bLPGw&*lr(mUgT<@6Hv=9q| z+}<^Jj$kaOCi!UIGZPnKd=AF)(Y$Zk)fmeL<)bN?wix55@xXHXz_e>He%7>ord^BH z-HtPuyyg2%T!Qg3d8@51&9-ih@($0pMmO7foGGudj9Idkr3R?(Q*+N{7|SLXTAP|} zeSRr#>2e@D%nfkGOp9SW%)C`!m?pb`yt13%FRl9ZwhrwNm{p_JV}RRnJ1o}@Ti0D! z{-M696}H|8>Oljr0XrConS;R)FciokQVyqbSd=5K9L(iVDtomYmCpm0-~lFpsbCzK z0w#iKUDhD|>?+V&q#ahZR4NW1SpM~vteo55OJ-LN`2wQK-MAg2@yGy{2a?*!|>4M5Jha*maA>N2n#ECttFpIEkD zO_$*7F$c^Bb3q8q0yBYJsI&!b!116HkPDTQKnEZfC~`q^GLVZ9x#*BxX*q9rlxBVYzl_u;VRijfM6=PWV3}eh+G3C&+-uKo;k*N!{AXc89VMo z5CG%BP%sP(u|C;o>(*r~zC{5r8pyT6P%sP(2R<;!I(w6CZpS%z@L^y$SOMbTHtWD9 zTZhT>FnkZZ3rgSvun#;0Qs7?j07!!^;4W|v*a5bI`@wx+6Sy0!2OF$*n{AUuJdV#m z&K+{9*aGB?Js+#@f#pmsXJI)zr-7V(w0`G$3@me~AQ@~=(yA~_~*EQ5xgSXi_ z1}$tn39iPASOiw#g)9USun5e;^SutpNlZ?Ga>kOA+>Joar=Nm6HuwVA3!VpWG+60v zwmX^<7_S6(fIGn^JkAn4#u{vQ9oPt7M1Kjq3|;}R133%!0UmG~u+VM?>%nF$Z^r$$ zfV;p}Fa~WjScmoUQ*9z%>=~`=tOvK-IyMf#6YOv8-EQmA6vTS@!6ZMB+JIw0JvbWF zaKEGAR-i5T9?L&~L%@#Z6?_S5KrX?)h7N&mz~|rq_!j&dd9=@PxVo z`K|R19(WZT2m2eE_zNh5-J&0O7RZm431BK159WhBcoVz@J_4Ts`E4P;8|3nTKRg|; zR4(1OfXgtJOLnGA{V_Kfn4ayh3!8<5wG|KFb=E*3-CndgDDuxlb35w@pE`Fa-AwyZ_?V> zaBZ~*3-a?Ud)TvIf(QKy)Zu|z0eNsio*>88=6h^zZw2>x4xEL46Sx`3{Z`Aiu+78Y zv74~DTfv$Jt9FlV7}nP98oa|cE!?_i{TzI2+gWd%)zII1^XP_A^*yc5Z5vMN+_OH^ z*VAu+XTUlC!i2Wi{ByYiRB?QHBlp|35cxA9A`>{r9fsP8Vsn20Y$4;QuE5 zG~e2>yYaG?e#9hM)3^EJCB3aLA8%}P_;&jKH1DTX*1jh&XX7M&tmgGM^!|TAp4<`7 zNS^f6A^QBAr;hyTN%lQrl5C1E>!)Yj+-a;%miw=tq-*7$_ZsBUt(%`}{HA3>^TzLW O>$0|Yc2DDO+y4TH$sRNS delta 11822 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 diff --git a/packages/api/package.json b/packages/api/package.json index b5be46ad3..59860b0b2 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -13,7 +13,7 @@ ], "dependencies": { "event-iterator": "^2.0.0", - "eventsource-parser": "^1.1.1" + "eventsource-parser": "^3.0.0" }, "devDependencies": { "swagger-typescript-api": "^13.0.3", diff --git a/packages/api/templates/http-client.ejs b/packages/api/templates/http-client.ejs index 8d72a187a..1663a4fcd 100644 --- a/packages/api/templates/http-client.ejs +++ b/packages/api/templates/http-client.ejs @@ -2,7 +2,7 @@ const { apiConfig, generateResponses, config } = it; %> import { EventIterator } from 'event-iterator'; -import { createParser, type ParsedEvent, type ReconnectInterval } from 'eventsource-parser'; +import { createParser, type EventSourceMessage } from 'eventsource-parser'; export type QueryParamsType = Record; export type ResponseFormat = keyof Omit; @@ -277,15 +277,23 @@ export class HttpClient { queue.stop(); } - const parser = createParser((event: ParsedEvent | ReconnectInterval) => { - if (event.type === 'event') { - if (event.data === 'done') { + const parser = createParser({ + onEvent: (event: EventSourceMessage) => { + if (event.event === 'done') { stop(); + } else if (event.event === 'error') { + const data = JSON.parse(event.data); + reader.cancel(); + queue.fail(new Error(data.error.message)); } else { const data = JSON.parse(event.data); queue.push(data); } - } + }, + onError: (error) => { + reader.cancel(); + queue.fail(error); + }, }) const decoder = new TextDecoder();