How Session Export to HTML Handles Deep Tree Traversal in Pi Web
Pi Web's session export uses an iterative post-processing patch to replace recursive tree rendering with a loop-based approach, preventing stack overflow crashes on deeply nested conversations.
The pi-web repository implements a robust server-side export mechanism that converts chat sessions into self-contained HTML files. When users request an export, the system must handle conversation trees of arbitrary depth—from a few messages to thousands of linear branches—without crashing the browser's JavaScript engine.
The Export Pipeline Architecture
Session export in Pi Web follows a three-stage pipeline designed for reliability at scale.
1. Session Retrieval via API Route
The export flow begins at app/api/sessions/[id]/export/route.ts. This API handler coordinates the entire conversion process:
// GET /api/sessions/[id]/export
// Located in: app/api/sessions/[id]/export/route.ts
The handler first retrieves the session data using lib/session-reader.ts, which parses the .jsonl file format that Pi Web uses for persistent storage. Each session is stored as newline-delimited JSON entries representing individual messages with parent-child relationships.
2. SDK HTML Generation
The handler delegates initial HTML production to Pi's internal SDK via exportSessionToHtml. This generates a complete HTML document including:
- Embedded CSS for message styling
- The raw message tree as a JavaScript data structure
- A
renderTreeRecursivefunction for DOM construction
The recursive renderer follows a natural but fragile pattern:
// Original SDK output (recursive, vulnerable to stack overflow)
function renderTreeRecursive(node, depth = 0) {
renderMessage(node, depth);
node.children.forEach(child => renderTreeRecursive(child, depth + 1));
}
For sessions with 1000+ messages in a linear chain, this exceeds typical browser call-stack limits (~10,000–50,000 frames depending on engine), causing the exported page to crash on load.
3. The Iterative Patch
Pi Web's critical innovation occurs immediately after SDK generation. The export route post-processes the HTML string to replace the vulnerable recursive implementation:
// Patched version inserted by Pi Web's export handler
function renderTreeIterative(root) {
const stack = [{ node: root, depth: 0 }];
while (stack.length) {
const { node, depth } = stack.pop();
// render node …
for (let i = node.children.length - 1; i >= 0; i--) {
stack.push({ node: node.children[i], depth: depth + 1 });
}
}
}
This transformation maintains identical rendering behavior while keeping call-stack depth constant at O(1) regardless of tree depth. The stack array grows in heap memory—safely handling millions of nodes.
Client-Side Export Trigger
Users initiate exports through a simple fetch pattern:
// In a React component
const exportSession = async (sessionId: string) => {
const res = await fetch(`/api/sessions/${sessionId}/export`);
const blob = await res.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `session-${sessionId}.html`;
a.click();
};
The server returns the fully patched HTML as a downloadable blob—no additional client-side processing required.
Key Implementation Files
| File Path | Responsibility |
|---|---|
app/api/sessions/[id]/export/route.ts |
API endpoint, orchestrates export and applies iterative patch |
lib/session-reader.ts |
Parses .jsonl session files into traversable objects |
lib/export-html.ts |
Internal SDK that generates initial HTML (recursive) |
Why Iterative Beats Recursive for Session Export
Recursion risks: Browser JavaScript engines impose strict call-stack limits. Deep conversation chains—common in extended research or coding assistant sessions—trigger RangeError: Maximum call stack size exceeded.
Iteration guarantees: The explicit stack pattern uses heap-allocated arrays with no engine-imposed depth limits. Memory consumption scales linearly with tree size, which is acceptable for practical session lengths.
The patch is applied server-side so exported files remain functional independently—critical for archives and shared downloads.
Summary
- Pi Web stores sessions as
.jsonlfiles parsed bylib/session-reader.ts - The export route in
app/api/sessions/[id]/export/route.tsgenerates HTML via SDK then patches it server-side - The iterative
renderTreeIterativereplacement eliminates stack overflow risks for deep conversation trees - Exported HTML files are self-contained and work across all browsers regardless of session depth
Frequently Asked Questions
What file format does Pi Web use for session storage?
Pi Web uses JSON Lines (.jsonl) format, with each line representing one message entry containing content, timestamps, and parent references. This append-only structure supports efficient streaming reads during export.
Why not fix the recursion in the SDK directly?
The SDK's exportSessionToHtml is internal to Pi's broader platform. Pi Web applies the iterative patch as a consumer-side safeguard, ensuring compatibility without requiring upstream changes. This defensive approach also permits SDK updates without regression risk.
Does the iterative version change visual output?
No. The renderTreeIterative function produces identical DOM structures to the original recursive implementation. The transformation is purely algorithmic—swapping the engine's call stack for an explicit array stack while preserving traversal order.
How deep can sessions be after the patch?
The iterative approach supports millions of messages limited only by available memory. The explicit stack array grows in heap space, bypassing browser call-stack limits entirely.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →