feat: event emitter
This commit is contained in:
@ -80,15 +80,16 @@ export class NostrConnectWallet implements LNWallet {
|
|||||||
|
|
||||||
return await new Promise<boolean>(resolve => {
|
return await new Promise<boolean>(resolve => {
|
||||||
this.#conn = new Connection(this.#config.relayUrl, { read: true, write: true });
|
this.#conn = new Connection(this.#config.relayUrl, { read: true, write: true });
|
||||||
this.#conn.OnConnected = () => resolve(true);
|
this.#conn.on("connected", () => resolve(true));
|
||||||
this.#conn.Auth = async (c, r) => {
|
this.#conn.on("auth", async (c, r, cb) => {
|
||||||
const eb = new EventBuilder();
|
const eb = new EventBuilder();
|
||||||
eb.kind(EventKind.Auth).tag(["relay", r]).tag(["challenge", c]);
|
eb.kind(EventKind.Auth).tag(["relay", r]).tag(["challenge", c]);
|
||||||
return await eb.buildAndSign(this.#config.secret);
|
const ev = await eb.buildAndSign(this.#config.secret);
|
||||||
};
|
cb(ev);
|
||||||
this.#conn.OnEvent = (s, e) => {
|
});
|
||||||
|
this.#conn.on("event", (s, e) => {
|
||||||
this.#onReply(s, e);
|
this.#onReply(s, e);
|
||||||
};
|
});
|
||||||
this.#conn.Connect();
|
this.#conn.Connect();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -87,13 +87,14 @@ const System = new NostrSystem({
|
|||||||
relayMetrics: RelayMetrics,
|
relayMetrics: RelayMetrics,
|
||||||
queryOptimizer: hasWasm ? WasmQueryOptimizer : undefined,
|
queryOptimizer: hasWasm ? WasmQueryOptimizer : undefined,
|
||||||
db: SystemDb,
|
db: SystemDb,
|
||||||
authHandler: async (c, r) => {
|
});
|
||||||
const { id } = LoginStore.snapshot();
|
|
||||||
const pub = LoginStore.getPublisher(id);
|
System.on("auth", async (c, r, cb) => {
|
||||||
if (pub) {
|
const { id } = LoginStore.snapshot();
|
||||||
return await pub.nip42Auth(c, r);
|
const pub = LoginStore.getPublisher(id);
|
||||||
}
|
if (pub) {
|
||||||
},
|
cb(await pub.nip42Auth(c, r));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function fetchProfile(key: string) {
|
async function fetchProfile(key: string) {
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
"@snort/shared": "^1.0.7",
|
"@snort/shared": "^1.0.7",
|
||||||
"@stablelib/xchacha20": "^1.0.1",
|
"@stablelib/xchacha20": "^1.0.1",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
|
"events": "^3.3.0",
|
||||||
"isomorphic-ws": "^5.0.0",
|
"isomorphic-ws": "^5.0.0",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"ws": "^8.14.0"
|
"ws": "^8.14.0"
|
||||||
|
@ -8,8 +8,7 @@ import { ConnectionStats } from "./connection-stats";
|
|||||||
import { NostrEvent, ReqCommand, ReqFilter, TaggedNostrEvent, u256 } from "./nostr";
|
import { NostrEvent, ReqCommand, ReqFilter, TaggedNostrEvent, u256 } from "./nostr";
|
||||||
import { RelayInfo } from "./relay-info";
|
import { RelayInfo } from "./relay-info";
|
||||||
import EventKind from "./event-kind";
|
import EventKind from "./event-kind";
|
||||||
|
import EventEmitter from "events";
|
||||||
export type AuthHandler = (challenge: string, relay: string) => Promise<NostrEvent | undefined>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Relay settings
|
* Relay settings
|
||||||
@ -46,7 +45,22 @@ export interface ConnectionStateSnapshot {
|
|||||||
address: string;
|
address: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
interface ConnectionEvents {
|
||||||
|
change: (snapshot: ConnectionStateSnapshot) => void;
|
||||||
|
connected: (wasReconnect: boolean) => void;
|
||||||
|
event: (sub: string, e: TaggedNostrEvent) => void;
|
||||||
|
eose: (sub: string) => void;
|
||||||
|
disconnect: (code: number) => void;
|
||||||
|
auth: (challenge: string, relay: string, cb: (ev: NostrEvent) => void) => void;
|
||||||
|
notice: (msg: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare interface Connection {
|
||||||
|
on<U extends keyof ConnectionEvents>(event: U, listener: ConnectionEvents[U]): this;
|
||||||
|
once<U extends keyof ConnectionEvents>(event: U, listener: ConnectionEvents[U]): this;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Connection extends EventEmitter {
|
||||||
#log: debug.Debugger;
|
#log: debug.Debugger;
|
||||||
#ephemeralCheck?: ReturnType<typeof setInterval>;
|
#ephemeralCheck?: ReturnType<typeof setInterval>;
|
||||||
#activity: number = unixNowMs();
|
#activity: number = unixNowMs();
|
||||||
@ -72,16 +86,12 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
IsClosed: boolean;
|
IsClosed: boolean;
|
||||||
ReconnectTimer?: ReturnType<typeof setTimeout>;
|
ReconnectTimer?: ReturnType<typeof setTimeout>;
|
||||||
EventsCallback: Map<u256, (msg: Array<string | boolean>) => void>;
|
EventsCallback: Map<u256, (msg: Array<string | boolean>) => void>;
|
||||||
OnConnected?: (wasReconnect: boolean) => void;
|
|
||||||
OnEvent?: (sub: string, e: TaggedNostrEvent) => void;
|
|
||||||
OnEose?: (sub: string) => void;
|
|
||||||
OnDisconnect?: (code: number) => void;
|
|
||||||
Auth?: AuthHandler;
|
|
||||||
AwaitingAuth: Map<string, boolean>;
|
AwaitingAuth: Map<string, boolean>;
|
||||||
Authed = false;
|
Authed = false;
|
||||||
Down = true;
|
Down = true;
|
||||||
|
|
||||||
constructor(addr: string, options: RelaySettings, auth?: AuthHandler, ephemeral: boolean = false) {
|
constructor(addr: string, options: RelaySettings, ephemeral: boolean = false) {
|
||||||
super();
|
super();
|
||||||
this.Id = uuid();
|
this.Id = uuid();
|
||||||
this.Address = addr;
|
this.Address = addr;
|
||||||
@ -89,7 +99,6 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
this.IsClosed = false;
|
this.IsClosed = false;
|
||||||
this.EventsCallback = new Map();
|
this.EventsCallback = new Map();
|
||||||
this.AwaitingAuth = new Map();
|
this.AwaitingAuth = new Map();
|
||||||
this.Auth = auth;
|
|
||||||
this.#ephemeral = ephemeral;
|
this.#ephemeral = ephemeral;
|
||||||
this.#log = debug("Connection").extend(addr);
|
this.#log = debug("Connection").extend(addr);
|
||||||
}
|
}
|
||||||
@ -154,7 +163,7 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
this.#log(`Open!`);
|
this.#log(`Open!`);
|
||||||
this.Down = false;
|
this.Down = false;
|
||||||
this.#setupEphemeral();
|
this.#setupEphemeral();
|
||||||
this.OnConnected?.(wasReconnect);
|
this.emit("connected", wasReconnect);
|
||||||
this.#sendPendingRaw();
|
this.#sendPendingRaw();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +191,7 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
this.ReconnectTimer = undefined;
|
this.ReconnectTimer = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.OnDisconnect?.(e.code);
|
this.emit("disconnected", e.code);
|
||||||
this.#reset();
|
this.#reset();
|
||||||
this.notifyChange();
|
this.notifyChange();
|
||||||
}
|
}
|
||||||
@ -206,7 +215,7 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "EVENT": {
|
case "EVENT": {
|
||||||
this.OnEvent?.(msg[1] as string, {
|
this.emit("event", msg[1] as string, {
|
||||||
...(msg[2] as NostrEvent),
|
...(msg[2] as NostrEvent),
|
||||||
relays: [this.Address],
|
relays: [this.Address],
|
||||||
});
|
});
|
||||||
@ -215,7 +224,7 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "EOSE": {
|
case "EOSE": {
|
||||||
this.OnEose?.(msg[1] as string);
|
this.emit("eose", msg[1] as string);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "OK": {
|
case "OK": {
|
||||||
@ -230,6 +239,7 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "NOTICE": {
|
case "NOTICE": {
|
||||||
|
this.emit("notice", msg[1]);
|
||||||
this.#log(`NOTICE: ${msg[1]}`);
|
this.#log(`NOTICE: ${msg[1]}`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -346,7 +356,7 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
CloseReq(id: string) {
|
CloseReq(id: string) {
|
||||||
if (this.ActiveRequests.delete(id)) {
|
if (this.ActiveRequests.delete(id)) {
|
||||||
this.#sendJson(["CLOSE", id]);
|
this.#sendJson(["CLOSE", id]);
|
||||||
this.OnEose?.(id);
|
this.emit("eose", id);
|
||||||
this.#SendQueuedRequests();
|
this.#SendQueuedRequests();
|
||||||
}
|
}
|
||||||
this.notifyChange();
|
this.notifyChange();
|
||||||
@ -435,11 +445,11 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
const authCleanup = () => {
|
const authCleanup = () => {
|
||||||
this.AwaitingAuth.delete(challenge);
|
this.AwaitingAuth.delete(challenge);
|
||||||
};
|
};
|
||||||
if (!this.Auth) {
|
|
||||||
throw new Error("Auth hook not registered");
|
|
||||||
}
|
|
||||||
this.AwaitingAuth.set(challenge, true);
|
this.AwaitingAuth.set(challenge, true);
|
||||||
const authEvent = await this.Auth(challenge, this.Address);
|
const authEvent = await new Promise<NostrEvent>((resolve, reject) =>
|
||||||
|
this.emit("auth", challenge, this.Address, resolve),
|
||||||
|
);
|
||||||
|
this.#log("Auth result: %o", authEvent);
|
||||||
if (!authEvent) {
|
if (!authEvent) {
|
||||||
authCleanup();
|
authCleanup();
|
||||||
throw new Error("No auth event");
|
throw new Error("No auth event");
|
||||||
@ -486,4 +496,8 @@ export class Connection extends ExternalStore<ConnectionStateSnapshot> {
|
|||||||
}, 5_000);
|
}, 5_000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notifyChange() {
|
||||||
|
this.emit("change", this.takeSnapshot());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,10 +85,10 @@ export class Nip46Signer implements EventSigner {
|
|||||||
}
|
}
|
||||||
return await new Promise<void>((resolve, reject) => {
|
return await new Promise<void>((resolve, reject) => {
|
||||||
this.#conn = new Connection(this.#relay, { read: true, write: true });
|
this.#conn = new Connection(this.#relay, { read: true, write: true });
|
||||||
this.#conn.OnEvent = async (sub, e) => {
|
this.#conn.on("event", async (sub, e) => {
|
||||||
await this.#onReply(e);
|
await this.#onReply(e);
|
||||||
};
|
});
|
||||||
this.#conn.OnConnected = async () => {
|
this.#conn.on("connected", async () => {
|
||||||
this.#conn!.QueueReq(
|
this.#conn!.QueueReq(
|
||||||
[
|
[
|
||||||
"REQ",
|
"REQ",
|
||||||
@ -110,7 +110,7 @@ export class Nip46Signer implements EventSigner {
|
|||||||
resolve,
|
resolve,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
this.#conn.Connect();
|
this.#conn.Connect();
|
||||||
this.#didInit = true;
|
this.#didInit = true;
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { AuthHandler, RelaySettings, ConnectionStateSnapshot, OkResponse } from "./connection";
|
import { RelaySettings, ConnectionStateSnapshot, OkResponse } from "./connection";
|
||||||
import { RequestBuilder } from "./request-builder";
|
import { RequestBuilder } from "./request-builder";
|
||||||
import { NoteStore, NoteStoreSnapshotData } from "./note-collection";
|
import { NoteStore, NoteStoreSnapshotData } from "./note-collection";
|
||||||
import { Query } from "./query";
|
import { Query } from "./query";
|
||||||
@ -46,11 +46,6 @@ export interface SystemInterface {
|
|||||||
*/
|
*/
|
||||||
checkSigs: boolean;
|
checkSigs: boolean;
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler function for NIP-42
|
|
||||||
*/
|
|
||||||
HandleAuth?: AuthHandler;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a snapshot of the relay connections
|
* Get a snapshot of the relay connections
|
||||||
*/
|
*/
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import debug from "debug";
|
import debug from "debug";
|
||||||
|
import EventEmitter from "events";
|
||||||
|
|
||||||
import { unwrap, sanitizeRelayUrl, ExternalStore, FeedCache, removeUndefined } from "@snort/shared";
|
import { unwrap, sanitizeRelayUrl, FeedCache, removeUndefined } from "@snort/shared";
|
||||||
import { NostrEvent, TaggedNostrEvent } from "./nostr";
|
import { NostrEvent, TaggedNostrEvent } from "./nostr";
|
||||||
import { AuthHandler, Connection, RelaySettings, ConnectionStateSnapshot, OkResponse } from "./connection";
|
import { Connection, RelaySettings, ConnectionStateSnapshot, OkResponse } from "./connection";
|
||||||
import { Query } from "./query";
|
import { Query } from "./query";
|
||||||
import { NoteCollection, NoteStore, NoteStoreSnapshotData } from "./note-collection";
|
import { NoteCollection, NoteStore, NoteStoreSnapshotData } from "./note-collection";
|
||||||
import { BuiltRawReqFilter, RequestBuilder, RequestStrategy } from "./request-builder";
|
import { BuiltRawReqFilter, RequestBuilder, RequestStrategy } from "./request-builder";
|
||||||
@ -25,10 +26,20 @@ import { RelayCache } from "./gossip-model";
|
|||||||
import { QueryOptimizer, DefaultQueryOptimizer } from "./query-optimizer";
|
import { QueryOptimizer, DefaultQueryOptimizer } from "./query-optimizer";
|
||||||
import { trimFilters } from "./request-trim";
|
import { trimFilters } from "./request-trim";
|
||||||
|
|
||||||
|
interface NostrSystemEvents {
|
||||||
|
change: (state: SystemSnapshot) => void;
|
||||||
|
auth: (challenge: string, relay: string, cb: (ev: NostrEvent) => void) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare interface NostrSystem {
|
||||||
|
on<U extends keyof NostrSystemEvents>(event: U, listener: NostrSystemEvents[U]): this;
|
||||||
|
once<U extends keyof NostrSystemEvents>(event: U, listener: NostrSystemEvents[U]): this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages nostr content retrieval system
|
* Manages nostr content retrieval system
|
||||||
*/
|
*/
|
||||||
export class NostrSystem extends ExternalStore<SystemSnapshot> implements SystemInterface {
|
export class NostrSystem extends EventEmitter implements SystemInterface {
|
||||||
#log = debug("System");
|
#log = debug("System");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,11 +52,6 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
|
|||||||
*/
|
*/
|
||||||
Queries: Map<string, Query> = new Map();
|
Queries: Map<string, Query> = new Map();
|
||||||
|
|
||||||
/**
|
|
||||||
* NIP-42 Auth handler
|
|
||||||
*/
|
|
||||||
#handleAuth?: AuthHandler;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Storage class for user relay lists
|
* Storage class for user relay lists
|
||||||
*/
|
*/
|
||||||
@ -87,7 +93,6 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
|
|||||||
checkSigs: boolean;
|
checkSigs: boolean;
|
||||||
|
|
||||||
constructor(props: {
|
constructor(props: {
|
||||||
authHandler?: AuthHandler;
|
|
||||||
relayCache?: FeedCache<UsersRelays>;
|
relayCache?: FeedCache<UsersRelays>;
|
||||||
profileCache?: FeedCache<MetadataCache>;
|
profileCache?: FeedCache<MetadataCache>;
|
||||||
relayMetrics?: FeedCache<RelayMetrics>;
|
relayMetrics?: FeedCache<RelayMetrics>;
|
||||||
@ -97,7 +102,6 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
|
|||||||
checkSigs?: boolean;
|
checkSigs?: boolean;
|
||||||
}) {
|
}) {
|
||||||
super();
|
super();
|
||||||
this.#handleAuth = props.authHandler;
|
|
||||||
this.#relayCache = props.relayCache ?? new UserRelaysCache(props.db?.userRelays);
|
this.#relayCache = props.relayCache ?? new UserRelaysCache(props.db?.userRelays);
|
||||||
this.#profileCache = props.profileCache ?? new UserProfileCache(props.db?.users);
|
this.#profileCache = props.profileCache ?? new UserProfileCache(props.db?.users);
|
||||||
this.#relayMetricsCache = props.relayMetrics ?? new RelayMetricCache(props.db?.relayMetrics);
|
this.#relayMetricsCache = props.relayMetrics ?? new RelayMetricCache(props.db?.relayMetrics);
|
||||||
@ -110,14 +114,12 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
|
|||||||
this.#cleanup();
|
this.#cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
HandleAuth?: AuthHandler | undefined;
|
|
||||||
|
|
||||||
get ProfileLoader() {
|
get ProfileLoader() {
|
||||||
return this.#profileLoader;
|
return this.#profileLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
get Sockets(): ConnectionStateSnapshot[] {
|
get Sockets(): ConnectionStateSnapshot[] {
|
||||||
return [...this.#sockets.values()].map(a => a.snapshot());
|
return [...this.#sockets.values()].map(a => a.takeSnapshot());
|
||||||
}
|
}
|
||||||
|
|
||||||
get RelayCache(): RelayCache {
|
get RelayCache(): RelayCache {
|
||||||
@ -149,12 +151,12 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
|
|||||||
const addr = unwrap(sanitizeRelayUrl(address));
|
const addr = unwrap(sanitizeRelayUrl(address));
|
||||||
const existing = this.#sockets.get(addr);
|
const existing = this.#sockets.get(addr);
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
const c = new Connection(addr, options, this.#handleAuth?.bind(this));
|
const c = new Connection(addr, options);
|
||||||
this.#sockets.set(addr, c);
|
this.#sockets.set(addr, c);
|
||||||
c.OnEvent = (s, e) => this.#onEvent(s, e);
|
c.on("event", (s, e) => this.#onEvent(s, e));
|
||||||
c.OnEose = s => this.#onEndOfStoredEvents(c, s);
|
c.on("eose", s => this.#onEndOfStoredEvents(c, s));
|
||||||
c.OnDisconnect = code => this.#onRelayDisconnect(c, code);
|
c.on("disconnect", code => this.#onRelayDisconnect(c, code));
|
||||||
c.OnConnected = r => this.#onRelayConnected(c, r);
|
c.on("connected", r => this.#onRelayConnected(c, r));
|
||||||
await c.Connect();
|
await c.Connect();
|
||||||
} else {
|
} else {
|
||||||
// update settings if already connected
|
// update settings if already connected
|
||||||
@ -210,12 +212,12 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
|
|||||||
try {
|
try {
|
||||||
const addr = unwrap(sanitizeRelayUrl(address));
|
const addr = unwrap(sanitizeRelayUrl(address));
|
||||||
if (!this.#sockets.has(addr)) {
|
if (!this.#sockets.has(addr)) {
|
||||||
const c = new Connection(addr, { read: true, write: true }, this.#handleAuth?.bind(this), true);
|
const c = new Connection(addr, { read: true, write: true }, true);
|
||||||
this.#sockets.set(addr, c);
|
this.#sockets.set(addr, c);
|
||||||
c.OnEvent = (s, e) => this.#onEvent(s, e);
|
c.on("event", (s, e) => this.#onEvent(s, e));
|
||||||
c.OnEose = s => this.#onEndOfStoredEvents(c, s);
|
c.on("eose", s => this.#onEndOfStoredEvents(c, s));
|
||||||
c.OnDisconnect = code => this.#onRelayDisconnect(c, code);
|
c.on("disconnect", code => this.#onRelayDisconnect(c, code));
|
||||||
c.OnConnected = r => this.#onRelayConnected(c, r);
|
c.on("connected", r => this.#onRelayConnected(c, r));
|
||||||
await c.Connect();
|
await c.Connect();
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
@ -404,15 +406,15 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
|
|||||||
return await existing.SendAsync(ev);
|
return await existing.SendAsync(ev);
|
||||||
} else {
|
} else {
|
||||||
return await new Promise<OkResponse>((resolve, reject) => {
|
return await new Promise<OkResponse>((resolve, reject) => {
|
||||||
const c = new Connection(address, { write: true, read: true }, this.#handleAuth?.bind(this), true);
|
const c = new Connection(address, { write: true, read: true }, true);
|
||||||
|
|
||||||
const t = setTimeout(reject, 10_000);
|
const t = setTimeout(reject, 10_000);
|
||||||
c.OnConnected = async () => {
|
c.once("connected", async () => {
|
||||||
clearTimeout(t);
|
clearTimeout(t);
|
||||||
const rsp = await c.SendAsync(ev);
|
const rsp = await c.SendAsync(ev);
|
||||||
c.Close();
|
c.Close();
|
||||||
resolve(rsp);
|
resolve(rsp);
|
||||||
};
|
});
|
||||||
c.Connect();
|
c.Connect();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -430,6 +432,10 @@ export class NostrSystem extends ExternalStore<SystemSnapshot> implements System
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notifyChange() {
|
||||||
|
this.emit("change", this.takeSnapshot());
|
||||||
|
}
|
||||||
|
|
||||||
#cleanup() {
|
#cleanup() {
|
||||||
let changed = false;
|
let changed = false;
|
||||||
for (const [k, v] of this.Queries) {
|
for (const [k, v] of this.Queries) {
|
||||||
|
@ -3243,6 +3243,7 @@ __metadata:
|
|||||||
"@types/uuid": ^9.0.2
|
"@types/uuid": ^9.0.2
|
||||||
"@types/ws": ^8.5.5
|
"@types/ws": ^8.5.5
|
||||||
debug: ^4.3.4
|
debug: ^4.3.4
|
||||||
|
events: ^3.3.0
|
||||||
isomorphic-ws: ^5.0.0
|
isomorphic-ws: ^5.0.0
|
||||||
jest: ^29.5.0
|
jest: ^29.5.0
|
||||||
jest-environment-jsdom: ^29.5.0
|
jest-environment-jsdom: ^29.5.0
|
||||||
|
Reference in New Issue
Block a user