When working on Fake Linux Terminal and then Hacking Cafe, I've created an abstraction of a Linux process in the form of a web worker. Those executable scripts lived in Filesystem and needed an access to DOM (in my case jQuery, becasuse the used jQuery Terminal).
In this article I will show how to give access to the DOM from a web worker (service workers will work exactly the same) using Mitty.
Installation
If you're building an application, first you need to install Mitty
npm install @jcubic/mitty
Main Thread
In the main thread, you expose the document or window object:
import { Host } from '@jcubic/mitty';
// helper object to have all exposed objects in one place
const modules = {
document: () => document,
window: () => window
};
const channel = new BroadcastChannel('my-app');
const worker = new Worker('./worker.js', { type: 'module' });
const host = new Host({
// worker instance can also be used as a channel
channel: channel,
resolve(name) {
// you can just return the object by name here
if (Object.hasOwn(modules, name)) {
return modules[name]();
}
return null;
}
});
Web Worker
Then in a web worker (or service worker):
import { connect } from 'https://cdn.jsdelivr.net/npm/@jcubic/mitty';
const { require } = connect(new BroadcastChannel('my-app'));
// or when using worker as a channel
const { require } = connect(self);
const document = require('document');
// methods need await
const body = await document.querySelector('body');
await body.style.setProperty('color', 'red');
// setting properties are send right away
body.style.backgroundColor = '#000';
console.log(await body.style.color);
console.log(await body.style.backgroundColor);
Universal Transport
The Mitty library is not limited to web/service workers. You can use it to connect:
- Server with client (expose Node.js object in browser)
- Client with server (use the browser DOM inside Node.js), you can hide the logic on the server.
- Different tabs using Sysend or BroadcastChannel.
- Connect peers using WebRTC
To create a channel, you need to implement this interface:
interface Channel {
postMessage(message: string): void;
addEventListener(type: 'message', listener: ChannelListener): void;
removeEventListener(type: 'message', listener: ChannelListener): void;
}
As you can see, the channel interface has the same methods as the web/service worker. That's why both can be used as channels.
Access Any Objects
You're not limited to DOM nodes. You can expose any objects that have methods. You can expose part of the backend code to be executed directly on the client.
GitHub Project
More information and examples on GitHub
jcubic
/
mitty
A transport-agnostic proxy RPC for executing method chains from any isolated context (Workers, Tabs, or Servers) using lazy evaluation.
Mitty: A transport-agnostic proxy RPC for executing method chains from any isolated context (Workers, Tabs, or Servers).
Use objects that only exist on the main thread — DOM nodes, jQuery objects, class instances with methods, or anything that is inaccessible from inside a Web/Service Worker.
A worker has no DOM, and postMessage cannot carry a DOM node, a jQuery object, or
anything else that isn't structured-cloneable. Mitty leaves the real object on the main
thread and puts a proxy in the worker. The proxy records property accesses and calls
without touching the channel; the whole chain is replayed on the main thread when you
await it.
// inside a worker — no DOM here
const $ = require('$');
await $('#list').find('li').first().text(); // one message, not four
Universal Communication
While originally…
Top comments (0)