Coincident
An Atomics based Proxy to simplify, and synchronize, Worker related tasks.
Install / Use
/learn @WebReflection/CoincidentREADME
coincident
<sup>Social Media Photo by bady abbas on Unsplash</sup>
An Atomics based Proxy to simplify, and synchronize, Worker related tasks.
Coincident V4
This is the latest iteration of this module where everything is explained in the related merge request and it can be summarized as such:
- there is one default encoder/decoder that brings the best of all worlds out of the box
- there are more utilities that helps reducing roundtrips
- views and buffers are compatible and fast by default
- the whole FFI is now 100% code covered in its dedicated, dependency-free, project
API
main
import coincident from 'coincident/main';
const {
// the Worker to be used (this extends the global one and add proxy)
Worker:globalThis.Worker & { proxy: Proxy },
// true if SharedArrayBuffer and sync operations are usable
native:boolean,
// a utility to transfer buffers directly via `postMessage`
// use this at the end of any proxied function signature/call
transfer:(...buffers:ArrayBuffer[]) => buffers,
} = coincident({
// an optional way to transform values before sending these elsewhere
transform: value => any,
// an optional way to encode any value as binary
// reflected-ffi/encoder as default
encoder: reflectedFFIEncoder,
// if `false` disable/ignore the transfer ability (perf boost)
transfer:boolean,
});
worker
import coincident from 'coincident/worker';
const {
// the proxy to invoke sync or async main thread exposed utility
// it can expose utilities itself too that the main can invoke
proxy: Proxy,
// true if SharedArrayBuffer and sync operations are usable
native:boolean,
// a utility to transfer buffers directly via `postMessage`
// use this at the end of any proxied function signature/call
transfer:(...buffers:ArrayBuffer[]) => buffers,
// a way to directly transfer a value as it is
direct: value => value,
} = coincident({
// an optional way to transform values before sending these elsewhere
transform: value => any,
// an optional way to decode any bonary as value
// reflected-ffi/decoder as default
decoder: reflectedFFIDirectDecoder,
// if `false` disable/ignore the transfer ability (perf boost)
transfer:boolean,
// optional minimum SharedArrayBuffer size
minByteLength: 0x7FFF,
// optional maximum SharedArrayBuffer size
maxByteLength: 0x1000000,
});
window/worker
It returns as part of the object literal also window, usable only when native is true, and isWindowProxy which returns true or false accordingly if the tested reference is from the main thread or not.
server/worker
It returns as part of the object literal what window/worker returns but also server, usable only when native is true, and isServerProxy which returns true or false accordingly if the tested reference is from the backend or not.
V2 API
Following the description of all different imports to use either on the main or the worker thread.
coincident/main
This is the import that provides the ability to expose main thread's callbacks to the worker thread and to also await callbacks exposed via the worker code.
import coincident from 'coincident/main';
const {
// the Worker to be used (this extends the global one)
Worker,
// a boolean indicating if shared array buffer is supported
native,
// a utility to transfer values directly via `postMessage`
// (...args: Transferable[]) => Transferable[]
transfer,
} = coincident({
// an optional utility to transform values (FFI / Proxy related)
transform: value => value,
});
coincident/main - Worker class
The Worker class returned by coincident() has these features:
- it always starts a Worker as
{ type: "module" }<sup><sub>( mostly because the worker needs toawait coincident()on bootstrap )</sub></sup> - it optionally accepts a
{ serviceWorker: "../sw.js" }to help coincident falling back to synchronous behavior, which is mandatory to use any DOM orwindowrelated functionality - it provides to each instance a
proxyreference where utilities, as callbacks, can be assigned or asynchronously awaited if exposed within worker's code
const { proxy } = new Worker('./worker.js');
// can be invoked from the worker
proxy.location = () => location.href;
// exposed via worker code
await proxy.compute();
coincident/worker
This is the import that provides the ability to expose worker thread's callbacks to the main thread and to also directly invoke callbacks exposed via the main proxied reference.
import coincident from 'coincident/worker';
const {
// the counter-part of the main worker.proxy reference
proxy,
// a boolean indicating if shared array buffer is supported
native,
// a utility to transfer values directly via `postMessage`
// (...args: Transferable[]) => Transferable[]
transfer,
// a way to transfer a value directly as it is
direct: value => value,
// a namespace to batch multiple operations into a single roundtrip
ffi: {
assign, query, gather, evaluate,
}
} = await coincident({
// an optional utility to transform values (FFI / Proxy related)
transform: value => value,
});
// exposed to the main thread
proxy.compute = async () => {
// super expensive task ...
return 'result';
};
// consumed from the main thread
// synchronous if COI is enabled or
// the Service Worker was passed
console.log(proxy.location());
Window
These exports and their coincident/dist/... pre-optimized counter-parts allow coincident to drive, from a Worker the main thread and operate directly on it.
coincident/window/main
When the worker code expects the main window reference, this import is needed to allow just that.
import coincident from 'coincident/window/main';
// ^^^^^^
const { Worker, polyfill, transfer } = coincident();
The signature, on the main thread, is identical.
coincident/window/worker
On the worker side, this import is also identical to the non-window variant but it's returned namespace, after bootstrap, contains two extra utilities:
import coincident from 'coincident/window/worker';
// ^^^^^^
const {
proxy, native, transfer,
// it's a synchronous, Atomic.wait based, Proxy
// to the actual globalThis reference on the main
window,
// it's an introspection helper that returns `true`
// only when a reference points at the main thread
// (value: any) => boolean
isWindowProxy,
} = await coincident();
// direct synchronous access to the main `window`
console.log(window.location.href);
window.document.body.textContent = 'Hello World 👋';
Server
These exports and their coincident/dist/... pre-optimized counter-parts allow coincident to drive, from a Worker both the main thread and operate directly on the running server too.
⚠️ WARNING
This feature exists mostly to enable Kiosk or IoT related projects and it should not be publicly available as any malicious worker code could fully take over the server or harm the service.
coincident/server
This is what node or bun or others should import to instrument connected WebSockets.
import coincident from 'coincident/server';
// Bun example
serve({
port,
fetch,
// here coincident options should have
// a "truthy" bun 🐰
websocket: coincident({ bun: true })
});
// NodeJS ⬡ or any other with `ws` module as example
import { WebSocketServer } from 'ws';
const server = ...;
coincident({
// the `wss` property must be there
wss: new WebSocketServer({ server })
});
The coincident utility here simply instruments every connected WebSocket to react on message and close events.
coincident/server/main
When the worker code expects both the main window and the server references, this import is needed to allow just that.
import coincident from 'coincident/server/main';
// ^^^^^^
const { Worker, polyfill, transfer } = coincident({
ws: 'ws://localhotst:8080/'
// ^^^^^^^^^^^^^^^^^^^^^
});
The signature, on the main thread, is identical except the WebSocket url must be provided during initialization.
coincident/server/worker
On the worker side, this import is also identical to the window variant but it's returned namespace, after bootstrap, contains two extra utilities:
import coincident from 'coincident/server/worker';
// ^^^^^^
const {
proxy, native, transfer,
window, isWindowProxy,
// it's a synchronous, Atomic.wait based, Proxy
// to the actual globalThis reference on the server
server,
// it's an introspection helper that returns `true`
// only when a reference points at the server
// (value: any) => boolean
isServerProxy,
// a namespace with both FFIs (direct methods are window only)
ffi: {
assign, gather, query, evaluate,
window: { ... }, // replicates above
server: { ... }, // brings above to the server
}
} = await coincident();
// direct synchronous access to the main `server`
server.console.log('Hello World 👋');
// example of module import
const os = await server.import('os');
console.log(os.platform());
<details id="performance"> <summary><strong>A note about performance</strong></summary> <div markdown=1>
Every single property retrieved via the window reference is a whole worker ↔ main roun
