import xid from "@xmpp/id"; import StanzaError from "@xmpp/middleware/lib/StanzaError.js"; import { Deferred, timeout as timeoutPromise } from "@xmpp/events"; import xml from "@xmpp/xml"; function isReply({ name, type }) { if (name !== "iq") return false; if (type !== "error" && type !== "result") return false; return true; } class IQCaller { constructor({ entity, middleware }) { this.handlers = new Map(); this.entity = entity; this.middleware = middleware; } start() { this.middleware.use(this._route.bind(this)); } _route({ type, name, id, stanza }, next) { if (!isReply({ name, type })) return next(); const deferred = this.handlers.get(id); if (!deferred) { return next(); } if (type === "error") { deferred.reject(StanzaError.fromElement(stanza.getChild("error"))); } else { deferred.resolve(stanza); } this.handlers.delete(id); } async request(stanza, timeout = 30 * 1000) { if (!stanza.attrs.id) { stanza.attrs.id = xid(); } const deferred = new Deferred(); this.handlers.set(stanza.attrs.id, deferred); try { await this.entity.send(stanza); await timeoutPromise(deferred.promise, timeout); } catch (err) { this.handlers.delete(stanza.attrs.id); throw err; } return deferred.promise; } _childRequest(type, element, to, ...args) { const { name, attrs: { xmlns }, } = element; return this.request(xml("iq", { type, to }, element), ...args).then( (stanza) => stanza.getChild(name, xmlns), ); } async get(...args) { return this._childRequest("get", ...args); } async set(...args) { return this._childRequest("set", ...args); } } export default function iqCaller(...args) { const iqCaller = new IQCaller(...args); iqCaller.start(); return iqCaller; }