RenderJSONL renders a claude session jsonl byte-stream to w. Exposed (not just used by RenderTranscript) so tests can exercise the decoder against fixture data in tempdir without going through path resolution. The entire stream is rendered. There is no time cutoff: an earlier design dropped entries
(r io.Reader, compact bool, w io.Writer)
| 86 | // everything is the safer contract — an over-inclusive sweep beats |
| 87 | // silent data loss. |
| 88 | func RenderJSONL(r io.Reader, compact bool, w io.Writer) error { |
| 89 | scanner := bufio.NewScanner(r) |
| 90 | // Session jsonl lines can be very long (tool results with file contents). |
| 91 | scanner.Buffer(make([]byte, 0, 64*1024), 10*1024*1024) |
| 92 | |
| 93 | first := true |
| 94 | for scanner.Scan() { |
| 95 | line := scanner.Bytes() |
| 96 | if len(line) == 0 { |
| 97 | continue |
| 98 | } |
| 99 | |
| 100 | var rec jsonlRecord |
| 101 | if err := json.Unmarshal(line, &rec); err != nil { |
| 102 | continue // skip malformed lines |
| 103 | } |
| 104 | |
| 105 | switch rec.Type { |
| 106 | case "user": |
| 107 | if !first { |
| 108 | fmt.Fprintln(w) |
| 109 | } |
| 110 | first = false |
| 111 | renderUserRecord(w, rec.Message, compact) |
| 112 | case "assistant": |
| 113 | if !first { |
| 114 | fmt.Fprintln(w) |
| 115 | } |
| 116 | first = false |
| 117 | renderAssistantRecord(w, rec.Message, compact) |
| 118 | } |
| 119 | // Skip permission-mode, file-history-snapshot, attachment, etc. |
| 120 | } |
| 121 | |
| 122 | if err := scanner.Err(); err != nil { |
| 123 | return fmt.Errorf("read session: %w", err) |
| 124 | } |
| 125 | return nil |
| 126 | } |
| 127 | |
| 128 | // ---------- jsonl record types ---------- |
| 129 |