Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

metacom

Package Overview
Dependencies
Maintainers
1
Versions
70
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

metacom - npm Package Compare versions

Comparing version 0.1.0-alpha.11 to 1.0.0-alpha.0

dist/events.js

12

CHANGELOG.md

@@ -5,7 +5,15 @@ # Changelog

To be released in 0.1.0
To be released in 1.0.0
- Metacom protocol implementation for client and server
- Support ws, wss, http and https transports
- Automatic reconnect on network errors or disconnect
- Server-side introspection and Client-side scaffolding
- Domain errors passing to browser client code
- Support ping interval (default 60s) and call timeout (default 7s)
- Reconnect active connections on browser `onlene` event
## [0.0.0][]
Module stub v0.0.0 and all before 0.1.0 are experiments with syntactic and
Module stub v0.0.0 and all before 1.0.0 are experiments with syntactic and
binary structures and multiple different ideas originated from JSTP and old

@@ -12,0 +20,0 @@ protocols like USP and CLEAR.

179

dist/metacom.js

@@ -0,1 +1,15 @@

import { EventEmitter } from './events.js';
const CALL_TIMEOUT = 7 * 1000;
const PING_INTERVAL = 60 * 1000;
const RECONNECT_TIMEOUT = 2 * 1000;
const connections = new Set();
window.addEventListener('online', () => {
for (const connection of connections) {
if (!connection.connected) connection.open();
}
});
class MetacomError extends Error {

@@ -26,20 +40,32 @@ constructor({ message, code }) {

export class Metacom {
constructor(url) {
export class Metacom extends EventEmitter {
constructor(url, options = {}) {
super();
this.url = url;
this.socket = new WebSocket(url);
this.socket = null;
this.api = {};
this.callId = 0;
this.calls = new Map();
this.socket.addEventListener('message', ({ data }) => {
this.message(data);
});
this.active = false;
this.connected = false;
this.lastActivity = new Date().getTime();
this.callTimeout = options.callTimeout || CALL_TIMEOUT;
this.pingInterval = options.pingInterval || PING_INTERVAL;
this.reconnectTimeout = options.reconnectTimeout || RECONNECT_TIMEOUT;
this.open();
}
static create(url, options) {
const { transport } = Metacom;
const Transport = url.startsWith('ws') ? transport.ws : transport.http;
return new Transport(url, options);
}
message(data) {
if (data === '{}') return;
this.lastActivity = new Date().getTime();
let packet;
try {
packet = JSON.parse(data);
} catch (err) {
console.error(err);
} catch {
return;

@@ -70,11 +96,4 @@ }

ready() {
return new Promise(resolve => {
if (this.socket.readyState === WebSocket.OPEN) resolve();
else this.socket.addEventListener('open', resolve);
});
}
async load(...interfaces) {
const introspect = this.httpCall('system')('introspect');
const introspect = this.scaffold('system')('introspect');
const introspection = await introspect(interfaces);

@@ -86,3 +105,3 @@ const available = Object.keys(introspection);

const iface = introspection[interfaceName];
const request = this.socketCall(interfaceName);
const request = this.scaffold(interfaceName);
const methodNames = Object.keys(iface);

@@ -96,29 +115,3 @@ for (const methodName of methodNames) {

httpCall(iname, ver) {
return methodName => (args = {}) => {
const callId = ++this.callId;
const interfaceName = ver ? `${iname}.${ver}` : iname;
const target = interfaceName + '/' + methodName;
const packet = { call: callId, [target]: args };
const dest = new URL(this.url);
const protocol = dest.protocol === 'ws:' ? 'http' : 'https';
const url = `${protocol}://${dest.host}/api`;
return fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(packet),
}).then(res => {
const { status } = res;
if (status === 200) {
return res.json().then(packet => {
if (packet.error) throw new MetacomError(packet.error);
return packet.result;
});
}
throw new Error(`Status Code: ${status}`);
});
};
}
socketCall(iname, ver) {
scaffold(iname, ver) {
return methodName => async (args = {}) => {

@@ -128,7 +121,13 @@ const callId = ++this.callId;

const target = interfaceName + '/' + methodName;
await this.ready();
if (!this.connected) await this.open();
return new Promise((resolve, reject) => {
setTimeout(() => {
if (this.calls.has(callId)) {
this.calls.delete(callId);
reject(new Error('Request timeout'));
}
}, this.callTimeout);
this.calls.set(callId, [resolve, reject]);
const packet = { call: callId, [target]: args };
this.socket.send(JSON.stringify(packet));
this.send(JSON.stringify(packet));
});

@@ -138,1 +137,89 @@ };

}
class WebsocketTransport extends Metacom {
async open() {
if (this.connected) return;
const socket = new WebSocket(this.url);
this.active = true;
this.socket = socket;
connections.add(this);
socket.addEventListener('message', ({ data }) => {
this.message(data);
});
socket.addEventListener('close', () => {
this.connected = false;
setTimeout(() => {
if (this.active) this.open();
}, this.reconnectTimeout);
});
socket.addEventListener('error', () => {
socket.close();
});
setInterval(() => {
if (this.active) {
const interval = new Date().getTime() - this.lastActivity;
if (interval > this.pingInterval) this.send('{}');
}
}, this.pingInterval);
return new Promise(resolve => {
socket.addEventListener('open', () => {
this.connected = true;
resolve();
});
});
}
close() {
this.active = false;
connections.delete(this);
if (!this.socket) return;
this.socket.close();
this.socket = null;
}
send(data) {
if (!this.connected) return;
this.lastActivity = new Date().getTime();
this.socket.send(data);
}
}
class HttpTransport extends Metacom {
async open() {
this.active = true;
this.connected = true;
}
close() {
this.active = false;
this.connected = false;
}
send(data) {
this.lastActivity = new Date().getTime();
fetch(this.url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: data,
}).then(res => {
const { status } = res;
if (status === 200) {
return res.text().then(packet => {
if (packet.error) throw new MetacomError(packet.error);
this.message(packet);
});
}
throw new Error(`Status Code: ${status}`);
});
}
}
Metacom.transport = {
ws: WebsocketTransport,
http: HttpTransport,
};

@@ -110,2 +110,6 @@ 'use strict';

message(data) {
if (data === '{}') {
this.connection.send('{}');
return;
}
let packet;

@@ -112,0 +116,0 @@ try {

{
"name": "metacom",
"version": "0.1.0-alpha.11",
"version": "1.0.0-alpha.0",
"author": "Timur Shemsedinov <timur.shemsedinov@gmail.com>",

@@ -52,3 +52,3 @@ "description": "Communication protocol for Metarhia stack with rpc, events, binary streams, memory and db access",

"eslint-plugin-import": "^2.22.1",
"eslint-plugin-prettier": "^3.2.0",
"eslint-plugin-prettier": "^3.3.0",
"metatests": "^0.7.2",

@@ -55,0 +55,0 @@ "prettier": "^2.2.1"

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc