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.

Native browser setup

js-message works with bundlers and without a bundler.

Bundlers resolve the bare js-message import normally. Native browser ESM resolves it through a standard import map, with no build or transpilation step.

<script type="importmap">
{
  "imports": {
    "js-message": "./node_modules/js-message/index.js",
    "js-message/": "./node_modules/js-message/"
  }
}
</script>
<script type="module">
  import Message from 'js-message';

  console.log(new Message({ type: 'page.ready' }).JSON);
</script>

This map is complete because js-message has zero runtime dependencies, so it needs no dependency or scope entries. Its paths are relative to the HTML document. Serve the app over HTTP(S), and configure the server to expose these node_modules files; file:// is not supported.

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.