@veksa/transport
v0.0.22
Published
A reactive transport layer for messaging communication with pluggable adapters and RxJS observables
Maintainers
Readme
@veksa/transport
A reactive transport layer for messaging communication with pluggable adapters and RxJS observables
Installation
@veksa/transport requires TypeScript 5.8 or later.
Using npm or yarn
# npm
npm install @veksa/transport
# yarn
yarn add @veksa/transportFeatures
- Stream-based messaging with RxJS observables
- Pluggable transport adapters (socket, host, client)
- Cross-window and iframe messaging support
- Reactive state management for connection status
- JSON encoding/decoding with customizable codecs
- Automatic message ID handling
- TypeScript support with comprehensive interfaces
- Integration with @veksa/logger for debugging
Basic Usage
Socket Transport
import { createTransport, createJsonCodec, createSocketAdapter } from '@veksa/transport';
// Create transport with socket adapter
const transport = createTransport({
adapter: createSocketAdapter({
url: 'wss://example.com/socket',
codec: createJsonCodec(),
}),
});
// Connect to the transport
transport.connect();
// Subscribe to state changes
transport.state$.subscribe((state) => {
console.log('Transport state:', state);
});
// Send a message and get response
transport.api.send({
payloadType: 1,
payload: { userId: 123 },
clientMsgId: 'abc-123',
}).then((response) => {
console.log('Received response:', response);
});
// Subscribe to incoming events
transport.api.event$.subscribe((message) => {
console.log('Received event:', message);
});
// Disconnect when done
transport.disconnect();Cross-Window Communication
Host Page (Parent Window)
import { createTransport, createHostAdapter } from '@veksa/transport';
import { createLogger } from '@veksa/logger';
const logger = createLogger(true);
const iframe = document.getElementById('my-iframe') as HTMLIFrameElement;
// Create host adapter to communicate with the iframe
const transport = createTransport({
adapter: createHostAdapter({
frame: iframe,
prefix: 'Host',
prefixColor: '#4CAF50',
getMessageName: (type) => `Message-${type}`,
logger,
}),
});
// Connect to the transport
transport.connect();
// Send a message to the iframe client
transport.api.send({
payloadType: 1,
payload: { action: 'initialize' },
clientMsgId: 'init-123',
});Client Page (Inside iframe)
import { createTransport, createClientAdapter } from '@veksa/transport';
import { createLogger } from '@veksa/logger';
const logger = createLogger(true);
// Create client adapter to communicate with the host
const transport = createTransport({
adapter: createClientAdapter({
prefix: 'Client',
prefixColor: '#2196F3',
getMessageName: (type) => `Message-${type}`,
logger,
}),
});
// Connect to the transport
transport.connect();
// Listen for messages from the host
transport.api.event$.subscribe((message) => {
console.log('Received from host:', message);
});Codecs
createJsonCodec(getBinaryKeys?)
Creates a JSON codec for encoding/decoding messages. Supports optional binary field handling for Uint8Array data.
Without binaryKeys (default)
Serializes messages as plain JSON. Note that Uint8Array fields are serialized as objects, which is inefficient:
const codec = createJsonCodec();
const message = {
payloadType: 1,
clientMsgId: 'abc',
payload: { chunk: new Uint8Array([1, 2, 3]) }
};
// Encodes as: {"payloadType":1,"clientMsgId":"abc","payload":{"chunk":{"0":1,"1":2,"2":3}}}
// Inefficient for large binary data!With binaryKeys
Optimizes encoding/decoding of Uint8Array fields by converting them to base64 strings:
const codec = createJsonCodec(() => ['payload.chunk']);
const message = {
payloadType: 1,
clientMsgId: 'abc',
payload: { chunk: new Uint8Array([1, 2, 3]) }
};
// Encodes as: {"payloadType":1,"clientMsgId":"abc","payload":{"chunk":"AQID"}}
// Much more efficient for binary data!Nested Fields
Binary keys support dot notation for nested paths:
const codec = createJsonCodec(() => ['payload.data.binary']);
const message = {
clientMsgId: 'test',
payload: {
data: {
binary: new Uint8Array([1, 2, 3])
}
}
};
// The nested binary field is correctly encoded/decoded
const encoded = codec.encode(message);
const decoded = codec.decode(encoded);
console.log(decoded.payload.data.binary); // Uint8Array [1, 2, 3]Multiple Binary Fields
Specify multiple paths for handling multiple binary fields:
const codec = createJsonCodec(() => [
'payload.chunk',
'payload.thumbnail'
]);
const message = {
payloadType: 1,
clientMsgId: 'abc',
payload: {
chunk: new Uint8Array([1, 2, 3]),
thumbnail: new Uint8Array([4, 5, 6])
}
};Performance Considerations
- Without binaryKeys: Fast for plain JSON, but very slow for Uint8Array (serializes as object with thousands of properties)
- With binaryKeys: ~100x faster encode for binary data, slight overhead for decode but overall much more efficient
- Size: Binary fields encoded as base64 are ~33% larger than raw binary, but still 10-100x smaller than object serialization
API Reference
createTransport(type, adapter, logger, isError)
Creates a new transport instance.
Parameters
type: A string identifier for the transport typeadapter: An implementation ofITransportAdapterlogger: An instance ofILoggerfrom @veksa/loggerisError: Optional function to determine if a message represents an error
Transport Methods
connect(): Establishes a connectiondisconnect(): Terminates the connectionstate$: Observable of transport connection stateapi.send(message): Sends a message and returns a Promise with the response payloadapi.event$: Observable of incoming eventsgetLogs(): Retrieves logs if using @veksa/logger
Adapter Types
createSocketAdapter(options)
Creates a WebSocket-based transport adapter.
prefix: Logger prefix for identifying messagesprefixColor: Optional color for log messagesurl: WebSocket endpoint URLprotocol: WebSocket protocolcodec: Message encoding/decoding implementationgetPayloadName: Function to convert payload type to a readable namelogger: Instance of @veksa/logger
createHostAdapter(options)
Creates an adapter for the host/parent window to communicate with an iframe.
frame: Reference to the iframe elementprefix: Logger prefix for identifying messagesprefixColor: Color for log messages (optional)getPayloadName: Function to convert payload type to a readable namelogger: Instance of @veksa/logger
createClientAdapter(options)
Creates an adapter for iframe content to communicate with the host window.
prefix: Logger prefix for identifying messagesprefixColor: Color for log messages (optional)getPayloadName: Function to convert payload type to a readable namelogger: Instance of @veksa/logger
Interface Reference
ITransport<Type, InputMessage, OutputMessage, EventMessage>: Main transport interfaceITransportAdapter<InputMessage, OutputMessage, EventMessage>: Adapter interface for various connection typesITransportApi<Type, InputMessage, OutputMessage, EventMessage>: API interface for sending messages and subscribing to eventsITransportCodec<Message>: Interface for message encoding/decodingIMessage<Payload>: Standard message format with payload and client message IDISocketMessage: Interface for socket messagesIPostMessage: Interface for post messages (cross-window communication)
Contributing
This project welcomes contributions and suggestions.
