Boundary examples

Use the same envelope everywhere.

These recipes keep transport details outside the message contract. Each receiver creates or loads a Message, then routes type and data.

WebSocket client

import Message from 'js-message';

const outgoing = new Message({
    type: 'chat.send',
    data: { text: 'Hello' }
});

socket.send(outgoing.JSON);

socket.addEventListener('message', ({ data }) => {
    const incoming = new Message(data);
    handle(incoming.type, incoming.data);
});

Fetch request and response

const request = new Message({
    type: 'profile.read',
    data: { id: 42 }
});

const response = await fetch('/messages', {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: request.JSON
});

const incoming = new Message(await response.text());

Web Worker

// page
worker.postMessage(new Message({
    type: 'image.resize',
    data: { width: 640 }
}).toJSON());

// worker
self.addEventListener('message', ({ data }) => {
    const message = new Message(data);
    resize(message.data);
});

Structured clone can carry the plain object from toJSON() without turning it into text first.

Node child-process IPC

// parent
child.send(new Message({
    type: 'job.run',
    data: { id: 'job_8' }
}).toJSON());

// child
process.on('message', (value) => {
    const message = new Message(value);
    run(message.data);
});
Choose text or objects at the transport edge.

Use message.JSON for text transports and message.toJSON() for structured-clone or object transports.