*Update to new system design

*Remove rpc code for now
This commit is contained in:
Derrick Hammer 2023-02-19 15:23:34 -05:00
parent 19deff0198
commit f005ebfcf9
Signed by: pcfreak30
GPG Key ID: C997C339BE476FF2
7 changed files with 191 additions and 461 deletions

View File

@ -23,19 +23,22 @@
"@lumeweb/safe-buffer": "^5.2.1", "@lumeweb/safe-buffer": "^5.2.1",
"@rollup/plugin-json": "^4.1.0", "@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.3.0", "@rollup/plugin-node-resolve": "^13.3.0",
"@scure/bip39": "^1.1.1",
"@skynetlabs/skynet-nodejs": "^2.6.0", "@skynetlabs/skynet-nodejs": "^2.6.0",
"@types/node": "^18.14.0",
"@types/read": "^0.0.29", "@types/read": "^0.0.29",
"@typescript-eslint/eslint-plugin": "^5.18.0", "@typescript-eslint/eslint-plugin": "^5.18.0",
"assert": "^2.0.0", "assert": "^2.0.0",
"buffer-browserify": "^0.2.5", "buffer-browserify": "^0.2.5",
"buffer-fill": "^1.0.0", "buffer-fill": "^1.0.0",
"cli-progress": "^3.12.0",
"cpy-cli": "^4.1.0", "cpy-cli": "^4.1.0",
"crypto-browserify": "^3.12.0", "crypto-browserify": "^3.12.0",
"esbuild": "^0.14.47", "esbuild": "^0.14.47",
"eslint": "^8.13.0", "eslint": "^8.13.0",
"events": "^3.3.0", "events": "^3.3.0",
"https-browserify": "^1.0.0", "https-browserify": "^1.0.0",
"libskynet": "^0.0.43", "libskynet": "^0.1.9",
"libskynetnode": "^0.1.2", "libskynetnode": "^0.1.2",
"os-browserify": "^0.3.0", "os-browserify": "^0.3.0",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
@ -49,13 +52,13 @@
"whatwg-fetch": "^3.6.2" "whatwg-fetch": "^3.6.2"
}, },
"dependencies": { "dependencies": {
"@lumeweb/kernel-dht-client": "https://github.com/LumeWeb/kernel-dht-client.git", "@lumeweb/kernel-swarm-client": "git+https://git.lumeweb.com/LumeWeb/kernel-swarm-client.git",
"@lumeweb/kernel-rpc-client": "https://github.com/LumeWeb/kernel-rpc-client.git", "@lumeweb/libkernel-universal": "git+https://git.lumeweb.com/LumeWeb/libkernel-universal.git",
"@lumeweb/kernel-utils": "https://github.com/LumeWeb/kernel-utils.git", "@lumeweb/libresolver": "git+https://git.lumeweb.com/LumeWeb/libresolver.git",
"@lumeweb/resolver-common": "github:LumeWeb/resolver-common", "@siaweb/libweb": "git+https://git.lumeweb.com/LumeWeb/libsiaweb.git",
"libkmodule": "^0.2.11", "libkmodule": "^0.2.11",
"randombytes": "https://github.com/LumeWeb/randombytes-browser.git", "randombytes": "git+https://github.com/LumeWeb/randombytes-browser.git",
"randomfill": "https://github.com/LumeWeb/randomfill.git" "randomfill": "git+https://github.com/LumeWeb/randomfill.git"
}, },
"browser": { "browser": {
"crypto": "crypto-browserify", "crypto": "crypto-browserify",

View File

@ -1,37 +1,27 @@
// This is the standard build script for a kernel module. // This is the standard build script for a kernel module.
import * as fs from "fs" import * as fs from "fs";
import { import read from "read";
addContextToErr, import * as bip39 from "@scure/bip39";
b64ToBuf, import { wordlist } from "@scure/bip39/wordlists/english.js";
bufToHex, //@ts-ignore
deriveRegistryEntryID, import { SkynetClient } from "@skynetlabs/skynet-nodejs";
generateSeedPhraseDeterministic,
resolverLink,
sha512,
taggedRegistryEntryKeys,
validSeedPhrase,
} from "libskynet"
import { generateSeedPhraseRandom, overwriteRegistryEntry, upload } from "libskynetnode"
import read from "read"
// @ts-ignore
import {SkynetClient} from "@skynetlabs/skynet-nodejs"
// Helper variables to make it easier to return empty values alongside errors. // Helper variables to make it easier to return empty values alongside errors.
const nu8 = new Uint8Array(0) const nu8 = new Uint8Array(0);
const nkp = { const nkp = {
publicKey: nu8, publicKey: nu8,
secretKey: nu8, secretKey: nu8,
} };
// readFile is a wrapper for fs.readFileSync that handles the try-catch for the // readFile is a wrapper for fs.readFileSync that handles the try-catch for the
// caller. // caller.
function readFile(fileName: string): [string, string | null] { function readFile(fileName: string): [string, string | null] {
try { try {
let data = fs.readFileSync(fileName, "utf8") let data = fs.readFileSync(fileName, "utf8");
return [data, null] return [data, null];
} catch (err) { } catch (err) {
return ["", "unable to read file: " + JSON.stringify(err)] return ["", "unable to read file: " + JSON.stringify(err)];
} }
} }
@ -39,10 +29,10 @@ function readFile(fileName: string): [string, string | null] {
// for the caller. // for the caller.
function readFileBinary(fileName: string): [Uint8Array, string | null] { function readFileBinary(fileName: string): [Uint8Array, string | null] {
try { try {
let data = fs.readFileSync(fileName, null) let data = fs.readFileSync(fileName, null);
return [data, null] return [data, null];
} catch (err) { } catch (err) {
return [nu8, "unable to read file: " + JSON.stringify(err)] return [nu8, "unable to read file: " + JSON.stringify(err)];
} }
} }
@ -50,58 +40,13 @@ function readFileBinary(fileName: string): [Uint8Array, string | null] {
// non-exception way. // non-exception way.
function writeFile(fileName: string, fileData: string): string | null { function writeFile(fileName: string, fileData: string): string | null {
try { try {
fs.writeFileSync(fileName, fileData) fs.writeFileSync(fileName, fileData);
return null return null;
} catch (err) { } catch (err) {
return "unable to write file: " + JSON.stringify(err) return "unable to write file: " + JSON.stringify(err);
} }
} }
// hardenedSeedPhrase will take a password, harden it with 100,000 iterations
// of hashing, and then turn it into a seed phrase.
function hardenedSeedPhrase(password: string): [string, string | null] {
let pw = password
// Add some hashing iterations to the password to make it stronger.
for(let i = 0; i < 1000000; i++) {
let passU8 = new TextEncoder().encode(password)
let hashIter = sha512(passU8)
password = bufToHex(hashIter)
}
return generateSeedPhraseDeterministic(password)
}
// seedPhraseToRegistryKeys will convert a seed phrase to the set of registry
// keys that govern the registry entry where the module is published.
function seedPhraseToRegistryKeys(seedPhrase: string): [any, Uint8Array, string | null] {
let [seed, errVSP] = validSeedPhrase(seedPhrase)
if (errVSP !== null) {
return [nkp, nu8, addContextToErr(errVSP, "unable to compute seed phrase")]
}
let [keypair, datakey, errTREK] = taggedRegistryEntryKeys(seed, "module-build", "module-key")
if (errTREK !== null) {
return [nkp, nu8, addContextToErr(errTREK, "unable to compute registry entry keys")]
}
return [keypair, datakey, null]
}
// seedPhraseToRegistryLink will take a seedPhrase as input and convert it to
// the registry link for the module.
function seedPhraseToRegistryLink(seedPhrase: string): [string, string | null] {
let [keypair, datakey, errSPTRK] = seedPhraseToRegistryKeys(seedPhrase)
if (errSPTRK !== null) {
return ["", addContextToErr(errSPTRK, "unable to compute registry keys")]
}
let [entryID, errDREID] = deriveRegistryEntryID(keypair.publicKey, datakey)
if (errDREID !== null) {
return ["", addContextToErr(errDREID, "unable to compute registry entry id")]
}
let [registryLink, errRL] = resolverLink(entryID)
if (errRL !== null) {
return ["", addContextToErr(errRL, "unable to compute registry link")]
}
return [registryLink, null]
}
// handlePass handles all portions of the script that occur after the password // handlePass handles all portions of the script that occur after the password
// has been requested. If no password needs to be requested, handlePass will be // has been requested. If no password needs to be requested, handlePass will be
// called with a null input. We need to structure the code this way because the // called with a null input. We need to structure the code this way because the
@ -115,41 +60,46 @@ function handlePass(password: string) {
if (!fs.existsSync(seedFile) && process.argv[2] === "prod") { if (!fs.existsSync(seedFile) && process.argv[2] === "prod") {
// The file does not exist, we need to confirm the // The file does not exist, we need to confirm the
// password. // password.
console.log() console.log();
console.log("No production entry found for module. Creating new production module...") console.log(
console.log("If someone can guess the password, they can push arbitrary changes to your module.") "No production entry found for module. Creating new production module..."
console.log("Please use a secure password.") );
console.log() console.log(
read({ prompt: "Confirm Password: ", silent: true }, function (err: any, confirmPassword: string) { "If someone can guess the password, they can push arbitrary changes to your module."
);
console.log("Please use a secure password.");
console.log();
read(
{ prompt: "Confirm Password: ", silent: true },
function (err: any, confirmPassword: string) {
if (err) { if (err) {
console.error("unable to fetch password:", err) console.error("unable to fetch password:", err);
process.exit(1) process.exit(1);
} }
if (password !== confirmPassword) { if (password !== confirmPassword) {
console.error("passwords do not match") console.error("passwords do not match");
process.exit(1) process.exit(1);
} }
password = password + moduleSalt handlePassConfirm(moduleSalt, password);
handlePassConfirm(password) }
}) );
} else { } else {
// If the seed file does exist, or if we are using dev, // If the seed file does exist, or if we are using dev,
// there's no need to confirm the password but we do // there's no need to confirm the password but we do
// need to pass the logic off to the handlePassConfirm // need to pass the logic off to the handlePassConfirm
// callback. // callback.
password = password + moduleSalt handlePassConfirm(moduleSalt, password);
handlePassConfirm(password)
} }
} catch (err) { } catch (err) {
console.error("Unable to read seedFile:", err) console.error("Unable to read seedFile:", err);
process.exit(1) process.exit(1);
} }
} }
// handlePassConfirm handles the full script after the confirmation password // handlePassConfirm handles the full script after the confirmation password
// has been provided. If not confirmation password is needed, this function // has been provided. If not confirmation password is needed, this function
// will be called anyway using the unconfirmed password as input. // will be called anyway using the unconfirmed password as input.
function handlePassConfirm(password: string) { function handlePassConfirm(seed: string, password: string) {
// Create the seedFile if it does not exist. For dev we just save the // Create the seedFile if it does not exist. For dev we just save the
// seed to disk outright, because this is a dev build and therefore not // seed to disk outright, because this is a dev build and therefore not
// security sensitive. Also the dev seed does not get pushed to the // security sensitive. Also the dev seed does not get pushed to the
@ -162,176 +112,107 @@ function handlePassConfirm(password: string) {
// devices. // devices.
if (!fs.existsSync(seedFile) && process.argv[2] !== "prod") { if (!fs.existsSync(seedFile) && process.argv[2] !== "prod") {
// Generate the seed phrase and write it to the file. // Generate the seed phrase and write it to the file.
let [seedPhrase, errGSP] = generateSeedPhraseRandom() let seedPhrase = bip39.generateMnemonic(wordlist);
if (errGSP !== null) { let errWF = writeFile(seedFile, seedPhrase);
console.error("Unable to generate seed phrase:", errGSP)
process.exit(1)
}
let errWF = writeFile(seedFile, seedPhrase)
if (errWF !== null) { if (errWF !== null) {
console.error("unable to write file:", errWF) console.error("unable to write file:", errWF);
process.exit(1) process.exit(1);
} }
} else if (!fs.existsSync(seedFile) && process.argv[2] === "prod") { } else if (!fs.existsSync(seedFile) && process.argv[2] === "prod") {
// Generate the seed phrase. // Generate the seed phrase.
let [seedPhrase, errGSP] = hardenedSeedPhrase(password) let seedPhrase = bip39.generateMnemonic(wordlist);
if (errGSP !== null) {
console.error("Unable to generate seed phrase:", errGSP)
process.exit(1)
}
let [registryLink, errSPTRL] = seedPhraseToRegistryLink(seedPhrase)
if (errSPTRL !== null) {
console.error("Unable to generate registry link:", errSPTRL)
process.exit(1)
}
// Write the registry link to the file. // Write the registry link to the file.
let errWF = writeFile(seedFile, registryLink)
if (errWF !== null) {
console.error("unable to write registry link file:", errWF)
process.exit(1)
}
} }
// Load or verify the seed. If this is prod, the password is used to // Load or verify the seed. If this is prod, the password is used to
// create and verify the seed. If this is dev, we just load the seed // create and verify the seed. If this is dev, we just load the seed
// with no password. // with no password.
let seedPhrase: string let seedPhrase: string;
let registryLink: string let registryLink: string;
if (process.argv[2] === "prod") { if (process.argv[2] === "prod") {
// Generate the seed phrase from the password. // Generate the seed phrase from the password.
let [sp, errGSP] = hardenedSeedPhrase(password) seedPhrase = bip39.generateMnemonic(wordlist);
if (errGSP !== null) {
console.error("Unable to generate seed phrase: ", errGSP)
process.exit(1)
}
let [rl, errSPTRL] = seedPhraseToRegistryLink(sp)
registryLink = rl
if (errSPTRL !== null) {
console.error("Unable to generate registry link:", errSPTRL)
process.exit(1)
}
let [registryLinkVerify, errRF] = readFile(seedFile)
if (errRF !== null) {
console.error("unable to read seedFile")
process.exit(1)
}
registryLinkVerify = registryLinkVerify.replace(/\n$/, "")
if (registryLink !== registryLinkVerify) {
console.error("Incorrect password")
process.exit(1)
}
seedPhrase = sp
} else { } else {
let [sp, errRF] = readFile(seedFile) let [sp, errRF] = readFile(seedFile);
if (errRF !== null) { if (errRF !== null) {
console.error("unable to read seed phrase for dev command from disk") console.error("unable to read seed phrase for dev command from disk");
process.exit(1) process.exit(1);
} }
let [rl, errSPTRL] = seedPhraseToRegistryLink(sp) seedPhrase = sp;
registryLink = rl
if (errSPTRL !== null) {
console.error("Unable to generate registry link:", errSPTRL)
process.exit(1)
}
// Write the registry link to the module skylink dev file.
let errWF = writeFile("build/module-skylink-dev", registryLink)
if (errWF !== null) {
console.error("unable to write registry link file:", errWF)
process.exit(1)
}
seedPhrase = sp
} }
console.log("Uploading module...") let metadata = {
Filename: "index.js",
};
const client = new SkynetClient("https://web3portal.com"); const client = new SkynetClient("https://web3portal.com");
client.uploadFile("dist/index.js") client
.then((result:any) => { .uploadFile("dist/index.js")
console.log("Updating module's registry entry...") .then((result: any) => {
// Update the v2 skylink. console.log("Immutable Link for kernel:", result);
let [keypair, datakey, errSPTRK] = seedPhraseToRegistryKeys(seedPhrase)
if (errSPTRK !== null) {
return ["", addContextToErr(errSPTRK, "unable to compute registry keys")]
}
let [bufLink, errBTB] = b64ToBuf(result.replace("sia://",""))
if (errBTB !== null) {
return ["", addContextToErr(errBTB, "unable to decode skylink")]
}
overwriteRegistryEntry(keypair, datakey, bufLink)
.then(() => {
console.log("registry entry is updated")
console.log("Immutable Link for Module:", result)
console.log("Resolver Link for Module:", registryLink)
}) })
.catch((err: any) => { .catch((err: any) => {
console.log("unable to update registry entry:", err) console.error("unable to upload file", err);
}) process.exit(1);
}) });
.catch((err) => {
console.error("unable to upload file", err)
process.exit(1)
})
} }
// Add a newline for readability. // Add a newline for readability.
console.log() console.log();
// Check for a 'dev' or 'prod' input to the script. // Check for a 'dev' or 'prod' input to the script.
if (process.argv.length !== 3) { if (process.argv.length !== 3) {
console.error("need to provide either 'dev' or 'prod' as an input") console.error("need to provide either 'dev' or 'prod' as an input");
process.exit(1) process.exit(1);
} }
// Create the build folder if it does not exist. // Create the build folder if it does not exist.
if (!fs.existsSync("build")) { if (!fs.existsSync("build")) {
fs.mkdirSync("build") fs.mkdirSync("build");
} }
// Determine the seed file. // Determine the seed file.
let seedFile: string let seedFile: string;
if (process.argv[2] === "prod") { if (process.argv[2] === "prod") {
seedFile = "module-skylink" seedFile = "module-skylink";
} else if (process.argv[2] === "dev") { } else if (process.argv[2] === "dev") {
seedFile = "build/dev-seed" seedFile = "build/dev-seed";
} else { } else {
console.error("need to provide either 'dev' or 'prod' as an input") console.error("need to provide either 'dev' or 'prod' as an input");
process.exit(1) process.exit(1);
} }
// If doing a prod deployment, check whether the salt file exists. If it does // If doing a prod deployment, check whether the salt file exists. If it does
// not, create it. // not, create it.
let moduleSalt: string let moduleSalt: string;
if (!fs.existsSync(".module-salt")) { if (!fs.existsSync(".module-salt")) {
let [ms, errGSPR] = generateSeedPhraseRandom() moduleSalt = bip39.generateMnemonic(wordlist);
if (errGSPR !== null) { let errWF = writeFile(".module-salt", moduleSalt);
console.error("unable to generate module salt:", errGSPR)
process.exit(1)
}
moduleSalt = ms
let errWF = writeFile(".module-salt", moduleSalt)
if (errWF !== null) { if (errWF !== null) {
console.error("unable to write module salt file:", errWF) console.error("unable to write module salt file:", errWF);
process.exit(1) process.exit(1);
} }
} else { } else {
let [ms, errRF] = readFile(".module-salt") let [ms, errRF] = readFile(".module-salt");
if (errRF !== null) { if (errRF !== null) {
console.error("unable to read moduleSalt") console.error("unable to read moduleSalt");
process.exit(1) process.exit(1);
} }
ms = ms.replace(/\n$/, "") ms = ms.replace(/\n$/, "");
moduleSalt = ms moduleSalt = ms;
} }
// Need to get a password if this is a prod build. // Need to get a password if this is a prod build.
if (process.argv[2] === "prod") { if (process.argv[2] === "prod") {
read({ prompt: "Password: ", silent: true }, function (err: any, password: string) { read(
{ prompt: "Password: ", silent: true },
function (err: any, password: string) {
if (err) { if (err) {
console.error("unable to fetch password:", err) console.error("unable to fetch password:", err);
process.exit(1) process.exit(1);
} }
handlePass(password) handlePass(password);
}) }
);
} else { } else {
handlePass("") handlePass("");
} }

View File

@ -1,6 +1,3 @@
import { RpcNetwork } from "@lumeweb/kernel-rpc-client";
import { ResolverRegistry } from "./resolverRegistry.js"; import { ResolverRegistry } from "./resolverRegistry.js";
const network: RpcNetwork = new RpcNetwork(); export const resolver = new ResolverRegistry();
export const resolver = new ResolverRegistry(network as any);

View File

@ -2,12 +2,10 @@ import { addHandler, handleMessage } from "libkmodule";
import type { ActiveQuery } from "libkmodule"; import type { ActiveQuery } from "libkmodule";
import { resolver } from "./common.js"; import { resolver } from "./common.js";
import { relayReady, setupRelayListSubscription } from "./relays.js"; import { validateSkylink } from "@siaweb/libweb/dist/skylinkValidate.js";
import { validSkylink } from "libskynet/dist/skylinkvalidate.js";
import { ResolverModule } from "./resolverRegistry.js"; import { ResolverModule } from "./resolverRegistry.js";
import { b64ToBuf } from "libskynet"; import { b64ToBuf } from "@siaweb/libweb";
import { factory } from "@lumeweb/libkernel-universal";
setupRelayListSubscription();
addHandler("resolve", handleResolve); addHandler("resolve", handleResolve);
addHandler("register", handleRegister); addHandler("register", handleRegister);
@ -22,23 +20,24 @@ async function handleResolve(aq: ActiveQuery) {
return; return;
} }
await relayReady();
aq.respond( aq.respond(
await resolver.resolve( await resolver.resolve(
aq.callerInput.domain, query?.domain,
aq.callerInput.options ?? {}, query?.options,
aq.callerInput.bypassCache || false query?.bypassCache || false
) )
); );
} }
async function handleRegister(aq: ActiveQuery) { async function handleRegister(aq: ActiveQuery) {
if (!validSkylink(b64ToBuf(aq.domain).shift() as Uint8Array)) { if (!validateSkylink(b64ToBuf(aq.domain).shift() as Uint8Array)?.[1]) {
aq.reject("invalid skylink"); aq.reject("invalid skylink");
return; return;
} }
resolver.register(new ResolverModule(resolver, aq.domain)); resolver.register(
factory<ResolverModule>(ResolverModule, aq.domain)(resolver, aq.domain)
);
aq.respond(); aq.respond();
} }

