applesauce-relay 0.0.0-next-20250430200159 → 0.0.0-next-20250505205231
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/pool.test.js +64 -49
- package/dist/__tests__/relay.test.js +1 -3
- package/dist/pool.js +12 -5
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { expect, beforeEach, afterEach, describe, it } from "vitest";
|
|
2
2
|
import { subscribeSpyTo } from "@hirez_io/observer-spy";
|
|
3
3
|
import { WS } from "vitest-websocket-mock";
|
|
4
4
|
import { RelayPool } from "../pool.js";
|
|
@@ -27,55 +27,70 @@ afterEach(async () => {
|
|
|
27
27
|
// Clean up WebSocket mocks
|
|
28
28
|
await WS.clean();
|
|
29
29
|
});
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
describe("relay", () => {
|
|
31
|
+
it("should create a new relay", () => {
|
|
32
|
+
const url = "wss://relay1.example.com/";
|
|
33
|
+
const relay = pool.relay(url);
|
|
34
|
+
expect(relay).toBeDefined();
|
|
35
|
+
expect(pool.relays.get(url)).toBe(relay);
|
|
36
|
+
});
|
|
37
|
+
it("should return existing relay connection if already exists", () => {
|
|
38
|
+
const url = "wss://relay1.example.com";
|
|
39
|
+
const relay1 = pool.relay(url);
|
|
40
|
+
const relay2 = pool.relay(url);
|
|
41
|
+
expect(relay1).toBe(relay2);
|
|
42
|
+
expect(pool.relays.size).toBe(1);
|
|
43
|
+
});
|
|
44
|
+
it("should normalize relay urls", () => {
|
|
45
|
+
expect(pool.relay("wss://relay.example.com")).toBe(pool.relay("wss://relay.example.com/"));
|
|
46
|
+
expect(pool.relay("wss://relay.example.com:443")).toBe(pool.relay("wss://relay.example.com/"));
|
|
47
|
+
expect(pool.relay("ws://relay.example.com:80")).toBe(pool.relay("ws://relay.example.com/"));
|
|
48
|
+
});
|
|
35
49
|
});
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
50
|
+
describe("group", () => {
|
|
51
|
+
it("should create a relay group", () => {
|
|
52
|
+
const urls = ["wss://relay1.example.com/", "wss://relay2.example.com/"];
|
|
53
|
+
const group = pool.group(urls);
|
|
54
|
+
expect(group).toBeDefined();
|
|
55
|
+
expect(pool.groups.get(urls.sort().join(","))).toBe(group);
|
|
56
|
+
});
|
|
42
57
|
});
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
58
|
+
describe("req", () => {
|
|
59
|
+
it("should send subscription to multiple relays", async () => {
|
|
60
|
+
const urls = ["wss://relay1.example.com", "wss://relay2.example.com"];
|
|
61
|
+
const filters = { kinds: [1] };
|
|
62
|
+
const spy = subscribeSpyTo(pool.req(urls, filters));
|
|
63
|
+
// Verify REQ was sent to both relays
|
|
64
|
+
const req1 = await mockServer1.nextMessage;
|
|
65
|
+
const req2 = await mockServer2.nextMessage;
|
|
66
|
+
// Both messages should be REQ messages with the same filter
|
|
67
|
+
expect(JSON.parse(req1)[0]).toBe("REQ");
|
|
68
|
+
expect(JSON.parse(req2)[0]).toBe("REQ");
|
|
69
|
+
expect(JSON.parse(req1)[2]).toEqual(filters);
|
|
70
|
+
expect(JSON.parse(req2)[2]).toEqual(filters);
|
|
71
|
+
// Send EVENT from first relay
|
|
72
|
+
mockServer1.send(JSON.stringify(["EVENT", JSON.parse(req1)[1], mockEvent]));
|
|
73
|
+
// Send EOSE from both relays
|
|
74
|
+
mockServer1.send(JSON.stringify(["EOSE", JSON.parse(req1)[1]]));
|
|
75
|
+
mockServer2.send(JSON.stringify(["EOSE", JSON.parse(req2)[1]]));
|
|
76
|
+
expect(spy.getValues()).toContainEqual(expect.objectContaining(mockEvent));
|
|
77
|
+
});
|
|
48
78
|
});
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
expect(spy.getValues()).toContainEqual(expect.objectContaining(mockEvent));
|
|
67
|
-
});
|
|
68
|
-
test("event method publishes to multiple relays", async () => {
|
|
69
|
-
const urls = ["wss://relay1.example.com", "wss://relay2.example.com"];
|
|
70
|
-
const spy = subscribeSpyTo(pool.event(urls, mockEvent));
|
|
71
|
-
// Verify EVENT was sent to both relays
|
|
72
|
-
const event1 = await mockServer1.nextMessage;
|
|
73
|
-
const event2 = await mockServer2.nextMessage;
|
|
74
|
-
expect(JSON.parse(event1)).toEqual(["EVENT", mockEvent]);
|
|
75
|
-
expect(JSON.parse(event2)).toEqual(["EVENT", mockEvent]);
|
|
76
|
-
// Send OK responses from both relays
|
|
77
|
-
mockServer1.send(JSON.stringify(["OK", mockEvent.id, true, ""]));
|
|
78
|
-
mockServer2.send(JSON.stringify(["OK", mockEvent.id, true, ""]));
|
|
79
|
-
expect(spy.getValues()).toContainEqual({ ok: true, from: "wss://relay1.example.com", message: "" });
|
|
80
|
-
expect(spy.getValues()).toContainEqual({ ok: true, from: "wss://relay2.example.com", message: "" });
|
|
79
|
+
describe("event", () => {
|
|
80
|
+
it("should publish to multiple relays", async () => {
|
|
81
|
+
const urls = ["wss://relay1.example.com/", "wss://relay2.example.com/"];
|
|
82
|
+
const spy = subscribeSpyTo(pool.event(urls, mockEvent));
|
|
83
|
+
// Verify EVENT was sent to both relays
|
|
84
|
+
const event1 = await mockServer1.nextMessage;
|
|
85
|
+
const event2 = await mockServer2.nextMessage;
|
|
86
|
+
expect(JSON.parse(event1)).toEqual(["EVENT", mockEvent]);
|
|
87
|
+
expect(JSON.parse(event2)).toEqual(["EVENT", mockEvent]);
|
|
88
|
+
// Send OK responses from both relays
|
|
89
|
+
mockServer1.send(JSON.stringify(["OK", mockEvent.id, true, ""]));
|
|
90
|
+
mockServer2.send(JSON.stringify(["OK", mockEvent.id, true, ""]));
|
|
91
|
+
expect(spy.getValues()).toEqual([
|
|
92
|
+
{ ok: true, from: "wss://relay1.example.com/", message: "" },
|
|
93
|
+
{ ok: true, from: "wss://relay2.example.com/", message: "" },
|
|
94
|
+
]);
|
|
95
|
+
});
|
|
81
96
|
});
|
|
@@ -49,9 +49,7 @@ describe("req", () => {
|
|
|
49
49
|
});
|
|
50
50
|
it("should send expected messages to relay", async () => {
|
|
51
51
|
subscribeSpyTo(relay.req([{ kinds: [1] }], "sub1"));
|
|
52
|
-
|
|
53
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
54
|
-
expect(server.messages).toEqual([["REQ", "sub1", { kinds: [1] }]]);
|
|
52
|
+
await expect(server).toReceiveMessage(["REQ", "sub1", { kinds: [1] }]);
|
|
55
53
|
});
|
|
56
54
|
it("should not close the REQ when EOSE is received", async () => {
|
|
57
55
|
// Create subscription that completes after first EOSE
|
package/dist/pool.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BehaviorSubject } from "rxjs";
|
|
2
2
|
import { RelayGroup } from "./group.js";
|
|
3
3
|
import { Relay } from "./relay.js";
|
|
4
|
+
import { normalizeURL } from "applesauce-core/helpers";
|
|
4
5
|
export class RelayPool {
|
|
5
6
|
options;
|
|
6
7
|
groups$ = new BehaviorSubject(new Map());
|
|
@@ -21,19 +22,25 @@ export class RelayPool {
|
|
|
21
22
|
}
|
|
22
23
|
/** Get or create a new relay connection */
|
|
23
24
|
relay(url) {
|
|
25
|
+
// Normalize the url
|
|
26
|
+
url = normalizeURL(url);
|
|
27
|
+
// Check if the url is blacklisted
|
|
24
28
|
if (this.blacklist.has(url))
|
|
25
29
|
throw new Error("Relay is on blacklist");
|
|
30
|
+
// Check if the relay already exists
|
|
26
31
|
let relay = this.relays.get(url);
|
|
27
32
|
if (relay)
|
|
28
33
|
return relay;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
+
// Create a new relay
|
|
35
|
+
relay = new Relay(url, this.options);
|
|
36
|
+
this.relays$.next(this.relays.set(url, relay));
|
|
37
|
+
return relay;
|
|
34
38
|
}
|
|
35
39
|
/** Create a group of relays */
|
|
36
40
|
group(relays) {
|
|
41
|
+
// Normalize all urls
|
|
42
|
+
relays = relays.map((url) => normalizeURL(url));
|
|
43
|
+
// Filter out any blacklisted relays
|
|
37
44
|
relays = this.filterBlacklist(relays);
|
|
38
45
|
const key = relays.sort().join(",");
|
|
39
46
|
let group = this.groups.get(key);
|