or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mdindex.mdjson-conversion.mdtransport.md
tile.json

transport.mddocs/

Transport System

Back-channel messaging infrastructure for communication between JSON layer instances and host applications, particularly useful for interactive visualizations and notebook environments.

Capabilities

Transport Class

Base transport class providing infrastructure for bidirectional communication between deck.gl JSON layers and their host environment.

/**
 * Base transport class for back-channel messaging between JSON layers and host applications
 */
class Transport {
  /** Name identifier for this transport instance */
  name: string;
  
  /** User-defined data storage */
  userData: Record<string, any>;
  
  /**
   * Creates a new transport instance
   * @param name - Optional name for the transport (default: 'Transport')
   */
  constructor(name?: string);
  
  /**
   * Sets global callbacks for transport events
   * @param callbacks - Object containing callback functions
   */
  static setCallbacks(callbacks: TransportCallbacks): void;
  
  /**
   * Returns the root DOM element for this transport connection
   * @returns HTMLElement for browser environments, null for Node.js
   */
  getRootDOMElement(): HTMLElement | null;
  
  /**
   * Sends a JSON message via back-channel (not implemented in base class)
   * Override in transport implementations
   */
  sendJSONMessage(): void;
  
  /**
   * Sends a binary message via back-channel (not implemented in base class)
   * Override in transport implementations  
   */
  sendBinaryMessage(): void;
  
  /**
   * Static utility for safe JSON stringification with circular reference handling
   * @param value - Value to stringify
   * @returns JSON string with circular references resolved
   */
  static _stringifyJSONSafe(value: any): string;
}

interface TransportCallbacks {
  /** Called when a transport connection initializes */
  onInitialize?: (message: TransportMessage) => void;
  
  /** Called when a transport connection finalizes/closes */
  onFinalize?: (message: TransportMessage) => void;
  
  /** Called when a message is received from a transport */
  onMessage?: (message: TransportMessage) => void;
}

interface TransportMessage {
  /** The transport instance that sent the message */
  transport: Transport;
  /** Additional message data */
  [key: string]: any;
}

Usage Examples:

import { Transport } from "@deck.gl/json";

// Set up global transport callbacks
Transport.setCallbacks({
  onInitialize: (message) => {
    console.log('Transport initialized:', message.transport.name);
  },
  onFinalize: (message) => {
    console.log('Transport finalized:', message.transport.name);
  },
  onMessage: (message) => {
    console.log('Message received:', message);
  }
});

// Create a custom transport
class CustomTransport extends Transport {
  constructor(name) {
    super(name);
    this.userData.endpoint = 'ws://localhost:8080';
  }
  
  sendJSONMessage(data) {
    // Custom implementation for sending JSON messages
    fetch('/api/messages', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });
  }
  
  sendBinaryMessage(data) {
    // Custom implementation for sending binary data
    const formData = new FormData();
    formData.append('data', data);
    fetch('/api/binary', {
      method: 'POST',
      body: formData
    });
  }
}

// Use the custom transport
const transport = new CustomTransport('MyAppTransport');

Transport Lifecycle

Transports follow a specific lifecycle with callback hooks:

Initialization

// Internal usage - typically called by transport implementations
transport._initialize({
  customData: 'initialization data'
});

// Triggers onInitialize callback with:
// {
//   transport: transportInstance,
//   customData: 'initialization data'
// }

Message Handling

// Internal usage - called when messages are received
transport._messageReceived({
  messageType: 'update',
  payload: { layerId: 'layer-1', data: [...] }
});

// Triggers onMessage callback with:
// {
//   transport: transportInstance,
//   messageType: 'update',
//   payload: { layerId: 'layer-1', data: [...] }
// }

Finalization

// Internal usage - called when transport is being destroyed
transport._finalize({
  reason: 'user_disconnect'
});

// Triggers onFinalize callback with:
// {
//   transport: transportInstance,
//   reason: 'user_disconnect'
// }

DOM Integration

The transport system provides DOM integration for browser environments:

const transport = new Transport('browser-transport');

// Get the root DOM element (returns document.body by default)
const rootElement = transport.getRootDOMElement();

// Custom transport can override this method
class NotebookTransport extends Transport {
  constructor(cellElement) {
    super('notebook-transport');
    this.cellElement = cellElement;
  }
  
  getRootDOMElement() {
    // Return the specific notebook cell element
    return this.cellElement;
  }
}

Safe JSON Stringification

The Transport class provides a utility for safely stringifying objects with circular references:

const objectWithCircularRef = {
  name: 'test',
  self: null
};
objectWithCircularRef.self = objectWithCircularRef;

// Safe stringification
const jsonString = Transport._stringifyJSONSafe(objectWithCircularRef);
// Returns: '{"name":"test","self":{"name":"test"}}'

// Regular JSON.stringify would throw an error
// JSON.stringify(objectWithCircularRef); // TypeError: Converting circular structure to JSON

Use Cases

The transport system is particularly useful for:

  • Jupyter Notebook Integration: Communication between deck.gl visualizations and notebook cells
  • Real-time Updates: Streaming data updates to visualizations
  • Interactive Dashboards: Bidirectional communication between UI controls and visualizations
  • Embedded Visualizations: Communication between iframe-embedded visualizations and parent pages
  • Development Tools: Debug messages and development-time communication

Custom Transport Implementation

To create a custom transport, extend the base Transport class:

class WebSocketTransport extends Transport {
  constructor(url) {
    super('websocket-transport');
    this.ws = new WebSocket(url);
    this.userData.url = url;
    
    this.ws.onopen = () => this._initialize({ url });
    this.ws.onclose = () => this._finalize({ reason: 'connection_closed' });
    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      this._messageReceived(data);
    };
  }
  
  sendJSONMessage(data) {
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(data));
    }
  }
  
  sendBinaryMessage(data) {
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(data);
    }
  }
}