View File

@ -1,64 +0,0 @@
import WSReconnect from "./ws.js"
import {resolver} from "./common.js"
import {hashDataKey} from "@lumeweb/kernel-utils";
import {bufToHex} from "libskynet";
import {deriveRegistryEntryID, downloadSkylink} from "libskynet";
import {entryIDToSkylink} from "libskynet";
import {RpcNetwork} from "@lumeweb/kernel-rpc-client";
const relayListName = "lumeweb-relays"
const relayListOwner = Buffer.from("86c7421160eb5cb4a39495fc3e3ae25a60b330fff717e06aab978ad353722014", "hex");
/*
TODO: Use kernel code to use many different portals and not hard code a portal
*/
let portalConnection: WSReconnect
let relayPromiseResolve: any = () => true;
let relayPromise: Promise<any>;
export function relayReady(): Promise<any> {
return relayPromise;
}
function resetRelayPromise() {
relayPromise = new Promise((resolve) => {
relayPromiseResolve = resolve;
})
}
async function fetchRelays() {
/* const [data, err] = await downloadSkylink(entryIDToSkylink(deriveRegistryEntryID(relayListOwner, hashDataKey(relayListName))[0]))
if (err) {
return;
}*/
// const list: string[] = JSON.parse(Buffer.from(data).toString());
const list: string[] = ["ef92890bec753c86cfecbe1adea632a8aa130d479bab69ec999c0ea28afd48cf"];
resolver.rpcNetwork.clearRelays();
list.forEach((item) => {
resolver.rpcNetwork.addRelay(item);
});
await (resolver.rpcNetwork as unknown as RpcNetwork).processQueue()
relayPromiseResolve()
// resetRelayPromise();
}
export function setupRelayListSubscription() {
/* portalConnection = new WSReconnect("wss://web3portal.com/skynet/registry/subscription")
// @ts-ignore
portalConnection.on("connect", () => {
portalConnection.send(
JSON.stringify({
action: "subscribe",
pubkey: `ed25519:${relayListOwner}`,
datakey: bufToHex(hashDataKey(relayListName)),
})
)
})
// @ts-ignore
portalConnection.on("message", fetchRelays)
portalConnection.start()*/
resetRelayPromise();
fetchRelays()
}

