import { EventEmitter } from 'node:events'; import { dlopen } from 'node:process'; import load from 'node-gyp-build'; import createCrypto from './crypto.js'; const binding = { exports: {} }; dlopen(binding, process.env.OPENDHT_NODE_BINDING || load.path(import.meta.dirname)); const native = binding.exports; const { PublicKey, PrivateKey, Certificate, Identity, handle } = createCrypto(native); function eventReporter(owner, event) { return value => { owner.deref()?.emit(event, value); }; } class DhtRunner extends EventEmitter { #native; #closing; #listeners = 0; #pending = 0; constructor() { super(); const owner = new WeakRef(this); this.#native = new native.NativeRunner(eventReporter(owner, 'error'), eventReporter(owner, 'statusChanged')); } #updateOwner() { this.#native.setOwner(this.#listeners || this.#pending || this.listenerCount('error') || this.listenerCount('statusChanged') ? this : null); } #track(operation) { this.#pending++; this.#updateOwner(); return operation.finally(() => { this.#pending--; this.#updateOwner(); }); } on(event, listener) { super.on(event, listener); this.#updateOwner(); return this; } addListener(event, listener) { return this.on(event, listener); } prependListener(event, listener) { super.prependListener(event, listener); this.#updateOwner(); return this; } removeListener(event, listener) { super.removeListener(event, listener); this.#updateOwner(); return this; } off(event, listener) { return this.removeListener(event, listener); } removeAllListeners(...args) { super.removeAllListeners(...args); this.#updateOwner(); return this; } run(options) { if (options?.identity !== undefined) options = { ...options, identity: handle(options.identity, Identity) }; this.#native.run(options); } isRunning() { return this.#native.isRunning(); } getBoundPort(family = 4) { return this.#native.getBoundPort(family); } getNodeId() { return this.#native.getNodeId(); } getStatus() { return this.#native.getStatus(); } bootstrap(host, port) { this.#native.bootstrap(host, port); } get(key) { return this.#track(this.#native.get(key)); } put(key, data, options) { return this.#track(this.#native.put(key, data, options)); } putSigned(key, data, options) { return this.#track(this.#native.putSigned(key, data, options)); } putEncrypted(key, data, recipient, options) { return this.#track(this.#native.putEncrypted(key, data, options, handle(recipient, PublicKey))); } listen(key, callback) { const token = this.#native.listen(key, callback); this.#listeners++; this.#updateOwner(); let cancelled = false; return { cancel: () => { if (cancelled) return; this.#native.cancelListen(token); cancelled = true; this.#listeners--; this.#updateOwner(); }, }; } close() { return this.#closing ??= this.#native.close(); } ref() { this.#native.ref(); return this; } unref() { this.#native.unref(); return this; } [Symbol.asyncDispose]() { return this.close(); } } export { DhtRunner, PublicKey, PrivateKey, Certificate, Identity }; export const hash = native.hash; export const version = native.version;