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.
@@ -1,4 +1,4 @@
1
- import { test, expect, beforeEach, afterEach } from "vitest";
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
- test("creates new relay connections", () => {
31
- const url = "wss://relay1.example.com";
32
- const relay = pool.relay(url);
33
- expect(relay).toBeDefined();
34
- expect(pool.relays.get(url)).toBe(relay);
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
- test("returns existing relay connection if already exists", () => {
37
- const url = "wss://relay1.example.com";
38
- const relay1 = pool.relay(url);
39
- const relay2 = pool.relay(url);
40
- expect(relay1).toBe(relay2);
41
- expect(pool.relays.size).toBe(1);
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
- test("creates relay group with multiple relays", () => {
44
- const urls = ["wss://relay1.example.com", "wss://relay2.example.com"];
45
- const group = pool.group(urls);
46
- expect(group).toBeDefined();
47
- expect(pool.groups.get(urls.sort().join(","))).toBe(group);
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
- test("req method sends subscription to multiple relays", async () => {
50
- const urls = ["wss://relay1.example.com", "wss://relay2.example.com"];
51
- const filters = { kinds: [1] };
52
- const spy = subscribeSpyTo(pool.req(urls, filters));
53
- // Verify REQ was sent to both relays
54
- const req1 = await mockServer1.nextMessage;
55
- const req2 = await mockServer2.nextMessage;
56
- // Both messages should be REQ messages with the same filter
57
- expect(JSON.parse(req1)[0]).toBe("REQ");
58
- expect(JSON.parse(req2)[0]).toBe("REQ");
59
- expect(JSON.parse(req1)[2]).toEqual(filters);
60
- expect(JSON.parse(req2)[2]).toEqual(filters);
61
- // Send EVENT from first relay
62
- mockServer1.send(JSON.stringify(["EVENT", JSON.parse(req1)[1], mockEvent]));
63
- // Send EOSE from both relays
64
- mockServer1.send(JSON.stringify(["EOSE", JSON.parse(req1)[1]]));
65
- mockServer2.send(JSON.stringify(["EOSE", JSON.parse(req2)[1]]));
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
- // Wait for all message to be sent
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
- else {
30
- relay = new Relay(url, this.options);
31
- this.relays$.next(this.relays.set(url, relay));
32
- return relay;
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-relay",
3
- "version": "0.0.0-next-20250430200159",
3
+ "version": "0.0.0-next-20250505205231",
4
4
  "description": "nostr relay communication framework built on rxjs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",