From 05f3916b4a014f38748f18718db5df34b8dd953a Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Jul 2022 13:03:18 -0400 Subject: [PATCH] *Initial version --- build.js | 13 ++++ package.json | 42 +++++++++++ src/id.ts | 12 ++++ src/index.ts | 169 ++++++++++++++++++++++++++++++++++++++++++++ tsconfig.build.json | 14 ++++ tsconfig.json | 12 ++++ 6 files changed, 262 insertions(+) create mode 100644 build.js create mode 100644 package.json create mode 100644 src/id.ts create mode 100644 src/index.ts create mode 100644 tsconfig.build.json create mode 100644 tsconfig.json diff --git a/build.js b/build.js new file mode 100644 index 0000000..17611a1 --- /dev/null +++ b/build.js @@ -0,0 +1,13 @@ +import esbuild from "esbuild"; + +esbuild.buildSync({ + entryPoints: ["src/index.ts"], + outfile: "dist/index.js", + format: "esm", + bundle: true, + legalComments: "external", + // minify: true + define: { + global: "self", + }, +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..11dd8d3 --- /dev/null +++ b/package.json @@ -0,0 +1,42 @@ +{ + "name": "@lumeweb/kernel-dht", + "author": { + "name": "Hammer Technologies LLC", + "email": "contact@lumeweb.com" + }, + "scripts": { + "test": "jest", + "build-script": "tsc --project tsconfig.build.json && mv dist-build/build.js dist-build/build.mjs", + "compile": "npm run build-script && node build.js", + "build": "npm run compile && node ./dist-build/build.mjs dev" + }, + "type": "module", + "dependencies": { + "@lumeweb/dht-web": "https://github.com/LumeWeb/dht-web.git", + "hyperswarm": "^4.0.2", + "libkmodule": "^0.2.12", + "libskynet": "^0.0.62" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^22.0.1", + "@rollup/plugin-node-resolve": "^13.3.0", + "@rollup/plugin-typescript": "^8.3.3", + "@types/jest": "^28.1.3", + "@types/read": "^0.0.29", + "buffer": "^6.0.3", + "esbuild": "^0.14.48", + "inspectpack": "^4.7.1", + "jest": "^28.1.1", + "jest-puppeteer": "^6.1.0", + "libskynetnode": "^0.1.3", + "prettier": "^2.7.1", + "puppeteer": "^15.2.0", + "read": "^1.0.7", + "rollup": "^2.75.7", + "rollup-plugin-polyfill-node": "^0.9.0", + "ts-loader": "^9.3.1", + "typescript": "^4.7.4", + "webpack": "^5.73.0", + "webpack-cli": "^4.10.0" + } +} diff --git a/src/id.ts b/src/id.ts new file mode 100644 index 0000000..15292e7 --- /dev/null +++ b/src/id.ts @@ -0,0 +1,12 @@ +function idFactory(start = 1, step = 1, limit = 2 ** 32) { + let id = start; + + return function nextId() { + const nextId = id; + id += step; + if (id >= limit) id = start; + return nextId; + }; +} + +export const nextId = idFactory(1); diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..3ec8841 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,169 @@ +// @ts-ignore +import DHT from "@lumeweb/dht-web"; +import { addHandler, handleMessage } from "libkmodule"; +import type { ActiveQuery } from "libkmodule"; +import { nextId } from "./id"; +import { Buffer } from "buffer"; + +let dht: DHT; + +const connections = new Map(); + +onmessage = handleMessage; + +addHandler("presentSeed", handlePresentSeed); +addHandler("connect", handleConnect); +addHandler("listenSocketEvent", handleListenSocketEvent, { + receiveUpdates: true, +}); +addHandler("close", handleCloseSocketEvent); +addHandler("write", handleWriteSocketEvent); +addHandler("addRelay", handleAddRelay); +addHandler("removeRelay", handleRemoveRelay); +addHandler("clearRelays", handleClearRelays); +addHandler("ready", handleReady); + +function handlePresentSeed(aq: ActiveQuery) { + const keyPair = aq.callerInput.myskyRootKeypair; + if (!dht) { + dht = new DHT({ keyPair }); + } +} + +async function handleConnect(aq: ActiveQuery) { + const { pubkey, options = {} } = aq.callerInput; + + let socket: any; + + try { + // @ts-ignore + socket = await dht.connect( + typeof pubkey === "string" ? Buffer.from(pubkey, "hex") : pubkey, + options + ); + } catch (e: any) { + aq.reject(e); + return; + } + + const id = nextId(); + + socket.on("open", () => { + connections.set(id, socket); + aq.respond({ id }); + }); + + socket.on("error", (e: any) => { + connections.set(id, socket); + aq.reject(e); + }); +} + +function handleListenSocketEvent(aq: ActiveQuery) { + const { event = null } = aq.callerInput; + const id = validateConnection(aq); + + if (!id) { + return; + } + + if (!event) { + aq.reject("Invalid event"); + return; + } + + const socket = connections.get(id); + const cb = (data: Buffer) => { + aq.sendUpdate(data); + }; + + socket.on(event, cb); + socket.on("close", () => { + socket.off(socket, cb); + aq.respond(); + }); + + aq.setReceiveUpdate?.((data: any) => { + switch (data?.action) { + case "off": + socket.off(socket, cb); + aq.respond(); + break; + } + }); +} + +function handleCloseSocketEvent(aq: ActiveQuery) { + const id = validateConnection(aq); + + if (!id) { + return; + } + + connections.get(id).end(); + + aq.respond(); +} + +function handleWriteSocketEvent(aq: ActiveQuery) { + const id = validateConnection(aq); + + if (!id) { + return; + } + const { message = null } = aq.callerInput; + + if (!message) { + aq.reject("empty message"); + return false; + } + + connections.get(id).write(message); + + aq.respond(); +} + +function validateConnection(aq: ActiveQuery): number | boolean { + const { id = null } = aq.callerInput; + + if (!id || !connections.has(id)) { + aq.reject("Invalid connection id"); + return false; + } + + return id; +} + +async function handleAddRelay(aq: ActiveQuery) { + const { pubkey = null } = aq.callerInput; + + if (!pubkey) { + aq.reject("invalid pubkey"); + return; + } + + aq.respond(await dht.addRelay(pubkey)); +} + +function handleRemoveRelay(aq: ActiveQuery) { + const { pubkey = null } = aq.callerInput; + + if (!pubkey) { + aq.reject("invalid pubkey"); + return; + } + + aq.respond(dht.removeRelay(pubkey)); +} + +function handleClearRelays(aq: ActiveQuery) { + dht.clearRelays(); + + aq.respond(); +} + +async function handleReady(aq: ActiveQuery) { + // @ts-ignore + await dht.ready(); + aq.respond(); +} diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..90439ba --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "declaration": true, + "outDir": "./dist-build", + "strict": true, + "esModuleInterop": true + }, + "include": ["src-build"], + "exclude": ["node_modules", "**/__tests__/*"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7baf8cf --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "esnext", + "declaration": true, + "moduleResolution": "node", + "outDir": "./build", + "strict": true, + "allowSyntheticDefaultImports": true + }, + "include": ["src"], + "exclude": ["node_modules", "**/__tests__/*"] +}