rempl
v1.0.0-alpha.24
Published
Framework for moderated access with custom UI to remote JavaScript runtime
Downloads
4,110
Readme
The general idea behind Rempl
is to simplify moderated remote access to JavaScript runtime. Rempl
provides a transport between environments and a set of UI hosts.
Examples
Built on Rempl
:
- React Render Tracker – a tool to discover performance issues related to unintentional re-renders and unmounts
Install
npm install rempl
How to use
Browser
<script type="module">
import { createPublisher } from 'rempl/dist/rempl.esm.js';
const myTool = createPublisher('myTool', function (settings) {
/* return a UI bundle or url */
});
// ...
</script>
or
<script src="rempl/dist/rempl.js"></script>
<script>
const myTool = rempl.createPublisher('myTool', ...);
</script>
By default publisher attempts to connect to WS server with the same hostname
as page but 8177
as port. There are some options to specify rempl end point:
using
<meta name="rempl:server" content="{rempl server host}">
:<meta name="rempl:server" content="//1.2.3.4:1234" /> <!-- or content="none" to disable connection to WS server -->
using
connectPublisherWs()
functionimport { connectPublisherWs, createPublisher } from 'rempl'; const myPublisher = createPublisher('name', function() { ... }); connectPublisherWs('//1.2.3.4:1234')
Node.js
import { createPublisher } from 'rempl';
const myTool = createPublisher('myTool', function (settings) {
/* return a UI bundle or url */
});
// ...
When publisher is running on Node.js, it doesn't connect to WS server until WS server is not specified (there is no location
object available like in browser's environment). There some options to specify server host:
using environment variable
REMPL_SERVER
when start a script or before rempl is required for a first time. Example for MacOS:> REMPL_SERVER=//1.2.3.4:1234 node my-script.js
using
connectWS()
method ofPulisher
's instanceimport { connectPublisherWs, createPublisher } from 'rempl'; const myPublisher = createPublisher('name', function() { ... }); connectPublisherWs('//1.2.3.4:1234')
Overview
Subject
– something to be inspected, i.e. app, page, environment etc.Publisher
– monitors asubject
, collects data and publishes it forsubscribers
Subscriber
– consumer ofpublisher
's data, provides an UI for received dataTransport
– channels and protocols betweenpublisher
andsubscriber
; WebSocket (socket.io) or DOM Event-based communication may to be used between endpointsHost
– integrates in some environment (app), allows to choose a publisher and creates asandbox
for itssubscriber
; usually it's a plugin for something like browser, editor etc.Sandbox
– creates asubscriber
, request an UI and activate it when received
Publisher and subscriber are two parts of single app (tool). Transports, hosts and sandboxes are parts of rempl
.
Server
For most cases you need a WebSocket transport. In this case a WS server is required. Rempl provides rempl-cli package – a command line app to launch a server.
Host
Rempl
server provides web interface to monitor list of publishers and to launch selected publisher's UI in sandbox. Just open server's origin (by default http://localhost:8177
) in your browser.
- Browsers [repo]
- Chromium based browsers (Google Chrome, Edge, Opera)
- Firefox
- Edge
- Editors
Publisher environment
Publisher doesn't depends hard on environment. It's mostly limited by transports allowed to process. Currently rempl
publisher works well in:
- Browser's page
- Node.js process
Publisher can theoretically be created in non-JavaScript environment. In this case publisher interface and socket.io client should be implemented in language you use.
Distribution of UI
For tools based on rempl
, a publisher is a source of UI. When new sandbox for subscriber is created, it sends a request to publisher to provide an UI. Publisher should provide UI in some way:
script
– JavaScript bundle that includes everything is needed to build an UI (i.e. JavaScript, CSS, templates etc.). Whenscript
type is using,rempl
injects itself before script evaluation. Therefore no need to include rempl source to subscriber.url
– url of page subscriber. In this caserempl
should be included to page by author.
createPublisher('...', () => {
return { type: 'script', value: 'console.log("Hi!")' };
});
createPublisher('...', () =>
fetch('some-file.js')
.then((res) => res.text())
.then((value) => ({ type: 'script', value }))
);
createPublisher('...', async () => {
const res = await fetch('some-file.js');
const value = await res.text();
return { type: 'script', value };
});
createPublisher('...', () => {
return { type: 'url', value: 'http://...' };
});
API
Publisher
import { createPublisher } from 'rempl';
const myTool = createPublisher('myTool', function (settings) {
return { type: 'script', value: 'alert("myTool UI inited")' };
});
setInterval(() => {
myTool.publish(Date.now());
}, 1000);
myTool.provide('pong', () => {
console.log('Remote subscriber invoke `pong`');
});
publish(data: any)
provide(methodName, fn)
orprovide(methods)
isMethodProvided(method)
revoke(methodName)
orrevoke(methodNamesArray)
callRemote(method, ...args): Promise<any>
ns(namespaceName)
publish()
/provide()
/revoke()
/isMethodProvided()
/callRemote()
Subscriber
import { getSubscriber } from 'rempl';
const myTool = getSubscriber();
myTool.subscribe((data) => {
console.log('Receive data from publisher:', data);
myTool.callRemote('pong');
});
subscribe(callback)
provide(methodName, fn)
orprovide(methods)
isMethodProvided(methodName)
revoke(methodName)
orrevoke(methodNamesArray)
callRemote(methodName, ...args, callback)
getRemoteMethod(methodName)
isRemoteMethodExists(methodName)
onRemoteMethodsChanged(callback)
ns(namespace)
- subscribe/provide/revoke/isMethodProvided/callRemote
Host
Rempl provides a host that can be injected right in the inspecting page, so called in-page
host. To get that host use rempl.getHost()
method.
const inpageHost = rempl.getHost();
inpageHost.activate();
inpageHost.activate(publisherName);
inpageHost.deactivate();
inpageHost.deactivate(publisherName);
Host has two methods activate()
and deactivate()
.
activate([publisherName])
- uses to add host's view to page. When used with no argument method show UI and selects a first publisher. When argument is passed, it selects publisher with specified name.deactivate([publisherName])
- hide host's view if it showed. WhenpublisherName
is passed, method deactivates view only if publisher with passed name is selected.
RPC
Publishers and Subcribers can provide methods for remote side invocation and invoke methods of other side. API of both sides is symetric. Every namespace have the same RPC API as Publisher or Subscriber have.
NOTE: Examples are given for a Publisher, but the same API is available for any Subscriber since API is symetric.
provide(methodName, fn)
Method to provide a method(s) for remote side. It allows to provide a single method or batch of methods.
publisher.provide('foo', () => {
console.log('Method `foo` was invoked by subscriber');
});
publisher.provide('syncMethod', () => {
return 'result';
});
publisher.provide('asyncMethod', async () => {
const value = await doSomething();
return value;
});
publisher.ns('something').provide({
method1() {
/* do something */
},
method2() {
/* do something */
},
});
revoke(methodName)
Method to revoke a method(s) that was provided before. It allows to revoke a single method or several methods at once.
publisher.revoke('foo');
publisher.ns('something').revoke(['method1', 'method2']);
callRemote(methodName[, ...args][, callback])
Invoke remote side method with given arguments. All arguments should be a transferable through JSON data types, i.e. number
, string
, boolean
, Array
, plain object or null. The last argument can be a function that remote side can use to send data back.
publisher.callRemote('methodName', 1, 2).then((res) => {
console.log('response from subscriber');
});
isMethodProvided(methodName)
Returns true
when own method is provided for remote side by provide()
method.
publisher.isMethodProvided('test'); // false
publisher.provide('test', () => {});
publisher.isMethodProvided('test'); // true
publisher.revoke('test');
publisher.isMethodProvided('test'); // false
isRemoteMethodExists(methodName)
Returns true
when remote method is available to be invoked.
Currently method doesn't work for publisher since there can be several subscribers with different method set provided.
if (subscriber.isRemoteMethodExists('test')) {
subscriber.callRemote('test');
}
onRemoteMethodsChanged(callback)
Allows to subscribe to remote side API state of namespace. Method invoke passed callback on subscription and return a function to unsubscribe.
Currently method doesn't work for publisher since there can be several subscribers with different method set provided.
const unsubscribeDefaultNsMethodsLogging = subscriber.onRemoteMethodsChanged((methods) => {
console.log(methods);
});
// call returned function when need to stop listen for API changes
unsubscribeDefaultNsMethodsLogging();
getRemoteMethod(methodName)
Returns a function that invokes callRemote
with specified methodName
. This function is context free (namespace is binded) and invokes callRemote
only when remote method is available, otherwise it outputs a warning. available
property of function can be used to check a remote method is available.
Currently method doesn't work for publisher since there can be several subscribers with different method set provided.
const fooMethod = subscriber.getRemoteMethod('foo');
const nsBarBazMethod = subscriber.ns('bar').getRemoteMethod('baz');
if (fooMethod.available) {
fooMethod(1, 2, 3).then((result) => console.log('Result:', result));
}
nsBarBazMethod();
// with no check a warning message might to be outputed
License
MIT