@@ -34,6 +34,7 @@ import {
3434} from "./result-set-names" ;
3535import { compareInterpretedResults } from "./interpreted-results" ;
3636import { isCanary } from "../config" ;
37+ import { nanoid } from "nanoid" ;
3738
3839interface ComparePair {
3940 from : CompletedLocalQueryInfo ;
@@ -183,13 +184,97 @@ export class CompareView extends AbstractWebview<
183184 message = getErrorMessage ( e ) ;
184185 }
185186
187+ await this . streamResults ( result , currentResultSetDisplayName , message ) ;
188+ }
189+ }
190+
191+ private async streamResults (
192+ result : QueryCompareResult | undefined ,
193+ currentResultSetName : string ,
194+ message : string | undefined ,
195+ ) {
196+ // Since there is a string limit of 1GB in Node.js, the comparison is send as a JSON.stringified string to the webview
197+ // and some comparisons may be larger than that, we sometimes need to stream results. This uses a heuristic of 2,000 results
198+ // to determine if we should stream results.
199+
200+ if ( ! this . shouldStreamResults ( result ) ) {
186201 await this . postMessage ( {
187202 t : "setComparisons" ,
188203 result,
189- currentResultSetName : currentResultSetDisplayName ,
204+ currentResultSetName,
190205 message,
191206 } ) ;
207+ return ;
192208 }
209+
210+ const id = nanoid ( ) ;
211+
212+ // Streaming itself is implemented like this:
213+ // - 1 setup message which contains the first 1,000 results
214+ // - n "add results" messages which contain 1,000 results each
215+ // - 1 complete message which just tells the webview that we're done
216+
217+ await this . postMessage ( {
218+ t : "streamingComparisonSetup" ,
219+ id,
220+ result : this . chunkResults ( result , 0 , 1000 ) ,
221+ currentResultSetName,
222+ message,
223+ } ) ;
224+
225+ const { from, to } = result ;
226+
227+ const maxResults = Math . max ( from . length , to . length ) ;
228+ for ( let i = 1000 ; i < maxResults ; i += 1000 ) {
229+ const chunk = this . chunkResults ( result , i , i + 1000 ) ;
230+
231+ await this . postMessage ( {
232+ t : "streamingComparisonAddResults" ,
233+ id,
234+ result : chunk ,
235+ } ) ;
236+ }
237+
238+ await this . postMessage ( {
239+ t : "streamingComparisonComplete" ,
240+ id,
241+ } ) ;
242+ }
243+
244+ private shouldStreamResults (
245+ result : QueryCompareResult | undefined ,
246+ ) : result is QueryCompareResult {
247+ if ( result === undefined ) {
248+ return false ;
249+ }
250+
251+ // We probably won't run into limits if we have less than 2,000 total results
252+ const totalResults = result . from . length + result . to . length ;
253+ return totalResults > 2000 ;
254+ }
255+
256+ private chunkResults (
257+ result : QueryCompareResult ,
258+ start : number ,
259+ end : number ,
260+ ) : QueryCompareResult {
261+ if ( result . kind === "raw" ) {
262+ return {
263+ ...result ,
264+ from : result . from . slice ( start , end ) ,
265+ to : result . to . slice ( start , end ) ,
266+ } ;
267+ }
268+
269+ if ( result . kind === "interpreted" ) {
270+ return {
271+ ...result ,
272+ from : result . from . slice ( start , end ) ,
273+ to : result . to . slice ( start , end ) ,
274+ } ;
275+ }
276+
277+ assertNever ( result ) ;
193278 }
194279
195280 protected getPanelConfig ( ) : WebviewPanelConfig {
0 commit comments