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

@fleekhq/browser-rpc

Package Overview
Dependencies
Maintainers
8
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@fleekhq/browser-rpc - npm Package Compare versions

Comparing version 1.0.0 to 2.0.0

dist/BackgroundController.d.ts

3

dist/esm/index.js

@@ -0,4 +1,7 @@

export { default as BackgroundController } from './BackgroundController';
export { default as BrowserRPC } from './BrowserRPC';
export { default as ProxyRPC } from './ProxyRPC';
export { default as PortRPC } from './PortRPC';
export { default as RPC } from './RPC';
export { default } from './BrowserRPC';
export * from './types';

28

dist/esm/RPC.js

@@ -43,3 +43,3 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

import { v4 as uuid } from 'uuid';
import validateMessageObject from './schema';
import { validateMessageSchema } from './helpers';
import { JSON_RPC_VERSION, JSON_RPC_ERROR_CODES } from './constants';

@@ -124,25 +124,8 @@ var RPC = (function () {

}
return [2, this._call(handler, (config === null || config === void 0 ? void 0 : config.target) || this.target, timeout, args)];
return [2, this._call(handler, (config === null || config === void 0 ? void 0 : config.target) || this.target, timeout, args || [])];
});
});
};
RPC.prototype.validateMessage = function (message) {
var result = validateMessageObject(message);
if (Array.isArray(result))
return { isValid: false, type: null };
var type = null;
if (message.data.data.method && message.data.data.params) {
type = 'req';
}
else if (message.data.data.hasOwnProperty('result')
|| typeof message.data.data.hasOwnProperty('error')) {
type = 'res';
}
return {
type: type,
isValid: (type === 'req' || type === 'res'),
};
};
RPC.prototype.onMessage = function (eventMessage) {
var _a = this.validateMessage(eventMessage), type = _a.type, isValid = _a.isValid;
var _a = validateMessageSchema(eventMessage), type = _a.type, isValid = _a.isValid;
if (!isValid)

@@ -193,3 +176,6 @@ return;

};
handler.apply(void 0, __spreadArray([callback], message.data.data.params));
handler.apply(void 0, __spreadArray([{
callback: callback,
message: message,
}], message.data.data.params));
}

