├── tsconfig.json ├── package.json ├── .gitignore └── swarm.ts /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es2017", "dom"], 4 | "outDir": "build", 5 | "target": "es5" 6 | } 7 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hivenjs", 3 | "version": "1.0.6", 4 | "description": "", 5 | "main": "build/swarm.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/hivenapp/hivenjs.git" 9 | }, 10 | "author": "phineas", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@types/events": "^3.0.0", 14 | "@types/pako": "^1.0.1", 15 | "events": "^3.0.0", 16 | "pako": "^1.0.11" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | out 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and not Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Stores VSCode versions used for testing VSCode extensions 109 | .vscode-test 110 | 111 | # yarn v2 112 | .yarn/cache 113 | .yarn/unplugged 114 | .yarn/build-state.yml 115 | .yarn/install-state.gz 116 | .pnp.* 117 | -------------------------------------------------------------------------------- /swarm.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events'; 2 | import {inflate} from 'pako'; 3 | 4 | export enum DecompressionStrategy { 5 | ZlibJson = 'zlib_json', 6 | TextJson = 'text_json' 7 | } 8 | 9 | export class SwarmClient extends EventEmitter { 10 | swarmHost: string; 11 | encoding: string; 12 | compression: DecompressionStrategy; 13 | 14 | state: string; 15 | socket: WebSocket; 16 | heartbeatInterval: number; 17 | heartbeat: any; 18 | seq: number; 19 | 20 | constructor(swarmHost, encoding, compression) { 21 | super(); 22 | 23 | this.swarmHost = swarmHost || "wss://swarm.hiven.io"; 24 | this.encoding = encoding || "json"; 25 | this.compression = compression || DecompressionStrategy.ZlibJson; 26 | 27 | this.state = "not_connected"; 28 | this.socket = null; 29 | this.heartbeatInterval = null; 30 | this.heartbeat = null; 31 | this.seq = 0; 32 | } 33 | 34 | connect() { 35 | const socket = new WebSocket(`${this.swarmHost}/socket?encoding=${this.encoding}&compression=${this.compression}`); 36 | 37 | if(this.compression === DecompressionStrategy.ZlibJson) { 38 | socket.binaryType = 'arraybuffer'; 39 | } 40 | 41 | this.socket = socket; 42 | this.state = "connecting"; 43 | 44 | this.emit("SOCKET_STATE_CHANGE", "connecting"); 45 | 46 | socket.addEventListener('open', (event) => { 47 | this.state = "tcp_connected"; 48 | 49 | this.emit("SOCKET_STATE_CHANGE", "tcp_connected"); 50 | }); 51 | 52 | socket.addEventListener('message', (event) => { 53 | const compressed = event.data; 54 | const decompressed = this.decompress(compressed); 55 | 56 | this.seq = decompressed.seq; 57 | 58 | switch (decompressed.op) { 59 | case 0: 60 | this.emit("HIVEN_EVENT", {type: decompressed.e, data: decompressed.d}); 61 | break; 62 | case 1: 63 | this.heartbeatInterval = decompressed.d.hbt_int; 64 | this.startHeartbeating(); 65 | 66 | this.state = "connected"; 67 | this.emit("SOCKET_STATE_CHANGE", "connected"); 68 | 69 | break; 70 | default: 71 | console.error("Swarm received unknown opcode") 72 | } 73 | }); 74 | 75 | socket.addEventListener('close', (event) => { 76 | switch(event.code) { 77 | case 4003: { 78 | this.state = event.reason; 79 | this.emit("SOCKET_STATE_CHANGE", event.reason); 80 | } 81 | default: { 82 | this.state = "closed"; 83 | this.emit("SOCKET_STATE_CHANGE", "closed"); 84 | } 85 | } 86 | }); 87 | } 88 | 89 | private decompress(data: any): any { 90 | switch (this.compression) { 91 | case DecompressionStrategy.ZlibJson: { 92 | return JSON.parse(inflate(data, {to: 'string'})); 93 | } 94 | 95 | default: { 96 | return JSON.parse(data); 97 | } 98 | } 99 | } 100 | 101 | sendRaw(data) { 102 | const encoded = JSON.stringify(data); 103 | 104 | this.socket.send(encoded); 105 | } 106 | 107 | startHeartbeating() { 108 | this.heartbeat = setInterval(() => { 109 | if(this.state !== "connected") { 110 | clearInterval(this.heartbeat); 111 | return; 112 | } 113 | 114 | this.sendRaw({"op": 3}); 115 | }, this.heartbeatInterval || 30000); 116 | } 117 | 118 | identify(token) { 119 | this.sendRaw({"op": 2, "d": {"token": token}}); 120 | } 121 | } --------------------------------------------------------------------------------