| 3674 | } |
| 3675 | |
| 3676 | public *traverse( |
| 3677 | source: GraphSource<any>, |
| 3678 | input: Iterable<TraversalPath<any, any, any>>, |
| 3679 | _context?: QueryContext, |
| 3680 | ): IterableIterator<TraversalPath<any, any, any>> { |
| 3681 | const { times, stepLabels, emit, emitStart, emitInput } = this.config; |
| 3682 | const seen = new Set<ElementId>(); |
| 3683 | |
| 3684 | const { repeatTraverser, untilTraverser } = this; |
| 3685 | |
| 3686 | // Collect input into an array so we can iterate multiple times if needed |
| 3687 | // (for emitInput and then for the traversal queue) |
| 3688 | const inputArray = [...input] as TraversalPath<any, any, any>[]; |
| 3689 | |
| 3690 | // For zero-min quantifiers ({0,n}), emit the input paths first |
| 3691 | // Note: We DON'T mark them as seen yet - we still need to traverse from them |
| 3692 | if (emitInput) { |
| 3693 | for (const path of inputArray) { |
| 3694 | if (path instanceof TraversalPath) { |
| 3695 | this.emitted++; |
| 3696 | if (stepLabels !== undefined && stepLabels.length > 0) { |
| 3697 | yield new TraversalPath(path.parent, path.value, stepLabels); |
| 3698 | } else { |
| 3699 | yield path; |
| 3700 | } |
| 3701 | } |
| 3702 | } |
| 3703 | } |
| 3704 | |
| 3705 | // Special case: times=0 means no traversal |
| 3706 | // - If emitInput was already set (Cypher quantifiers with min=0), input was already emitted above |
| 3707 | // - Otherwise (Gremlin API times(0)), emit the input here |
| 3708 | if (times === 0) { |
| 3709 | if (!emitInput) { |
| 3710 | for (const path of inputArray) { |
| 3711 | if (path instanceof TraversalPath) { |
| 3712 | this.emitted++; |
| 3713 | if (stepLabels !== undefined && stepLabels.length > 0) { |
| 3714 | yield new TraversalPath(path.parent, path.value, stepLabels); |
| 3715 | } else { |
| 3716 | yield path; |
| 3717 | } |
| 3718 | } |
| 3719 | } |
| 3720 | } |
| 3721 | return; |
| 3722 | } |
| 3723 | |
| 3724 | let queue: Iterable<TraversalPath<any, any, any>> = inputArray; |
| 3725 | let counter = 0; |
| 3726 | // emitStart defaults to 1 (start emitting from first iteration) |
| 3727 | const effectiveEmitStart = emitStart ?? 1; |
| 3728 | // Get maxIterations from context options, falling back to default |
| 3729 | const maxIterations = _context?.options.maxIterations ?? DEFAULT_MAX_REPEATS; |
| 3730 | |
| 3731 | while (counter < maxIterations) { |
| 3732 | counter++; |
| 3733 | const isFinalIteration = times !== undefined && counter >= times; |