@@ -196,0 +182,0 @@ catch (error) {

@@ -1,24 +0,20 @@

import Validator from 'fastest-validator';
var validator = new Validator();
var messageSchema = {
target: 'string',
data: {
type: 'object',
props: {
name: 'string',
data: {
type: 'object',
props: {
id: ['string', 'number'],
jsonrpc: { type: 'equal', value: '2.0' },
result: { type: 'any', optional: true },
error: { type: 'object', optional: true },
method: { type: 'string', optional: true },
params: { type: 'array', optional: true, items: 'any' }
},
},
},
},
};
var validateMessageObject = validator.compile(messageSchema);
export default validateMessageObject;
import Joi from 'joi';
var schema = Joi.object({
target: Joi.string().required(),
data: Joi.object({
name: Joi.string().required(),
data: Joi.object({
id: Joi.alternatives().try(Joi.number(), Joi.string()).required(),
jsonrpc: Joi.string().valid('2.0').required(),
result: Joi.any(),
method: Joi.string(),
params: Joi.array().items(Joi.any()),
error: Joi.object({
code: Joi.number().required(),
message: Joi.string().required(),
data: Joi.any(),
}),
}).required(),
}).required(),
});
export default schema;

@@ -0,4 +1,7 @@

export { default as BackgroundController } from './BackgroundController';
export { default as BrowserRPC } from './BrowserRPC';
export { default as ProxyRPC } from './ProxyRPC';
export { default as PortRPC } from './PortRPC';
export { default as RPC } from './RPC';
export { default } from './BrowserRPC';
export * from './types';

@@ -16,5 +16,11 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.default = exports.RPC = exports.BrowserRPC = void 0;
exports.default = exports.RPC = exports.PortRPC = exports.ProxyRPC = exports.BrowserRPC = exports.BackgroundController = void 0;
var BackgroundController_1 = require("./BackgroundController");
Object.defineProperty(exports, "BackgroundController", { enumerable: true, get: function () { return __importDefault(BackgroundController_1).default; } });
var BrowserRPC_1 = require("./BrowserRPC");
Object.defineProperty(exports, "BrowserRPC", { enumerable: true, get: function () { return __importDefault(BrowserRPC_1).default; } });
var ProxyRPC_1 = require("./ProxyRPC");
Object.defineProperty(exports, "ProxyRPC", { enumerable: true, get: function () { return __importDefault(ProxyRPC_1).default; } });
var PortRPC_1 = require("./PortRPC");
Object.defineProperty(exports, "PortRPC", { enumerable: true, get: function () { return __importDefault(PortRPC_1).default; } });
var RPC_1 = require("./RPC");

@@ -21,0 +27,0 @@ Object.defineProperty(exports, "RPC", { enumerable: true, get: function () { return __importDefault(RPC_1).default; } });

@@ -1,7 +0,7 @@

import { Handler, Message, RpcConfig, CallConfigObject } from './types';
import { Handler, Message, RpcConfig, ReqMessage, CallConfigObject } from './types';
export default abstract class RPC {
private name;
private target;
protected name: string;
protected target: string;
private timeout;
private handlers;
protected handlers: Map<string, Handler>;
private calls;

@@ -12,6 +12,5 @@ constructor(config: RpcConfig);

private _call;
call(handler: string, args: any[], config?: CallConfigObject): Promise<any>;
private validateMessage;
call(handler: string, args?: any[] | null, config?: CallConfigObject): Promise<any>;
protected onMessage(eventMessage: any): void;
private onRequestMessage;
protected onRequestMessage(message: ReqMessage): void;
private onResponseMessage;

@@ -18,0 +17,0 @@ stop(): void;

@@ -11,8 +11,5 @@ "use strict";

};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const uuid_1 = require("uuid");
const schema_1 = __importDefault(require("./schema"));
const helpers_1 = require("./helpers");
const constants_1 = require("./constants");

@@ -90,24 +87,7 @@ class RPC {

}
return this._call(handler, (config === null || config === void 0 ? void 0 : config.target) || this.target, timeout, args);
return this._call(handler, (config === null || config === void 0 ? void 0 : config.target) || this.target, timeout, args || []);
});
}
validateMessage(message) {
const result = schema_1.default(message);
if (Array.isArray(result))
return { isValid: false, type: null };
let type = null;
if (message.data.data.method && message.data.data.params) {
type = 'req';
}
else if (message.data.data.hasOwnProperty('result')
|| typeof message.data.data.hasOwnProperty('error')) {
type = 'res';
}
return {
type,
isValid: (type === 'req' || type === 'res'),
};
}
onMessage(eventMessage) {
const { type, isValid } = this.validateMessage(eventMessage);
const { type, isValid } = helpers_1.validateMessageSchema(eventMessage);
if (!isValid)

@@ -157,3 +137,6 @@ return;

};
handler(callback, ...message.data.data.params);
handler({
callback,
message,
}, ...message.data.data.params);
}