View File

@ -3,33 +3,21 @@ import {
ResolverOptions, ResolverOptions,
DNS_RECORD_TYPE, DNS_RECORD_TYPE,
resolverError, resolverError,
} from "@lumeweb/resolver-common"; } from "@lumeweb/libresolver";
import type { RpcNetwork } from "@lumeweb/kernel-rpc-client"; import { Client } from "@lumeweb/libkernel-universal";
import { callModule } from "libkmodule/dist";
export class ResolverRegistry { export class ResolverRegistry {
constructor(network: RpcNetwork) {
this._rpcNetwork = network;
}
private _resolvers: Set<ResolverModule> = new Set<ResolverModule>(); private _resolvers: Set<ResolverModule> = new Set<ResolverModule>();
get resolvers(): Set<ResolverModule> { get resolvers(): Set<ResolverModule> {
return this._resolvers; return this._resolvers;
} }
private _rpcNetwork: RpcNetwork;
get rpcNetwork(): RpcNetwork {
return this._rpcNetwork;
}
public async resolve( public async resolve(
domain: string, domain: string,
options: ResolverOptions = { type: DNS_RECORD_TYPE.DEFAULT }, options: ResolverOptions = { type: DNS_RECORD_TYPE.CONTENT },
bypassCache: boolean = false bypassCache: boolean = false
): Promise<DNSResult> { ): Promise<DNSResult> {
for (const resolver: ResolverModule of this._resolvers) { for (const resolver of this._resolvers) {
const result = await resolver.resolve(domain, options, bypassCache); const result = await resolver.resolve(domain, options, bypassCache);
if (!result.error && result.records.length) { if (!result.error && result.records.length) {
return result; return result;
@ -55,10 +43,11 @@ export class ResolverRegistry {
} }
} }
export class ResolverModule { export class ResolverModule extends Client {
private _resolver: ResolverRegistry; private _resolver: ResolverRegistry;
constructor(resolver: ResolverRegistry, domain: string) { constructor(resolver: ResolverRegistry, domain: string) {
super();
this._resolver = resolver; this._resolver = resolver;
this._domain = domain; this._domain = domain;
} }
@ -74,15 +63,14 @@ export class ResolverModule {
options: ResolverOptions, options: ResolverOptions,
bypassCache: boolean bypassCache: boolean
): Promise<DNSResult> { ): Promise<DNSResult> {
const [ret, err] = await callModule(this._domain, "resolve", { try {
return this.callModuleReturn("resolve", {
domain, domain,
options, options,
bypassCache, bypassCache,
}); });
if (err) { } catch (e: any) {
return resolverError(err); return resolverError(e as Error);
} }
return ret;
} }
} }

View File

@ -1,74 +0,0 @@
import EventEmitter from "events"
interface Options {
retryCount?: number
reconnectInterval?: number
}
/*
Ported from https://github.com/ofirattia/ws-reconnect
*/
export default class WSReconnect extends EventEmitter {
private url: string
private options: Options
private retryCount: number
private _retryCount: number
private reconnectInterval: number
private shouldAttemptReconnect: boolean
private isConnected = false
private socket: WebSocket | undefined
private reconnectTimeoutId: NodeJS.Timeout | undefined
constructor(url: string, options: Options = {}) {
super()
this.url = url
this.options = options
this.retryCount = this.options.retryCount || -1
this._retryCount = this.retryCount
this.reconnectInterval = this.options?.reconnectInterval ?? 5
this.shouldAttemptReconnect = !!this.reconnectInterval
}
start() {
this.shouldAttemptReconnect = !!this.reconnectInterval
this.isConnected = false
this.socket = new WebSocket(this.url)
this.socket.onmessage = this.onMessage.bind(this)
this.socket.onopen = this.onOpen.bind(this)
this.socket.onclose = this.onClose.bind(this)
}
destroy() {
clearTimeout(this.reconnectTimeoutId as NodeJS.Timeout)
this.shouldAttemptReconnect = false
this.socket?.close()
}
onOpen() {
this.isConnected = true
this.emit("connect")
// set again the retry count
this.retryCount = this._retryCount
}
onClose() {
if (this.shouldAttemptReconnect && (this.retryCount > 0 || this.retryCount == -1)) {
if (this.retryCount !== -1) this.retryCount--
clearTimeout(this.reconnectTimeoutId as NodeJS.Timeout)
this.reconnectTimeoutId = setTimeout(() => {
this.emit("reconnect")
this.start()
}, this.reconnectInterval * 1000)
} else {
this.emit("destroyed")
}
}
onMessage(message: MessageEvent) {
this.emit("message", message.data)
}
send(message: string | ArrayBufferLike | Blob | ArrayBufferView) {
this.socket?.send(message)
}
}