*Major refactor to swarm based DHT design, switch to protomux-rpc, merging core plugin into main daemon, and creating a new internal rpc plugin for cache and broadcast requests
ci/woodpecker/push/woodpecker Pipeline failed Details

This commit is contained in:
Derrick Hammer 2022-11-26 02:59:07 -05:00
parent b7a5d3a99f
commit 00fc999169
Signed by: pcfreak30
GPG Key ID: C997C339BE476FF2
16 changed files with 896 additions and 393 deletions

View File

@ -15,8 +15,6 @@ contents:
- src: ./systemd.service
dst: /etc/systemd/system/lumeweb-relay.service
type: config
depends:
- lumeweb-relay-plugin-core
scripts:
postinstall: ./pkg/scripts/postinstall.sh
preremove: ./pkg/scripts/preremove.sh

View File

@ -21,6 +21,7 @@
"@hyperswarm/dht": "^6.0.1",
"@hyperswarm/dht-relay": "^0.3.0",
"@lumeweb/cfg": "https://github.com/LumeWeb/bcfg.git",
"@lumeweb/dht-cache": "https://git.lumeweb.com/LumeWeb/dht-cache.git",
"@lumeweb/kernel-utils": "https://github.com/LumeWeb/kernel-utils.git",
"@lumeweb/pokt-rpc-endpoints": "https://github.com/LumeWeb/pokt-rpc-endpoints.git",
"@skynetlabs/skynet-nodejs": "^2.6.0",
@ -31,11 +32,14 @@
"@types/ws": "^8.5.3",
"ajv": "^8.11.0",
"async-mutex": "^0.3.2",
"b4a": "^1.6.1",
"compact-encoding": "^2.11.0",
"date-fns": "^2.28.0",
"dotenv": "^16.0.1",
"ethers": "^5.6.9",
"express": "^4.18.1",
"fetch-blob": "https://github.com/LumeWeb/fetch-blob.git",
"hyperswarm": "^3.0.4",
"json-stable-stringify": "^1.0.1",
"libskynet": "https://github.com/LumeWeb/libskynet.git",
"libskynetnode": "https://github.com/LumeWeb/libskynetnode.git",
@ -45,13 +49,18 @@
"node-cache": "^5.1.2",
"node-cron": "^3.0.1",
"node-fetch": "2",
"ordered-json": "^0.1.1",
"promise-retry": "^2.0.1",
"protomux": "^3.4.0",
"protomux-rpc": "^1.3.0",
"random-access-memory": "^4.1.0",
"random-key": "^0.3.2",
"slugify": "^1.6.5"
"slugify": "^1.6.5",
"sodium-universal": "^3.1.0"
},
"devDependencies": {
"@lumeweb/relay-types": "https://github.com/LumeWeb/relay-types.git",
"@lumeweb/relay-types": "https://git.lumeweb.com/LumeWeb/relay-types.git",
"@types/b4a": "^1.6.0",
"@types/express": "^4.17.13",
"@types/minimatch": "^3.0.5",
"@types/node-fetch": "^2.6.2",

View File

@ -2,7 +2,7 @@ import express, { Express } from "express";
import http from "http";
import { AddressInfo } from "net";
import log from "loglevel";
import { getKeyPair } from "./dht";
import { getKeyPair } from "./swarm.js";
let app: Express;
let router = express.Router();

View File

@ -1,5 +1,5 @@
import cron from "node-cron";
import { get as getDHT } from "./dht.js";
import { get as getSwarm } from "./swarm.js";
import { Buffer } from "buffer";
import { pack } from "msgpackr";
import config from "../config.js";
@ -36,19 +36,19 @@ async function ipUpdate() {
}
export async function start() {
const dht = (await getDHT()) as any;
const swarm = (await getSwarm()) as any;
await ipUpdate();
await overwriteRegistryEntry(
dht.defaultKeyPair,
swarm.dht.defaultKeyPair,
hashDataKey(REGISTRY_NODE_KEY),
pack(`${config.str("domain")}:${config.uint("port")}`)
);
log.info(
"Relay Identity is",
Buffer.from(dht.defaultKeyPair.publicKey).toString("hex")
Buffer.from(swarm.dht.defaultKeyPair.publicKey).toString("hex")
);
cron.schedule("0 * * * *", ipUpdate);

View File

@ -1,5 +1,5 @@
import config from "../config.js";
import { getRpcServer } from "../rpc/server.js";
import { getRpcServer } from "./rpc/server.js";
import type { PluginAPI, RPCMethod, Plugin } from "@lumeweb/relay-types";
import slugify from "slugify";
import * as fs from "fs";
@ -22,6 +22,8 @@ import {
overwriteIndependentFileSmall,
} from "../lib/file";
import { setDnsProvider } from "./dns";
import pluginRpc from "./plugins/rpc";
import pluginCore from "./plugins/core";
let pluginApi: PluginApiManager;
@ -57,6 +59,11 @@ export class PluginApiManager {
} catch (e) {
throw e;
}
return this.loadPluginInstance(plugin);
}
public async loadPluginInstance(plugin: Plugin): Promise<Plugin> {
if ("default" in plugin) {
plugin = plugin?.default as Plugin;
}
@ -81,8 +88,7 @@ export class PluginApiManager {
getRpcServer().registerMethod(pluginName, methodName, method);
},
loadPlugin: getPluginAPI().loadPlugin.bind(getPluginAPI()),
getMethods: getRpcServer().getMethods.bind(getRpcServer()),
getRpcServer,
ssl: {
setContext: setSslContext,
getContext: getSslContext,
@ -120,7 +126,12 @@ export function getPluginAPI(): PluginApiManager {
}
export async function loadPlugins() {
const api = await getPluginAPI();
api.loadPluginInstance(pluginCore);
api.loadPluginInstance(pluginRpc);
for (const plugin of [...new Set(config.array("plugins", []))] as []) {
await getPluginAPI().loadPlugin(plugin);
api.loadPlugin(plugin);
}
}

View File

@ -0,0 +1,23 @@
import { Plugin, PluginAPI } from "@lumeweb/relay-types";
import { getRpcServer } from "../rpc/server";
const plugin: Plugin = {
name: "core",
async plugin(api: PluginAPI): Promise<void> {
api.registerMethod("ping", {
cacheable: false,
async handler(): Promise<any> {
return "pong";
},
});
api.registerMethod("get_methods", {
cacheable: false,
async handler(): Promise<any> {
return api.getRpcServer().getMethods();
},
});
},
};
export default plugin;

132
src/modules/plugins/rpc.ts Normal file
View File

@ -0,0 +1,132 @@
import { getRpcServer } from "../rpc/server";
import {
Plugin,
PluginAPI,
RPCBroadcastRequest,
RPCBroadcastResponse,
RPCClearCacheRequest,
RPCClearCacheResponse,
RPCClearCacheResponseRelayList,
RPCRequest,
RPCResponse,
} from "@lumeweb/relay-types";
import { getRpcByPeer } from "../rpc";
async function broadcastRequest(
request: RPCRequest,
relays: string[]
): Promise<Map<string, Promise<any>>> {
const makeRequest = async (relay: string) => {
const rpc = await getRpcByPeer(relay);
return rpc.request(`${request.module}.${request.method}`, request.data);
};
let relayMap = new Map<string, Promise<any>>();
for (const relay of relays) {
relayMap.set(relay, makeRequest(relay));
}
await Promise.allSettled([...relays.values()]);
return relayMap;
}
const plugin: Plugin = {
name: "rpc",
async plugin(api: PluginAPI): Promise<void> {
api.registerMethod("get_cached_item", {
cacheable: false,
async handler(req: string): Promise<RPCResponse> {
if (typeof req !== "string") {
throw new Error("item must be a string");
}
const cache = getRpcServer().cache.data;
if (!Object.keys(cache).includes(req)) {
throw new Error("item does not exist");
}
return {
data: true,
...cache[req]?.value,
signature: cache[req]?.signature,
};
},
});
api.registerMethod("clear_cached_item", {
cacheable: false,
async handler(req: RPCClearCacheRequest): Promise<RPCClearCacheResponse> {
if (req?.relays?.length) {
let resp = await broadcastRequest(
{
module: "rpc",
method: "clear_cached_item",
data: req.request,
},
req?.relays
);
let results: RPCClearCacheResponse = {
relays: {},
data: true,
signedField: "relays",
};
for (const relay in resp) {
let ret: RPCClearCacheResponse;
try {
ret = await resp.get(relay);
} catch (e: any) {
(results.relays as RPCClearCacheResponseRelayList)[relay] = {
error: e.message,
};
}
}
return results;
}
try {
api.getRpcServer().cache.deleteItem(req.request);
} catch (e: any) {
throw e;
}
return {
data: true,
};
},
});
api.registerMethod("broadcast_request", {
cacheable: false,
async handler(req: RPCBroadcastRequest): Promise<RPCBroadcastResponse> {
if (!req?.request) {
throw new Error("request required");
}
if (!req?.relays?.length) {
throw new Error("relays required");
}
let resp = await broadcastRequest(req.request, req.relays);
const result: RPCBroadcastResponse = {
relays: {},
data: true,
signedField: "relays",
};
for (const relay in resp) {
let ret: RPCClearCacheResponse;
try {
ret = await resp.get(relay);
} catch (e: any) {
result.relays[relay] = { error: e.message };
}
}
return result;
},
});
},
};
export default plugin;

View File

@ -8,7 +8,7 @@ import express, { Express } from "express";
import config from "../config.js";
import * as http from "http";
import * as https from "https";
import { get as getDHT } from "./dht.js";
import { get as getSwarm } from "./swarm.js";
import WS from "ws";
// @ts-ignore
import log from "loglevel";
@ -20,7 +20,7 @@ import { getSslContext } from "./ssl.js";
export async function start() {
const relayPort = config.uint("port");
const dht = await getDHT();
const dht = await getSwarm();
const statusCodeServer = http.createServer(function (req, res) {
// @ts-ignore

View File

@ -5,12 +5,35 @@ import config from "../config.js";
import { errorExit } from "../lib/error.js";
// @ts-ignore
import stringify from "json-stable-stringify";
import { getRpcServer } from "../rpc/server.js";
import { getRpcServer, RPC_PROTOCOL_SYMBOL } from "./rpc/server.js";
import { get as getSwarm, SecretStream } from "./swarm.js";
export async function start() {
if (!config.str("pocket-app-id") || !config.str("pocket-app-key")) {
errorExit("Please set pocket-app-id and pocket-app-key config options.");
}
getRpcServer();
(await getSwarm()).on("connection", (stream: SecretStream) =>
getRpcServer().setup(stream)
);
}
export async function getRpcByPeer(peer: string) {
const swarm = await getSwarm();
if (swarm._allConnections.has(peer)) {
return swarm._allConnections.get(peer)[RPC_PROTOCOL_SYMBOL];
}
return new Promise((resolve) => {
const listener = () => {};
swarm.on("connection", (peer: any, info: any) => {
if (info.publicKey.toString("hex") !== peer) {
return;
}
swarm.removeListener("connection", listener);
resolve(peer[RPC_PROTOCOL_SYMBOL]);
});
});
}

135
src/modules/rpc/cache.ts Normal file
View File

@ -0,0 +1,135 @@
import EventEmitter from "events";
import DHTCache from "@lumeweb/dht-cache";
import {
RPCCacheData,
RPCCacheItem,
RPCRequest,
RPCResponse,
} from "@lumeweb/relay-types";
import { getRpcByPeer } from "../rpc";
import b4a from "b4a";
import { get as getSwarm } from "../swarm";
import { RPCServer } from "./server";
// @ts-ignore
import orderedJSON from "ordered-json";
// @ts-ignore
import crypto from "hypercore-crypto";
export class RPCCache extends EventEmitter {
private dhtCache?: DHTCache;
private server: RPCServer;
private _swarm?: any;
get swarm(): any {
return this._swarm;
}
private _data: RPCCacheData = {};
get data(): RPCCacheData {
return this._data;
}
constructor(server: RPCServer) {
super();
this.server = server;
this.init();
}
public async getNodeQuery(
node: string,
queryHash: string
): Promise<boolean | RPCResponse> {
if (!this.dhtCache?.peerHasItem(node, queryHash)) {
return false;
}
const rpc = await getRpcByPeer(node);
let response;
try {
response = rpc.request("rpc.get_cached_item", queryHash) as RPCCacheItem;
} catch (e: any) {
return false;
}
if (!this.verifyResponse(b4a.from(node, "hex") as Buffer, response)) {
return false;
}
return { ...response?.value };
}
public signResponse(item: RPCCacheItem): string {
const field = item.value.signedField || "data";
const updated = item.value.updated;
// @ts-ignore
const data = item.value[field];
const json = orderedJSON.stringify(data);
return this.server.signData(`${updated}${json}`);
}
public verifyResponse(pubkey: Buffer, item: RPCCacheItem): boolean | Buffer {
const field = item.value.signedField || "data";
const updated = item.value.updated;
// @ts-ignore
const data = item.value[field];
const json = orderedJSON.stringify(data);
try {
if (
!crypto.verify(
Buffer.from(`${updated}${json}`),
Buffer.from(item?.signature as string, "hex"),
pubkey
)
) {
return false;
}
} catch {
return false;
}
return true;
}
public addItem(query: RPCRequest, response: RPCResponse) {
const queryHash = RPCServer.hashQuery(query);
const clonedResponse = { ...response };
clonedResponse.updated = Date.now();
const item = {
value: clonedResponse,
signature: "",
};
item.signature = this.signResponse(item);
this._data[queryHash] = item;
}
public deleteItem(queryHash: string): boolean {
const cache = this.dhtCache?.cache;
if (!cache?.includes(queryHash)) {
throw Error("item does not exist");
}
this.dhtCache?.removeItem(queryHash);
delete this._data[queryHash];
return true;
}
private async init() {
this.dhtCache = new DHTCache(await getSwarm(), {
protocol: "lumeweb.rpccache",
});
this._swarm = await getSwarm();
}
}

247
src/modules/rpc/server.ts Normal file
View File

@ -0,0 +1,247 @@
import {
RPCCacheData,
RPCCacheItem,
RPCMethod,
RPCRequest,
RPCResponse,
} from "@lumeweb/relay-types";
import EventEmitter from "events";
// @ts-ignore
import ProtomuxRPC from "protomux-rpc";
import b4a from "b4a";
import { get as getSwarm, SecretStream } from "../swarm";
// @ts-ignore
import c from "compact-encoding";
import DHTCache from "@lumeweb/dht-cache";
// @ts-ignore
import crypto from "hypercore-crypto";
// @ts-ignore
import orderedJSON from "ordered-json";
import { Mutex } from "async-mutex";
import { RPCCache } from "./cache";
const sodium = require("sodium-universal");
let server: RPCServer;
const RPC_PROTOCOL_ID = b4a.from("lumeweb");
export const RPC_PROTOCOL_SYMBOL = Symbol.for(RPC_PROTOCOL_ID.toString());
export function getRpcServer(): RPCServer {
if (!server) {
server = new RPCServer();
}
return server as RPCServer;
}
export class RPCServer extends EventEmitter {
private _modules: Map<string, Map<string, RPCMethod>> = new Map<
string,
Map<string, RPCMethod>
>();
private pendingRequests: Map<string, Mutex> = new Map<string, Mutex>();
private _cache: RPCCache = new RPCCache(this);
get cache(): RPCCache {
return this._cache;
}
public static hashQuery(query: RPCRequest): string {
const clonedQuery: RPCRequest = {
module: query.module,
method: query.method,
data: query.data,
};
const queryHash = Buffer.allocUnsafe(32);
sodium.crypto_generichash(
queryHash,
Buffer.from(orderedJSON.stringify(clonedQuery))
);
return queryHash.toString("hex");
}
public registerMethod(
moduleName: string,
methodName: string,
options: RPCMethod
): void {
const module = this._modules.get(moduleName);
if (module && module.get(methodName)) {
throw new Error(
`Method ${methodName} already exists for module ${moduleName}`
);
}
let methodMap: Map<string, RPCMethod> | null = null;
if (!module) {
methodMap = new Map<string, RPCMethod>();
this._modules.set(moduleName, methodMap);
}
if (!methodMap) {
methodMap = this._modules.get(moduleName) as Map<string, RPCMethod>;
}
methodMap.set(methodName, options);
}
public getMethods(): string[] {
const methods = [];
for (const module of this._modules.keys()) {
for (const method of (
this._modules.get(module) as Map<string, RPCMethod>
).keys()) {
methods.push(`${module}.${method}`);
}
}
return methods;
}
public setup(stream: SecretStream) {
const existing = stream[RPC_PROTOCOL_SYMBOL];
if (existing) return existing;
const options = {
id: RPC_PROTOCOL_ID,
valueEncoding: c.json,
};
const rpc = new ProtomuxRPC(stream, options);
stream[RPC_PROTOCOL_SYMBOL] = rpc;
for (const module of this._modules.keys()) {
for (const method of (
this._modules.get(module) as Map<string, RPCMethod>
).keys()) {
rpc.respond(`${module}.${method}`, {}, (data: any) =>
this.handleRequest({ module, method, data })
);
}
}
return rpc;
}
public signData(data: any): string {
let raw = data;
if (typeof data !== "string") {
raw = orderedJSON.stringify(data);
}
return crypto
.sign(Buffer.from(raw, this._cache.swarm.keyPair.privateKey))
.toString("hex");
}
private async handleRequest(request: RPCRequest) {
let lockedRequest = await this.waitOnRequestLock(request);
if (lockedRequest) {
return lockedRequest;
}
let cachedRequest = this.getCachedRequest(request) as RPCCacheItem;
if (cachedRequest) {
return cachedRequest.value;
}
let method = this.getMethodByRequest(request) as RPCMethod;
let ret;
let error;
try {
ret = (await method.handler(request.data)) as RPCResponse | any;
} catch (e) {
error = e;
}
if (error) {
throw error;
}
let rpcResult: RPCResponse = {};
if (ret?.data) {
rpcResult = { ...ret };
const field = rpcResult?.signedField || "data";
// @ts-ignore
rpcResult.signature = this.signData(rpcResult[field]);
} else {
rpcResult = {
data: ret,
signature: this.signData(ret),
};
}
if (method.cacheable) {
this.cache.addItem(request, rpcResult);
}
return rpcResult;
}
private getCachedRequest(request: RPCRequest): RPCCacheItem | boolean {
const req = RPCServer.hashQuery(request);
if (RPCServer.hashQuery(request) in this._cache.data) {
this._cache.data[req];
}
return false;
}
private getMethodByRequest(request: RPCRequest): Error | RPCMethod {
return this.getMethod(request.module, request.method);
}
private getMethod(moduleName: string, method: string): Error | RPCMethod {
let item: any = this._modules.get(moduleName);
if (!item) {
return new Error("INVALID_MODULE");
}
item = item.get(method);
if (!item) {
return new Error("INVALID_METHOD");
}
return item;
}
private async waitOnRequestLock(
request: RPCRequest
): Promise<RPCCacheItem | undefined> {
let method = this.getMethodByRequest(request) as RPCMethod;
if (!method.cacheable) {
return;
}
const reqId = RPCServer.hashQuery(request);
let lock: Mutex = this.pendingRequests.get(reqId) as Mutex;
const lockExists = !!lock;
if (!lockExists) {
lock = new Mutex();
this.pendingRequests.set(reqId, lock);
}
if (lock.isLocked()) {
await lock.waitForUnlock();
if (reqId in this._cache.data) {
return this._cache.data[reqId] as RPCCacheItem;
}
}
await lock.acquire();
}
}

View File

@ -1,6 +1,8 @@
//const require = createRequire(import.meta.url);
//import { createRequire } from "module";
// @ts-ignore
import Hyperswarm from "hyperswarm";
// @ts-ignore
import DHT from "@hyperswarm/dht";
import config from "../config.js";
@ -11,13 +13,15 @@ import {
validSeedPhrase,
} from "libskynet";
let node: {
ready: () => any;
createServer: () => any;
defaultKeyPair: any;
on: any;
};
let server: any;
// @ts-ignore
import sodium from "sodium-universal";
import b4a from "b4a";
const LUMEWEB = b4a.from("lumeweb");
export type SecretStream = any;
let node: Hyperswarm;
export function getKeyPair() {
const seed = config.str("seed");
@ -33,26 +37,22 @@ export function getKeyPair() {
async function start() {
const keyPair = getKeyPair();
node = new DHT({ keyPair });
node = new Hyperswarm({ keyPair, dht: new DHT({ keyPair }) });
const topic = b4a.allocUnsafe(32);
sodium.crypto_generichash(topic, LUMEWEB);
await node.ready();
server = node.createServer();
await server.listen(keyPair);
// @ts-ignore
await node.dht.ready();
await node.listen();
node.join(topic);
return node;
}
export async function get(
ret: "server" | "dht" = "dht"
): Promise<typeof server | typeof node> {
export async function get(): Promise<Hyperswarm> {
if (!node) {
await start();
}
if (ret == "server") {
return server;
}
return node;
}

View File

@ -1,90 +0,0 @@
import type {
RPCRequest,
RPCResponse,
RPCStreamHandler,
StreamFileResponse,
} from "@lumeweb/relay-types";
import { pack, unpack } from "msgpackr";
import log from "loglevel";
import { getRpcServer } from "./server";
interface CancelRequest {
cancel: true;
}
export default class RPCConnection {
private _socket: any;
private _canceled = false;
constructor(socket: any) {
this._socket = socket;
socket.rawStream._ondestroy = () => false;
socket.once("data", this.checkRpc.bind(this));
}
private async checkRpc(data: Buffer) {
if (data.toString() === "rpc") {
this._socket.once("data", this.processRequest);
this._socket.on("data", this.listenForCancel);
}
}
private async listenForCancel(data: Buffer) {
let request: any;
try {
request = unpack(data) as CancelRequest;
} catch (e) {
return;
}
if (request.cancel) {
this._canceled = true;
}
}
private async processRequest(data: Buffer) {
let request: RPCRequest;
try {
request = unpack(data) as RPCRequest;
} catch (e) {
return;
}
const that = this as any;
let response;
const handleStream: RPCStreamHandler = async (
stream: AsyncIterable<Uint8Array>
): Promise<RPCResponse> => {
const emptyData = Uint8Array.from([]);
const streamResp = {
data: {
data: emptyData,
done: false,
} as StreamFileResponse,
};
for await (const chunk of stream) {
if (this._canceled) {
break;
}
streamResp.data.data = chunk as unknown as Uint8Array;
await new Promise((resolve) => setTimeout(resolve, 15));
that.write(pack(streamResp));
}
streamResp.data.data = emptyData;
streamResp.data.done = true;
return streamResp;
};
try {
response = await getRpcServer().handleRequest(request, handleStream);
} catch (error) {
log.trace(error);
that.write(pack({ error }));
that.end();
return;
}
if (!this._canceled) {
that.write(pack(response));
}
that.end();
}
}

View File

@ -1,238 +0,0 @@
import {
RPCMethod,
RPCRequest,
RPCResponse,
RPCStreamHandler,
} from "@lumeweb/relay-types";
import NodeCache from "node-cache";
import { get as getDHT } from "../modules/dht.js";
import { Mutex } from "async-mutex";
import crypto from "crypto";
// @ts-ignore
import stringify from "json-stable-stringify";
import Ajv from "ajv";
import RPCConnection from "./connection.js";
import { RPC_REQUEST_SCHEMA } from "../types.js";
const ajv = new Ajv({ allowUnionTypes: true });
const validateRpcRequest = ajv.compile(RPC_REQUEST_SCHEMA);
let server: RPCServer;
export function getRpcServer(): RPCServer {
if (!server) {
server = new RPCServer();
}
return server as RPCServer;
}
export class RPCServer {
private methods = new Map<string, Map<string, RPCMethod>>();
private pendingRequests = new NodeCache();
private processedRequests = new NodeCache({
stdTTL: 60 * 60 * 12,
});
constructor() {
this.init();
}
public registerMethod(
moduleName: string,
methodName: string,
options: RPCMethod
): void {
const module = this.methods.get(moduleName);
if (module && module.get(methodName)) {
throw new Error(
`Method ${methodName} already exists for module ${moduleName}`
);
}
let methodMap: Map<string, RPCMethod> | null = null;
if (!module) {
methodMap = new Map<string, RPCMethod>();
this.methods.set(moduleName, methodMap);
}
if (!methodMap) {
methodMap = this.methods.get(moduleName) as Map<string, RPCMethod>;
}
methodMap.set(methodName, options);
}
public getMethods(): string[] {
const methods = [];
for (const module of this.methods.keys()) {
for (const method of (
this.methods.get(module) as Map<string, RPCMethod>
).keys()) {
methods.push(`${module}.${method}`);
}
}
return methods;
}
private async init(): Promise<void> {
(await getDHT("server")).on(
"connection",
(socket: any) => new RPCConnection(socket)
);
}
public async handleRequest(
request: RPCRequest,
streamHandler: RPCStreamHandler
): Promise<RPCResponse> {
let valid = this.verifyRequest(request);
if (valid instanceof Error) {
return {
error: valid.message,
};
}
let lockedRequest = await this.waitOnRequestLock(request);
if (lockedRequest) {
return lockedRequest;
}
let cachedRequest = this.getCachedRequest(request);
if (cachedRequest) {
return cachedRequest;
}
let method = this.getMethodByRequest(request) as RPCMethod;
let result;
let isStream: AsyncIterable<Uint8Array> | boolean = false;
const flagIsStream = (stream: AsyncIterable<Uint8Array>) => {
isStream = stream;
};
try {
result = await method.handler(request, flagIsStream);
} catch (e) {
return {
error: (e as Error).message,
};
}
if (isStream) {
result = await streamHandler(isStream);
} else {
if (result && !result.error && !("data" in result)) {
result = { data: result };
}
}
result = result as RPCResponse;
cachedRequest = this.getCachedRequest(request);
if (!cachedRequest && !isStream && method.cacheable) {
this.cacheRequest(request, result);
}
return result;
}
private async waitOnRequestLock(request: RPCRequest) {
let method = this.getMethodByRequest(request) as RPCMethod;
if (!method.cacheable) {
return;
}
const reqId = RPCServer.getRequestId(request);
let lock: Mutex = this.pendingRequests.get(reqId) as Mutex;
const lockExists = !!lock;
if (!lockExists) {
lock = new Mutex();
this.pendingRequests.set(reqId, lock);
}
if (lock.isLocked()) {
await lock.waitForUnlock();
return this.processedRequests.get(reqId) as RPCResponse;
}
await lock.acquire();
}
private getCachedRequest(request: RPCRequest): RPCResponse | undefined {
let method = this.getMethodByRequest(request) as RPCMethod;
if (!method.cacheable) {
return;
}
const reqId = RPCServer.getRequestId(request);
if (!request.bypassCache && this.processedRequests.get(reqId)) {
return this.processedRequests.get(reqId) as RPCResponse;
}
}
private cacheRequest(request: RPCRequest, response: RPCResponse): void {
const reqId = RPCServer.getRequestId(request);
response.updated = Date.now();
this.processedRequests.set(reqId, response);
}
private static hash(data: string): string {
return crypto.createHash("sha256").update(data).digest("hex");
}
private static getRequestId(request: RPCRequest) {
const clonedRequest = Object.assign({}, request) as RPCRequest;
delete clonedRequest.bypassCache;
return RPCServer.hash(stringify(clonedRequest));
}
private verifyRequest(request: RPCRequest) {
let valid: boolean | Error | RPCMethod = validateRpcRequest(request);
if (!valid) {
return new Error(ajv.errorsText(validateRpcRequest.errors));
}
valid = this.getMethodByRequest(request);
if (valid instanceof Error) {
return valid;
}
return true;
}
private getMethodByRequest(request: RPCRequest): Error | RPCMethod {
return this.getMethod(request.module, request.method);
}
private getMethod(moduleName: string, method: string): Error | RPCMethod {
let item: any = this.methods.get(moduleName);
if (!item) {
return new Error("INVALID_MODULE");
}
item = item.get(method);
if (!item) {
return new Error("INVALID_METHOD");
}
return item;
}
}

View File

@ -1,21 +0,0 @@
import { JSONSchemaType } from "ajv";
// @ts-ignore
export const RPC_REQUEST_SCHEMA: JSONSchemaType<RPCRequest> = {
type: "object",
properties: {
module: {
type: "string",
},
method: {
type: "string",
},
data: {
type: ["number", "string", "boolean", "object", "array"],
},
bypassCache: {
type: "boolean",
nullable: true,
},
},
};

290
yarn.lock
View File

@ -436,6 +436,40 @@
udx-native "^1.1.0"
xache "^1.1.0"
"@hyperswarm/dht@next":
version "5.0.25"
resolved "https://registry.yarnpkg.com/@hyperswarm/dht/-/dht-5.0.25.tgz#eb9f4c314715723ed7e7dcec2b3dbe2722fddda2"
integrity sha512-x8Fpvp96NSb3M/0Fap2rm70obpNd0fe8oJnwZxJfIvxQtItYFVCrD8URsI+0Fxt4tNINMxnE9h3MaKoaxePP2A==
dependencies:
"@hyperswarm/secret-stream" "^5.1.0"
b4a "^1.3.1"
bind-easy "^1.0.1"
bogon "^1.0.0"
compact-encoding "^2.4.1"
compact-encoding-net "^1.0.1"
debugging-stream "^2.0.0"
dht-rpc "^5.0.1"
noise-curve-ed "^1.0.2"
noise-handshake "^2.1.0"
record-cache "^1.1.1"
safety-catch "^1.0.1"
sodium-universal "^3.0.4"
utp-native "^2.5.3"
xache "^1.0.0"
"@hyperswarm/secret-stream@^5.1.0":
version "5.2.0"
resolved "https://registry.yarnpkg.com/@hyperswarm/secret-stream/-/secret-stream-5.2.0.tgz#26621646d3f696e81a33a92f61db31b20be9bfd3"
integrity sha512-GwgLlbJV0DgvdTm0hPfyM4IWcWqJXIPCgkZ/DAh5CJ0HX8WW/4pDw70h7fRK5zBU1XUT6IYO2QAOeqZS+e9Dvg==
dependencies:
b4a "^1.1.0"
noise-curve-ed "^1.0.2"
noise-handshake "^2.1.0"
sodium-secretstream "^1.0.0"
sodium-universal "^3.0.4"
streamx "^2.10.2"
timeout-refresh "^2.0.0"
"@hyperswarm/secret-stream@^6.0.0":
version "6.0.0"
resolved "https://registry.yarnpkg.com/@hyperswarm/secret-stream/-/secret-stream-6.0.0.tgz#67db820308cc9fed899cb8f5e9f47ae819d5a4e3"
@ -488,6 +522,30 @@
dependencies:
bsert "~0.0.10"
"@lumeweb/dht-cache@https://git.lumeweb.com/LumeWeb/dht-cache.git":
version "0.1.0"
resolved "https://git.lumeweb.com/LumeWeb/dht-cache.git#323530c4bc136301b94699039452b483360e4c11"
dependencies:
"@lumeweb/dht-flood" "https://git.lumeweb.com/LumeWeb/dht-flood.git"
"@protobuf-ts/plugin" "^2.8.1"
b4a "^1.6.1"
compact-encoding "^2.11.0"
hypercore-crypto "^3.3.0"
jsnetworkx "^0.3.4"
lru "^3.1.0"
ordered-json "^0.1.1"
protocol-buffers-encodings "^1.2.0"
protomux-rpc "^1.3.0"
"@lumeweb/dht-flood@https://git.lumeweb.com/LumeWeb/dht-flood.git":
version "0.1.0"
resolved "https://git.lumeweb.com/LumeWeb/dht-flood.git#21f7f2e67d41cbd0df2f637670d87fe9c827fc8a"
dependencies:
compact-encoding "^2.11.0"
lru "^3.1.0"
protocol-buffers-encodings "^1.2.0"
protomux-rpc "^1.3.0"
"@lumeweb/kernel-utils@https://github.com/LumeWeb/kernel-utils.git":
version "0.1.0"
resolved "https://github.com/LumeWeb/kernel-utils.git#47a39d618e8278c1fff21bc74d7b091c8dc57224"
@ -498,9 +556,12 @@
version "0.1.0"
resolved "https://github.com/LumeWeb/pokt-rpc-endpoints.git#0223b743f913aa46e914b1dc237ae2e12571d96d"
"@lumeweb/relay-types@https://github.com/LumeWeb/relay-types.git":
"@lumeweb/relay-types@../relay-types/":
version "0.1.0"
resolved "https://github.com/LumeWeb/relay-types.git#83b831b62171a28a6d24dfae2df228c3849634f1"
"@lumeweb/relay-types@https://git.lumeweb.com/LumeWeb/relay-types.git":
version "0.1.0"
resolved "https://git.lumeweb.com/LumeWeb/relay-types.git#4c618a311dc31e036d3670a37df8f835a22b5e11"
"@msgpackr-extract/msgpackr-extract-darwin-arm64@2.1.2":
version "2.1.2"
@ -584,6 +645,42 @@
mkdirp "^1.0.4"
rimraf "^3.0.2"
"@protobuf-ts/plugin-framework@^2.8.2":
version "2.8.2"
resolved "https://registry.yarnpkg.com/@protobuf-ts/plugin-framework/-/plugin-framework-2.8.2.tgz#8699938576f68d01a9a937aa383db8fdcef6f64d"
integrity sha512-ivcJdNVB3Iee8044f8erZGBgmB6ZfQbbKyxRgDBXRVKYxsruLr432WcT5upw9autK9OnlSVLaebi8kDneFXd2g==
dependencies:
"@protobuf-ts/runtime" "^2.8.2"
typescript "^3.9"
"@protobuf-ts/plugin@^2.8.1":
version "2.8.2"
resolved "https://registry.yarnpkg.com/@protobuf-ts/plugin/-/plugin-2.8.2.tgz#f65f4362721e6a3bb3ce04f6d9760901e576eaa7"
integrity sha512-rTPxaeKBfUar8ubKxbVdv4XL6AcGA0OOgHNHFyrfODP7Epy80omwuvgFJex1YpeNFJxm/FZXXj5Z+nHuhYEqJg==
dependencies:
"@protobuf-ts/plugin-framework" "^2.8.2"
"@protobuf-ts/protoc" "^2.8.2"
"@protobuf-ts/runtime" "^2.8.2"
"@protobuf-ts/runtime-rpc" "^2.8.2"
typescript "^3.9"
"@protobuf-ts/protoc@^2.8.2":
version "2.8.2"
resolved "https://registry.yarnpkg.com/@protobuf-ts/protoc/-/protoc-2.8.2.tgz#45809eda3037caff300b9061262444ae7ad891ec"
integrity sha512-1e+rOgp22ElyqRWunSc8bhatJcvRe90AGPceVn67IFYzybvfKl17vP1igHddeYkN0dzOucnOrwqn2v1jnDfE2w==
"@protobuf-ts/runtime-rpc@^2.8.2":
version "2.8.2"
resolved "https://registry.yarnpkg.com/@protobuf-ts/runtime-rpc/-/runtime-rpc-2.8.2.tgz#8af6d5eab44e2fc92cfe9a83a5c351b5f2fcdfbe"
integrity sha512-vum/Y7AXdUTWGFu7dke/jCSB9dV3Oo3iVPcce3j7KudpzzWarDkEGvXjKv3Y8zJPj5waToyxwBNSb7eo5Vw5WA==
dependencies:
"@protobuf-ts/runtime" "^2.8.2"
"@protobuf-ts/runtime@^2.8.2":
version "2.8.2"
resolved "https://registry.yarnpkg.com/@protobuf-ts/runtime/-/runtime-2.8.2.tgz#5d5424a6ae7fb846c3f4d0f2dd6448db65bb69d6"
integrity sha512-PVxsH81y9kEbHldxxG/8Y3z2mTXWQytRl8zNS0mTPUjkEC+8GUX6gj6LsA8EFp25fAs9V0ruh+aNWmPccEI9MA==
"@skynetlabs/skynet-nodejs@^2.6.0":
version "2.8.0"
resolved "https://registry.yarnpkg.com/@skynetlabs/skynet-nodejs/-/skynet-nodejs-2.8.0.tgz#46972beca4a54bfaa87502663aa89baf828d8055"
@ -649,6 +746,13 @@
dependencies:
acme-client "*"
"@types/b4a@^1.6.0":
version "1.6.0"
resolved "https://registry.yarnpkg.com/@types/b4a/-/b4a-1.6.0.tgz#91a22117b6c8d79963ed2eed3328143df77c2542"
integrity sha512-rYU2r5nSUPyKyufWijxgTjsFp2kLCwydj2TmKU4StJeGPHS/Fs5KHgP89DNF0jddyeAbN5mdjNDqIrjIHca60g==
dependencies:
"@types/node" "*"
"@types/body-parser@*":
version "1.19.2"
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0"
@ -901,6 +1005,18 @@ b4a@^1.0.1, b4a@^1.1.0, b4a@^1.1.1, b4a@^1.1.4, b4a@^1.3.0, b4a@^1.3.1, b4a@^1.5
resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.0.tgz#5430a9cac1af388910dd1a1c1aa9d3a0a796ed68"
integrity sha512-fsTxXxj1081Yq5MOQ06gZ5+e2QcSyP2U6NofdOWyq+lrNI4IjkZ+fLVmoQ6uUCiNg1NWePMMVq93vOTdbJmErw==
b4a@^1.6.0, b4a@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.1.tgz#9effac93a469a868d024e16fd77162c653544cbd"
integrity sha512-AsKjNhz72yxteo/0EtQEiwkMUgk/tGmycXlbG4g3Ard2/ULtNLUykGOkeK0egmN27h0xMAhb76jYccW+XTBExA==
babel-runtime@^5:
version "5.8.38"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-5.8.38.tgz#1c0b02eb63312f5f087ff20450827b425c9d4c19"
integrity sha512-KpgoA8VE/pMmNCrnEeeXqFG24TIH11Z3ZaimIhJWsin8EbfZy3WzFKUTIan10ZIDgRVvi9EkLbruJElJC9dRlg==
dependencies:
core-js "^1.0.0"
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
@ -942,6 +1058,11 @@ bigint-buffer@^1.1.5:
dependencies:
bindings "^1.3.0"
bind-easy@^1.0.0, bind-easy@^1.0.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/bind-easy/-/bind-easy-1.1.2.tgz#d10f9be896e53fb84f49465be5b1ab9b089dbcff"
integrity sha512-2+VjZ87WFdOFnsH4tHnmtf0HF6D2T3ZNdU1t1FYIz2jt4N3tyqbg2J0bYbflXdBkVi3xfVc8Pm8NB062SPvVVA==
bindings@^1.3.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
@ -949,6 +1070,13 @@ bindings@^1.3.0:
dependencies:
file-uri-to-path "1.0.0"
bits-to-bytes@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/bits-to-bytes/-/bits-to-bytes-1.3.0.tgz#b6b0b547ff5747b0609a42e31a0b57212f09f4e7"
integrity sha512-OJoHTpFXS9bXHBCekGTByf3MqM8CGblBDIduKQeeVVeiU9dDWywSSirXIBYGgg3d1zbVuvnMa1vD4r6PA0kOKg==
dependencies:
b4a "^1.5.0"
bl@^4.0.3:
version "4.1.0"
resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
@ -1222,6 +1350,13 @@ commander@^2.20.3:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
compact-encoding-bitfield@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/compact-encoding-bitfield/-/compact-encoding-bitfield-1.0.0.tgz#39f923263dffb68b266b6cb9dc0bae2d42cf5c4a"
integrity sha512-3nMVKUg+PF72UHfainmCL8uKvyWfxsjqOtUY+HiMPGLPCTjnwzoKfFAMo1Ad7nwTPdjBqtGK5b3BOFTFW4EBTg==
dependencies:
compact-encoding "^2.4.1"
compact-encoding-net@^1.0.1, compact-encoding-net@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/compact-encoding-net/-/compact-encoding-net-1.2.0.tgz#19d2efd55df10f2ee793576fc4483bf6d2218743"
@ -1268,6 +1403,11 @@ cookie@0.5.0:
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
core-js@^1.0.0:
version "1.2.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
integrity sha512-ZiPp9pZlgxpWRu0M+YWbm6+aQ84XEfH1JRXvfOc/fILWI0VKhLC2LX13X1NYq4fULzLMq7Hfh43CSo2/aIaUPA==
core-util-is@~1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
@ -1356,6 +1496,22 @@ detect-libc@^1.0.3:
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
dht-rpc@^5.0.1:
version "5.0.6"
resolved "https://registry.yarnpkg.com/dht-rpc/-/dht-rpc-5.0.6.tgz#f37af2ae965d71d1034f852414673ed86286f7c4"
integrity sha512-8NxLsJ3WFoM2D/kQDu/4SOMnsjm/0NmBm4cJgUS9Idj1u/AuvY0bKpTNzUsVR25SdHfSEcbjzDPq40z3I3AVwg==
dependencies:
b4a "^1.3.1"
bind-easy "^1.0.0"
compact-encoding "^2.1.0"
compact-encoding-net "^1.0.1"
fast-fifo "^1.0.0"
kademlia-routing-table "^1.0.0"
nat-sampler "^1.0.1"
sodium-universal "^3.0.4"
streamx "^2.10.3"
time-ordered-set "^1.0.2"
dht-rpc@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/dht-rpc/-/dht-rpc-6.3.0.tgz#bd9fbdc0a558182af35eb9c4e92aad38e6a6e68a"
@ -1918,6 +2074,16 @@ hypercore-crypto@^3.3.0:
compact-encoding "^2.5.1"
sodium-universal "^3.0.0"
hyperswarm@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/hyperswarm/-/hyperswarm-3.0.4.tgz#0b0b763bd7dd75dcd045746bd61d9cb5c38eac11"
integrity sha512-+wNuhabxPpCRzrP98dtXV/Iwq7TnvcaKYx/yHXaMqZ7qqZOc8j/5aJcUyctT/5hSx0Uflwolit9BIx3L/F800w==
dependencies:
"@hyperswarm/dht" next
b4a "^1.3.1"
events "^3.3.0"
shuffled-priority-queue "^2.1.0"
iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@ -2102,6 +2268,16 @@ jsesc@^2.5.1:
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
jsnetworkx@^0.3.4:
version "0.3.4"
resolved "https://registry.yarnpkg.com/jsnetworkx/-/jsnetworkx-0.3.4.tgz#1c0025df94208ceb5cc59e658f9a9bf5fef4f6f4"
integrity sha512-3wLBxtTWsgMUADKiEXyVr6s0BNnXBtB+A13cYToatl65OFF9UG1BTOij1Jx7AhK7Q9fbrfFCNppDuSOmTFkB1Q==
dependencies:
babel-runtime "^5"
lodash "^3.3.1"
through "^2.3.6"
tiny-sprintf "^0.3.0"
json-schema-traverse@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
@ -2213,6 +2389,11 @@ lodash.uniqby@4.5.0:
lodash._baseiteratee "~4.7.0"
lodash._baseuniq "~4.6.0"
lodash@^3.3.1:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
integrity sha512-9mDDwqVIma6OZX79ZlDACZl8sBm0TEnkf99zV3iMA4GzkIT/9hiqP5mY0HoT1iNLCrKc/R1HByV+yJfRWVJryQ==
lodash@^4.17.20:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
@ -2235,6 +2416,13 @@ lru-cache@^7.7.1:
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.14.0.tgz#21be64954a4680e303a09e9468f880b98a0b3c7f"
integrity sha512-EIRtP1GrSJny0dqb50QXRUNBxHJhcpxHC++M5tD7RYbvLLn5KVWKsbyswSSqDuU15UFi3bgTQIY8nhDMeF6aDQ==
lru@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lru/-/lru-3.1.0.tgz#ea7fb8546d83733396a13091d76cfeb4c06837d5"
integrity sha512-5OUtoiVIGU4VXBOshidmtOsvBIvcQR6FD/RzWSvaeHyxCGB+PCUCu+52lqMfdc0h/2CLvHhZS4TwUmMQrrMbBQ==
dependencies:
inherits "^2.0.1"
make-fetch-happen@^10.0.3:
version "10.2.1"
resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164"
@ -2538,7 +2726,7 @@ node-gyp-build-optional-packages@5.0.3:
resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz#92a89d400352c44ad3975010368072b41ad66c17"
integrity sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==
node-gyp-build@^4.3.0, node-gyp-build@^4.4.0:
node-gyp-build@^4.2.0, node-gyp-build@^4.3.0, node-gyp-build@^4.4.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40"
integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==
@ -2641,6 +2829,18 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
dependencies:
wrappy "1"
ordered-json@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/ordered-json/-/ordered-json-0.1.1.tgz#dd5392d6ef6e085fcef2a1ea57a478d30aceef6f"
integrity sha512-qw4OYAxofa+WAZAP90eoXftAErUCjs8OII5ddDzKAZBsPMpQvWEIvuKCmUgGV22Cyd3/bT6i12KeuBBZixThDg==
dependencies:
ordered-object "^0.2.0"
ordered-object@^0.2.0:
version "0.2.3"
resolved "https://registry.yarnpkg.com/ordered-object/-/ordered-object-0.2.3.tgz#2e187a7d29f9dfdb2894b695f11899fdaa83a60c"
integrity sha512-UKBtJiO7PsKqAAenewZ/moHQIRbcjZ4HE0J/+RyzgnpCTIn5ZLe3N2izno1kViTCXtHB4xuewjPgYLEiuS6t5A==
p-is-promise@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-3.0.0.tgz#58e78c7dfe2e163cf2a04ff869e7c1dba64a5971"
@ -2800,7 +3000,26 @@ proper-lockfile@^2.0.1:
graceful-fs "^4.1.2"
retry "^0.10.0"
protomux@^3.1.1:
protocol-buffers-encodings@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/protocol-buffers-encodings/-/protocol-buffers-encodings-1.2.0.tgz#39900b85dcff3172a23f15bdf3fda70daa2b38d3"
integrity sha512-daeNPuKh1NlLD1uDfbLpD+xyUTc07nEtfHwmBZmt/vH0B7VOM+JOCOpDcx9ZRpqHjAiIkGqyTDi+wfGSl17R9w==
dependencies:
b4a "^1.6.0"
signed-varint "^2.0.1"
varint "5.0.0"
protomux-rpc@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/protomux-rpc/-/protomux-rpc-1.3.0.tgz#ffc735f4b05d64b053a7568e64f2c65de948e056"
integrity sha512-G+zCpEujLsW+lQ75YXVdPfXKzxnP5F7IceVyz2B1ypWdH4NDf2n2ObkoUjdUrEMWgn+3fjXdY7ehkXLac+mNPQ==
dependencies:
bits-to-bytes "^1.0.0"
compact-encoding "^2.6.1"
compact-encoding-bitfield "^1.0.0"
protomux "^3.2.1"
protomux@^3.1.1, protomux@^3.2.1, protomux@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/protomux/-/protomux-3.4.0.tgz#b6269fc15c0bd22964acb6e8f68a002e1da9a370"
integrity sha512-jZBytPrL5o+eZeSfIA+5OhPBwfS4A3DucHU4R6KkwFVr2sPqtNoKOX/xXdGzQzHzfVOqbsFfC7oFQ5FqZ6XKWw==
@ -2922,7 +3141,7 @@ readable-stream@^2.0.0, readable-stream@^2.0.6, readable-stream@^2.1.4:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@ -3124,6 +3343,13 @@ sha512-wasm@^2.3.1:
b4a "^1.0.1"
nanoassert "^2.0.0"
shuffled-priority-queue@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/shuffled-priority-queue/-/shuffled-priority-queue-2.1.0.tgz#432bf14dd90f7c4dd1705752d81aadf454fd3af6"
integrity sha512-xhdh7fHyMsr0m/w2kDfRJuBFRS96b9l8ZPNWGaQ+PMvnUnZ/Eh+gJJ9NsHBd7P9k0399WYlCLzsy18EaMfyadA==
dependencies:
unordered-set "^2.0.1"
side-channel@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
@ -3138,6 +3364,13 @@ signal-exit@^3.0.0, signal-exit@^3.0.7:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
signed-varint@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/signed-varint/-/signed-varint-2.0.1.tgz#50a9989da7c98c2c61dad119bc97470ef8528129"
integrity sha512-abgDPg1106vuZZOvw7cFwdCABddfJRz5akcCcchzTbhyhYnsG31y4AlZEgp315T7W3nQq5P4xeOm186ZiPVFzw==
dependencies:
varint "~5.0.0"
simple-concat@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
@ -3254,7 +3487,7 @@ sodium-secretstream@^1.0.0:
b4a "^1.1.1"
sodium-universal "^3.0.4"
sodium-universal@^3.0.0, sodium-universal@^3.0.4:
sodium-universal@^3.0.0, sodium-universal@^3.0.4, sodium-universal@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/sodium-universal/-/sodium-universal-3.1.0.tgz#f2fa0384d16b7cb99b1c8551a39cc05391a3ed41"
integrity sha512-N2gxk68Kg2qZLSJ4h0NffEhp4BjgWHCHXVlDi1aG1hA3y+ZeWEmHqnpml8Hy47QzfL1xLy5nwr9LcsWAg2Ep0A==
@ -3407,7 +3640,7 @@ text-encoding-utf-8@^1.0.2:
resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13"
integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==
"through@>=2.2.7 <3":
"through@>=2.2.7 <3", through@^2.3.6:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
@ -3417,11 +3650,21 @@ time-ordered-set@^1.0.2:
resolved "https://registry.yarnpkg.com/time-ordered-set/-/time-ordered-set-1.0.2.tgz#3bd931fc048234147f8c2b8b1ebbebb0a3ecb96f"
integrity sha512-vGO99JkxvgX+u+LtOKQEpYf31Kj3i/GNwVstfnh4dyINakMgeZCpew1e3Aj+06hEslhtHEd52g7m5IV+o1K8Mw==
timeout-refresh@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/timeout-refresh/-/timeout-refresh-1.0.3.tgz#7024a8ce0a09a57acc2ea86002048e6c0bff7375"
integrity sha512-Mz0CX4vBGM5lj8ttbIFt7o4ZMxk/9rgudJRh76EvB7xXZMur7T/cjRiH2w4Fmkq0zxf2QpM8IFvOSRn8FEu3gA==
timeout-refresh@^2.0.0, timeout-refresh@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/timeout-refresh/-/timeout-refresh-2.0.1.tgz#f8ec7cf1f9d93b2635b7d4388cb820c5f6c16f98"
integrity sha512-SVqEcMZBsZF9mA78rjzCrYrUs37LMJk3ShZ851ygZYW1cMeIjs9mL57KO6Iv5mmjSQnOe/29/VAfGXo+oRCiVw==
tiny-sprintf@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/tiny-sprintf/-/tiny-sprintf-0.3.0.tgz#4272fd5c1d2f92807223fc16d728fdf30595a33e"
integrity sha512-2GsAMPBTxDYKjJVsK3Do2nLAMV7hteGNTy3CuNbJwRtBGgbzuzlmYIehmzJPaPyj0IsjChgcGutBZcpCe76flg==
to-data-view@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/to-data-view/-/to-data-view-1.1.0.tgz#08d6492b0b8deb9b29bdf1f61c23eadfa8994d00"
@ -3474,6 +3717,11 @@ type-is@~1.6.18:
media-typer "0.3.0"
mime-types "~2.1.24"
typescript@^3.9:
version "3.9.10"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8"
integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==
typescript@^4.7.4:
version "4.8.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.3.tgz#d59344522c4bc464a65a730ac695007fdb66dd88"
@ -3509,6 +3757,11 @@ universalify@^2.0.0:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
unordered-set@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/unordered-set/-/unordered-set-2.0.1.tgz#4cd0fe27b8814bcf5d6073e5f0966ec7a50841e6"
integrity sha512-eUmNTPzdx+q/WvOHW0bgGYLWvWHNT3PTKEQLg0MAQhc0AHASHVHoP/9YytYd4RBVariqno/mEUhVZN98CmD7bg==
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
@ -3556,11 +3809,32 @@ utils-merge@1.0.1:
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
utp-native@^2.5.3:
version "2.5.3"
resolved "https://registry.yarnpkg.com/utp-native/-/utp-native-2.5.3.tgz#7c04c2a8c2858716555a77d10adb9819e3119b25"
integrity sha512-sWTrWYXPhhWJh+cS2baPzhaZc89zwlWCfwSthUjGhLkZztyPhcQllo+XVVCbNGi7dhyRlxkWxN4NKU6FbA9Y8w==
dependencies:
napi-macros "^2.0.0"
node-gyp-build "^4.2.0"
readable-stream "^3.0.2"
timeout-refresh "^1.0.0"
unordered-set "^2.0.1"
uuid@8.3.2, uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
varint@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.0.tgz#d826b89f7490732fabc0c0ed693ed475dcb29ebf"
integrity sha512-gC13b/bWrqQoKY2EmROCZ+AR0jitc6DnDGaQ6Ls9QpKmuSgJB1eQ7H3KETtQm7qSdMWMKCmsshyCmUwMLh3OAA==
varint@~5.0.0:
version "5.0.2"
resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4"
integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==
vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
@ -3627,7 +3901,7 @@ ws@^8.5.0:
resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0"
integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==
xache@^1.1.0:
xache@^1.0.0, xache@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/xache/-/xache-1.1.0.tgz#afc20dec9ff8b2260eea03f5ad9422dc0200c6e9"
integrity sha512-RQGZDHLy/uCvnIrAvaorZH/e6Dfrtxj16iVlGjkj4KD2/G/dNXNqhk5IdSucv5nSSnDK00y8Y/2csyRdHveJ+Q==