@@ -160,0 +143,0 @@ catch (error) {

@@ -1,3 +0,3 @@

/// <reference types="fastest-validator" />
declare const validateMessageObject: (value: any) => true | import("fastest-validator").ValidationError[];
export default validateMessageObject;
import Joi from 'joi';
declare const schema: Joi.ObjectSchema<any>;
export default schema;

@@ -6,25 +6,21 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
const fastest_validator_1 = __importDefault(require("fastest-validator"));
const validator = new fastest_validator_1.default();
const messageSchema = {
target: 'string',
data: {
type: 'object',
props: {
name: 'string',
data: {
type: 'object',
props: {
id: ['string', 'number'],
jsonrpc: { type: 'equal', value: '2.0' },
result: { type: 'any', optional: true },
error: { type: 'object', optional: true },
method: { type: 'string', optional: true },
params: { type: 'array', optional: true, items: 'any' }
},
},
},
},
};
const validateMessageObject = validator.compile(messageSchema);
exports.default = validateMessageObject;
const joi_1 = __importDefault(require("joi"));
const schema = joi_1.default.object({
target: joi_1.default.string().required(),
data: joi_1.default.object({
name: joi_1.default.string().required(),
data: joi_1.default.object({
id: joi_1.default.alternatives().try(joi_1.default.number(), joi_1.default.string()).required(),
jsonrpc: joi_1.default.string().valid('2.0').required(),
result: joi_1.default.any(),
method: joi_1.default.string(),
params: joi_1.default.array().items(joi_1.default.any()),
error: joi_1.default.object({
code: joi_1.default.number().required(),
message: joi_1.default.string().required(),
data: joi_1.default.any(),
}),
}).required(),
}).required(),
});
exports.default = schema;

@@ -41,3 +41,7 @@ /// <reference types="node" />

export declare type CallBackHandler = (err: null | ErrorRes, res?: null | any) => void;
export declare type Handler = (cb: CallBackHandler, ...args: any[]) => any;
export declare type HandlerProps = {
callback: CallBackHandler;
message: ReqMessage;
};
export declare type Handler = (props: HandlerProps, ...args: any[]) => any;
export declare type CallConfigObject = {

@@ -44,0 +48,0 @@ timeout: number;

{
"name": "@fleekhq/browser-rpc",
"version": "1.0.0",
"version": "2.0.0",
"main": "dist/index.js",

@@ -35,2 +35,3 @@ "module": "dist/esm/index.js",

"@babel/core": "^7.13.16",
"@types/chrome": "^0.0.135",
"@types/jest": "^26.0.22",

@@ -56,5 +57,6 @@ "@types/react-test-renderer": "^17.0.1",

"dependencies": {
"fastest-validator": "^1.10.1",
"extensionizer": "^1.0.1",
"joi": "^17.4.0",
"uuid": "^8.3.2"
}
}

@@ -73,6 +73,7 @@ # BrowserRPC

*/
server.exposeHandler('sum', (cb, val1, val2) => {
server.exposeHandler('sum', ({ callback, message }, val1, val2) => {
console.log(message); // Full message
const result = val1 + val2;
cb(null, result);
callback(null, result);
});

@@ -137,1 +138,225 @@

https://www.jsonrpc.org/specification#error_object
# ProxyRPC
This class is meant to be used on browser extensions content script. It allow proxy all the calls that arn't handled by the instance to the background script controller (see BackgroundController class). Same way, all the responses received from the BackgroundScript controller are redirected to the requester. (This class implements window.postMessage for the communication between webpages and content script, and browser.runtime.port for the cummunication between the content script and the background script).
```js
import { ProxyRPC } from '@fleekhq/browser-rpc';
// Create a new ProxyRPC instance
// name property is injected as part of the responses messages to identify who processed the response
// name property is also usid as the PORT name for the communication with the background script
// target property specifies who can call methods on this new instance and receive the responses. If a different target try to call methods on this instance, the calls are ignored.
const server = new ProxyRPC(window, {
name: 'rpc-server',
target: 'browser-client',
});
server.exposeHandler('sum', ({ callback, message }, val1, val2) => {
console.log(message); // Full message
const result = val1 + val2;
callback(null, result);
});
server.start();
```
now if you try to call the method `sum` from your client, you'll receive the response from the ProxyRPC instance, but if you try to call a method not defined into the Proxy, the call is going to be redirected to the background script using `Port` communication in order to be processed. If the method also doesn't exists in the background script controller, the call is rejected with an error.
# BackgroundController
This class implements Port communication between the content script and other resources with access to the Port API. Similar to the BorwserRPC and ProxyRPC, you can expose handlers to process the requests.
```js
import { BackgroundController } from '@fleekhq/browser-rpc';
const backgroundController = new BackgroundController({
name: 'bg-script',
trustedSources: [
'rpc-server',
'another-port-source',
],
});
backgroundController.exposeController('hello', (opts, name) => {
const { message, sender, callback } = opts;
console.log(message); // Full message
console.log(sender); // Sender information: port id, port name, port
callback(null, `hello ${name}!!!`);
});
backgroundController.exposeController('remoteCall', (opts, myNumber, callId, portId) => {
const { callback } = opts;
callback(null, myNumber);
callback(null, myNumber, [{ portId, callId }]);
});
backgroundController.start();
```
### BackgroundController.exposeHandler(name: string, handler: ControllerHandler): void
Add a new handler to the instance
#### handler: (opts, ...args) => void
- opts (object):
- sender: Sender information object
- id: port id assigned by the instance
- name: port name
- port: port
- ports: Map with all the ports handled by the instance: Map<id: number, port>;
- message: The full message
- callback: callback used to send a response back to the sender port or to another port
- err: ErrorRes type if you want to send an error or null if is not required
- res: any, the respose that you want to send back to the sender
- targetPorts: optional, this is an array of objects with a portId and a callId (message call id). If you want to send a response to a different port, you have to pass this information, otherwise the callback is going to respond directly to the sender
- portId: number, the target port id
- callId: number | string, the call id
```typescript
type HandlerProps = {
sender: {
id: number,
name: string,
port: chrome.runtime.Port,
},
ports: Map<number, chrome.runtime.Port>,
message: Message,
callback: (
err: ErrorRes | null,
res: any,
targetPorts?: {
portId: number,
callId: CallID,
}[],
) => void,
};
type ControllerHandler = (
props: HandlerProps,
...args: any[]
) => void;
```
# PortRPC
Similar to BrowserRPC, this one is used to send requests to the BackgroundController using Port communication
```js
import { PortRPC } from '@fleekhq/browser-rpc';
const client = new PortRPC({
name: 'browser-port-rpc',
target: 'bg-script',
timeout: 10000,
});
client.start();
client
.call('sum', [1, 2])
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(error);
});
```
# Full Example
Here is a full implementation of BrowserRPC, ProxyRPC, PortRPC and BackgroundController
webpage
```js
import { BrowserRPC } from '@fleekhq/browser-rpc';
const client = new BrowserRPC(window, {
name: 'browser-client',
target: 'rpc-server',
timeout: 10000,
});
client.start();
// This call is handled directly by ProxyRPC since the handler is definded
client
.call('sum', [1, 2])
.then((result) => {
console.log(result); // result: 3. response processed by ProxyRPC handler
});
// In this case the method isn't defined on ProxyRPC so the call is redirected to the Background controller
client
.call('hello', ['Jhon'])
.then((result) => {
console.log(result); // result: "hello John!!!". response processed by BackgroundController handler
});
```
content script
```js
import { ProxyRPC } from '@fleekhq/browser-rpc';
const server = new ProxyRPC(window, {
name: 'rpc-server',
target: 'browser-client',
});
server.exposeHandler('sum', ({ callback, message }, val1, val2) => {
console.log(message); // Full message
const result = val1 + val2;
callback(null, result);
});
server.start();
```
background script
```js
import { BackgroundController } from '@fleekhq/browser-rpc';
const backgroundController = new BackgroundController({
name: 'bg-script',
trustedSources: [
'rpc-server',
'another-port-source',
],
});
backgroundController.exposeController('hello', (opts, name) => {
const { message, sender, callback } = opts;
console.log(message); // Full message
console.log(sender); // Sender information: port id, port name, port
callback(null, `hello ${name}!!!`);
});
backgroundController.exposeController('remoteCall', (opts, myNumber, callId, portId) => {
const { callback } = opts;
callback(null, myNumber);
// here you can use another callID and portID to send a response to a different port if you want
callback(null, myNumber, [{ portId, callId }]);
});
backgroundController.start();
```

@@ -0,4 +1,7 @@

export { default as BackgroundController } from './BackgroundController';
export { default as BrowserRPC } from './BrowserRPC';
export { default as ProxyRPC } from './ProxyRPC';
export { default as PortRPC } from './PortRPC';
export { default as RPC } from './RPC';
export { default } from './BrowserRPC';
export * from './types';
import { v4 as uuid } from 'uuid';
import validateMessageObject from './schema';
import { validateMessageSchema } from './helpers';
import { JSON_RPC_VERSION, JSON_RPC_ERROR_CODES } from './constants';

@@ -16,10 +16,9 @@ import {

CallConfigObject,
ValidationMessageObject,
} from './types';
export default abstract class RPC {
private name: string;
private target: string;
protected name: string;
protected target: string;
private timeout: number = 5000;
private handlers = new Map<string, Handler>();
protected handlers = new Map<string, Handler>();
private calls = new Map<CallID, ResolverObject>();

@@ -98,3 +97,3 @@

async call(handler: string, args: any[], config?: CallConfigObject): Promise<any> {
async call(handler: string, args?: any[] | null, config?: CallConfigObject): Promise<any> {
let timeout = this.timeout;

@@ -110,30 +109,8 @@

timeout,
args,
args || [],
);
}
private validateMessage(message: any): ValidationMessageObject {
const result = validateMessageObject(message);
if (Array.isArray(result)) return { isValid: false, type: null };
let type: ValidationMessageObject['type'] = null;
if (message.data.data.method && message.data.data.params) {
type = 'req';
} else if (
message.data.data.hasOwnProperty('result')
|| typeof message.data.data.hasOwnProperty('error')
) {
type = 'res';
}
return {
type,
isValid: (type === 'req' || type === 'res'),
};
}
protected onMessage(eventMessage: any): void {
const { type, isValid } = this.validateMessage(eventMessage);
const { type, isValid } = validateMessageSchema(eventMessage);
if (!isValid) return;

@@ -156,3 +133,3 @@

private onRequestMessage(message: ReqMessage): void {
protected onRequestMessage(message: ReqMessage): void {
const resMessage: ResMessage = {

@@ -193,3 +170,6 @@ target: this.target,

handler(callback, ...message.data.data.params);
handler({
callback,
message,
}, ...message.data.data.params);
} catch (error) {

@@ -196,0 +176,0 @@ if (resMessage.data.data.hasOwnProperty('error')) return;

@@ -1,27 +0,22 @@

import Validator, { ValidationSchema } from 'fastest-validator';
import Joi from 'joi';
const validator = new Validator();
const schema = Joi.object({
target: Joi.string().required(),
data: Joi.object({
name: Joi.string().required(),
data: Joi.object({
id: Joi.alternatives().try(Joi.number(), Joi.string()).required(),
jsonrpc: Joi.string().valid('2.0').required(),
result: Joi.any(),
method: Joi.string(),
params: Joi.array().items(Joi.any()),
error: Joi.object({
code: Joi.number().required(),
message: Joi.string().required(),
data: Joi.any(),
}),
}).required(),
}).required(),
});
const messageSchema: ValidationSchema = {
target: 'string',
data: {
type: 'object',
props: {
name: 'string',
data: {
type: 'object',
props: {
id: ['string', 'number'],
jsonrpc: { type: 'equal', value: '2.0' },
result: { type: 'any', optional: true },
error: { type: 'object', optional: true },
method: { type: 'string', optional: true },
params: { type: 'array', optional: true, items: 'any' }
},
},
},
},
};
const validateMessageObject = validator.compile(messageSchema);
export default validateMessageObject;
export default schema;

@@ -50,4 +50,10 @@ export type CallID = string | number;

export type CallBackHandler = (err: null | ErrorRes, res?: null | any) => void;
export type Handler = (cb: CallBackHandler, ...args: any[]) => any;
export type HandlerProps = {
callback: CallBackHandler,
message: ReqMessage,
};
export type Handler = (props: HandlerProps, ...args: any[]) => any;
export type CallConfigObject = {

@@ -54,0 +60,0 @@ timeout: number;

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