DevTools
Teleportal includes built-in DevTools for debugging and monitoring your application. The DevTools provide real-time visibility into message flow, connection state, document activity, and system statistics.
Overview
Section titled “Overview”The Teleportal DevTools helps you debug and monitor your Teleportal applications by providing:
- Real-time message monitoring: Track all sent and received messages with detailed metadata
- Connection state tracking: Monitor connection status, transport type, and errors
- Message filtering: Filter messages by document, type, direction, and search text
- Message inspection: View detailed payloads, metadata, and acknowledgment status
- Statistics: Track message counts, rates, document counts, and message types
The UI is organized into three tabs – Messages, Documents, and Presence – plus an always-visible connection status area with a details popover:
- Header bar: a tab bar (Messages / Documents / Presence) with count badges (document count, peer count), and a connection status indicator. Clicking the connection status opens a popover with live internals (in-flight/buffered counts, AIMD batch window, reconnect attempts, SharedWorker pooling details, and a timeline of state transitions).
- Messages tab: a collapsible filters row (search, direction selector, document checkboxes, message type checkboxes, message limit input), a scrollable message list (left, newest first), and a message inspector (right). RPC request/stream/response messages collapse into a single call row with status, latency, and error details.
- Documents tab: a tree of documents and subdocuments with live sync handshake state (sync-step-1 → sync-step-2 → synced), traffic counters, encryption, and last activity. Clicking a document filters the Messages tab.
- Presence tab: a live peer roster derived from presence-join/leave/heartbeat messages, with expandable per-peer data and a recent join/leave feed.
The connection status indicator uses color coding: green for connected, yellow for connecting, gray for disconnected, and red for errored.
All settings – including filters, message limit, and panel state – persist in localStorage across sessions.
Basic Integration
Section titled “Basic Integration”import { createTeleportalDevtools } from "teleportal/devtools";
// Create the devtools elementconst devtoolsElement = createTeleportalDevtools();
// Append to your DOMdocument.body.appendChild(devtoolsElement);React Integration
Section titled “React Integration”import { useState, useEffect, useRef } from "react";import { createTeleportalDevtools, getDevtoolsState } from "teleportal/devtools";
export function TeleportalDevtoolsPanel() { const containerRef = useRef<HTMLDivElement>(null); const devtoolsRef = useRef<HTMLElement | null>(null); const [state] = useState(() => getDevtoolsState());
useEffect(() => { if (!containerRef.current) return;
const devtoolsElement = createTeleportalDevtools(state); containerRef.current.appendChild(devtoolsElement); devtoolsRef.current = devtoolsElement;
return () => { if (devtoolsRef.current) { const cleanup = (devtoolsRef.current as any).__teleportalDevtoolsCleanup; if (cleanup) { cleanup(); } if (containerRef.current && devtoolsRef.current.parentNode === containerRef.current) { containerRef.current.removeChild(devtoolsRef.current); } } }; }, []);
return <div ref={containerRef} style={{ height: "100%", width: "100%" }} />;}Complete Vite + React Integration Example
Section titled “Complete Vite + React Integration Example”In a real application you typically want the devtools to appear only during development and to be toggleable so it does not obscure the rest of the UI. The example below shows a bottom-drawer pattern with conditional rendering and proper cleanup.
import { useState, useEffect, useRef, useCallback } from "react";import { createTeleportalDevtools, getDevtoolsState } from "teleportal/devtools";
export function DevtoolsDrawer() { const [open, setOpen] = useState(false); const containerRef = useRef<HTMLDivElement>(null); const devtoolsRef = useRef<HTMLElement | null>(null); const [state] = useState(() => getDevtoolsState());
useEffect(() => { if (!open || !containerRef.current) return;
const devtoolsElement = createTeleportalDevtools(state); containerRef.current.appendChild(devtoolsElement); devtoolsRef.current = devtoolsElement;
return () => { if (devtoolsRef.current) { const cleanup = (devtoolsRef.current as any).__teleportalDevtoolsCleanup; if (cleanup) cleanup(); devtoolsRef.current.remove(); devtoolsRef.current = null; } }; }, [open]);
return ( <> <button onClick={() => setOpen((prev) => !prev)} style={{ position: "fixed", bottom: open ? 320 : 8, right: 8, zIndex: 10000, }} > {open ? "Close DevTools" : "Open DevTools"} </button>
{open && ( <div ref={containerRef} style={{ position: "fixed", bottom: 0, left: 0, right: 0, height: 300, zIndex: 9999, borderTop: "2px solid #333", }} /> )} </> );}import { DevtoolsDrawer } from "./components/DevtoolsDrawer";
function App() { return ( <> {/* Your application content */} {import.meta.env.DEV && <DevtoolsDrawer />} </> );}Key points:
import.meta.env.DEVis a Vite built-in that istrueonly during development, so the drawer is tree-shaken out of production builds.- The devtools element is created when the drawer opens and cleaned up when it closes, so resources are not held when the panel is hidden.
- The
__teleportalDevtoolsCleanupfunction unsubscribes all event listeners and clears internal state, preventing memory leaks.
Message Monitoring Capabilities
Section titled “Message Monitoring Capabilities”The DevTools capture every message flowing through the Teleportal provider in real time. Each message is stored with metadata including its unique ID, direction (sent or received), timestamp, associated document, and the provider instance that handled it.
Key behaviors:
- ACK tracking: ACK messages are hidden from the message list to reduce noise, but they are tracked separately. When an ACK is received, the original message it acknowledges is updated with an ACK indicator, and the inspector shows the ACK details (ACK message ID, acknowledged message ID, and timestamp) in an expandable section.
- Message deduplication: Messages are deduplicated by their ID using an O(1) index lookup, so duplicate deliveries do not produce duplicate entries in the list.
- Configurable message limit: The message limit (default: 200) caps how many messages are kept in memory. When the limit is exceeded, the oldest messages are removed and their contributions to the statistics are subtracted. You can change the limit at any time via the input in the filters header.
- Message rate: The rate is calculated as the number of messages received or sent within the last 10 seconds, divided by 10, giving a smoothed messages-per-second value.
- Per-document tracking: A
DocumentTrackermaintains a registry of all documents seen in messages, recording their message counts, last activity timestamps, and provider associations.
Message Types and Color Coding
Section titled “Message Types and Color Coding”The DevTools recognize and color-code messages by the wire protocol’s message kinds – doc, awareness, presence, rpc, and ack. getMessageTypeLabel() derives the row label and getMessageTypeColor() its badge color.
Document (doc) Messages
Section titled “Document (doc) Messages”| Type | Color |
|---|---|
sync-step-1 |
Blue |
sync-step-2 |
Darker blue |
update |
Green |
sync-done |
Darker green |
auth-message |
Red |
Awareness Messages
Section titled “Awareness Messages”| Type | Color |
|---|---|
awareness-update |
Yellow |
awareness-request |
Darker yellow |
Presence Messages
Section titled “Presence Messages”| Type | Color |
|---|---|
presence-join / presence-leave / presence-heartbeat / presence-announce |
Purple |
The row label is the payload’s type; presence messages also feed the roster in the Presence tab.
RPC (rpc) Messages
Section titled “RPC (rpc) Messages”RPC is the transport for milestones (listMilestones, …), the key registry, and file transfers (fileUpload / fileDownload). There is no standalone milestone-* or file-* message type – those are RPC methods carried over rpc messages. The badge is indigo, shaded by requestType:
| Request type | Label | Color |
|---|---|---|
| request | <rpcMethod> |
Darkest indigo (600) |
| response | <rpcMethod> |
Medium indigo (500) |
| stream / part | <rpcMethod> (part) |
Lightest indigo (400) |
RPC messages sharing an originalRequestId are grouped into a single call row. File-transfer upload chunks never appear as messages – upload progress comes from the file protocol’s progress events instead.
ACK Messages
Section titled “ACK Messages”| Type | Color |
|---|---|
ack |
Gray (hidden from list but tracked) |
Filtering
Section titled “Filtering”The DevTools provide filtering controls in the collapsible filters panel. All filter settings persist in localStorage and are restored on page reload.
Document Filter
Section titled “Document Filter”Multi-select checkboxes listing every document that has appeared in a message. Documents are auto-discovered as messages arrive – there is no manual configuration. Select one or more documents to show only their messages.
Message Type Filter
Section titled “Message Type Filter”Checkboxes for each message type allow you to hide specific types from the list. This is useful for focusing on a particular category, for example hiding all awareness updates to concentrate on sync traffic.
Direction Filter
Section titled “Direction Filter”A selector with three options:
- All: Show both sent and received messages
- Sent: Show only outgoing messages
- Received: Show only incoming messages
Search Filter
Section titled “Search Filter”A text input that searches across message payloads and document IDs. The search is case-insensitive and debounced by 300ms so that filtering does not block the UI during rapid typing.
Debugging Sync Issues
Section titled “Debugging Sync Issues”The DevTools are especially useful for diagnosing common synchronization problems.
Sync not completing
Section titled “Sync not completing”Look for a sync-step-1 message that was sent but no corresponding sync-step-2 received. This means the server did not respond to the initial sync request. Check whether an auth-message was received instead, which indicates a permission or authentication error.
Duplicate updates
Section titled “Duplicate updates”If you see an unusually high message rate or the same document data arriving repeatedly, check the message IDs. The DevTools deduplicate by ID, so identical IDs will not appear twice. If many distinct IDs carry the same payload, the application may have an update loop – a common cause is subscribing to Y.js update events and re-broadcasting them without checking their origin.
Connection instability
Section titled “Connection instability”Watch the connection status indicator in the filters header. If it cycles rapidly between connecting (yellow) and disconnected (gray), there is likely a network issue, a server-side rejection, or a misconfigured reconnection strategy. Check for auth-message types with error codes that might explain the rejection.
Missing awareness updates
Section titled “Missing awareness updates”Filter to awareness messages only using the message type filter. Verify that awareness-request messages are being sent. If they are sent but no awareness-update is received in response, the server may not be forwarding awareness state, or the document ID in the request may not match any active document on the server.
File upload failures
Section titled “File upload failures”File transfers run over RPC. Filter to rpc messages and look for the fileUpload / fileDownload call rows. A failed call shows its error code and details in the call row and inspector. Note that upload chunks never appear as messages – they stay off the event pipeline for throughput, so upload progress and completion come from the file protocol’s progress events rather than visible stream parts. Download parts, by contrast, arrive as received messages and are grouped under their call row.
Troubleshooting Tips
Section titled “Troubleshooting Tips”DevTools not showing messages
Section titled “DevTools not showing messages”Ensure the devtools element is created after the Provider is initialized. The EventManager subscribes to teleportalEventClient events at construction time. If the devtools are created before the provider emits events, the subscriptions will be in place but no events will flow until the provider connects.
High memory usage
Section titled “High memory usage”Lower the message limit in the filters header. The default of 200 is conservative, but for high-traffic applications with many concurrent documents or frequent awareness updates, consider setting it to 50-100. Each stored message retains references to the original message object and the provider, so large payloads can add up.
Settings not persisting
Section titled “Settings not persisting”The DevTools store settings in localStorage under the key teleportal-devtools-settings. If settings are not persisting, check whether localStorage is available in your environment. Private browsing modes, iframe sandbox restrictions, and certain browser extensions can block localStorage access.
Cleanup
Section titled “Cleanup”Always call the cleanup function when the devtools element is removed from the DOM to prevent memory leaks from lingering event listeners:
const devtoolsElement = createTeleportalDevtools();
// Later, when cleaning up:const cleanup = (devtoolsElement as any).__teleportalDevtoolsCleanup;if (cleanup) { cleanup();}This unsubscribes from all teleportalEventClient event listeners, clears internal state, and removes subscriber callbacks.
Event System Integration
Section titled “Event System Integration”The DevTools integrate with the Teleportal event system through teleportalEventClient from teleportal/providers. The EventManager subscribes to the following events at construction time:
received-message– captures every incoming message with its provider and connection contextsent-message– captures every outgoing messageconnected– updates the connection state indicator to connecteddisconnected– updates the connection state indicator to disconnectedupdate– handles connection state transitions (connecting, errored, etc.)load-subdoc– registers a new subdocument in the document trackerunload-subdoc– removes a subdocument from the document tracker
Because these are global events emitted by the provider, the DevTools automatically capture traffic from all providers and connections active on the page.
Next Steps
Section titled “Next Steps”- Provider – Learn how providers work and how events flow
- Protocol – Understand the message types the DevTools display
- Performance – Optimize your Teleportal application