mirror of
https://github.com/cerc-io/watcher-ts
synced 2025-01-02 09:36:47 +00:00
Implement peer package to send messages between peers (#279)
* Initial implementation of class and discoving other peers * Implement peer connection and sending messages between peers * Add react app and use peer package for broadcasting * Maintain stream for each remote peer * Refactor code in peer package * Add serve package for react-app * Add readme for running react app * Add peer package readme * Add logs for events with details * Add a chat CLI using peer package (#280) * Add a flag to instantiate Peer for nodejs * Add a basic chat CLI using peer * Add a signal server arg to chat CLI * Add instructions for chat CLI * Fix typescript and ESM issues after adding peer package (#282) * Fix build issues in util package * Update eslint TS plugins * Scope react app package name * Convert cli package back to CJS and dynamically import ESM * Upgrade ts-node version * Fix tests * Setup a relay node and pubsub based discovery (#284) * Add a script to setup a relay node * Use pubsub based peer discovery * Add peer multiaddr to connection log * Catch relay node dial errors * Increase discovery interval and dial all mutiaddr * Add UI to display self peer ID and multiaddrs * Add UI for displaying live remote connections * Send js objects in peer broadcast messages * Add react-peer package for using peer in react app * Reduce disconnect frequency between peers (#287) * Restrict number of max concurrent dials per peer * Increase hop relay timeout to 1 day * Self review changes * Review changes * Increase pubsub discovery interval and add steps to create a peer id (#290) * Increase pubsub discovery interval * Disable autodial to avoid peer dials without protocol on a reconnect * Add steps to create a peer id and use for relay node * Add back dependency to run signalling server * Avoid bootstrapping and dial to relay node directly Co-authored-by: prathamesh0 <42446521+prathamesh0@users.noreply.github.com> Co-authored-by: prathamesh0 <prathamesh.musale0@gmail.com>
This commit is contained in:
parent
aa4a954330
commit
cd29b47ecc
2
.npmrc
2
.npmrc
@ -1 +1 @@
|
||||
registry=https://git.vdb.to/api/packages/cerc-io/npm/
|
||||
@cerc-io:registry=https://git.vdb.to/api/packages/cerc-io/npm/
|
||||
|
@ -48,8 +48,8 @@
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/mocha": "^8.2.2",
|
||||
"@types/yargs": "^17.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"chai": "^4.3.4",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-semistandard": "^15.0.1",
|
||||
|
4
packages/cache/package.json
vendored
4
packages/cache/package.json
vendored
@ -29,8 +29,8 @@
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/fs-extra": "^9.0.11",
|
||||
"@types/level": "^6.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-semistandard": "^15.0.1",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
|
2
packages/cache/src/cache.ts
vendored
2
packages/cache/src/cache.ts
vendored
@ -69,7 +69,7 @@ export class Cache {
|
||||
log(`${this._name}: cache hit ${key}`);
|
||||
|
||||
return [value, true];
|
||||
} catch (err) {
|
||||
} catch (err: any) {
|
||||
log(`${this._name}: cache miss ${key}`);
|
||||
|
||||
if (err.notFound) {
|
||||
|
56
packages/cli/README.md
Normal file
56
packages/cli/README.md
Normal file
@ -0,0 +1,56 @@
|
||||
# cli
|
||||
|
||||
## chat
|
||||
|
||||
A basic CLI to pass messages between peers using `stdin`/`stdout`
|
||||
|
||||
* Install dependencies:
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
* Build the `peer` package:
|
||||
|
||||
```
|
||||
cd packages/peer
|
||||
yarn build
|
||||
```
|
||||
|
||||
* (Optional) Run a local signalling server:
|
||||
|
||||
```bash
|
||||
# In packages/peer
|
||||
yarn signal-server
|
||||
```
|
||||
|
||||
* (Optional) Create and export a peer id for the relay node:
|
||||
|
||||
```bash
|
||||
# In packages/peer
|
||||
yarn create-peer --file [PEER_ID_FILE_PATH]
|
||||
```
|
||||
|
||||
* `file (f)`: file path to export the peer id to (json)
|
||||
|
||||
* (Optional) Run a local relay node:
|
||||
|
||||
```bash
|
||||
# In packages/peer
|
||||
yarn relay-node --signal-server [SIGNAL_SERVER_URL] --peer-id-file [PEER_ID_FILE_PATH]
|
||||
```
|
||||
|
||||
* `signal-server`: multiaddr of a signalling server (default: local signalling server multiaddr)
|
||||
* `peer-id-file`: file path for peer id to be used (json)
|
||||
|
||||
* Start the node:
|
||||
|
||||
```bash
|
||||
# In packages/cli
|
||||
yarn chat --signal-server [SIGNAL_SERVER_URL] --relay-node [RELAY_NODE_URL]
|
||||
```
|
||||
|
||||
* `signal-server`: multiaddr of a signalling server (default: local signalling server multiaddr)
|
||||
* `relay-node`: multiaddr of a hop enabled relay node
|
||||
|
||||
* The process starts reading from `stdin` and outputs messages from others peers to `stdout`.
|
@ -7,13 +7,16 @@
|
||||
"lint": "eslint .",
|
||||
"build": "yarn clean && tsc && yarn copy-assets",
|
||||
"clean": "rm -rf ./dist",
|
||||
"copy-assets": "copyfiles -u 1 src/**/*.gql dist/"
|
||||
"copy-assets": "copyfiles -u 1 src/**/*.gql dist/",
|
||||
"chat": "node dist/chat.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cerc-io/peer": "^0.2.18",
|
||||
"@cerc-io/util": "^0.2.18",
|
||||
"@ethersproject/providers": "^5.4.4",
|
||||
"@graphql-tools/utils": "^9.1.1",
|
||||
"@ipld/dag-cbor": "^6.0.12",
|
||||
"@ipld/dag-cbor": "^8.0.0",
|
||||
"@libp2p/interface-peer-id": "^1.1.2",
|
||||
"apollo-server-express": "^3.11.1",
|
||||
"debug": "^4.3.1",
|
||||
"express": "^4.18.2",
|
||||
@ -24,13 +27,15 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.14",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"@types/node": "16.11.7",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"eslint-config-semistandard": "^15.0.1",
|
||||
"eslint-config-standard": "^5.0.0",
|
||||
"eslint-plugin-import": "^2.23.3",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-standard": "^5.0.0"
|
||||
"eslint-plugin-standard": "^5.0.0",
|
||||
"typescript": "^4.9.4"
|
||||
}
|
||||
}
|
||||
|
63
packages/cli/src/chat.ts
Normal file
63
packages/cli/src/chat.ts
Normal file
@ -0,0 +1,63 @@
|
||||
//
|
||||
// Copyright 2022 Vulcanize, Inc.
|
||||
//
|
||||
|
||||
import * as readline from 'readline';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
import yargs from 'yargs';
|
||||
|
||||
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1319854183
|
||||
import { PeerId } from '@libp2p/interface-peer-id';
|
||||
|
||||
interface Arguments {
|
||||
signalServer: string;
|
||||
relayNode: string;
|
||||
}
|
||||
|
||||
async function main (): Promise<void> {
|
||||
const argv: Arguments = _getArgv();
|
||||
if (!argv.signalServer) {
|
||||
console.log('Using the default signalling server URL');
|
||||
}
|
||||
|
||||
// https://adamcoster.com/blog/commonjs-and-esm-importexport-compatibility-examples#importing-esm-into-commonjs-cjs
|
||||
const { Peer } = await import('@cerc-io/peer');
|
||||
const peer = new Peer(true);
|
||||
await peer.init(argv.signalServer, argv.relayNode);
|
||||
|
||||
peer.subscribeMessage((peerId: PeerId, message: string) => {
|
||||
console.log(`> ${peerId.toString()} > ${message}`);
|
||||
});
|
||||
|
||||
console.log(`Peer ID: ${peer.peerId?.toString()}`);
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
rl.on('line', (input: string) => {
|
||||
peer.broadcastMessage(input);
|
||||
});
|
||||
|
||||
console.log('Reading input...');
|
||||
}
|
||||
|
||||
function _getArgv (): any {
|
||||
return yargs(hideBin(process.argv)).parserConfiguration({
|
||||
'parse-numbers': false
|
||||
}).options({
|
||||
signalServer: {
|
||||
type: 'string',
|
||||
describe: 'Signalling server URL'
|
||||
},
|
||||
relayNode: {
|
||||
type: 'string',
|
||||
describe: 'Relay node URL'
|
||||
}
|
||||
}).argv;
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.log(err);
|
||||
});
|
@ -22,7 +22,6 @@ import {
|
||||
GraphWatcherInterface,
|
||||
Config
|
||||
} from '@cerc-io/util';
|
||||
import * as codec from '@ipld/dag-cbor';
|
||||
|
||||
import { BaseCmd } from './base';
|
||||
|
||||
@ -164,6 +163,7 @@ export class ExportStateCmd {
|
||||
}
|
||||
|
||||
if (this._argv.exportFile) {
|
||||
const codec = await import('@ipld/dag-cbor');
|
||||
const encodedExportData = codec.encode(exportData);
|
||||
|
||||
const filePath = path.resolve(this._argv.exportFile);
|
||||
|
@ -25,7 +25,6 @@ import {
|
||||
updateEntitiesFromState,
|
||||
Config
|
||||
} from '@cerc-io/util';
|
||||
import * as codec from '@ipld/dag-cbor';
|
||||
|
||||
import { BaseCmd } from './base';
|
||||
|
||||
@ -112,6 +111,7 @@ export class ImportStateCmd {
|
||||
// Import data.
|
||||
const importFilePath = path.resolve(this._argv.importFile);
|
||||
const encodedImportData = fs.readFileSync(importFilePath);
|
||||
const codec = await import('@ipld/dag-cbor');
|
||||
const importData = codec.decode(Buffer.from(encodedImportData)) as any;
|
||||
|
||||
// Fill the snapshot block.
|
||||
|
@ -5,7 +5,7 @@
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
"module": "CommonJS", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
"lib": [ "ES5", "ES6", "ES2020" ], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
@ -44,13 +44,11 @@
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
"moduleResolution": "Node16", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
"typeRoots": [
|
||||
"./src/types"
|
||||
], /* List of folders to include type definitions from. */
|
||||
"typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
|
@ -41,8 +41,8 @@
|
||||
"@openzeppelin/contracts": "^4.3.2",
|
||||
"@types/js-yaml": "^4.0.3",
|
||||
"@types/node": "^16.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-semistandard": "^15.0.1",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
import assert from 'assert';
|
||||
import debug from 'debug';
|
||||
import { DeepPartial, FindConditions, FindManyOptions } from 'typeorm';
|
||||
import { DeepPartial, FindConditions, FindManyOptions, ObjectLiteral } from 'typeorm';
|
||||
import JSONbig from 'json-bigint';
|
||||
import { ethers } from 'ethers';
|
||||
{{#if (subgraphPath)}}
|
||||
@ -368,7 +368,7 @@ export class Indexer implements IndexerInterface {
|
||||
}
|
||||
|
||||
{{#if (subgraphPath)}}
|
||||
async getSubgraphEntity<Entity> (
|
||||
async getSubgraphEntity<Entity extends ObjectLiteral> (
|
||||
entity: new () => Entity,
|
||||
id: string,
|
||||
block: BlockHeight,
|
||||
@ -379,7 +379,7 @@ export class Indexer implements IndexerInterface {
|
||||
return data;
|
||||
}
|
||||
|
||||
async getSubgraphEntities<Entity> (
|
||||
async getSubgraphEntities<Entity extends ObjectLiteral> (
|
||||
entity: new () => Entity,
|
||||
block: BlockHeight,
|
||||
where: { [key: string]: any } = {},
|
||||
|
@ -61,8 +61,8 @@
|
||||
"devDependencies": {
|
||||
"@ethersproject/abi": "^5.3.0",
|
||||
"@types/yargs": "^17.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-semistandard": "^15.0.1",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
@ -70,7 +70,7 @@
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-standard": "^5.0.0",
|
||||
"ts-node": "^10.0.0",
|
||||
"ts-node": "^10.2.1",
|
||||
"typescript": "^4.3.2",
|
||||
"copyfiles": "^2.4.1"
|
||||
}
|
||||
|
@ -56,8 +56,8 @@
|
||||
"devDependencies": {
|
||||
"@ethersproject/abi": "^5.3.0",
|
||||
"@types/yargs": "^17.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"copyfiles": "^2.4.1",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-semistandard": "^15.0.1",
|
||||
@ -66,7 +66,7 @@
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-standard": "^5.0.0",
|
||||
"ts-node": "^10.0.0",
|
||||
"ts-node": "^10.2.1",
|
||||
"typescript": "^4.3.2"
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
//
|
||||
|
||||
import assert from 'assert';
|
||||
import { DeepPartial, FindConditions, FindManyOptions } from 'typeorm';
|
||||
import { DeepPartial, FindConditions, FindManyOptions, ObjectLiteral } from 'typeorm';
|
||||
import { ethers } from 'ethers';
|
||||
import { SelectionNode } from 'graphql';
|
||||
|
||||
@ -281,13 +281,13 @@ export class Indexer implements IndexerInterface {
|
||||
await this._baseIndexer.removeStates(blockNumber, kind);
|
||||
}
|
||||
|
||||
async getSubgraphEntity<Entity> (entity: new () => Entity, id: string, block: BlockHeight, selections: ReadonlyArray<SelectionNode> = []): Promise<any> {
|
||||
async getSubgraphEntity<Entity extends ObjectLiteral> (entity: new () => Entity, id: string, block: BlockHeight, selections: ReadonlyArray<SelectionNode> = []): Promise<any> {
|
||||
const data = await this._graphWatcher.getEntity(entity, id, this._relationsMap, block, selections);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
async getSubgraphEntities<Entity> (
|
||||
async getSubgraphEntities<Entity extends ObjectLiteral> (
|
||||
entity: new () => Entity,
|
||||
block: BlockHeight,
|
||||
where: { [key: string]: any } = {},
|
||||
|
@ -2,7 +2,7 @@
|
||||
host = "127.0.0.1"
|
||||
port = 3001
|
||||
mode = "eth_call"
|
||||
kind = "lazy"
|
||||
kind = "active"
|
||||
|
||||
[metrics]
|
||||
host = "127.0.0.1"
|
||||
|
@ -62,8 +62,8 @@
|
||||
"@nomiclabs/hardhat-waffle": "^2.0.1",
|
||||
"@openzeppelin/contracts": "^4.3.2",
|
||||
"@types/json-bigint": "^1.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-semistandard": "^15.0.1",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
@ -72,6 +72,7 @@
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-standard": "^5.0.0",
|
||||
"hardhat": "^2.3.0",
|
||||
"nodemon": "^2.0.7"
|
||||
"nodemon": "^2.0.7",
|
||||
"ts-node": "^10.2.1"
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ interface EventResult {
|
||||
to?: string;
|
||||
owner?: string;
|
||||
spender?: string;
|
||||
value?: BigInt;
|
||||
value?: bigint;
|
||||
__typename: string;
|
||||
}
|
||||
proof?: string;
|
||||
|
@ -68,8 +68,8 @@
|
||||
"@nomiclabs/hardhat-waffle": "^2.0.1",
|
||||
"@openzeppelin/contracts": "^4.3.2",
|
||||
"@types/yargs": "^17.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"copyfiles": "^2.4.1",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-semistandard": "^15.0.1",
|
||||
@ -79,7 +79,7 @@
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-standard": "^5.0.0",
|
||||
"hardhat": "^2.3.0",
|
||||
"ts-node": "^10.0.0",
|
||||
"ts-node": "^10.2.1",
|
||||
"typescript": "^4.3.2"
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,8 @@
|
||||
"@types/chai": "^4.2.18",
|
||||
"@types/chai-spies": "^1.0.3",
|
||||
"@types/pluralize": "^0.0.29",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"chai": "^4.3.4",
|
||||
"chai-spies": "^1.0.0",
|
||||
"eslint": "^7.27.0",
|
||||
@ -27,7 +27,7 @@
|
||||
"hardhat": "^2.3.0",
|
||||
"mocha": "^8.4.0",
|
||||
"nodemon": "^2.0.7",
|
||||
"ts-node": "^10.0.0",
|
||||
"ts-node": "^10.2.1",
|
||||
"typescript": "^4.3.2"
|
||||
},
|
||||
"bin": {
|
||||
|
@ -102,7 +102,7 @@ describe('wasm loader tests', () => {
|
||||
}
|
||||
|
||||
expect.fail('wasm code should throw error');
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
expect(error).to.be.instanceof(WebAssembly.RuntimeError);
|
||||
expect(error.message).to.equal('unreachable');
|
||||
}
|
||||
|
@ -576,7 +576,7 @@ describe('numbers wasm tests', () => {
|
||||
it('should throw an error for DECIMAL128_PMIN times DECIMAL128_NMAX', async () => {
|
||||
try {
|
||||
await testBigDecimalTimes(await __newString(DECIMAL128_PMIN), await __newString(DECIMAL128_NMAX));
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.be.equal('Big decimal exponent \'-12286\' is outside the \'-6143\' to \'6144\' range');
|
||||
}
|
||||
});
|
||||
@ -621,7 +621,7 @@ describe('numbers wasm tests', () => {
|
||||
it('should throw an error for DECIMAL128_PMIN divideBy DECIMAL128_MAX', async () => {
|
||||
try {
|
||||
await testBigDecimalDividedBy(await __newString(DECIMAL128_PMIN), await __newString(DECIMAL128_MAX));
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
expect(error.message).to.be.equal('Big decimal exponent \'-12288\' is outside the \'-6143\' to \'6144\' range');
|
||||
}
|
||||
});
|
||||
|
@ -31,6 +31,7 @@ import {
|
||||
} from '@cerc-io/util';
|
||||
|
||||
import { Context, GraphData, instantiate } from './loader';
|
||||
import { ObjectLiteral } from 'typeorm';
|
||||
|
||||
const log = debug('vulcanize:graph-watcher');
|
||||
|
||||
@ -281,7 +282,7 @@ export class GraphWatcher {
|
||||
this._indexer = indexer;
|
||||
}
|
||||
|
||||
async getEntity<Entity> (
|
||||
async getEntity<Entity extends ObjectLiteral> (
|
||||
entity: new () => Entity,
|
||||
id: string,
|
||||
relationsMap: Map<any, { [key: string]: any }>,
|
||||
@ -305,7 +306,7 @@ export class GraphWatcher {
|
||||
}
|
||||
}
|
||||
|
||||
async getEntities<Entity> (
|
||||
async getEntities<Entity extends ObjectLiteral> (
|
||||
entity: new () => Entity,
|
||||
relationsMap: Map<any, { [key: string]: any }>,
|
||||
block: BlockHeight,
|
||||
|
@ -57,8 +57,8 @@
|
||||
"devDependencies": {
|
||||
"@ethersproject/abi": "^5.3.0",
|
||||
"@types/yargs": "^17.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"copyfiles": "^2.4.1",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-semistandard": "^15.0.1",
|
||||
@ -67,7 +67,7 @@
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-standard": "^5.0.0",
|
||||
"ts-node": "^10.0.0",
|
||||
"ts-node": "^10.2.1",
|
||||
"typescript": "^4.3.2"
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
import assert from 'assert';
|
||||
import debug from 'debug';
|
||||
import { DeepPartial, FindConditions, FindManyOptions } from 'typeorm';
|
||||
import { DeepPartial, FindConditions, FindManyOptions, ObjectLiteral } from 'typeorm';
|
||||
import JSONbig from 'json-bigint';
|
||||
import { ethers } from 'ethers';
|
||||
import { SelectionNode } from 'graphql';
|
||||
@ -313,7 +313,7 @@ export class Indexer implements IndexerInterface {
|
||||
await this._baseIndexer.removeStates(blockNumber, kind);
|
||||
}
|
||||
|
||||
async getSubgraphEntity<Entity> (
|
||||
async getSubgraphEntity<Entity extends ObjectLiteral> (
|
||||
entity: new () => Entity,
|
||||
id: string,
|
||||
block: BlockHeight,
|
||||
@ -324,7 +324,7 @@ export class Indexer implements IndexerInterface {
|
||||
return data;
|
||||
}
|
||||
|
||||
async getSubgraphEntities<Entity> (
|
||||
async getSubgraphEntities<Entity extends ObjectLiteral> (
|
||||
entity: new () => Entity,
|
||||
block: BlockHeight,
|
||||
where: { [key: string]: any } = {},
|
||||
|
@ -32,8 +32,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ws": "^8.5.3",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-semistandard": "^15.0.1",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
|
@ -55,8 +55,8 @@
|
||||
"devDependencies": {
|
||||
"@ethersproject/abi": "^5.3.0",
|
||||
"@types/yargs": "^17.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"copyfiles": "^2.4.1",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-semistandard": "^15.0.1",
|
||||
@ -65,7 +65,7 @@
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-standard": "^5.0.0",
|
||||
"ts-node": "^10.0.0",
|
||||
"ts-node": "^10.2.1",
|
||||
"typescript": "^4.3.2"
|
||||
}
|
||||
}
|
||||
|
2
packages/peer-test-app/.env
Normal file
2
packages/peer-test-app/.env
Normal file
@ -0,0 +1,2 @@
|
||||
REACT_APP_SIGNAL_SERVER=/ip4/127.0.0.1/tcp/13579/ws/p2p-webrtc-star/
|
||||
REACT_APP_RELAY_NODE=
|
23
packages/peer-test-app/.gitignore
vendored
Normal file
23
packages/peer-test-app/.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
116
packages/peer-test-app/README.md
Normal file
116
packages/peer-test-app/README.md
Normal file
@ -0,0 +1,116 @@
|
||||
# Getting Started with Create React App
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
## Instructions
|
||||
|
||||
* Install dependencies:
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
* Build the peer package:
|
||||
|
||||
```bash
|
||||
# From repo root
|
||||
cd packages/peer
|
||||
|
||||
yarn build
|
||||
```
|
||||
|
||||
* (Optional) Run a local signalling server:
|
||||
|
||||
```bash
|
||||
# In packages/peer
|
||||
yarn signal-server
|
||||
```
|
||||
|
||||
* (Optional) Create and export a peer id for the relay node:
|
||||
|
||||
```bash
|
||||
# In packages/peer
|
||||
yarn create-peer --file [PEER_ID_FILE_PATH]
|
||||
```
|
||||
|
||||
* `file (f)`: file path to export the peer id to (json)
|
||||
|
||||
* (Optional) Run a local relay node:
|
||||
|
||||
```bash
|
||||
# In packages/peer
|
||||
yarn relay-node --signal-server [SIGNAL_SERVER_URL] --peer-id-file [PEER_ID_FILE_PATH]
|
||||
```
|
||||
|
||||
* `signal-server`: multiaddr of a signalling server (default: local signalling server multiaddr)
|
||||
* `peer-id-file`: file path for peer id to be used (json)
|
||||
|
||||
* Set the signalling server and relay node multiaddrs in the [env](./.env) file:
|
||||
|
||||
```
|
||||
REACT_APP_SIGNAL_SERVER=/ip4/127.0.0.1/tcp/13579/ws/p2p-webrtc-star/
|
||||
REACT_APP_RELAY_NODE=/ip4/127.0.0.1/tcp/13579/wss/p2p-webrtc-star/p2p/12D3KooWRzH3ZRFP6RDbs2EKA8jSrD4Y6VYtLnCRMj3mYCiMHCJP
|
||||
```
|
||||
|
||||
* Start the react app in development mode:
|
||||
|
||||
```bash
|
||||
# In packages/peer-test-app
|
||||
yarn start
|
||||
```
|
||||
|
||||
* The app can be opened in multiple browsers
|
||||
|
||||
## Development
|
||||
|
||||
* After making changes in [peer](../peer/) package run build
|
||||
|
||||
```bash
|
||||
# In packages/peer
|
||||
yarn build
|
||||
```
|
||||
|
||||
* The react app server running in development mode should recompile after changes are made in peer package
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm start`
|
||||
|
||||
Runs the app in the development mode.\
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||
|
||||
The page will reload if you make edits.\
|
||||
You will also see any lint errors in the console.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.\
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `npm run build`
|
||||
|
||||
Builds the app for production to the `build` folder.\
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.\
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
### `npm run eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||
|
||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||
|
||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
55
packages/peer-test-app/package.json
Normal file
55
packages/peer-test-app/package.json
Normal file
@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "@cerc-io/peer-test-app",
|
||||
"version": "0.2.18",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@cerc-io/peer": "^0.2.18",
|
||||
"@cerc-io/react-peer": "^0.2.18",
|
||||
"@emotion/react": "^11.10.5",
|
||||
"@emotion/styled": "^11.10.5",
|
||||
"@mui/material": "^5.11.3",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^16.18.10",
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"it-pushable": "^3.1.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"typescript": "^4.9.4",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --watchAll=false",
|
||||
"eject": "react-scripts eject",
|
||||
"serve": "serve -s build"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.18.6",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"serve": "^14.1.2"
|
||||
}
|
||||
}
|
BIN
packages/peer-test-app/public/favicon.ico
Normal file
BIN
packages/peer-test-app/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
47
packages/peer-test-app/public/index.html
Normal file
47
packages/peer-test-app/public/index.html
Normal file
@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
|
||||
/>
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>Peer Test App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
BIN
packages/peer-test-app/public/logo192.png
Normal file
BIN
packages/peer-test-app/public/logo192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
BIN
packages/peer-test-app/public/logo512.png
Normal file
BIN
packages/peer-test-app/public/logo512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
25
packages/peer-test-app/public/manifest.json
Normal file
25
packages/peer-test-app/public/manifest.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
3
packages/peer-test-app/public/robots.txt
Normal file
3
packages/peer-test-app/public/robots.txt
Normal file
@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
34
packages/peer-test-app/src/App.css
Normal file
34
packages/peer-test-app/src/App.css
Normal file
@ -0,0 +1,34 @@
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
10
packages/peer-test-app/src/App.test.tsx
Normal file
10
packages/peer-test-app/src/App.test.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
// import App from './App';
|
||||
|
||||
// https://github.com/facebook/create-react-app/issues/12063
|
||||
xtest('renders learn react link', () => {
|
||||
// render(<App />);
|
||||
const linkElement = screen.getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
164
packages/peer-test-app/src/App.tsx
Normal file
164
packages/peer-test-app/src/App.tsx
Normal file
@ -0,0 +1,164 @@
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import { PeerContext } from '@cerc-io/react-peer'
|
||||
|
||||
import { Peer } from '@cerc-io/peer';
|
||||
import { createTheme, ThemeProvider } from '@mui/material/styles';
|
||||
import { AppBar, Box, CssBaseline, Paper, Table, TableBody, TableCell, TableContainer, TableRow, Toolbar, Typography } from '@mui/material';
|
||||
|
||||
import './App.css';
|
||||
import { useForceUpdate } from './hooks/forceUpdate';
|
||||
|
||||
declare global {
|
||||
interface Window { broadcast: (message: string) => void; }
|
||||
}
|
||||
|
||||
const theme = createTheme();
|
||||
|
||||
function App() {
|
||||
const forceUpdate = useForceUpdate();
|
||||
const peer: Peer = useContext(PeerContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (!peer || !peer.node) {
|
||||
return
|
||||
}
|
||||
|
||||
// Subscribe to messages from remote peers
|
||||
const unsubscribeMessage = peer.subscribeMessage((peerId, message) => {
|
||||
console.log(`${peerId.toString()} > ${message}`)
|
||||
})
|
||||
|
||||
// Expose broadcast method in browser to send messages
|
||||
window.broadcast = (message: string) => {
|
||||
peer.broadcastMessage(message)
|
||||
}
|
||||
|
||||
peer.node.peerStore.addEventListener('change:multiaddrs', forceUpdate)
|
||||
peer.node.connectionManager.addEventListener('peer:connect', forceUpdate)
|
||||
|
||||
let lastDisconnect = new Date()
|
||||
const disconnectHandler = () => {
|
||||
forceUpdate()
|
||||
|
||||
const now = new Date();
|
||||
const disconnectAfterSeconds = (now.getTime() - lastDisconnect.getTime()) / 1000;
|
||||
console.log("Disconnected after seconds:", disconnectAfterSeconds);
|
||||
lastDisconnect = now;
|
||||
}
|
||||
|
||||
peer.node.connectionManager.addEventListener('peer:disconnect', disconnectHandler)
|
||||
|
||||
return () => {
|
||||
unsubscribeMessage()
|
||||
peer.node?.peerStore.removeEventListener('change:multiaddrs', forceUpdate)
|
||||
peer.node?.connectionManager.removeEventListener('peer:connect', forceUpdate)
|
||||
peer.node?.connectionManager.removeEventListener('peer:disconnect', disconnectHandler)
|
||||
}
|
||||
}, [peer, forceUpdate])
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<AppBar position="relative">
|
||||
<Toolbar>
|
||||
<Typography variant="h6" color="inherit" noWrap>
|
||||
Peer Test App
|
||||
</Typography>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<main>
|
||||
<Box
|
||||
sx={{
|
||||
bgcolor: 'background.paper',
|
||||
py: 3,
|
||||
px: 3
|
||||
}}
|
||||
>
|
||||
<Typography variant="subtitle1" color="inherit" noWrap>
|
||||
Self Node Info
|
||||
</Typography>
|
||||
<br/>
|
||||
<TableContainer component={Paper}>
|
||||
<Table>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell><b>Peer ID</b></TableCell>
|
||||
<TableCell>{peer && peer.peerId && peer.peerId.toString()}</TableCell>
|
||||
<TableCell align="right"><b>Node started</b></TableCell>
|
||||
<TableCell>{peer && peer.node && peer.node.isStarted().toString()}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell><b>Signal server</b></TableCell>
|
||||
<TableCell>{process.env.REACT_APP_SIGNAL_SERVER}</TableCell>
|
||||
<TableCell align="right"><b>Relay node</b></TableCell>
|
||||
<TableCell>{process.env.REACT_APP_RELAY_NODE}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell><b>Multiaddrs</b></TableCell>
|
||||
<TableCell colSpan={3}>
|
||||
<TableContainer>
|
||||
<Table size="small">
|
||||
<TableBody>
|
||||
{
|
||||
peer && peer.node && peer.node.getMultiaddrs().map(multiaddr => (
|
||||
<TableRow key={multiaddr.toString()}>
|
||||
<TableCell sx={{ px: 0 }}>
|
||||
{multiaddr.toString()}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<br/>
|
||||
{
|
||||
peer && peer.node && (
|
||||
<>
|
||||
<Typography variant="subtitle1" color="inherit" noWrap>
|
||||
Remote Peer Connections (Count: {peer.node.connectionManager.getConnections().length})
|
||||
</Typography>
|
||||
<br/>
|
||||
{peer.node.connectionManager.getConnections().map(connection => (
|
||||
<TableContainer sx={{ mb: 2 }} key={connection.id} component={Paper}>
|
||||
<Table size="small">
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell sx={{ width: 175 }}><b>Connection ID</b></TableCell>
|
||||
<TableCell>{connection.id}</TableCell>
|
||||
<TableCell align="right"><b>Direction</b></TableCell>
|
||||
<TableCell>{connection.stat.direction}</TableCell>
|
||||
<TableCell align="right"><b>Status</b></TableCell>
|
||||
<TableCell>{connection.stat.status}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell sx={{ width: 175 }}><b>Peer ID</b></TableCell>
|
||||
<TableCell colSpan={5}>{connection.remotePeer.toString()}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell sx={{ width: 175 }}><b>Connected multiaddr</b></TableCell>
|
||||
<TableCell colSpan={5}>
|
||||
{connection.remoteAddr.toString()}
|
||||
|
||||
<b>{connection.remoteAddr.toString() === process.env.REACT_APP_RELAY_NODE && "(RELAY NODE)"}</b>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
</Box>
|
||||
</main>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
11
packages/peer-test-app/src/hooks/forceUpdate.ts
Normal file
11
packages/peer-test-app/src/hooks/forceUpdate.ts
Normal file
@ -0,0 +1,11 @@
|
||||
//
|
||||
// Copyright 2023 Vulcanize, Inc.
|
||||
//
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
export function useForceUpdate(){
|
||||
const [, setValue] = useState(0); // integer state
|
||||
|
||||
return () => setValue(value => value + 1); // update state to force render
|
||||
}
|
13
packages/peer-test-app/src/index.css
Normal file
13
packages/peer-test-app/src/index.css
Normal file
@ -0,0 +1,13 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
24
packages/peer-test-app/src/index.tsx
Normal file
24
packages/peer-test-app/src/index.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { PeerProvider } from '@cerc-io/react-peer';
|
||||
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
);
|
||||
root.render(
|
||||
// TODO: StrictMode renders component twice in development which currently causes problems with instantiating peer node.
|
||||
// <React.StrictMode>
|
||||
<PeerProvider signalServer={process.env.REACT_APP_SIGNAL_SERVER} relayNode={process.env.REACT_APP_RELAY_NODE}>
|
||||
<App />
|
||||
</PeerProvider>
|
||||
// </React.StrictMode>
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
// to log results (for example: reportWebVitals(console.log))
|
||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||
reportWebVitals();
|
1
packages/peer-test-app/src/logo.svg
Normal file
1
packages/peer-test-app/src/logo.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
After Width: | Height: | Size: 2.6 KiB |
1
packages/peer-test-app/src/react-app-env.d.ts
vendored
Normal file
1
packages/peer-test-app/src/react-app-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
15
packages/peer-test-app/src/reportWebVitals.ts
Normal file
15
packages/peer-test-app/src/reportWebVitals.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { ReportHandler } from 'web-vitals';
|
||||
|
||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default reportWebVitals;
|
5
packages/peer-test-app/src/setupTests.ts
Normal file
5
packages/peer-test-app/src/setupTests.ts
Normal file
@ -0,0 +1,5 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
5
packages/peer-test-app/src/types/common/main.d.ts
vendored
Normal file
5
packages/peer-test-app/src/types/common/main.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
//
|
||||
// Copyright 2022 Vulcanize, Inc.
|
||||
//
|
||||
|
||||
declare module '@cerc-io/react-peer';
|
6
packages/peer-test-app/src/types/common/package.json
Normal file
6
packages/peer-test-app/src/types/common/package.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "common",
|
||||
"version": "0.1.0",
|
||||
"license": "AGPL-3.0",
|
||||
"typings": "main.d.ts"
|
||||
}
|
26
packages/peer-test-app/tsconfig.json
Normal file
26
packages/peer-test-app/tsconfig.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
5
packages/peer/.eslintignore
Normal file
5
packages/peer/.eslintignore
Normal file
@ -0,0 +1,5 @@
|
||||
# Don't lint node_modules.
|
||||
node_modules
|
||||
|
||||
# Don't lint build output.
|
||||
dist
|
21
packages/peer/.eslintrc.json
Normal file
21
packages/peer/.eslintrc.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"semistandard",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 12,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-explicit-any": "off"
|
||||
}
|
||||
}
|
11
packages/peer/.gitignore
vendored
Normal file
11
packages/peer/.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
node_modules
|
||||
|
||||
#Hardhat files
|
||||
cache
|
||||
artifacts
|
||||
|
||||
#yarn
|
||||
yarn-error.log
|
||||
|
||||
#Environment variables
|
||||
.env
|
15
packages/peer/README.md
Normal file
15
packages/peer/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# peer
|
||||
|
||||
Package used for connecting between peers and send messages
|
||||
|
||||
## Implementations
|
||||
|
||||
- [x] Discover peers
|
||||
- [x] Connect between peers and send messages
|
||||
- [x] Use package in browser
|
||||
- [x] Use package in server
|
||||
- [x] Send messages between systems in different LANs using relay node
|
||||
|
||||
## Known Issues
|
||||
|
||||
- `peer:disconnect` event is not fired when remote peer browser is closed
|
62
packages/peer/package.json
Normal file
62
packages/peer/package.json
Normal file
@ -0,0 +1,62 @@
|
||||
{
|
||||
"name": "@cerc-io/peer",
|
||||
"version": "0.2.18",
|
||||
"description": "libp2p module",
|
||||
"main": "dist/index.js",
|
||||
"exports": "./dist/index.js",
|
||||
"type": "module",
|
||||
"license": "AGPL-version-3.0",
|
||||
"private": false,
|
||||
"engines": {
|
||||
"node": ">=14.16",
|
||||
"npm": ">= 6.0.0"
|
||||
},
|
||||
"homepage": "",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": ""
|
||||
},
|
||||
"bugs": "",
|
||||
"keywords": [],
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"lint": "eslint .",
|
||||
"dev": "node dist/index.js",
|
||||
"signal-server": "webrtc-star --port=13579 --host=0.0.0.0",
|
||||
"create-peer": "node dist/create-peer.js",
|
||||
"relay-node": "node dist/relay.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chainsafe/libp2p-noise": "^10.2.0",
|
||||
"@libp2p/floodsub": "^5.0.0",
|
||||
"@libp2p/interface-peer-id": "^1.1.2",
|
||||
"@libp2p/mplex": "^7.1.1",
|
||||
"@libp2p/peer-id-factory": "^2.0.0",
|
||||
"@libp2p/pubsub-peer-discovery": "^7.0.1",
|
||||
"@libp2p/webrtc-star": "^5.0.3",
|
||||
"@multiformats/multiaddr": "^11.1.4",
|
||||
"it-length-prefixed": "^8.0.4",
|
||||
"it-map": "^2.0.0",
|
||||
"it-pipe": "^2.0.5",
|
||||
"it-pushable": "^3.1.2",
|
||||
"libp2p": "^0.41.0",
|
||||
"node-pre-gyp": "^0.13.0",
|
||||
"uint8arrays": "^4.0.3",
|
||||
"wrtc": "^0.4.7",
|
||||
"yargs": "^17.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@libp2p/webrtc-star-signalling-server": "^2.0.5",
|
||||
"@types/node": "16.11.7",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-semistandard": "^15.0.1",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
"eslint-plugin-import": "^2.23.3",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-standard": "^5.0.0",
|
||||
"typescript": "^4.9.4"
|
||||
}
|
||||
}
|
6
packages/peer/src/constants.ts
Normal file
6
packages/peer/src/constants.ts
Normal file
@ -0,0 +1,6 @@
|
||||
//
|
||||
// Copyright 2023 Vulcanize, Inc.
|
||||
//
|
||||
|
||||
export const PUBSUB_DISCOVERY_INTERVAL = 10000; // 10 seconds
|
||||
export const HOP_TIMEOUT = 24 * 60 * 60 * 1000; // 1 day
|
55
packages/peer/src/create-peer.ts
Normal file
55
packages/peer/src/create-peer.ts
Normal file
@ -0,0 +1,55 @@
|
||||
//
|
||||
// Copyright 2022 Vulcanize, Inc.
|
||||
//
|
||||
|
||||
import assert from 'assert';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
import yargs from 'yargs';
|
||||
|
||||
import { createEd25519PeerId } from '@libp2p/peer-id-factory';
|
||||
|
||||
interface Arguments {
|
||||
file: string;
|
||||
}
|
||||
|
||||
async function main (): Promise<void> {
|
||||
const argv: Arguments = _getArgv();
|
||||
|
||||
const exportFilePath = path.resolve(argv.file);
|
||||
const exportFileDir = path.dirname(exportFilePath);
|
||||
|
||||
const peerId = await createEd25519PeerId();
|
||||
assert(peerId.privateKey);
|
||||
|
||||
const obj = {
|
||||
id: peerId.toString(),
|
||||
privKey: Buffer.from(peerId.privateKey).toString('base64'),
|
||||
pubKey: Buffer.from(peerId.publicKey).toString('base64')
|
||||
};
|
||||
|
||||
if (!fs.existsSync(exportFileDir)) {
|
||||
fs.mkdirSync(exportFileDir, { recursive: true });
|
||||
}
|
||||
|
||||
fs.writeFileSync(exportFilePath, JSON.stringify(obj));
|
||||
console.log(`Peer id ${peerId.toString()} exported to file ${exportFilePath}`);
|
||||
}
|
||||
|
||||
function _getArgv (): any {
|
||||
return yargs(hideBin(process.argv)).parserConfiguration({
|
||||
'parse-numbers': false
|
||||
}).options({
|
||||
file: {
|
||||
type: 'string',
|
||||
alias: 'f',
|
||||
describe: 'Peer Id export file path (json)',
|
||||
demandOption: true
|
||||
}
|
||||
}).argv;
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.log(err);
|
||||
});
|
276
packages/peer/src/index.ts
Normal file
276
packages/peer/src/index.ts
Normal file
@ -0,0 +1,276 @@
|
||||
//
|
||||
// Copyright 2022 Vulcanize, Inc.
|
||||
//
|
||||
|
||||
import { createLibp2p, Libp2p } from 'libp2p';
|
||||
// For nodejs.
|
||||
import wrtc from 'wrtc';
|
||||
import assert from 'assert';
|
||||
import { pipe } from 'it-pipe';
|
||||
import * as lp from 'it-length-prefixed';
|
||||
import map from 'it-map';
|
||||
import { pushable, Pushable } from 'it-pushable';
|
||||
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
|
||||
import { toString as uint8ArrayToString } from 'uint8arrays/to-string';
|
||||
|
||||
import { webRTCStar, WebRTCStarTuple } from '@libp2p/webrtc-star';
|
||||
import { noise } from '@chainsafe/libp2p-noise';
|
||||
import { mplex } from '@libp2p/mplex';
|
||||
import type { Stream as P2PStream, Connection } from '@libp2p/interface-connection';
|
||||
import type { PeerInfo } from '@libp2p/interface-peer-info';
|
||||
import { PeerId } from '@libp2p/interface-peer-id';
|
||||
import { multiaddr, Multiaddr } from '@multiformats/multiaddr';
|
||||
import { floodsub } from '@libp2p/floodsub';
|
||||
import { pubsubPeerDiscovery } from '@libp2p/pubsub-peer-discovery';
|
||||
|
||||
import { PUBSUB_DISCOVERY_INTERVAL } from './constants.js';
|
||||
|
||||
export const CHAT_PROTOCOL = '/chat/1.0.0';
|
||||
export const DEFAULT_SIGNAL_SERVER_URL = '/ip4/127.0.0.1/tcp/13579/wss/p2p-webrtc-star';
|
||||
|
||||
export class Peer {
|
||||
_node?: Libp2p
|
||||
_wrtcStar: WebRTCStarTuple
|
||||
_relayNodeMultiaddr?: Multiaddr
|
||||
|
||||
_remotePeerIds: PeerId[] = []
|
||||
_peerStreamMap: Map<string, Pushable<any>> = new Map()
|
||||
_messageHandlers: Array<(peerId: PeerId, message: any) => void> = []
|
||||
|
||||
constructor (nodejs?: boolean) {
|
||||
// Instantiation in nodejs.
|
||||
if (nodejs) {
|
||||
this._wrtcStar = webRTCStar({ wrtc });
|
||||
} else {
|
||||
this._wrtcStar = webRTCStar();
|
||||
}
|
||||
}
|
||||
|
||||
get peerId (): PeerId | undefined {
|
||||
return this._node?.peerId;
|
||||
}
|
||||
|
||||
get node (): Libp2p | undefined {
|
||||
return this._node;
|
||||
}
|
||||
|
||||
async init (signalServerURL = DEFAULT_SIGNAL_SERVER_URL, relayNodeURL?: string): Promise<void> {
|
||||
let peerDiscovery: any;
|
||||
if (relayNodeURL) {
|
||||
this._relayNodeMultiaddr = multiaddr(relayNodeURL);
|
||||
|
||||
peerDiscovery = [
|
||||
pubsubPeerDiscovery({
|
||||
interval: PUBSUB_DISCOVERY_INTERVAL
|
||||
})
|
||||
];
|
||||
} else {
|
||||
peerDiscovery = [this._wrtcStar.discovery];
|
||||
}
|
||||
|
||||
this._node = await createLibp2p({
|
||||
addresses: {
|
||||
// Add the signaling server address, along with our PeerId to our multiaddrs list
|
||||
// libp2p will automatically attempt to dial to the signaling server so that it can
|
||||
// receive inbound connections from other peers
|
||||
listen: [
|
||||
// Public signal servers
|
||||
// '/dns4/wrtc-star1.par.dwebops.pub/tcp/443/wss/p2p-webrtc-star',
|
||||
// '/dns4/wrtc-star2.sjc.dwebops.pub/tcp/443/wss/p2p-webrtc-star'
|
||||
signalServerURL
|
||||
]
|
||||
},
|
||||
transports: [
|
||||
this._wrtcStar.transport
|
||||
],
|
||||
connectionEncryption: [noise()],
|
||||
streamMuxers: [mplex()],
|
||||
pubsub: floodsub(),
|
||||
peerDiscovery,
|
||||
relay: {
|
||||
enabled: true,
|
||||
autoRelay: {
|
||||
enabled: true,
|
||||
maxListeners: 2
|
||||
}
|
||||
},
|
||||
connectionManager: {
|
||||
maxDialsPerPeer: 3, // Number of max concurrent dials per peer
|
||||
autoDial: false
|
||||
}
|
||||
});
|
||||
|
||||
console.log('libp2p node created', this._node);
|
||||
|
||||
// Dial to the HOP enabled relay node if available
|
||||
if (this._relayNodeMultiaddr) {
|
||||
const relayMultiaddr = this._relayNodeMultiaddr;
|
||||
|
||||
console.log(`Dialling relay node ${relayMultiaddr.getPeerId()} using multiaddr ${relayMultiaddr.toString()}`);
|
||||
await this._node.dial(relayMultiaddr);
|
||||
}
|
||||
|
||||
// Listen for change in stored multiaddrs
|
||||
this._node.peerStore.addEventListener('change:multiaddrs', (evt) => {
|
||||
assert(this._node);
|
||||
const { peerId, multiaddrs } = evt.detail;
|
||||
|
||||
// Log updated self multiaddrs
|
||||
if (peerId.equals(this._node.peerId)) {
|
||||
console.log('Updated self multiaddrs', this._node.getMultiaddrs().map(addr => addr.toString()));
|
||||
} else {
|
||||
console.log('Updated peer node multiaddrs', multiaddrs.map((addr: Multiaddr) => addr.toString()));
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for peers discovery
|
||||
this._node.addEventListener('peer:discovery', (evt) => {
|
||||
// console.log('event peer:discovery', evt);
|
||||
this._handleDiscovery(evt.detail);
|
||||
});
|
||||
|
||||
// Listen for peers connection
|
||||
this._node.connectionManager.addEventListener('peer:connect', (evt) => {
|
||||
console.log('event peer:connect', evt);
|
||||
this._handleConnect(evt.detail);
|
||||
});
|
||||
|
||||
// Listen for peers disconnecting
|
||||
this._node.connectionManager.addEventListener('peer:disconnect', (evt) => {
|
||||
console.log('event peer:disconnect', evt);
|
||||
this._handleDisconnect(evt.detail);
|
||||
});
|
||||
|
||||
// Handle messages for the protocol
|
||||
await this._node.handle(CHAT_PROTOCOL, async ({ stream, connection }) => {
|
||||
this._handleStream(connection.remotePeer, stream);
|
||||
});
|
||||
}
|
||||
|
||||
async close (): Promise<void> {
|
||||
assert(this._node);
|
||||
|
||||
this._node.removeEventListener('peer:discovery');
|
||||
this._node.connectionManager.removeEventListener('peer:connect');
|
||||
this._node.connectionManager.removeEventListener('peer:disconnect');
|
||||
|
||||
await this._node.unhandle(CHAT_PROTOCOL);
|
||||
const hangUpPromises = this._remotePeerIds.map(async peerId => this._node?.hangUp(peerId));
|
||||
await Promise.all(hangUpPromises);
|
||||
}
|
||||
|
||||
broadcastMessage (message: any): void {
|
||||
for (const [, stream] of this._peerStreamMap) {
|
||||
stream.push(message);
|
||||
}
|
||||
}
|
||||
|
||||
subscribeMessage (handler: (peerId: PeerId, message: any) => void) : () => void {
|
||||
this._messageHandlers.push(handler);
|
||||
|
||||
const unsubscribe = () => {
|
||||
this._messageHandlers = this._messageHandlers
|
||||
.filter(registeredHandler => registeredHandler !== handler);
|
||||
};
|
||||
|
||||
return unsubscribe;
|
||||
}
|
||||
|
||||
_handleDiscovery (peer: PeerInfo): void {
|
||||
// Check connected peers as they are discovered repeatedly.
|
||||
if (!this._remotePeerIds.some(remotePeerId => remotePeerId.toString() === peer.id.toString())) {
|
||||
console.log('Discovered peer multiaddrs', peer.multiaddrs.map(addr => addr.toString()));
|
||||
this._connectPeer(peer);
|
||||
}
|
||||
}
|
||||
|
||||
_handleConnect (connection: Connection): void {
|
||||
const remotePeerId = connection.remotePeer;
|
||||
this._remotePeerIds.push(remotePeerId);
|
||||
|
||||
// Log connected peer
|
||||
console.log(`Connected to ${remotePeerId.toString()} using multiaddr ${connection.remoteAddr.toString()}`);
|
||||
}
|
||||
|
||||
_handleDisconnect (connection: Connection): void {
|
||||
const disconnectedPeerId = connection.remotePeer;
|
||||
this._remotePeerIds = this._remotePeerIds.filter(remotePeerId => remotePeerId.toString() !== disconnectedPeerId.toString());
|
||||
|
||||
// Log disconnected peer
|
||||
console.log(`Disconnected from ${disconnectedPeerId.toString()} using multiaddr ${connection.remoteAddr.toString()}`);
|
||||
}
|
||||
|
||||
async _connectPeer (peer: PeerInfo): Promise<void> {
|
||||
assert(this._node);
|
||||
|
||||
// Check if discovered the relay node
|
||||
if (this._relayNodeMultiaddr) {
|
||||
const relayMultiaddr = this._relayNodeMultiaddr;
|
||||
const relayNodePeerId = relayMultiaddr.getPeerId();
|
||||
|
||||
if (relayNodePeerId && relayNodePeerId === peer.id.toString()) {
|
||||
console.log(`Dialling relay peer ${peer.id.toString()} using multiaddr ${relayMultiaddr.toString()}`);
|
||||
await this._node.dial(relayMultiaddr).catch(err => {
|
||||
console.log(`Could not dial relay ${relayMultiaddr.toString()}`, err);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Dial them when we discover them
|
||||
// Attempt to dial all the multiaddrs of the discovered peer (to connect through relay)
|
||||
for (const peerMultiaddr of peer.multiaddrs) {
|
||||
try {
|
||||
console.log(`Dialling peer ${peer.id.toString()} using multiaddr ${peerMultiaddr.toString()}`);
|
||||
const stream = await this._node.dialProtocol(peerMultiaddr, CHAT_PROTOCOL);
|
||||
|
||||
this._handleStream(peer.id, stream);
|
||||
break;
|
||||
} catch (err) {
|
||||
console.log(`Could not dial ${peerMultiaddr.toString()}`, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_handleStream (peerId: PeerId, stream: P2PStream): void {
|
||||
// console.log('Stream after connection', stream);
|
||||
const messageStream = pushable<any>({ objectMode: true });
|
||||
|
||||
// Send message to pipe from stdin
|
||||
pipe(
|
||||
// Read from stream (the source)
|
||||
messageStream,
|
||||
// Turn objects into buffers
|
||||
(source) => map(source, (value) => {
|
||||
return uint8ArrayFromString(JSON.stringify(value));
|
||||
}),
|
||||
// Encode with length prefix (so receiving side knows how much data is coming)
|
||||
lp.encode(),
|
||||
// Write to the stream (the sink)
|
||||
stream.sink
|
||||
);
|
||||
|
||||
// Handle message from stream
|
||||
pipe(
|
||||
// Read from the stream (the source)
|
||||
stream.source,
|
||||
// Decode length-prefixed data
|
||||
lp.decode(),
|
||||
// Turn buffers into objects
|
||||
(source) => map(source, (buf) => {
|
||||
return JSON.parse(uint8ArrayToString(buf.subarray()));
|
||||
}),
|
||||
// Sink function
|
||||
async (source) => {
|
||||
// For each chunk of data
|
||||
for await (const msg of source) {
|
||||
this._messageHandlers.forEach(messageHandler => messageHandler(peerId, msg));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// TODO: Check if stream already exists for peer id
|
||||
this._peerStreamMap.set(peerId.toString(), messageStream);
|
||||
}
|
||||
}
|
98
packages/peer/src/relay.ts
Normal file
98
packages/peer/src/relay.ts
Normal file
@ -0,0 +1,98 @@
|
||||
//
|
||||
// Copyright 2022 Vulcanize, Inc.
|
||||
//
|
||||
|
||||
import { createLibp2p } from 'libp2p';
|
||||
import wrtc from 'wrtc';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
import yargs from 'yargs';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import { noise } from '@chainsafe/libp2p-noise';
|
||||
import { mplex } from '@libp2p/mplex';
|
||||
import { webRTCStar, WebRTCStarTuple } from '@libp2p/webrtc-star';
|
||||
import { floodsub } from '@libp2p/floodsub';
|
||||
import { pubsubPeerDiscovery } from '@libp2p/pubsub-peer-discovery';
|
||||
import { createFromJSON } from '@libp2p/peer-id-factory';
|
||||
|
||||
import { DEFAULT_SIGNAL_SERVER_URL } from './index.js';
|
||||
import { HOP_TIMEOUT, PUBSUB_DISCOVERY_INTERVAL } from './constants.js';
|
||||
|
||||
interface Arguments {
|
||||
signalServer: string;
|
||||
peerIdFile: string;
|
||||
}
|
||||
|
||||
async function main (): Promise<void> {
|
||||
const argv: Arguments = _getArgv();
|
||||
if (!argv.signalServer) {
|
||||
console.log('Using the default signalling server URL');
|
||||
}
|
||||
|
||||
let peerId: any;
|
||||
if (argv.peerIdFile) {
|
||||
const peerIdFilePath = path.resolve(argv.peerIdFile);
|
||||
console.log(`Reading peer id from file ${peerIdFilePath}`);
|
||||
|
||||
const peerIdObj = fs.readFileSync(peerIdFilePath, 'utf-8');
|
||||
const peerIdJson = JSON.parse(peerIdObj);
|
||||
peerId = await createFromJSON(peerIdJson);
|
||||
} else {
|
||||
console.log('Creating a new peer id');
|
||||
}
|
||||
|
||||
const wrtcStar: WebRTCStarTuple = webRTCStar({ wrtc });
|
||||
const node = await createLibp2p({
|
||||
peerId,
|
||||
addresses: {
|
||||
listen: [
|
||||
argv.signalServer || DEFAULT_SIGNAL_SERVER_URL
|
||||
]
|
||||
},
|
||||
transports: [
|
||||
wrtcStar.transport
|
||||
],
|
||||
connectionEncryption: [noise()],
|
||||
streamMuxers: [mplex()],
|
||||
pubsub: floodsub(),
|
||||
peerDiscovery: [
|
||||
pubsubPeerDiscovery({
|
||||
interval: PUBSUB_DISCOVERY_INTERVAL
|
||||
})
|
||||
],
|
||||
relay: {
|
||||
enabled: true,
|
||||
hop: {
|
||||
enabled: true,
|
||||
timeout: HOP_TIMEOUT
|
||||
},
|
||||
advertise: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Relay node started with id ${node.peerId.toString()}`);
|
||||
console.log('Listening on:');
|
||||
node.getMultiaddrs().forEach((ma) => console.log(ma.toString()));
|
||||
}
|
||||
|
||||
function _getArgv (): any {
|
||||
return yargs(hideBin(process.argv)).parserConfiguration({
|
||||
'parse-numbers': false
|
||||
}).options({
|
||||
signalServer: {
|
||||
type: 'string',
|
||||
describe: 'Signalling server URL'
|
||||
},
|
||||
peerIdFile: {
|
||||
type: 'string',
|
||||
describe: 'Relay Peer Id file path (json)'
|
||||
}
|
||||
}).argv;
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.log(err);
|
||||
});
|
5
packages/peer/src/types/common/main.d.ts
vendored
Normal file
5
packages/peer/src/types/common/main.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
//
|
||||
// Copyright 2022 Vulcanize, Inc.
|
||||
//
|
||||
|
||||
declare module 'wrtc';
|
6
packages/peer/src/types/common/package.json
Normal file
6
packages/peer/src/types/common/package.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "common",
|
||||
"version": "0.1.0",
|
||||
"license": "AGPL-3.0",
|
||||
"typings": "main.d.ts"
|
||||
}
|
78
packages/peer/tsconfig.json
Normal file
78
packages/peer/tsconfig.json
Normal file
@ -0,0 +1,78 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
|
||||
"module": "node16", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
"lib": [ "ES5", "ES6", "ES2020" ], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
|
||||
"declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
"sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
"outDir": "dist", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "composite": true, /* Enable project compilation */
|
||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
"downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
"moduleResolution": "node16", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
"typeRoots": [
|
||||
"./src/types",
|
||||
"node_modules/@types"
|
||||
], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
|
||||
/* Advanced Options */
|
||||
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
||||
"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
|
||||
"resolveJsonModule": true /* Enabling the option allows importing JSON, and validating the types in that JSON file. */
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["test", "dist", "artifacts", "cache"]
|
||||
}
|
7
packages/react-peer/.gitignore
vendored
Normal file
7
packages/react-peer/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
/coverage
|
||||
/demo/dist
|
||||
/es
|
||||
/lib
|
||||
/node_modules
|
||||
/umd
|
||||
npm-debug.log*
|
16
packages/react-peer/.travis.yml
Normal file
16
packages/react-peer/.travis.yml
Normal file
@ -0,0 +1,16 @@
|
||||
sudo: false
|
||||
|
||||
language: node_js
|
||||
node_js:
|
||||
- 10
|
||||
|
||||
before_install:
|
||||
- npm install codecov.io coveralls
|
||||
|
||||
after_success:
|
||||
- cat ./coverage/lcov.info | ./node_modules/codecov.io/bin/codecov.io.js
|
||||
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
25
packages/react-peer/CONTRIBUTING.md
Normal file
25
packages/react-peer/CONTRIBUTING.md
Normal file
@ -0,0 +1,25 @@
|
||||
## Prerequisites
|
||||
|
||||
[Node.js](http://nodejs.org/) >= 10 must be installed.
|
||||
|
||||
## Installation
|
||||
|
||||
- Running `npm install` in the component's root directory will install everything you need for development.
|
||||
|
||||
## Demo Development Server
|
||||
|
||||
- `npm start` will run a development server with the component's demo app at [http://localhost:3000](http://localhost:3000) with hot module reloading.
|
||||
|
||||
## Running Tests
|
||||
|
||||
- `npm test` will run the tests once.
|
||||
|
||||
- `npm run test:coverage` will run the tests and produce a coverage report in `coverage/`.
|
||||
|
||||
- `npm run test:watch` will run the tests on every change.
|
||||
|
||||
## Building
|
||||
|
||||
- `npm run build` will build the component for publishing to npm and also bundle the demo app.
|
||||
|
||||
- `npm run clean` will delete built resources.
|
16
packages/react-peer/README.md
Normal file
16
packages/react-peer/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
# react-peer
|
||||
|
||||
[![Travis][build-badge]][build]
|
||||
[![npm package][npm-badge]][npm]
|
||||
[![Coveralls][coveralls-badge]][coveralls]
|
||||
|
||||
Describe react-peer here.
|
||||
|
||||
[build-badge]: https://img.shields.io/travis/user/repo/master.png?style=flat-square
|
||||
[build]: https://travis-ci.org/user/repo
|
||||
|
||||
[npm-badge]: https://img.shields.io/npm/v/npm-package.png?style=flat-square
|
||||
[npm]: https://www.npmjs.org/package/npm-package
|
||||
|
||||
[coveralls-badge]: https://img.shields.io/coveralls/user/repo/master.png?style=flat-square
|
||||
[coveralls]: https://coveralls.io/github/user/repo
|
16
packages/react-peer/demo/src/index.js
vendored
Normal file
16
packages/react-peer/demo/src/index.js
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
import React, {Component} from 'react'
|
||||
import {render} from 'react-dom'
|
||||
|
||||
import { PeerProvider } from '../../src'
|
||||
|
||||
export default class Demo extends Component {
|
||||
render() {
|
||||
return <PeerProvider>
|
||||
<div>
|
||||
<h1>react-peer Demo</h1>
|
||||
</div>
|
||||
</PeerProvider>
|
||||
}
|
||||
}
|
||||
|
||||
render(<Demo/>, document.querySelector('#demo'))
|
7
packages/react-peer/nwb.config.js
Normal file
7
packages/react-peer/nwb.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
type: 'react-component',
|
||||
npm: {
|
||||
esModules: false,
|
||||
umd: false
|
||||
}
|
||||
}
|
38
packages/react-peer/package.json
Normal file
38
packages/react-peer/package.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@cerc-io/react-peer",
|
||||
"version": "0.2.18",
|
||||
"description": "react-peer React component",
|
||||
"main": "lib/index.js",
|
||||
"files": [
|
||||
"css",
|
||||
"es",
|
||||
"lib",
|
||||
"umd"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "nwb build-react-component --no-demo",
|
||||
"clean": "nwb clean-module && nwb clean-demo",
|
||||
"prepublishOnly": "npm run build",
|
||||
"start": "nwb serve-react-demo",
|
||||
"test:coverage": "nwb test-react --coverage",
|
||||
"test:watch": "nwb test-react --server"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cerc-io/peer": "^0.2.18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nwb": "0.25.x",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"author": "",
|
||||
"homepage": "",
|
||||
"license": "MIT",
|
||||
"repository": "",
|
||||
"keywords": [
|
||||
"react-component"
|
||||
]
|
||||
}
|
3
packages/react-peer/src/context/PeerContext.js
vendored
Normal file
3
packages/react-peer/src/context/PeerContext.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
import React from "react";
|
||||
|
||||
export const PeerContext = React.createContext({});
|
36
packages/react-peer/src/context/PeerProvider.js
vendored
Normal file
36
packages/react-peer/src/context/PeerProvider.js
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Peer } from '@cerc-io/peer';
|
||||
|
||||
import { PeerContext } from './PeerContext';
|
||||
|
||||
export const PeerProvider = ({ signalServer, relayNode, children }) => {
|
||||
const [peer, setPeer] = React.useState(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
const init = async () => {
|
||||
const peer = new Peer()
|
||||
await peer.init(signalServer, relayNode);
|
||||
|
||||
// Debug
|
||||
console.log(`Peer ID: ${peer.peerId.toString()}`);
|
||||
|
||||
setPeer(peer);
|
||||
};
|
||||
|
||||
init();
|
||||
|
||||
return () => {
|
||||
if (peer.node) {
|
||||
// TODO: Await for peer close
|
||||
peer.close()
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<PeerContext.Provider value={peer}>
|
||||
{children}
|
||||
</PeerContext.Provider>
|
||||
);
|
||||
};
|
2
packages/react-peer/src/index.js
vendored
Normal file
2
packages/react-peer/src/index.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
export { PeerContext } from './context/PeerContext'
|
||||
export { PeerProvider } from './context/PeerProvider'
|
@ -9,8 +9,8 @@
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.2",
|
||||
"@nomiclabs/hardhat-waffle": "^2.0.1",
|
||||
"@types/chai": "^4.2.18",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"chai": "^4.3.4",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-semistandard": "^15.0.1",
|
||||
|
@ -23,10 +23,10 @@
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.2.19",
|
||||
"@types/mocha": "^8.2.2",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"eslint": "^7.27.0",
|
||||
"ts-node": "^10.0.0",
|
||||
"ts-node": "^10.2.1",
|
||||
"typescript": "^4.3.2"
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,8 @@
|
||||
"yargs": "^17.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-semistandard": "^15.0.1",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
|
@ -45,8 +45,8 @@
|
||||
"@types/js-yaml": "^4.0.4",
|
||||
"@types/pg": "^8.6.5",
|
||||
"@types/ws": "^8.5.3",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.1",
|
||||
"@typescript-eslint/parser": "^5.47.1",
|
||||
"decimal.js": "^10.3.1",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-semistandard": "^15.0.1",
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
FindConditions,
|
||||
FindManyOptions,
|
||||
In,
|
||||
ObjectLiteral,
|
||||
QueryRunner,
|
||||
Repository,
|
||||
SelectQueryBuilder
|
||||
@ -427,7 +428,7 @@ export class Database {
|
||||
return event;
|
||||
}
|
||||
|
||||
async getFrothyEntity<Entity> (queryRunner: QueryRunner, repo: Repository<Entity>, data: { blockHash: string, id: string }): Promise<{ blockHash: string, blockNumber: number, id: string }> {
|
||||
async getFrothyEntity<Entity extends ObjectLiteral> (queryRunner: QueryRunner, repo: Repository<Entity>, data: { blockHash: string, id: string }): Promise<{ blockHash: string, blockNumber: number, id: string }> {
|
||||
// Hierarchical query for getting the entity in the frothy region.
|
||||
const heirerchicalQuery = `
|
||||
WITH RECURSIVE cte_query AS
|
||||
@ -478,7 +479,7 @@ export class Database {
|
||||
return { blockHash, blockNumber, id };
|
||||
}
|
||||
|
||||
async getPrevEntityVersion<Entity> (queryRunner: QueryRunner, repo: Repository<Entity>, findOptions: { [key: string]: any }): Promise<Entity | undefined> {
|
||||
async getPrevEntityVersion<Entity extends ObjectLiteral> (queryRunner: QueryRunner, repo: Repository<Entity>, findOptions: { [key: string]: any }): Promise<Entity | undefined> {
|
||||
const { blockHash, blockNumber, id } = await this.getFrothyEntity(queryRunner, repo, findOptions.where);
|
||||
|
||||
if (id) {
|
||||
@ -491,7 +492,7 @@ export class Database {
|
||||
return this.getLatestPrunedEntity(repo, findOptions.where.id, blockNumber + 1);
|
||||
}
|
||||
|
||||
async getLatestPrunedEntity<Entity> (repo: Repository<Entity>, id: string, canonicalBlockNumber: number): Promise<Entity | undefined> {
|
||||
async getLatestPrunedEntity<Entity extends ObjectLiteral> (repo: Repository<Entity>, id: string, canonicalBlockNumber: number): Promise<Entity | undefined> {
|
||||
// Filter out latest entity from pruned blocks.
|
||||
const entityInPrunedRegion = await repo.createQueryBuilder('entity')
|
||||
.where('entity.id = :id', { id })
|
||||
@ -797,7 +798,7 @@ export class Database {
|
||||
return repo.save(entity);
|
||||
}
|
||||
|
||||
buildQuery<Entity> (
|
||||
buildQuery<Entity extends ObjectLiteral> (
|
||||
repo: Repository<Entity>,
|
||||
selectQueryBuilder: SelectQueryBuilder<Entity>,
|
||||
where: Where = {},
|
||||
@ -867,7 +868,7 @@ export class Database {
|
||||
return selectQueryBuilder;
|
||||
}
|
||||
|
||||
orderQuery<Entity> (
|
||||
orderQuery<Entity extends ObjectLiteral> (
|
||||
repo: Repository<Entity>,
|
||||
selectQueryBuilder: SelectQueryBuilder<Entity>,
|
||||
orderOptions: { orderBy?: string, orderDirection?: string },
|
||||
|
@ -3,9 +3,9 @@ import { utils } from 'ethers';
|
||||
|
||||
const log = debug('vulcanize:eth');
|
||||
|
||||
function decodeInteger(value : string, defaultValue: BigInt): BigInt
|
||||
function decodeInteger(value : string) : BigInt | undefined
|
||||
function decodeInteger (value : string, defaultValue?: BigInt): BigInt | undefined {
|
||||
function decodeInteger(value : string, defaultValue: bigint): bigint
|
||||
function decodeInteger(value : string) : bigint | undefined
|
||||
function decodeInteger (value : string, defaultValue?: bigint): bigint | undefined {
|
||||
if (value === undefined || value === null || value.length === 0) return defaultValue;
|
||||
if (value === '0x') return BigInt(0);
|
||||
return BigInt(value);
|
||||
|
@ -13,7 +13,8 @@ import {
|
||||
QueryRunner,
|
||||
Repository,
|
||||
SelectQueryBuilder,
|
||||
UpdateResult
|
||||
UpdateResult,
|
||||
ObjectLiteral
|
||||
} from 'typeorm';
|
||||
import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata';
|
||||
import { RawSqlResultsToEntityTransformer } from 'typeorm/query-builder/transformer/RawSqlResultsToEntityTransformer';
|
||||
@ -93,7 +94,7 @@ export class GraphDatabase {
|
||||
return this._baseDatabase.createTransactionRunner();
|
||||
}
|
||||
|
||||
async getModelEntity<Entity> (repo: Repository<Entity>, whereOptions: any): Promise<Entity | undefined> {
|
||||
async getModelEntity<Entity extends ObjectLiteral> (repo: Repository<Entity>, whereOptions: any): Promise<Entity | undefined> {
|
||||
eventProcessingLoadEntityCount.inc();
|
||||
|
||||
const findOptions = {
|
||||
@ -165,7 +166,7 @@ export class GraphDatabase {
|
||||
return repo.findOne(findOptions);
|
||||
}
|
||||
|
||||
async getEntity<Entity> (entityName: string, id: string, blockHash?: string): Promise<Entity | undefined> {
|
||||
async getEntity<Entity extends ObjectLiteral> (entityName: string, id: string, blockHash?: string): Promise<Entity | undefined> {
|
||||
const queryRunner = this._conn.createQueryRunner();
|
||||
|
||||
try {
|
||||
@ -212,7 +213,7 @@ export class GraphDatabase {
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
async getEntityWithRelations<Entity> (
|
||||
async getEntityWithRelations<Entity extends ObjectLiteral> (
|
||||
queryRunner: QueryRunner,
|
||||
entityType: (new () => Entity),
|
||||
id: string,
|
||||
@ -343,7 +344,7 @@ export class GraphDatabase {
|
||||
return entityData;
|
||||
}
|
||||
|
||||
async getEntities<Entity> (
|
||||
async getEntities<Entity extends ObjectLiteral> (
|
||||
queryRunner: QueryRunner,
|
||||
entityType: new () => Entity,
|
||||
relationsMap: Map<any, { [key: string]: any }>,
|
||||
@ -411,7 +412,7 @@ export class GraphDatabase {
|
||||
return entities;
|
||||
}
|
||||
|
||||
async getEntitiesGroupBy<Entity> (
|
||||
async getEntitiesGroupBy<Entity extends ObjectLiteral> (
|
||||
queryRunner: QueryRunner,
|
||||
entityType: new () => Entity,
|
||||
block: BlockHeight,
|
||||
@ -475,7 +476,7 @@ export class GraphDatabase {
|
||||
return entities;
|
||||
}
|
||||
|
||||
async getEntitiesDistinctOn<Entity> (
|
||||
async getEntitiesDistinctOn<Entity extends ObjectLiteral> (
|
||||
queryRunner: QueryRunner,
|
||||
entityType: new () => Entity,
|
||||
block: BlockHeight,
|
||||
@ -516,7 +517,7 @@ export class GraphDatabase {
|
||||
`(${subQuery.getQuery()})`,
|
||||
'latestEntities'
|
||||
)
|
||||
.setParameters(subQuery.getParameters());
|
||||
.setParameters(subQuery.getParameters()) as SelectQueryBuilder<Entity>;
|
||||
|
||||
if (queryOptions.orderBy) {
|
||||
selectQueryBuilder = this._baseDatabase.orderQuery(repo, selectQueryBuilder, queryOptions, 'subTable_');
|
||||
@ -539,7 +540,7 @@ export class GraphDatabase {
|
||||
return entities as Entity[];
|
||||
}
|
||||
|
||||
async getEntitiesSingular<Entity> (
|
||||
async getEntitiesSingular<Entity extends ObjectLiteral> (
|
||||
queryRunner: QueryRunner,
|
||||
entityType: new () => Entity,
|
||||
block: BlockHeight,
|
||||
@ -574,7 +575,7 @@ export class GraphDatabase {
|
||||
return entities as Entity[];
|
||||
}
|
||||
|
||||
async getEntitiesUnique<Entity> (
|
||||
async getEntitiesUnique<Entity extends ObjectLiteral> (
|
||||
queryRunner: QueryRunner,
|
||||
entityType: new () => Entity,
|
||||
block: BlockHeight,
|
||||
@ -676,7 +677,7 @@ export class GraphDatabase {
|
||||
return selectQueryBuilder.getMany();
|
||||
}
|
||||
|
||||
async getEntitiesLateral<Entity> (
|
||||
async getEntitiesLateral<Entity extends ObjectLiteral> (
|
||||
queryRunner: QueryRunner,
|
||||
entityType: new () => Entity,
|
||||
latestEntity: new () => any,
|
||||
@ -717,7 +718,7 @@ export class GraphDatabase {
|
||||
return qb;
|
||||
},
|
||||
'result'
|
||||
);
|
||||
) as SelectQueryBuilder<Entity>;
|
||||
|
||||
selectQueryBuilder = this._baseDatabase.buildQuery(latestEntityRepo, selectQueryBuilder, where, 'latest');
|
||||
|
||||
@ -1040,11 +1041,11 @@ export class GraphDatabase {
|
||||
}
|
||||
|
||||
cacheUpdatedEntityByName (entityName: string, entity: any, pruned = false): void {
|
||||
const repo = this._conn.getRepository(entityName);
|
||||
const repo = this._conn.getRepository<ObjectLiteral>(entityName);
|
||||
this.cacheUpdatedEntity(repo, entity, pruned);
|
||||
}
|
||||
|
||||
cacheUpdatedEntity<Entity> (repo: Repository<Entity>, entity: any, pruned = false): void {
|
||||
cacheUpdatedEntity<Entity extends ObjectLiteral> (repo: Repository<Entity>, entity: any, pruned = false): void {
|
||||
const tableName = repo.metadata.tableName;
|
||||
|
||||
if (pruned) {
|
||||
@ -1204,8 +1205,8 @@ export class GraphDatabase {
|
||||
}
|
||||
|
||||
async canonicalizeLatestEntity (queryRunner: QueryRunner, entityType: any, latestEntityType: any, entities: any[], blockNumber: number): Promise<void> {
|
||||
const repo = queryRunner.manager.getRepository(entityType);
|
||||
const latestEntityRepo = queryRunner.manager.getRepository(latestEntityType);
|
||||
const repo = queryRunner.manager.getRepository<ObjectLiteral>(entityType);
|
||||
const latestEntityRepo = queryRunner.manager.getRepository<ObjectLiteral>(latestEntityType);
|
||||
|
||||
await Promise.all(entities.map(async (entity: any) => {
|
||||
// Get latest pruned (canonical) version for the given entity
|
||||
|
@ -3,7 +3,7 @@ import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import debug from 'debug';
|
||||
import yaml from 'js-yaml';
|
||||
import { DeepPartial, EntityTarget, InsertEvent, Repository, UpdateEvent } from 'typeorm';
|
||||
import { DeepPartial, EntityTarget, InsertEvent, ObjectLiteral, Repository, UpdateEvent } from 'typeorm';
|
||||
import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata';
|
||||
import assert from 'assert';
|
||||
import _ from 'lodash';
|
||||
@ -855,7 +855,7 @@ export const afterEntityInsertOrUpdate = async<Entity> (
|
||||
.execute();
|
||||
};
|
||||
|
||||
export function getLatestEntityFromEntity<Entity> (latestEntityRepo: Repository<Entity>, entity: any): Entity {
|
||||
export function getLatestEntityFromEntity<Entity extends ObjectLiteral> (latestEntityRepo: Repository<Entity>, entity: any): Entity {
|
||||
const latestEntityFields = latestEntityRepo.metadata.columns.map(column => column.propertyName);
|
||||
return latestEntityRepo.create(_.pick(entity, latestEntityFields) as DeepPartial<Entity>);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Copyright 2021 Vulcanize, Inc.
|
||||
//
|
||||
|
||||
import { Connection, DeepPartial, EntityTarget, FindConditions, FindManyOptions, QueryRunner } from 'typeorm';
|
||||
import { Connection, DeepPartial, EntityTarget, FindConditions, FindManyOptions, ObjectLiteral, QueryRunner } from 'typeorm';
|
||||
|
||||
import { MappingKey, StorageLayout } from '@cerc-io/solidity-mapper';
|
||||
import { EthClient } from '@cerc-io/ipld-eth-client';
|
||||
@ -182,7 +182,7 @@ export interface DatabaseInterface {
|
||||
}
|
||||
|
||||
export interface GraphDatabaseInterface {
|
||||
getEntity<Entity> (entity: (new () => Entity) | string, id: string, blockHash?: string): Promise<Entity | undefined>;
|
||||
getEntity<Entity extends ObjectLiteral> (entity: (new () => Entity) | string, id: string, blockHash?: string): Promise<Entity | undefined>;
|
||||
}
|
||||
|
||||
export interface GraphWatcherInterface {
|
||||
|
Loading…
Reference in New Issue
Block a user