Back-channel messaging infrastructure for communication between JSON layer instances and host applications, particularly useful for interactive visualizations and notebook environments.
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');Transports follow a specific lifecycle with callback hooks:
// Internal usage - typically called by transport implementations
transport._initialize({
customData: 'initialization data'
});
// Triggers onInitialize callback with:
// {
// transport: transportInstance,
// customData: 'initialization data'
// }// 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: [...] }
// }// Internal usage - called when transport is being destroyed
transport._finalize({
reason: 'user_disconnect'
});
// Triggers onFinalize callback with:
// {
// transport: transportInstance,
// reason: 'user_disconnect'
// }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;
}
}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 JSONThe transport system is particularly useful for:
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);
}
}
}