chore: upgrade applesauce packages to v4.0.0

- Update all applesauce packages from 3.1.0 to 4.0.0
- Add applesauce-factory dependency
- Version 4.0.0 includes app-data helpers needed for NIP-78
This commit is contained in:
Gigi
2025-10-05 02:35:28 +01:00
parent 1b381a0f8c
commit 1b8c276529
229 changed files with 2533 additions and 7592 deletions

View File

@@ -202,7 +202,10 @@ var AbstractRelay = class {
baseEoseTimeout = 4400;
connectionTimeout = 4400;
publishTimeout = 4400;
pingFrequency = 2e4;
pingTimeout = 2e4;
openSubs = /* @__PURE__ */ new Map();
enablePing;
connectionTimeoutHandle;
connectionPromise;
openCountRequests = /* @__PURE__ */ new Map();
@@ -219,6 +222,7 @@ var AbstractRelay = class {
this.url = normalizeURL(url);
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation || WebSocket;
this.enablePing = opts.enablePing;
}
static async connect(url, opts) {
const relay = new AbstractRelay(url, opts);
@@ -264,7 +268,7 @@ var AbstractRelay = class {
this.ws.onopen = () => {
clearTimeout(this.connectionTimeoutHandle);
this._connected = true;
if (this.ws && this.ws.ping) {
if (this.enablePing) {
this.pingpong();
}
resolve();
@@ -272,45 +276,54 @@ var AbstractRelay = class {
this.ws.onerror = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket error");
if (this._connected) {
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
}
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
};
this.ws.onclose = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket closed");
if (this._connected) {
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
}
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
};
this.ws.onmessage = this._onmessage.bind(this);
});
return this.connectionPromise;
}
async receivePong() {
async waitForPingPong() {
return new Promise((res, err) => {
;
this.ws && this.ws.on && this.ws.on("pong", () => res(true)) || err("ws can't listen for pong");
this.ws && this.ws.ping && this.ws.ping();
});
}
async waitForDummyReq() {
return new Promise((resolve, _) => {
const sub = this.subscribe([{ ids: ["a".repeat(64)] }], {
oneose: () => {
sub.close();
resolve(true);
},
eoseTimeout: this.pingTimeout + 1e3
});
});
}
async pingpong() {
if (this.ws?.readyState == 1) {
this.ws && this.ws.ping && this.ws.ping();
if (this.ws?.readyState === 1) {
const result = await Promise.any([
this.receivePong(),
new Promise((res) => setTimeout(() => res(false), 1e4))
this.ws && this.ws.ping && this.ws.on ? this.waitForPingPong() : this.waitForDummyReq(),
new Promise((res) => setTimeout(() => res(false), this.pingTimeout))
]);
console.error("pingpong result", result);
if (result) {
setTimeout(() => this.pingpong(), 1e4);
setTimeout(() => this.pingpong(), this.pingFrequency);
} else {
this.ws && this.ws.close();
this.closeAllSubscriptions("pingpong timed out");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
}
}
@@ -475,8 +488,8 @@ var AbstractRelay = class {
close() {
this.closeAllSubscriptions("relay connection closed by us");
this._connected = false;
this.ws?.close();
this.onclose?.();
this.ws?.close();
}
_onmessage(ev) {
this.incomingMessageQueue.enqueue(ev.data);
@@ -548,11 +561,13 @@ var AbstractSimplePool = class {
seenOn = /* @__PURE__ */ new Map();
trackRelays = false;
verifyEvent;
enablePing;
trustedRelayURLs = /* @__PURE__ */ new Set();
_WebSocket;
constructor(opts) {
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation;
this.enablePing = opts.enablePing;
}
async ensureRelay(url, params) {
url = normalizeURL(url);
@@ -560,7 +575,8 @@ var AbstractSimplePool = class {
if (!relay) {
relay = new AbstractRelay(url, {
verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,
websocketImplementation: this._WebSocket
websocketImplementation: this._WebSocket,
enablePing: this.enablePing
});
relay.onclose = () => {
this.relays.delete(url);
@@ -580,20 +596,38 @@ var AbstractSimplePool = class {
}
subscribe(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
return this.subscribeMap(
relays.map((url) => ({ url, filter })),
params
);
const request = [];
for (let i = 0; i < relays.length; i++) {
const url = normalizeURL(relays[i]);
if (!request.find((r) => r.url === url)) {
request.push({ url, filter });
}
}
return this.subscribeMap(request, params);
}
subscribeMany(relays, filters, params) {
subscribeMany(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
return this.subscribeMap(
relays.flatMap((url) => filters.map((filter) => ({ url, filter }))),
params
);
const request = [];
const uniqUrls = [];
for (let i = 0; i < relays.length; i++) {
const url = normalizeURL(relays[i]);
if (uniqUrls.indexOf(url) === -1) {
uniqUrls.push(url);
request.push({ url, filter });
}
}
return this.subscribeMap(request, params);
}
subscribeMap(requests, params) {
params.onauth = params.onauth || params.doauth;
const grouped = /* @__PURE__ */ new Map();
for (const req of requests) {
const { url, filter } = req;
if (!grouped.has(url))
grouped.set(url, []);
grouped.get(url).push(filter);
}
const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }));
if (this.trackRelays) {
params.receivedEvent = (relay, id) => {
let set = this.seenOn.get(id);
@@ -638,8 +672,7 @@ var AbstractSimplePool = class {
return have;
};
const allOpened = Promise.all(
requests.map(async ({ url, filter }, i) => {
url = normalizeURL(url);
groupedRequests.map(async ({ url, filters }, i) => {
let relay;
try {
relay = await this.ensureRelay(url, {
@@ -649,13 +682,13 @@ var AbstractSimplePool = class {
handleClose(i, err?.message || String(err));
return;
}
let subscription = relay.subscribe([filter], {
let subscription = relay.subscribe(filters, {
...params,
oneose: () => handleEose(i),
onclose: (reason) => {
if (reason.startsWith("auth-required: ") && params.onauth) {
relay.auth(params.onauth).then(() => {
relay.subscribe([filter], {
relay.subscribe(filters, {
...params,
oneose: () => handleEose(i),
onclose: (reason2) => {
@@ -696,9 +729,9 @@ var AbstractSimplePool = class {
});
return subcloser;
}
subscribeManyEose(relays, filters, params) {
subscribeManyEose(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
const subcloser = this.subscribeMany(relays, filters, {
const subcloser = this.subscribeMany(relays, filter, {
...params,
oneose() {
subcloser.close("closed automatically on eose");

File diff suppressed because one or more lines are too long

View File

@@ -197,7 +197,10 @@ var AbstractRelay = class {
baseEoseTimeout = 4400;
connectionTimeout = 4400;
publishTimeout = 4400;
pingFrequency = 2e4;
pingTimeout = 2e4;
openSubs = /* @__PURE__ */ new Map();
enablePing;
connectionTimeoutHandle;
connectionPromise;
openCountRequests = /* @__PURE__ */ new Map();
@@ -214,6 +217,7 @@ var AbstractRelay = class {
this.url = normalizeURL(url);
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation || WebSocket;
this.enablePing = opts.enablePing;
}
static async connect(url, opts) {
const relay = new AbstractRelay(url, opts);
@@ -259,7 +263,7 @@ var AbstractRelay = class {
this.ws.onopen = () => {
clearTimeout(this.connectionTimeoutHandle);
this._connected = true;
if (this.ws && this.ws.ping) {
if (this.enablePing) {
this.pingpong();
}
resolve();
@@ -267,45 +271,54 @@ var AbstractRelay = class {
this.ws.onerror = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket error");
if (this._connected) {
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
}
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
};
this.ws.onclose = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket closed");
if (this._connected) {
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
}
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
};
this.ws.onmessage = this._onmessage.bind(this);
});
return this.connectionPromise;
}
async receivePong() {
async waitForPingPong() {
return new Promise((res, err) => {
;
this.ws && this.ws.on && this.ws.on("pong", () => res(true)) || err("ws can't listen for pong");
this.ws && this.ws.ping && this.ws.ping();
});
}
async waitForDummyReq() {
return new Promise((resolve, _) => {
const sub = this.subscribe([{ ids: ["a".repeat(64)] }], {
oneose: () => {
sub.close();
resolve(true);
},
eoseTimeout: this.pingTimeout + 1e3
});
});
}
async pingpong() {
if (this.ws?.readyState == 1) {
this.ws && this.ws.ping && this.ws.ping();
if (this.ws?.readyState === 1) {
const result = await Promise.any([
this.receivePong(),
new Promise((res) => setTimeout(() => res(false), 1e4))
this.ws && this.ws.ping && this.ws.on ? this.waitForPingPong() : this.waitForDummyReq(),
new Promise((res) => setTimeout(() => res(false), this.pingTimeout))
]);
console.error("pingpong result", result);
if (result) {
setTimeout(() => this.pingpong(), 1e4);
setTimeout(() => this.pingpong(), this.pingFrequency);
} else {
this.ws && this.ws.close();
this.closeAllSubscriptions("pingpong timed out");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
}
}
@@ -470,8 +483,8 @@ var AbstractRelay = class {
close() {
this.closeAllSubscriptions("relay connection closed by us");
this._connected = false;
this.ws?.close();
this.onclose?.();
this.ws?.close();
}
_onmessage(ev) {
this.incomingMessageQueue.enqueue(ev.data);

File diff suppressed because one or more lines are too long

View File

@@ -647,7 +647,10 @@ var AbstractRelay = class {
baseEoseTimeout = 4400;
connectionTimeout = 4400;
publishTimeout = 4400;
pingFrequency = 2e4;
pingTimeout = 2e4;
openSubs = /* @__PURE__ */ new Map();
enablePing;
connectionTimeoutHandle;
connectionPromise;
openCountRequests = /* @__PURE__ */ new Map();
@@ -664,6 +667,7 @@ var AbstractRelay = class {
this.url = normalizeURL(url);
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation || WebSocket;
this.enablePing = opts.enablePing;
}
static async connect(url, opts) {
const relay = new AbstractRelay(url, opts);
@@ -709,7 +713,7 @@ var AbstractRelay = class {
this.ws.onopen = () => {
clearTimeout(this.connectionTimeoutHandle);
this._connected = true;
if (this.ws && this.ws.ping) {
if (this.enablePing) {
this.pingpong();
}
resolve();
@@ -717,45 +721,54 @@ var AbstractRelay = class {
this.ws.onerror = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket error");
if (this._connected) {
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
}
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
};
this.ws.onclose = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket closed");
if (this._connected) {
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
}
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
};
this.ws.onmessage = this._onmessage.bind(this);
});
return this.connectionPromise;
}
async receivePong() {
async waitForPingPong() {
return new Promise((res, err) => {
;
this.ws && this.ws.on && this.ws.on("pong", () => res(true)) || err("ws can't listen for pong");
this.ws && this.ws.ping && this.ws.ping();
});
}
async waitForDummyReq() {
return new Promise((resolve, _) => {
const sub = this.subscribe([{ ids: ["a".repeat(64)] }], {
oneose: () => {
sub.close();
resolve(true);
},
eoseTimeout: this.pingTimeout + 1e3
});
});
}
async pingpong() {
if (this.ws?.readyState == 1) {
this.ws && this.ws.ping && this.ws.ping();
if (this.ws?.readyState === 1) {
const result = await Promise.any([
this.receivePong(),
new Promise((res) => setTimeout(() => res(false), 1e4))
this.ws && this.ws.ping && this.ws.on ? this.waitForPingPong() : this.waitForDummyReq(),
new Promise((res) => setTimeout(() => res(false), this.pingTimeout))
]);
console.error("pingpong result", result);
if (result) {
setTimeout(() => this.pingpong(), 1e4);
setTimeout(() => this.pingpong(), this.pingFrequency);
} else {
this.ws && this.ws.close();
this.closeAllSubscriptions("pingpong timed out");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
}
}
@@ -920,8 +933,8 @@ var AbstractRelay = class {
close() {
this.closeAllSubscriptions("relay connection closed by us");
this._connected = false;
this.ws?.close();
this.onclose?.();
this.ws?.close();
}
_onmessage(ev) {
this.incomingMessageQueue.enqueue(ev.data);
@@ -1010,11 +1023,13 @@ var AbstractSimplePool = class {
seenOn = /* @__PURE__ */ new Map();
trackRelays = false;
verifyEvent;
enablePing;
trustedRelayURLs = /* @__PURE__ */ new Set();
_WebSocket;
constructor(opts) {
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation;
this.enablePing = opts.enablePing;
}
async ensureRelay(url, params) {
url = normalizeURL(url);
@@ -1022,7 +1037,8 @@ var AbstractSimplePool = class {
if (!relay) {
relay = new AbstractRelay(url, {
verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,
websocketImplementation: this._WebSocket
websocketImplementation: this._WebSocket,
enablePing: this.enablePing
});
relay.onclose = () => {
this.relays.delete(url);
@@ -1042,20 +1058,38 @@ var AbstractSimplePool = class {
}
subscribe(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
return this.subscribeMap(
relays.map((url) => ({ url, filter })),
params
);
const request = [];
for (let i2 = 0; i2 < relays.length; i2++) {
const url = normalizeURL(relays[i2]);
if (!request.find((r) => r.url === url)) {
request.push({ url, filter });
}
}
return this.subscribeMap(request, params);
}
subscribeMany(relays, filters, params) {
subscribeMany(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
return this.subscribeMap(
relays.flatMap((url) => filters.map((filter) => ({ url, filter }))),
params
);
const request = [];
const uniqUrls = [];
for (let i2 = 0; i2 < relays.length; i2++) {
const url = normalizeURL(relays[i2]);
if (uniqUrls.indexOf(url) === -1) {
uniqUrls.push(url);
request.push({ url, filter });
}
}
return this.subscribeMap(request, params);
}
subscribeMap(requests, params) {
params.onauth = params.onauth || params.doauth;
const grouped = /* @__PURE__ */ new Map();
for (const req of requests) {
const { url, filter } = req;
if (!grouped.has(url))
grouped.set(url, []);
grouped.get(url).push(filter);
}
const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }));
if (this.trackRelays) {
params.receivedEvent = (relay, id) => {
let set = this.seenOn.get(id);
@@ -1100,8 +1134,7 @@ var AbstractSimplePool = class {
return have;
};
const allOpened = Promise.all(
requests.map(async ({ url, filter }, i2) => {
url = normalizeURL(url);
groupedRequests.map(async ({ url, filters }, i2) => {
let relay;
try {
relay = await this.ensureRelay(url, {
@@ -1111,13 +1144,13 @@ var AbstractSimplePool = class {
handleClose(i2, err?.message || String(err));
return;
}
let subscription = relay.subscribe([filter], {
let subscription = relay.subscribe(filters, {
...params,
oneose: () => handleEose(i2),
onclose: (reason) => {
if (reason.startsWith("auth-required: ") && params.onauth) {
relay.auth(params.onauth).then(() => {
relay.subscribe([filter], {
relay.subscribe(filters, {
...params,
oneose: () => handleEose(i2),
onclose: (reason2) => {
@@ -1158,9 +1191,9 @@ var AbstractSimplePool = class {
});
return subcloser;
}
subscribeManyEose(relays, filters, params) {
subscribeManyEose(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
const subcloser = this.subscribeMany(relays, filters, {
const subcloser = this.subscribeMany(relays, filter, {
...params,
oneose() {
subcloser.close("closed automatically on eose");
@@ -1231,8 +1264,8 @@ try {
} catch {
}
var SimplePool = class extends AbstractSimplePool {
constructor() {
super({ verifyEvent, websocketImplementation: _WebSocket2 });
constructor(options) {
super({ verifyEvent, websocketImplementation: _WebSocket2, ...options });
}
};
@@ -2217,19 +2250,19 @@ function* parse3(content) {
if (prevIndex !== u - prefixLen) {
yield { type: "text", text: content.substring(prevIndex, u - prefixLen) };
}
if (url.pathname.endsWith(".png") || url.pathname.endsWith(".jpg") || url.pathname.endsWith(".jpeg") || url.pathname.endsWith(".gif") || url.pathname.endsWith(".webp")) {
if (/\.(png|jpe?g|gif|webp)$/i.test(url.pathname)) {
yield { type: "image", url: url.toString() };
index = end;
prevIndex = index;
continue;
}
if (url.pathname.endsWith(".mp4") || url.pathname.endsWith(".avi") || url.pathname.endsWith(".webm") || url.pathname.endsWith(".mkv")) {
if (/\.(mp4|avi|webm|mkv)$/i.test(url.pathname)) {
yield { type: "video", url: url.toString() };
index = end;
prevIndex = index;
continue;
}
if (url.pathname.endsWith(".mp3") || url.pathname.endsWith(".aac") || url.pathname.endsWith(".ogg") || url.pathname.endsWith(".opus")) {
if (/\.(mp3|aac|ogg|opus)$/i.test(url.pathname)) {
yield { type: "audio", url: url.toString() };
index = end;
prevIndex = index;
@@ -2522,41 +2555,30 @@ async function getZapEndpoint(metadata) {
}
return null;
}
function makeZapRequest({
profile,
event,
amount,
relays,
comment = ""
}) {
if (!amount)
throw new Error("amount not given");
if (!profile)
throw new Error("profile not given");
function makeZapRequest(params) {
let zr = {
kind: 9734,
created_at: Math.round(Date.now() / 1e3),
content: comment,
content: params.comment || "",
tags: [
["p", profile],
["amount", amount.toString()],
["relays", ...relays]
["p", "pubkey" in params ? params.pubkey : params.event.pubkey],
["amount", params.amount.toString()],
["relays", ...params.relays]
]
};
if (event && typeof event === "string") {
zr.tags.push(["e", event]);
}
if (event && typeof event === "object") {
if (isReplaceableKind(event.kind)) {
const a = ["a", `${event.kind}:${event.pubkey}:`];
if ("event" in params) {
zr.tags.push(["e", params.event.id]);
if (isReplaceableKind(params.event.kind)) {
const a = ["a", `${params.event.kind}:${params.event.pubkey}:`];
zr.tags.push(a);
} else if (isAddressableKind(event.kind)) {
let d = event.tags.find(([t, v]) => t === "d" && v);
} else if (isAddressableKind(params.event.kind)) {
let d = params.event.tags.find(([t, v]) => t === "d" && v);
if (!d)
throw new Error("d tag not found or is empty");
const a = ["a", `${event.kind}:${event.pubkey}:${d[1]}`];
const a = ["a", `${params.event.kind}:${params.event.pubkey}:${d[1]}`];
zr.tags.push(a);
}
zr.tags.push(["k", params.event.kind.toString()]);
}
return zr;
}

File diff suppressed because one or more lines are too long

View File

@@ -172,19 +172,19 @@ function* parse(content) {
if (prevIndex !== u - prefixLen) {
yield { type: "text", text: content.substring(prevIndex, u - prefixLen) };
}
if (url.pathname.endsWith(".png") || url.pathname.endsWith(".jpg") || url.pathname.endsWith(".jpeg") || url.pathname.endsWith(".gif") || url.pathname.endsWith(".webp")) {
if (/\.(png|jpe?g|gif|webp)$/i.test(url.pathname)) {
yield { type: "image", url: url.toString() };
index = end;
prevIndex = index;
continue;
}
if (url.pathname.endsWith(".mp4") || url.pathname.endsWith(".avi") || url.pathname.endsWith(".webm") || url.pathname.endsWith(".mkv")) {
if (/\.(mp4|avi|webm|mkv)$/i.test(url.pathname)) {
yield { type: "video", url: url.toString() };
index = end;
prevIndex = index;
continue;
}
if (url.pathname.endsWith(".mp3") || url.pathname.endsWith(".aac") || url.pathname.endsWith(".ogg") || url.pathname.endsWith(".opus")) {
if (/\.(mp3|aac|ogg|opus)$/i.test(url.pathname)) {
yield { type: "audio", url: url.toString() };
index = end;
prevIndex = index;

File diff suppressed because one or more lines are too long

View File

@@ -23,8 +23,10 @@ __export(nip46_exports, {
BUNKER_REGEX: () => BUNKER_REGEX,
BunkerSigner: () => BunkerSigner,
createAccount: () => createAccount,
createNostrConnectURI: () => createNostrConnectURI,
fetchBunkerProviders: () => fetchBunkerProviders,
parseBunkerInput: () => parseBunkerInput,
parseNostrConnectURI: () => parseNostrConnectURI,
queryBunkerProfile: () => queryBunkerProfile,
toBunkerURL: () => toBunkerURL,
useFetchImplementation: () => useFetchImplementation
@@ -405,7 +407,10 @@ var AbstractRelay = class {
baseEoseTimeout = 4400;
connectionTimeout = 4400;
publishTimeout = 4400;
pingFrequency = 2e4;
pingTimeout = 2e4;
openSubs = /* @__PURE__ */ new Map();
enablePing;
connectionTimeoutHandle;
connectionPromise;
openCountRequests = /* @__PURE__ */ new Map();
@@ -422,6 +427,7 @@ var AbstractRelay = class {
this.url = normalizeURL(url);
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation || WebSocket;
this.enablePing = opts.enablePing;
}
static async connect(url, opts) {
const relay = new AbstractRelay(url, opts);
@@ -467,7 +473,7 @@ var AbstractRelay = class {
this.ws.onopen = () => {
clearTimeout(this.connectionTimeoutHandle);
this._connected = true;
if (this.ws && this.ws.ping) {
if (this.enablePing) {
this.pingpong();
}
resolve();
@@ -475,45 +481,54 @@ var AbstractRelay = class {
this.ws.onerror = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket error");
if (this._connected) {
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
}
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
};
this.ws.onclose = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket closed");
if (this._connected) {
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
}
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
};
this.ws.onmessage = this._onmessage.bind(this);
});
return this.connectionPromise;
}
async receivePong() {
async waitForPingPong() {
return new Promise((res, err) => {
;
this.ws && this.ws.on && this.ws.on("pong", () => res(true)) || err("ws can't listen for pong");
this.ws && this.ws.ping && this.ws.ping();
});
}
async waitForDummyReq() {
return new Promise((resolve, _) => {
const sub = this.subscribe([{ ids: ["a".repeat(64)] }], {
oneose: () => {
sub.close();
resolve(true);
},
eoseTimeout: this.pingTimeout + 1e3
});
});
}
async pingpong() {
if (this.ws?.readyState == 1) {
this.ws && this.ws.ping && this.ws.ping();
if (this.ws?.readyState === 1) {
const result = await Promise.any([
this.receivePong(),
new Promise((res) => setTimeout(() => res(false), 1e4))
this.ws && this.ws.ping && this.ws.on ? this.waitForPingPong() : this.waitForDummyReq(),
new Promise((res) => setTimeout(() => res(false), this.pingTimeout))
]);
console.error("pingpong result", result);
if (result) {
setTimeout(() => this.pingpong(), 1e4);
setTimeout(() => this.pingpong(), this.pingFrequency);
} else {
this.ws && this.ws.close();
this.closeAllSubscriptions("pingpong timed out");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
}
}
@@ -678,8 +693,8 @@ var AbstractRelay = class {
close() {
this.closeAllSubscriptions("relay connection closed by us");
this._connected = false;
this.ws?.close();
this.onclose?.();
this.ws?.close();
}
_onmessage(ev) {
this.incomingMessageQueue.enqueue(ev.data);
@@ -751,11 +766,13 @@ var AbstractSimplePool = class {
seenOn = /* @__PURE__ */ new Map();
trackRelays = false;
verifyEvent;
enablePing;
trustedRelayURLs = /* @__PURE__ */ new Set();
_WebSocket;
constructor(opts) {
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation;
this.enablePing = opts.enablePing;
}
async ensureRelay(url, params) {
url = normalizeURL(url);
@@ -763,7 +780,8 @@ var AbstractSimplePool = class {
if (!relay) {
relay = new AbstractRelay(url, {
verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,
websocketImplementation: this._WebSocket
websocketImplementation: this._WebSocket,
enablePing: this.enablePing
});
relay.onclose = () => {
this.relays.delete(url);
@@ -783,20 +801,38 @@ var AbstractSimplePool = class {
}
subscribe(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
return this.subscribeMap(
relays.map((url) => ({ url, filter })),
params
);
const request = [];
for (let i2 = 0; i2 < relays.length; i2++) {
const url = normalizeURL(relays[i2]);
if (!request.find((r) => r.url === url)) {
request.push({ url, filter });
}
}
return this.subscribeMap(request, params);
}
subscribeMany(relays, filters, params) {
subscribeMany(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
return this.subscribeMap(
relays.flatMap((url) => filters.map((filter) => ({ url, filter }))),
params
);
const request = [];
const uniqUrls = [];
for (let i2 = 0; i2 < relays.length; i2++) {
const url = normalizeURL(relays[i2]);
if (uniqUrls.indexOf(url) === -1) {
uniqUrls.push(url);
request.push({ url, filter });
}
}
return this.subscribeMap(request, params);
}
subscribeMap(requests, params) {
params.onauth = params.onauth || params.doauth;
const grouped = /* @__PURE__ */ new Map();
for (const req of requests) {
const { url, filter } = req;
if (!grouped.has(url))
grouped.set(url, []);
grouped.get(url).push(filter);
}
const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }));
if (this.trackRelays) {
params.receivedEvent = (relay, id) => {
let set = this.seenOn.get(id);
@@ -841,8 +877,7 @@ var AbstractSimplePool = class {
return have;
};
const allOpened = Promise.all(
requests.map(async ({ url, filter }, i2) => {
url = normalizeURL(url);
groupedRequests.map(async ({ url, filters }, i2) => {
let relay;
try {
relay = await this.ensureRelay(url, {
@@ -852,13 +887,13 @@ var AbstractSimplePool = class {
handleClose(i2, err?.message || String(err));
return;
}
let subscription = relay.subscribe([filter], {
let subscription = relay.subscribe(filters, {
...params,
oneose: () => handleEose(i2),
onclose: (reason) => {
if (reason.startsWith("auth-required: ") && params.onauth) {
relay.auth(params.onauth).then(() => {
relay.subscribe([filter], {
relay.subscribe(filters, {
...params,
oneose: () => handleEose(i2),
onclose: (reason2) => {
@@ -899,9 +934,9 @@ var AbstractSimplePool = class {
});
return subcloser;
}
subscribeManyEose(relays, filters, params) {
subscribeManyEose(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
const subcloser = this.subscribeMany(relays, filters, {
const subcloser = this.subscribeMany(relays, filter, {
...params,
oneose() {
subcloser.close("closed automatically on eose");
@@ -972,8 +1007,8 @@ try {
} catch {
}
var SimplePool = class extends AbstractSimplePool {
constructor() {
super({ verifyEvent, websocketImplementation: _WebSocket });
constructor(options) {
super({ verifyEvent, websocketImplementation: _WebSocket, ...options });
}
};
@@ -1029,6 +1064,75 @@ async function queryBunkerProfile(nip05) {
return null;
}
}
function createNostrConnectURI(params) {
if (!params.clientPubkey) {
throw new Error("clientPubkey is required.");
}
if (!params.relays || params.relays.length === 0) {
throw new Error("At least one relay is required.");
}
if (!params.secret) {
throw new Error("secret is required.");
}
const queryParams = new URLSearchParams();
params.relays.forEach((relay) => {
queryParams.append("relay", relay);
});
queryParams.append("secret", params.secret);
if (params.perms && params.perms.length > 0) {
queryParams.append("perms", params.perms.join(","));
}
if (params.name) {
queryParams.append("name", params.name);
}
if (params.url) {
queryParams.append("url", params.url);
}
if (params.image) {
queryParams.append("image", params.image);
}
return `nostrconnect://${params.clientPubkey}?${queryParams.toString()}`;
}
function parseNostrConnectURI(uri) {
if (!uri.startsWith("nostrconnect://")) {
throw new Error('Invalid nostrconnect URI: Must start with "nostrconnect://".');
}
const [protocolAndPubkey, queryString] = uri.split("?");
if (!protocolAndPubkey || !queryString) {
throw new Error("Invalid nostrconnect URI: Missing query string.");
}
const clientPubkey = protocolAndPubkey.substring("nostrconnect://".length);
if (!clientPubkey) {
throw new Error("Invalid nostrconnect URI: Missing client-pubkey.");
}
const queryParams = new URLSearchParams(queryString);
const relays = queryParams.getAll("relay");
if (relays.length === 0) {
throw new Error('Invalid nostrconnect URI: Missing "relay" parameter.');
}
const secret = queryParams.get("secret");
if (!secret) {
throw new Error('Invalid nostrconnect URI: Missing "secret" parameter.');
}
const permsString = queryParams.get("perms");
const perms = permsString ? permsString.split(",") : void 0;
const name = queryParams.get("name") || void 0;
const url = queryParams.get("url") || void 0;
const image = queryParams.get("image") || void 0;
return {
protocol: "nostrconnect",
clientPubkey,
params: {
relays,
secret,
perms,
name,
url,
image
},
originalString: uri
};
}
var BunkerSigner = class {
params;
pool;
@@ -1042,21 +1146,68 @@ var BunkerSigner = class {
conversationKey;
bp;
cachedPubKey;
constructor(clientSecretKey, bp, params = {}) {
if (bp.relays.length === 0) {
throw new Error("no relays are specified for this bunker");
}
constructor(clientSecretKey, params) {
this.params = params;
this.pool = params.pool || new SimplePool();
this.secretKey = clientSecretKey;
this.conversationKey = getConversationKey(clientSecretKey, bp.pubkey);
this.bp = bp;
this.isOpen = false;
this.idPrefix = Math.random().toString(36).substring(7);
this.serial = 0;
this.listeners = {};
this.waitingForAuth = {};
this.setupSubscription(params);
}
static fromBunker(clientSecretKey, bp, params = {}) {
if (bp.relays.length === 0) {
throw new Error("No relays specified for this bunker");
}
const signer = new BunkerSigner(clientSecretKey, params);
signer.conversationKey = getConversationKey(clientSecretKey, bp.pubkey);
signer.bp = bp;
signer.setupSubscription(params);
return signer;
}
static async fromURI(clientSecretKey, connectionURI, params = {}, maxWait = 3e5) {
const signer = new BunkerSigner(clientSecretKey, params);
const parsedURI = parseNostrConnectURI(connectionURI);
const clientPubkey = getPublicKey(clientSecretKey);
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
sub.close();
reject(new Error(`Connection timed out after ${maxWait / 1e3} seconds`));
}, maxWait);
const sub = signer.pool.subscribe(
parsedURI.params.relays,
{ kinds: [NostrConnect], "#p": [clientPubkey] },
{
onevent: async (event) => {
try {
const tempConvKey = getConversationKey(clientSecretKey, event.pubkey);
const decryptedContent = decrypt(event.content, tempConvKey);
const response = JSON.parse(decryptedContent);
if (response.result === parsedURI.params.secret) {
clearTimeout(timer);
sub.close();
signer.bp = {
pubkey: event.pubkey,
relays: parsedURI.params.relays,
secret: parsedURI.params.secret
};
signer.conversationKey = getConversationKey(clientSecretKey, event.pubkey);
signer.setupSubscription(params);
resolve(signer);
}
} catch (e) {
console.warn("Failed to process potential connection event", e);
}
},
onclose: () => {
clearTimeout(timer);
reject(new Error("Subscription closed before connection was established."));
},
maxWait
}
);
});
}
setupSubscription(params) {
const listeners = this.listeners;
@@ -1166,7 +1317,7 @@ var BunkerSigner = class {
async function createAccount(bunker, params, username, domain, email, localSecretKey = generateSecretKey()) {
if (email && !EMAIL_REGEX.test(email))
throw new Error("Invalid email");
let rpc = new BunkerSigner(localSecretKey, bunker.bunkerPointer, params);
let rpc = BunkerSigner.fromBunker(localSecretKey, bunker.bunkerPointer, params);
let pubkey = await rpc.sendRequest("create_account", [username, domain, email || ""]);
rpc.bp.pubkey = pubkey;
await rpc.connect();

File diff suppressed because one or more lines are too long

View File

@@ -161,41 +161,30 @@ async function getZapEndpoint(metadata) {
}
return null;
}
function makeZapRequest({
profile,
event,
amount,
relays,
comment = ""
}) {
if (!amount)
throw new Error("amount not given");
if (!profile)
throw new Error("profile not given");
function makeZapRequest(params) {
let zr = {
kind: 9734,
created_at: Math.round(Date.now() / 1e3),
content: comment,
content: params.comment || "",
tags: [
["p", profile],
["amount", amount.toString()],
["relays", ...relays]
["p", "pubkey" in params ? params.pubkey : params.event.pubkey],
["amount", params.amount.toString()],
["relays", ...params.relays]
]
};
if (event && typeof event === "string") {
zr.tags.push(["e", event]);
}
if (event && typeof event === "object") {
if (isReplaceableKind(event.kind)) {
const a = ["a", `${event.kind}:${event.pubkey}:`];
if ("event" in params) {
zr.tags.push(["e", params.event.id]);
if (isReplaceableKind(params.event.kind)) {
const a = ["a", `${params.event.kind}:${params.event.pubkey}:`];
zr.tags.push(a);
} else if (isAddressableKind(event.kind)) {
let d = event.tags.find(([t, v]) => t === "d" && v);
} else if (isAddressableKind(params.event.kind)) {
let d = params.event.tags.find(([t, v]) => t === "d" && v);
if (!d)
throw new Error("d tag not found or is empty");
const a = ["a", `${event.kind}:${event.pubkey}:${d[1]}`];
const a = ["a", `${params.event.kind}:${params.event.pubkey}:${d[1]}`];
zr.tags.push(a);
}
zr.tags.push(["k", params.event.kind.toString()]);
}
return zr;
}

File diff suppressed because one or more lines are too long

View File

@@ -287,7 +287,10 @@ var AbstractRelay = class {
baseEoseTimeout = 4400;
connectionTimeout = 4400;
publishTimeout = 4400;
pingFrequency = 2e4;
pingTimeout = 2e4;
openSubs = /* @__PURE__ */ new Map();
enablePing;
connectionTimeoutHandle;
connectionPromise;
openCountRequests = /* @__PURE__ */ new Map();
@@ -304,6 +307,7 @@ var AbstractRelay = class {
this.url = normalizeURL(url);
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation || WebSocket;
this.enablePing = opts.enablePing;
}
static async connect(url, opts) {
const relay = new AbstractRelay(url, opts);
@@ -349,7 +353,7 @@ var AbstractRelay = class {
this.ws.onopen = () => {
clearTimeout(this.connectionTimeoutHandle);
this._connected = true;
if (this.ws && this.ws.ping) {
if (this.enablePing) {
this.pingpong();
}
resolve();
@@ -357,45 +361,54 @@ var AbstractRelay = class {
this.ws.onerror = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket error");
if (this._connected) {
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
}
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
};
this.ws.onclose = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket closed");
if (this._connected) {
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
}
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
};
this.ws.onmessage = this._onmessage.bind(this);
});
return this.connectionPromise;
}
async receivePong() {
async waitForPingPong() {
return new Promise((res, err) => {
;
this.ws && this.ws.on && this.ws.on("pong", () => res(true)) || err("ws can't listen for pong");
this.ws && this.ws.ping && this.ws.ping();
});
}
async waitForDummyReq() {
return new Promise((resolve, _) => {
const sub = this.subscribe([{ ids: ["a".repeat(64)] }], {
oneose: () => {
sub.close();
resolve(true);
},
eoseTimeout: this.pingTimeout + 1e3
});
});
}
async pingpong() {
if (this.ws?.readyState == 1) {
this.ws && this.ws.ping && this.ws.ping();
if (this.ws?.readyState === 1) {
const result = await Promise.any([
this.receivePong(),
new Promise((res) => setTimeout(() => res(false), 1e4))
this.ws && this.ws.ping && this.ws.on ? this.waitForPingPong() : this.waitForDummyReq(),
new Promise((res) => setTimeout(() => res(false), this.pingTimeout))
]);
console.error("pingpong result", result);
if (result) {
setTimeout(() => this.pingpong(), 1e4);
setTimeout(() => this.pingpong(), this.pingFrequency);
} else {
this.ws && this.ws.close();
this.closeAllSubscriptions("pingpong timed out");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
}
}
@@ -560,8 +573,8 @@ var AbstractRelay = class {
close() {
this.closeAllSubscriptions("relay connection closed by us");
this._connected = false;
this.ws?.close();
this.onclose?.();
this.ws?.close();
}
_onmessage(ev) {
this.incomingMessageQueue.enqueue(ev.data);
@@ -633,11 +646,13 @@ var AbstractSimplePool = class {
seenOn = /* @__PURE__ */ new Map();
trackRelays = false;
verifyEvent;
enablePing;
trustedRelayURLs = /* @__PURE__ */ new Set();
_WebSocket;
constructor(opts) {
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation;
this.enablePing = opts.enablePing;
}
async ensureRelay(url, params) {
url = normalizeURL(url);
@@ -645,7 +660,8 @@ var AbstractSimplePool = class {
if (!relay) {
relay = new AbstractRelay(url, {
verifyEvent: this.trustedRelayURLs.has(url) ? alwaysTrue : this.verifyEvent,
websocketImplementation: this._WebSocket
websocketImplementation: this._WebSocket,
enablePing: this.enablePing
});
relay.onclose = () => {
this.relays.delete(url);
@@ -665,20 +681,38 @@ var AbstractSimplePool = class {
}
subscribe(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
return this.subscribeMap(
relays.map((url) => ({ url, filter })),
params
);
const request = [];
for (let i2 = 0; i2 < relays.length; i2++) {
const url = normalizeURL(relays[i2]);
if (!request.find((r) => r.url === url)) {
request.push({ url, filter });
}
}
return this.subscribeMap(request, params);
}
subscribeMany(relays, filters, params) {
subscribeMany(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
return this.subscribeMap(
relays.flatMap((url) => filters.map((filter) => ({ url, filter }))),
params
);
const request = [];
const uniqUrls = [];
for (let i2 = 0; i2 < relays.length; i2++) {
const url = normalizeURL(relays[i2]);
if (uniqUrls.indexOf(url) === -1) {
uniqUrls.push(url);
request.push({ url, filter });
}
}
return this.subscribeMap(request, params);
}
subscribeMap(requests, params) {
params.onauth = params.onauth || params.doauth;
const grouped = /* @__PURE__ */ new Map();
for (const req of requests) {
const { url, filter } = req;
if (!grouped.has(url))
grouped.set(url, []);
grouped.get(url).push(filter);
}
const groupedRequests = Array.from(grouped.entries()).map(([url, filters]) => ({ url, filters }));
if (this.trackRelays) {
params.receivedEvent = (relay, id) => {
let set = this.seenOn.get(id);
@@ -723,8 +757,7 @@ var AbstractSimplePool = class {
return have;
};
const allOpened = Promise.all(
requests.map(async ({ url, filter }, i2) => {
url = normalizeURL(url);
groupedRequests.map(async ({ url, filters }, i2) => {
let relay;
try {
relay = await this.ensureRelay(url, {
@@ -734,13 +767,13 @@ var AbstractSimplePool = class {
handleClose(i2, err?.message || String(err));
return;
}
let subscription = relay.subscribe([filter], {
let subscription = relay.subscribe(filters, {
...params,
oneose: () => handleEose(i2),
onclose: (reason) => {
if (reason.startsWith("auth-required: ") && params.onauth) {
relay.auth(params.onauth).then(() => {
relay.subscribe([filter], {
relay.subscribe(filters, {
...params,
oneose: () => handleEose(i2),
onclose: (reason2) => {
@@ -781,9 +814,9 @@ var AbstractSimplePool = class {
});
return subcloser;
}
subscribeManyEose(relays, filters, params) {
subscribeManyEose(relays, filter, params) {
params.onauth = params.onauth || params.doauth;
const subcloser = this.subscribeMany(relays, filters, {
const subcloser = this.subscribeMany(relays, filter, {
...params,
oneose() {
subcloser.close("closed automatically on eose");
@@ -857,7 +890,7 @@ function useWebSocketImplementation(websocketImplementation) {
_WebSocket = websocketImplementation;
}
var SimplePool = class extends AbstractSimplePool {
constructor() {
super({ verifyEvent, websocketImplementation: _WebSocket });
constructor(options) {
super({ verifyEvent, websocketImplementation: _WebSocket, ...options });
}
};

File diff suppressed because one or more lines are too long

View File

@@ -285,7 +285,10 @@ var AbstractRelay = class {
baseEoseTimeout = 4400;
connectionTimeout = 4400;
publishTimeout = 4400;
pingFrequency = 2e4;
pingTimeout = 2e4;
openSubs = /* @__PURE__ */ new Map();
enablePing;
connectionTimeoutHandle;
connectionPromise;
openCountRequests = /* @__PURE__ */ new Map();
@@ -302,6 +305,7 @@ var AbstractRelay = class {
this.url = normalizeURL(url);
this.verifyEvent = opts.verifyEvent;
this._WebSocket = opts.websocketImplementation || WebSocket;
this.enablePing = opts.enablePing;
}
static async connect(url, opts) {
const relay = new AbstractRelay(url, opts);
@@ -347,7 +351,7 @@ var AbstractRelay = class {
this.ws.onopen = () => {
clearTimeout(this.connectionTimeoutHandle);
this._connected = true;
if (this.ws && this.ws.ping) {
if (this.enablePing) {
this.pingpong();
}
resolve();
@@ -355,45 +359,54 @@ var AbstractRelay = class {
this.ws.onerror = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket error");
if (this._connected) {
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
}
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection errored");
};
this.ws.onclose = (ev) => {
clearTimeout(this.connectionTimeoutHandle);
reject(ev.message || "websocket closed");
if (this._connected) {
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
}
this._connected = false;
this.connectionPromise = void 0;
this.onclose?.();
this.closeAllSubscriptions("relay connection closed");
};
this.ws.onmessage = this._onmessage.bind(this);
});
return this.connectionPromise;
}
async receivePong() {
async waitForPingPong() {
return new Promise((res, err) => {
;
this.ws && this.ws.on && this.ws.on("pong", () => res(true)) || err("ws can't listen for pong");
this.ws && this.ws.ping && this.ws.ping();
});
}
async waitForDummyReq() {
return new Promise((resolve, _) => {
const sub = this.subscribe([{ ids: ["a".repeat(64)] }], {
oneose: () => {
sub.close();
resolve(true);
},
eoseTimeout: this.pingTimeout + 1e3
});
});
}
async pingpong() {
if (this.ws?.readyState == 1) {
this.ws && this.ws.ping && this.ws.ping();
if (this.ws?.readyState === 1) {
const result = await Promise.any([
this.receivePong(),
new Promise((res) => setTimeout(() => res(false), 1e4))
this.ws && this.ws.ping && this.ws.on ? this.waitForPingPong() : this.waitForDummyReq(),
new Promise((res) => setTimeout(() => res(false), this.pingTimeout))
]);
console.error("pingpong result", result);
if (result) {
setTimeout(() => this.pingpong(), 1e4);
setTimeout(() => this.pingpong(), this.pingFrequency);
} else {
this.ws && this.ws.close();
this.closeAllSubscriptions("pingpong timed out");
this._connected = false;
this.onclose?.();
this.ws?.close();
}
}
}
@@ -558,8 +571,8 @@ var AbstractRelay = class {
close() {
this.closeAllSubscriptions("relay connection closed by us");
this._connected = false;
this.ws?.close();
this.onclose?.();
this.ws?.close();
}
_onmessage(ev) {
this.incomingMessageQueue.enqueue(ev.data);

File diff suppressed because one or more lines are too long