@@ -153,16 +153,26 @@ async function traverseSequential(
153153 context . parent &&
154154 ( context . parent . kind === 'parallel' || ! instructionPlan . divisible ) ;
155155 if ( mustEntirelyFitInCandidate ) {
156- const allInstructions = getAllInstructions ( instructionPlan ) ;
157- candidate = allInstructions
158- ? selectCandidate ( context . parentCandidates , allInstructions )
159- : null ;
160- if ( candidate && allInstructions ) {
161- await context . addInstructionsToSingleTransactionPlan (
162- candidate ,
163- allInstructions
156+ for ( const parentCandidate of context . parentCandidates ) {
157+ const transactionPlan = await traverseWithSingleCandidate (
158+ instructionPlan ,
159+ {
160+ ...context ,
161+ candidate : {
162+ kind : 'single' ,
163+ message : {
164+ ...parentCandidate . message ,
165+ instructions : [ ...parentCandidate . message . instructions ] ,
166+ } as CompilableTransactionMessage ,
167+ } ,
168+ }
164169 ) ;
165- return null ;
170+ if ( transactionPlan ) {
171+ ( parentCandidate as Mutable < SingleTransactionPlan > ) . message =
172+ transactionPlan . message ;
173+ // TODO: Use hook.
174+ return null ;
175+ }
166176 }
167177 } else {
168178 candidate =
@@ -327,27 +337,6 @@ function getParallelCandidates(
327337 return getAllSingleTransactionPlans ( latestPlan ) ;
328338}
329339
330- function getAllInstructions (
331- instructionPlan : InstructionPlan
332- ) : IInstruction [ ] | null {
333- if ( instructionPlan . kind === 'single' ) {
334- return [ instructionPlan . instruction ] ;
335- }
336- if ( instructionPlan . kind === 'iterable' ) {
337- return instructionPlan . getAll ( ) ;
338- }
339- return instructionPlan . plans . reduce (
340- ( acc , plan ) => {
341- if ( acc === null ) return null ;
342- const instructions = getAllInstructions ( plan ) ;
343- if ( instructions === null ) return null ;
344- acc . push ( ...instructions ) ;
345- return acc ;
346- } ,
347- [ ] as IInstruction [ ] | null
348- ) ;
349- }
350-
351340function selectCandidateForIterator (
352341 candidates : SingleTransactionPlan [ ] ,
353342 iterator : InstructionIterator
@@ -395,3 +384,121 @@ function isValidTransactionPlan(transactionPlan: TransactionPlan): boolean {
395384 }
396385 return transactionPlan . plans . every ( isValidTransactionPlan ) ;
397386}
387+
388+ type TraverseWithSingleCandidateContext = {
389+ abortSignal ?: AbortSignal ;
390+ candidate : SingleTransactionPlan | null ;
391+ createSingleTransactionPlan : (
392+ instructions ?: IInstruction [ ] ,
393+ abortSignal ?: AbortSignal
394+ ) => Promise < SingleTransactionPlan > ;
395+ addInstructionsToSingleTransactionPlan : (
396+ plan : SingleTransactionPlan ,
397+ instructions : IInstruction [ ] ,
398+ abortSignal ?: AbortSignal
399+ ) => Promise < void > ;
400+ } ;
401+
402+ async function traverseWithSingleCandidate (
403+ instructionPlan : InstructionPlan ,
404+ context : TraverseWithSingleCandidateContext
405+ ) : Promise < SingleTransactionPlan | null > {
406+ context . abortSignal ?. throwIfAborted ( ) ;
407+ switch ( instructionPlan . kind ) {
408+ case 'sequential' :
409+ return await traverseSequentialWithSingleCandidate (
410+ instructionPlan ,
411+ context
412+ ) ;
413+ case 'parallel' :
414+ return await traverseParallelWithSingleCandidate (
415+ instructionPlan ,
416+ context
417+ ) ;
418+ case 'single' :
419+ return await traverseSingleWithSingleCandidate ( instructionPlan , context ) ;
420+ case 'iterable' :
421+ return await traverseIterableWithSingleCandidate (
422+ instructionPlan ,
423+ context
424+ ) ;
425+ default :
426+ instructionPlan satisfies never ;
427+ throw new Error (
428+ `Unknown instruction plan kind: ${ ( instructionPlan as { kind : string } ) . kind } `
429+ ) ;
430+ }
431+ }
432+
433+ async function traverseSequentialWithSingleCandidate (
434+ instructionPlan : SequentialInstructionPlan ,
435+ context : TraverseWithSingleCandidateContext
436+ ) : Promise < SingleTransactionPlan | null > {
437+ if ( context . candidate === null ) {
438+ return null ;
439+ }
440+ for ( const plan of instructionPlan . plans ) {
441+ const candidate = await traverseWithSingleCandidate ( plan , {
442+ ...context ,
443+ candidate : context . candidate ,
444+ } ) ;
445+ if ( candidate === null ) {
446+ return null ;
447+ }
448+ }
449+ return context . candidate ;
450+ }
451+
452+ async function traverseParallelWithSingleCandidate (
453+ instructionPlan : ParallelInstructionPlan ,
454+ context : TraverseWithSingleCandidateContext
455+ ) : Promise < SingleTransactionPlan | null > {
456+ if ( context . candidate === null ) {
457+ return null ;
458+ }
459+ for ( const plan of instructionPlan . plans ) {
460+ const candidate = await traverseWithSingleCandidate ( plan , {
461+ ...context ,
462+ candidate : context . candidate ,
463+ } ) ;
464+ if ( candidate === null ) {
465+ return null ;
466+ }
467+ }
468+ return context . candidate ;
469+ }
470+
471+ async function traverseSingleWithSingleCandidate (
472+ instructionPlan : SingleInstructionPlan ,
473+ context : TraverseWithSingleCandidateContext
474+ ) : Promise < SingleTransactionPlan | null > {
475+ if ( context . candidate === null ) {
476+ return null ;
477+ }
478+ const ix = instructionPlan . instruction ;
479+ if ( ! isValidCandidate ( context . candidate , [ ix ] ) ) {
480+ return null ;
481+ }
482+ await context . addInstructionsToSingleTransactionPlan ( context . candidate , [ ix ] ) ;
483+ return context . candidate ;
484+ }
485+
486+ async function traverseIterableWithSingleCandidate (
487+ instructionPlan : IterableInstructionPlan ,
488+ context : TraverseWithSingleCandidateContext
489+ ) : Promise < SingleTransactionPlan | null > {
490+ if ( context . candidate === null ) {
491+ return null ;
492+ }
493+ const iterator = instructionPlan . getIterator ( ) ;
494+ while ( iterator . hasNext ( ) ) {
495+ const ix = iterator . next ( context . candidate . message ) ;
496+ if ( ! ix || ! isValidCandidate ( context . candidate , [ ix ] ) ) {
497+ return null ;
498+ }
499+ await context . addInstructionsToSingleTransactionPlan ( context . candidate , [
500+ ix ,
501+ ] ) ;
502+ }
503+ return context . candidate ;
504+ }
0 commit comments