@qntx/nostr 0.2.0 → 0.2.1
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/README.md +16 -0
- package/dist/{client-BWsX9Cv1.mjs → client-5DhgYV7P.mjs} +20 -2
- package/dist/client-5DhgYV7P.mjs.map +1 -0
- package/dist/client.d.mts +1 -1
- package/dist/client.mjs +1 -1
- package/dist/{index-JdaS_uce.d.mts → index-B-CGNDPS.d.mts} +2 -2
- package/dist/{index-B425CAcQ.d.mts → index-BIhSADwK.d.mts} +14 -3
- package/dist/{index-CUIvFQdE.d.mts → index-D-TK_KJG.d.mts} +14 -3
- package/dist/{index-BVyfUBuG.d.mts → index-D1bqFHKB.d.mts} +17 -10
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +3 -3
- package/dist/loaders.d.mts +1 -1
- package/dist/{pool-tEr7c5bG.d.mts → pool-B7p_reAm.d.mts} +33 -2
- package/dist/{pool-XmAGaufS.mjs → pool-C2xOcsun.mjs} +103 -3
- package/dist/pool-C2xOcsun.mjs.map +1 -0
- package/dist/relay.d.mts +3 -3
- package/dist/relay.mjs +2 -2
- package/dist/storage-xXxLGCVb.mjs.map +1 -1
- package/dist/storage.d.mts +1 -1
- package/dist/testing.d.mts +10 -0
- package/dist/testing.mjs +6 -0
- package/dist/testing.mjs.map +1 -1
- package/package.json +65 -33
- package/dist/client-BWsX9Cv1.mjs.map +0 -1
- package/dist/pool-XmAGaufS.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -6,6 +6,22 @@ Layered TypeScript Nostr library: events, keys, filters, signers, relays, storag
|
|
|
6
6
|
|
|
7
7
|
See [docs/](docs/).
|
|
8
8
|
|
|
9
|
+
## Per-relay invalid-event policy
|
|
10
|
+
|
|
11
|
+
`Pool`/`Client` can suspend a relay that keeps delivering events failing id/signature
|
|
12
|
+
verification: when more than `limit` delivered EVENTs fail inside a `windowMs` sliding window,
|
|
13
|
+
the connection is closed and `ensureRelay` rejects with `RelaySuspendedError` until `cooldownMs`
|
|
14
|
+
elapses. `onRelaySuspended(url, until)` fires once per suspension, and live subscriptions resume
|
|
15
|
+
automatically once the cooldown lifts.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
const client = new Client({
|
|
19
|
+
relays: ["wss://relay.example"],
|
|
20
|
+
invalidEventPolicy: { limit: 20, windowMs: 60_000, cooldownMs: 5 * 60_000 },
|
|
21
|
+
onRelaySuspended: (url, until) => console.warn(`${url} suspended until ${new Date(until)}`),
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
9
25
|
## License
|
|
10
26
|
|
|
11
27
|
Licensed under the MIT License ([LICENSE](LICENSE) or <https://opensource.org/licenses/MIT>).
|
|
@@ -5,7 +5,7 @@ import { t as EventBuilder } from "./builder-CyrGKRVP.mjs";
|
|
|
5
5
|
import { n as canonicalizeFilters, o as matchFilters, t as canonicalizeFilter } from "./filter-BQl3GlHt.mjs";
|
|
6
6
|
import { t as NoSignerError } from "./error-B__OMBRY.mjs";
|
|
7
7
|
import { t as invokeSafely } from "./report-CHld4uWj.mjs";
|
|
8
|
-
import { n as fanIn, r as fetchRouted, t as Pool } from "./pool-
|
|
8
|
+
import { n as fanIn, r as fetchRouted, t as Pool } from "./pool-C2xOcsun.mjs";
|
|
9
9
|
import { r as throwIfAborted } from "./abort-Ba8tDrOI.mjs";
|
|
10
10
|
import { storageFromItems } from "./nips/nip77.mjs";
|
|
11
11
|
import { isGiftWrapKind, requireNip59Crypto, unwrap } from "./nips/nip59.mjs";
|
|
@@ -124,6 +124,22 @@ var ClientBuilder = class {
|
|
|
124
124
|
};
|
|
125
125
|
return this;
|
|
126
126
|
}
|
|
127
|
+
/** Forwarded to the pool: suspend a relay delivering too many invalid events. */
|
|
128
|
+
invalidEventPolicy(policy) {
|
|
129
|
+
this.#opts = {
|
|
130
|
+
...this.#opts,
|
|
131
|
+
invalidEventPolicy: policy
|
|
132
|
+
};
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
/** Forwarded to the pool: fired once when a relay is suspended, with the lift time. */
|
|
136
|
+
onRelaySuspended(cb) {
|
|
137
|
+
this.#opts = {
|
|
138
|
+
...this.#opts,
|
|
139
|
+
onRelaySuspended: cb
|
|
140
|
+
};
|
|
141
|
+
return this;
|
|
142
|
+
}
|
|
127
143
|
enableReconnect(enabled) {
|
|
128
144
|
this.#opts = {
|
|
129
145
|
...this.#opts,
|
|
@@ -607,6 +623,8 @@ var Client = class Client {
|
|
|
607
623
|
idleTimeoutMs: opts.idleTimeoutMs,
|
|
608
624
|
maxRelays: opts.maxRelays,
|
|
609
625
|
pinnedUrls: opts.pinnedUrls,
|
|
626
|
+
invalidEventPolicy: opts.invalidEventPolicy,
|
|
627
|
+
onRelaySuspended: opts.onRelaySuspended,
|
|
610
628
|
automaticallyAuth: opts.automaticAuth ?? true ? () => async (template) => {
|
|
611
629
|
const signer = this.#signer;
|
|
612
630
|
if (!signer) throw new NoSignerError("no signer configured for AUTH");
|
|
@@ -991,4 +1009,4 @@ var Client = class Client {
|
|
|
991
1009
|
//#endregion
|
|
992
1010
|
export { ClientBuilder as i, ClientError as n, SyncDirection as r, Client as t };
|
|
993
1011
|
|
|
994
|
-
//# sourceMappingURL=client-
|
|
1012
|
+
//# sourceMappingURL=client-5DhgYV7P.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-5DhgYV7P.mjs","names":[],"sources":["../src/client/builder.ts","../src/client/dm.ts","../src/client/types.ts","../src/client/gossip-io.ts","../src/client/sync.ts","../src/client/client.ts"],"sourcesContent":["import type { Event } from \"../core/event.ts\";\nimport type { Gossip } from \"../gossip/gossip.ts\";\nimport type { InvalidEventPolicy } from \"../relay/pool.ts\";\nimport type { WebSocketConstructor } from \"../relay/websocket.ts\";\nimport type { NostrSigner } from \"../signer/types.ts\";\nimport type { StorageError } from \"../storage/error.ts\";\nimport type { EventStore } from \"../storage/types.ts\";\nimport type { ReactiveEventStore } from \"../store/reactive.ts\";\nimport type { Client } from \"./client.ts\";\nimport type { ClientOptions } from \"./types.ts\";\n\n/**\n * Fluent constructor for {@link Client}. The constructor is supplied by {@link Client.builder} so\n * this module never imports the `Client` value, keeping the client/builder dependency\n * one-directional.\n */\nexport class ClientBuilder {\n #opts: ClientOptions = {};\n readonly #newClient: (opts: ClientOptions) => Client;\n\n constructor(newClient: (opts: ClientOptions) => Client) {\n this.#newClient = newClient;\n }\n\n signer(signer: NostrSigner): this {\n this.#opts = { ...this.#opts, signer };\n return this;\n }\n\n relays(urls: ReadonlyArray<string>): this {\n this.#opts = { ...this.#opts, relays: [...urls] };\n return this;\n }\n\n addRelay(url: string): this {\n this.#opts = { ...this.#opts, relays: [...(this.#opts.relays ?? []), url] };\n return this;\n }\n\n websocketImplementation(impl: WebSocketConstructor): this {\n this.#opts = { ...this.#opts, websocketImplementation: impl };\n return this;\n }\n\n verifyEvent(fn: (event: Event) => boolean): this {\n this.#opts = { ...this.#opts, verifyEvent: fn };\n return this;\n }\n\n connectTimeoutMs(ms: number): this {\n this.#opts = { ...this.#opts, connectTimeoutMs: ms };\n return this;\n }\n\n publishTimeoutMs(ms: number): this {\n this.#opts = { ...this.#opts, publishTimeoutMs: ms };\n return this;\n }\n\n automaticAuth(enabled: boolean): this {\n this.#opts = { ...this.#opts, automaticAuth: enabled };\n return this;\n }\n\n /** Allow `ws://` relays. Default false. */\n allowInsecure(allow: boolean): this {\n this.#opts = { ...this.#opts, allowInsecure: allow };\n return this;\n }\n\n /** `ws://` URLs allowed despite `allowInsecure` being off. */\n trustedInsecureUrls(urls: ReadonlyArray<string>): this {\n this.#opts = { ...this.#opts, trustedInsecureUrls: urls };\n return this;\n }\n\n /** Close idle relays after this many ms. Unset = disabled. */\n idleTimeoutMs(ms: number): this {\n this.#opts = { ...this.#opts, idleTimeoutMs: ms };\n return this;\n }\n\n /** Soft cap on connected non-pinned relays. */\n maxRelays(n: number): this {\n this.#opts = { ...this.#opts, maxRelays: n };\n return this;\n }\n\n /** Relays never closed by idle cleanup or `maxRelays` eviction. */\n pinnedUrls(urls: ReadonlyArray<string>): this {\n this.#opts = { ...this.#opts, pinnedUrls: urls };\n return this;\n }\n\n /** Forwarded to the pool: suspend a relay delivering too many invalid events. */\n invalidEventPolicy(policy: InvalidEventPolicy): this {\n this.#opts = { ...this.#opts, invalidEventPolicy: policy };\n return this;\n }\n\n /** Forwarded to the pool: fired once when a relay is suspended, with the lift time. */\n onRelaySuspended(cb: (url: string, until: number) => void): this {\n this.#opts = { ...this.#opts, onRelaySuspended: cb };\n return this;\n }\n\n enableReconnect(enabled: boolean): this {\n this.#opts = { ...this.#opts, enableReconnect: enabled };\n return this;\n }\n\n enablePing(enabled: boolean): this {\n this.#opts = { ...this.#opts, enablePing: enabled };\n return this;\n }\n\n pingIntervalMs(ms: number): this {\n this.#opts = { ...this.#opts, pingIntervalMs: ms };\n return this;\n }\n\n pingTimeoutMs(ms: number): this {\n this.#opts = { ...this.#opts, pingTimeoutMs: ms };\n return this;\n }\n\n /**\n * Local event store. Defaults to {@link MemoryEventStore}. Browser apps that want persistence must\n * pass {@link IndexedDbEventStore} and `await open()`.\n */\n storage(store: EventStore): this {\n this.#opts = { ...this.#opts, storage: store };\n return this;\n }\n\n /**\n * Synchronous reactive index mirroring ingested events. Defaults to a new\n * {@link ReactiveEventStore}.\n */\n index(index: ReactiveEventStore): this {\n this.#opts = { ...this.#opts, index };\n return this;\n }\n\n /** When false, Client will not auto-write events to storage. Default true. */\n persistEvents(enabled: boolean): this {\n this.#opts = { ...this.#opts, persistEvents: enabled };\n return this;\n }\n\n gossip(gossip: Gossip): this {\n this.#opts = { ...this.#opts, gossip };\n return this;\n }\n\n onstorageerror(fn: (err: StorageError) => void): this {\n this.#opts = { ...this.#opts, onstorageerror: fn };\n return this;\n }\n\n build(): Client {\n return this.#newClient(this.#opts);\n }\n}\n","import type { EventBuilder } from \"../core/builder.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport { itemCompare } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\nimport type { Gossip } from \"../gossip/gossip.ts\";\nimport {\n Nip17Error,\n buildChatMessageRumor,\n dmRelayListEventBuilder,\n normalizeRecipients,\n requireDmRelays,\n wrapDirectMessage,\n} from \"../nips/nip17.ts\";\nimport type { Recipient } from \"../nips/nip17.ts\";\nimport { unwrap } from \"../nips/nip59.ts\";\nimport type { Nip59Crypto } from \"../nips/nip59.ts\";\nimport type { Pool, PoolPublishResult } from \"../relay/pool.ts\";\nimport type {\n FetchPrivateMessagesOptions,\n PrivateMessageSendResult,\n PublishOptions,\n ReceivedPrivateMessage,\n SendPrivateMessageOptions,\n SubscribePrivateMessagesOptions,\n} from \"./types.ts\";\n\nexport type DmDeps = {\n pool: Pool;\n gossip: Gossip;\n hydrateGossip: (pubkeys: ReadonlyArray<string>) => Promise<void>;\n ingest: (event: Event, relayUrl?: string) => void;\n markSeen: (id: string, relayUrl: string) => void;\n assertAlive: () => void;\n requireNip59Crypto: () => Nip59Crypto;\n throwIfAborted: (signal?: AbortSignal) => void;\n wantObserve: (flag?: boolean) => boolean;\n publish: (\n eventOrBuilder: Event | EventBuilder,\n opts?: PublishOptions,\n ) => Promise<PoolPublishResult[]>;\n};\n\nexport function giftWrapRelays(gossip: Gossip, event: Event): string[] {\n const targets: string[] = [];\n for (const tag of event.tags) {\n if (tag[0] === \"p\" && tag[1] !== undefined && tag[1] !== \"\") {\n targets.push(tag[1].toLowerCase());\n }\n }\n if (targets.length === 0) {\n throw new Nip17Error(\"gift wrap has no p-tag recipient\");\n }\n const [target] = targets;\n if (targets.length !== 1 || target === undefined) {\n throw new Nip17Error(\"gift wrap must have exactly one p-tag recipient\");\n }\n const relays = gossip.dmRelays(target);\n if (relays.length === 0) {\n throw new Nip17Error(\"no kind 10050 in gossip; use sendPrivateMessage or pass relays\");\n }\n return relays;\n}\n\nexport async function setDmRelays(\n deps: DmDeps,\n relays: ReadonlyArray<string>,\n opts?: PublishOptions,\n): Promise<PoolPublishResult[]> {\n return deps.publish(dmRelayListEventBuilder(relays), { gossip: true, ...opts });\n}\n\nexport async function sendPrivateMessage(\n deps: DmDeps,\n recipients: string | Recipient | ReadonlyArray<string | Recipient>,\n content: string,\n opts?: SendPrivateMessageOptions,\n): Promise<PrivateMessageSendResult> {\n deps.assertAlive();\n const crypto = deps.requireNip59Crypto();\n const sender = await crypto.getPublicKey();\n const list = normalizeRecipients(recipients);\n if (list.length === 0) {\n throw new Nip17Error(\"recipients must not be empty\");\n }\n\n const targets = new Set<string>([sender.toLowerCase(), ...list.map((r) => r.pubkey)]);\n await deps.hydrateGossip([...targets]);\n for (const pk of targets) {\n requireDmRelays(pk, deps.gossip.dmRelays(pk));\n }\n\n const rumor = buildChatMessageRumor(sender, list, content, {\n created_at: opts?.created_at,\n subject: opts?.subject,\n replyTo: opts?.replyTo,\n });\n const wraps = await wrapDirectMessage(crypto, list, rumor);\n const sent = await Promise.all(\n wraps.map(async ({ recipient, wrap }) => {\n const relays = requireDmRelays(recipient, deps.gossip.dmRelays(recipient));\n const results = await deps.pool.publish(relays, wrap, { timeoutMs: opts?.timeoutMs });\n if (results.some((r) => r.result?.ok === true) && deps.wantObserve(opts?.observe)) {\n deps.ingest(wrap);\n }\n return { recipient, wrap, results };\n }),\n );\n return { rumor, wraps: sent };\n}\n\nexport async function fetchPrivateMessages(\n deps: DmDeps,\n opts?: FetchPrivateMessagesOptions,\n): Promise<ReceivedPrivateMessage[]> {\n deps.assertAlive();\n const crypto = deps.requireNip59Crypto();\n const self = await crypto.getPublicKey();\n deps.throwIfAborted(opts?.signal);\n await deps.hydrateGossip([self]);\n deps.throwIfAborted(opts?.signal);\n const relays = requireDmRelays(self, deps.gossip.dmRelays(self));\n const urls = new Map<string, Set<string>>();\n const events = await deps.pool.fetch(\n relays,\n [\n {\n kinds: [Kind.GiftWrap],\n \"#p\": [self],\n since: opts?.since,\n until: opts?.until,\n },\n ],\n {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n onevent: (event, relayUrl) => {\n let set = urls.get(event.id);\n if (!set) {\n urls.set(event.id, (set = new Set()));\n }\n set.add(relayUrl);\n },\n },\n );\n\n const byRumor = new Map<string, ReceivedPrivateMessage>();\n for (const wrap of events) {\n try {\n // oxlint-disable-next-line no-await-in-loop -- unwraps run serially so ingest/seenOn keep relay order\n const rumor = await unwrap(crypto, wrap);\n const wrapUrls = urls.get(wrap.id);\n const firstUrl = wrapUrls?.values().next().value;\n if (deps.wantObserve(opts?.observe)) {\n deps.ingest(wrap, firstUrl);\n // Every other relay that delivered the same wrap is recorded too.\n if (wrapUrls) {\n for (const url of wrapUrls) {\n if (url !== firstUrl) {\n deps.markSeen(wrap.id, url);\n }\n }\n }\n }\n byRumor.set(rumor.id, {\n wrap,\n rumor,\n ...(firstUrl === undefined ? {} : { relayUrl: firstUrl }),\n });\n } catch {\n // junk / forgery / key mismatch — not stored\n }\n }\n\n return [...byRumor.values()].sort((a, b) => itemCompare(a.rumor, b.rumor));\n}\n\nexport async function subscribePrivateMessages(\n deps: DmDeps,\n opts?: SubscribePrivateMessagesOptions,\n): Promise<{ close: (reason?: string) => void }> {\n deps.assertAlive();\n const crypto = deps.requireNip59Crypto();\n const self = await crypto.getPublicKey();\n deps.throwIfAborted(opts?.signal);\n await deps.hydrateGossip([self]);\n deps.throwIfAborted(opts?.signal);\n const relays = requireDmRelays(self, deps.gossip.dmRelays(self));\n\n const seen = new Set<string>();\n // Wrap ids processed (observed) already; receipts before that are buffered\n // so every delivering relay URL lands in seenOn once the wrap is indexed.\n const processed = new Set<string>();\n const pendingUrls = new Map<string, string[]>();\n const flushSeen = (wrapId: string): void => {\n processed.add(wrapId);\n const extra = pendingUrls.get(wrapId);\n if (extra === undefined) {\n return;\n }\n pendingUrls.delete(wrapId);\n for (const url of extra) {\n deps.markSeen(wrapId, url);\n }\n };\n let tail = Promise.resolve();\n let closed = false;\n const markClosed = (): void => {\n closed = true;\n };\n if (opts?.signal?.aborted === true) {\n markClosed();\n } else {\n opts?.signal?.addEventListener(\"abort\", markClosed, { once: true });\n }\n\n const inner = deps.pool.subscribe(\n relays,\n // 21059 is ephemeral (relays MUST NOT store); live inbox has to REQ it.\n [{ kinds: [Kind.GiftWrap, Kind.GiftWrapEphemeral], \"#p\": [self], since: opts?.since }],\n {\n signal: opts?.signal,\n oneose: opts?.oneose,\n onclose: (reason) => {\n markClosed();\n opts?.onclose?.(reason);\n },\n eoseTimeoutMs: opts?.eoseTimeoutMs,\n receivedEvent: (id, relayUrl) => {\n if (closed) {\n return;\n }\n if (processed.has(id)) {\n deps.markSeen(id, relayUrl);\n return;\n }\n const list = pendingUrls.get(id);\n if (list === undefined) {\n pendingUrls.set(id, [relayUrl]);\n } else if (!list.includes(relayUrl)) {\n list.push(relayUrl);\n }\n },\n onevent: (wrap, relayUrl) => {\n if (closed) {\n return;\n }\n const prev = tail;\n let release: (() => void) | undefined;\n tail = new Promise<void>((resolve) => {\n release = resolve;\n });\n void (async () => {\n await prev;\n try {\n if (closed) {\n return;\n }\n const rumor = await unwrap(crypto, wrap);\n if (closed) {\n return;\n }\n if (seen.has(rumor.id)) {\n flushSeen(wrap.id);\n return;\n }\n seen.add(rumor.id);\n if (deps.wantObserve(opts?.observe)) {\n deps.ingest(wrap, relayUrl);\n }\n flushSeen(wrap.id);\n opts?.onevent?.({ wrap, rumor, relayUrl });\n } catch {\n // junk / forgery — not stored; keep the queue alive if a handler throws\n } finally {\n release?.();\n }\n })();\n },\n },\n );\n\n return {\n close: (reason?: string) => {\n markClosed();\n inner.close(reason);\n },\n };\n}\n","import { NostrError } from \"../core/error.ts\";\nimport type { Event } from \"../core/event.ts\";\nimport type { Gossip } from \"../gossip/gossip.ts\";\nimport type { ReplyTo } from \"../nips/nip17.ts\";\nimport type { Rumor } from \"../nips/nip59.ts\";\nimport type { InvalidEventPolicy, PoolPublishResult } from \"../relay/pool.ts\";\nimport type { WebSocketConstructor } from \"../relay/websocket.ts\";\nimport type { NostrSigner } from \"../signer/types.ts\";\nimport type { StorageError } from \"../storage/error.ts\";\nimport type { EventStore } from \"../storage/types.ts\";\nimport type { ReactiveEventStore } from \"../store/reactive.ts\";\n\n/** Direction of a {@link Client.sync} run: upload, download, or both. */\nexport const SyncDirection = {\n Up: \"up\",\n Down: \"down\",\n Both: \"both\",\n} as const;\n\n/** Union of the {@link SyncDirection} values. */\nexport type SyncDirectionName = (typeof SyncDirection)[keyof typeof SyncDirection];\n\n/** Options for {@link Client.sync} (NIP-77 set reconciliation). */\nexport type SyncOptions = {\n relays?: ReadonlyArray<string>;\n direction?: SyncDirectionName;\n /**\n * Wall-clock deadline for the Negentropy reconciliation session (`NEG-OPEN` through `NEG-CLOSE`),\n * in milliseconds. One clock for the whole session — not reset per `NEG-MSG`. Default: the relay\n * `publishTimeoutMs`. Upload/download phases reuse this value as their per-call timeout.\n */\n timeoutMs?: number;\n signal?: AbortSignal;\n dryRun?: boolean;\n /** When false, skip observe/storage on downloaded events. Default true. */\n observe?: boolean;\n};\n\n/** Outcome of a {@link Client.sync} run: ids compared, sent, received, and failures. */\nexport type SyncSummary = {\n local: string[];\n remote: string[];\n sent: string[];\n received: string[];\n sendFailures: Record<string, string>;\n persistFailures: Record<string, string>;\n};\n\n/** Options for {@link Client.builder} / `ClientOptions` accepted by the builder. */\nexport type ClientOptions = {\n signer?: NostrSigner;\n relays?: ReadonlyArray<string>;\n websocketImplementation?: WebSocketConstructor;\n /** Injected EVENT verifier. Default is core BIP-340. */\n verifyEvent?: (event: Event) => boolean;\n connectTimeoutMs?: number;\n publishTimeoutMs?: number;\n /**\n * When true (default), answer NIP-42 AUTH automatically. The signer is read at challenge time, so\n * `setSigner()` applies to live connections; challenges received while no signer is set are\n * ignored.\n */\n automaticAuth?: boolean;\n /** When true (default), relays reconnect with backoff after disconnect. */\n enableReconnect?: boolean;\n /** Keepalive ping. Default false. */\n enablePing?: boolean;\n pingIntervalMs?: number;\n pingTimeoutMs?: number;\n /** Forwarded to the pool: when false (default), `ws://` relays are rejected. */\n allowInsecure?: boolean;\n /** Forwarded to the pool: `ws://` URLs allowed despite `allowInsecure`. */\n trustedInsecureUrls?: ReadonlyArray<string>;\n /** Forwarded to the pool: close idle relays after this many ms. */\n idleTimeoutMs?: number;\n /** Forwarded to the pool: soft cap on connected non-pinned relays. */\n maxRelays?: number;\n /** Forwarded to the pool: relays never closed by idle cleanup or `maxRelays`. */\n pinnedUrls?: ReadonlyArray<string>;\n /**\n * Forwarded to the pool: a relay exceeding `limit` verification failures inside `windowMs` is\n * disconnected and suspended for `cooldownMs`.\n */\n invalidEventPolicy?: InvalidEventPolicy;\n /** Forwarded to the pool: fired once when a relay is suspended, with the lift time. */\n onRelaySuspended?: (url: string, until: number) => void;\n gossip?: Gossip;\n /**\n * Local event store. Defaults to {@link MemoryEventStore}. Browser apps that want persistence must\n * pass {@link IndexedDbEventStore} and `await open()`.\n */\n storage?: EventStore;\n /**\n * Synchronous reactive index that mirrors every ingested event before callbacks and persistence.\n * Defaults to a new {@link ReactiveEventStore}.\n */\n index?: ReactiveEventStore;\n /**\n * When true (default), every ingested event is written to storage. Set false to disable automatic\n * persistence while keeping the store for manual use.\n */\n persistEvents?: boolean;\n /**\n * Storage I/O failures: live `putMany` flush and `fetchEvents({ localFirst: true })` query. Those\n * paths do not throw.\n */\n onstorageerror?: (err: StorageError) => void;\n};\n\nexport type FetchEventsOptions = {\n relays?: string[];\n timeoutMs?: number;\n signal?: AbortSignal;\n gossip?: boolean;\n /** When true, query local storage first and merge with network results. Defaults to false. */\n localFirst?: boolean;\n /** When false, skip writing fetched events to storage/observe. Default true. */\n observe?: boolean;\n /**\n * Every event of every relay batch, including duplicates across relays. `relayUrl` is the\n * normalized URL of the relay that delivered it.\n */\n onevent?: (event: Event, relayUrl: string) => void;\n};\n\nexport type SubscribeOptions = {\n relays?: string[];\n /**\n * First receipt of each event only (deduped across relays). `relayUrl` is the normalized URL of\n * the relay that delivered it first.\n */\n onevent?: (event: Event, relayUrl: string) => void;\n /** Every receipt from every relay, including duplicates skipped by dedupe. */\n receivedEvent?: (id: string, relayUrl: string) => void;\n oneose?: () => void;\n onclose?: (reason: string) => void;\n signal?: AbortSignal;\n id?: string;\n /**\n * If set, fire `oneose` once after this many ms if not all relays have EOSEd. Does not close the\n * subscription.\n */\n eoseTimeoutMs?: number;\n /** Fan out REQs via NIP-65 gossip routes when available. */\n gossip?: boolean;\n /** When false, skip writing received events to storage/observe. Default true. */\n observe?: boolean;\n};\n\nexport type PublishOptions = {\n relays?: string[];\n timeoutMs?: number;\n gossip?: boolean;\n /** When false, skip writing the published event to storage/observe. Default true. */\n observe?: boolean;\n};\n\n/** Options for sending a NIP-17 private message. */\nexport type SendPrivateMessageOptions = {\n readonly subject?: string;\n readonly replyTo?: ReplyTo;\n readonly created_at?: number;\n readonly timeoutMs?: number;\n readonly observe?: boolean;\n};\n\n/** Result of sending a NIP-17 DM: the rumor plus per-recipient wrap publish results. */\nexport type PrivateMessageSendResult = {\n rumor: Rumor;\n wraps: ReadonlyArray<{\n recipient: string;\n wrap: Event;\n results: PoolPublishResult[];\n }>;\n};\n\n/** An unwrapped NIP-17 private message: the received gift wrap and its inner rumor. */\nexport type ReceivedPrivateMessage = {\n wrap: Event;\n rumor: Rumor;\n /** Normalized URL of the relay that delivered the wrap, when known. */\n relayUrl?: string;\n};\n\n/** Options for fetching NIP-17 private-message history. */\nexport type FetchPrivateMessagesOptions = {\n readonly since?: number;\n readonly until?: number;\n readonly timeoutMs?: number;\n readonly signal?: AbortSignal;\n readonly observe?: boolean;\n};\n\n/** Options for a live NIP-17 private-message subscription. */\nexport type SubscribePrivateMessagesOptions = {\n readonly since?: number;\n readonly onevent?: (msg: ReceivedPrivateMessage) => void;\n readonly oneose?: () => void;\n readonly onclose?: (reason: string) => void;\n readonly signal?: AbortSignal;\n readonly eoseTimeoutMs?: number;\n readonly observe?: boolean;\n};\n\n/** Client lifecycle, configuration, or abort failure (not cryptographic). */\nexport class ClientError extends NostrError {\n override name = \"ClientError\";\n}\n","import type { Event } from \"../core/event.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport type { Gossip } from \"../gossip/gossip.ts\";\nimport { fanIn, fetchRouted } from \"../relay/fan-in.ts\";\nimport type { FanInOptions, RoutedJob } from \"../relay/fan-in.ts\";\nimport type { Pool } from \"../relay/pool.ts\";\nimport { ClientError } from \"./types.ts\";\n\n/** Remainder is one job on defaults; throw before any REQ when defaults are empty. */\nexport function jobsForFilters(\n gossip: Gossip,\n filters: Filter[],\n defaultRelays: () => string[],\n): RoutedJob[] {\n const routed = filters.map((f) => gossip.route(f));\n const needsDefaults = routed.some((r) => r.remainder !== undefined);\n const defaults = needsDefaults ? defaultRelays() : undefined;\n const jobs: RoutedJob[] = [];\n for (const r of routed) {\n for (const [url, sub] of r.perRelay) {\n jobs.push({ urls: [url], filters: [sub] });\n }\n if (r.remainder !== undefined) {\n if (defaults === undefined) {\n throw new ClientError(\"unrouted remainder without default relays\");\n }\n jobs.push({ urls: defaults, filters: [r.remainder] });\n }\n }\n return jobs;\n}\n\nexport async function fetchGossip(\n pool: Pool,\n gossip: Gossip,\n filters: Filter[],\n defaultRelays: () => string[],\n opts?: {\n timeoutMs?: number | undefined;\n signal?: AbortSignal | undefined;\n onevent?: ((event: Event, relayUrl: string) => void) | undefined;\n },\n): Promise<Event[]> {\n return fetchRouted(pool, jobsForFilters(gossip, filters, defaultRelays), {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n onevent: opts?.onevent,\n });\n}\n\nexport function subscribeGossip(\n pool: Pool,\n gossip: Gossip,\n filters: Filter[],\n defaultRelays: () => string[],\n opts: FanInOptions,\n): { close: (reason?: string) => void } {\n const jobs = jobsForFilters(gossip, filters, defaultRelays);\n if (jobs.length === 0) {\n queueMicrotask(() => opts.oneose?.());\n return {\n close: () => {\n /* empty */\n },\n };\n }\n return fanIn(pool, jobs, opts);\n}\n","import type { Event } from \"../core/event.ts\";\nimport { canonicalizeFilter } from \"../core/filter.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { storageFromItems } from \"../nips/nip77.ts\";\nimport type { NegentropyStorageVector } from \"../nips/nip77.ts\";\nimport type { Pool } from \"../relay/pool.ts\";\nimport type { EventStore, PutResult } from \"../storage/types.ts\";\nimport { SyncDirection } from \"./types.ts\";\nimport type { SyncOptions, SyncSummary } from \"./types.ts\";\n\nexport type SyncDeps = {\n pool: Pool;\n storage: EventStore;\n persistEvents: boolean;\n assertAlive: () => void;\n throwIfAborted: (signal?: AbortSignal) => void;\n wantObserve: (flag?: boolean) => boolean;\n /**\n * The client's single ingest path: index (with relay URL) → meta → persistence. `persist: false`\n * when this function already wrote storage via an awaited `putMany`.\n */\n ingest: (event: Event, relayUrl?: string, opts?: { persist?: boolean }) => void;\n /** Record a relay sighting for an id already in the index. */\n markSeen: (id: string, relayUrl: string) => void;\n defaultRelays: (urls?: string[]) => string[];\n};\n\nconst SYNC_ID_BATCH = 100;\nconst SYNC_UPLOAD_CONCURRENCY = 8;\n\nfunction uniqueIds(ids: ReadonlyArray<string>): string[] {\n return [...new Set(ids)];\n}\n\nfunction emptySummary(): SyncSummary {\n return {\n local: [],\n remote: [],\n sent: [],\n received: [],\n sendFailures: {},\n persistFailures: {},\n };\n}\n\nfunction mergeSyncSummary(into: SyncSummary, other: SyncSummary): SyncSummary {\n const sendFailures = { ...into.sendFailures, ...other.sendFailures };\n const persistFailures = { ...into.persistFailures, ...other.persistFailures };\n return {\n local: uniqueIds([...into.local, ...other.local]),\n remote: uniqueIds([...into.remote, ...other.remote]),\n sent: uniqueIds([...into.sent, ...other.sent]),\n received: uniqueIds([...into.received, ...other.received]),\n sendFailures,\n persistFailures,\n };\n}\n\n/**\n * NIP-77 sync against one relay: reconcile, then optionally upload local-only events and/or\n * download remote-only events. `observe: false` skips putMany and ingest; received ids are still\n * listed. `persistEvents: false` skips putMany, still ingests when observe is on.\n */\nexport async function syncToRelay(\n deps: SyncDeps,\n url: string,\n filter: Filter,\n opts?: Omit<SyncOptions, \"relays\">,\n): Promise<SyncSummary> {\n deps.assertAlive();\n deps.throwIfAborted(opts?.signal);\n const direction = opts?.direction ?? SyncDirection.Down;\n const canon = canonicalizeFilter(filter);\n const items = await deps.storage.negentropyItems(canon);\n const storage: NegentropyStorageVector = storageFromItems(items);\n const relay = await deps.pool.ensureRelay(url, { signal: opts?.signal });\n const { have, need } = await relay.negReconcile(canon, storage, {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n });\n\n const summary: SyncSummary = {\n local: have,\n remote: need,\n sent: [],\n received: [],\n sendFailures: {},\n persistFailures: {},\n };\n\n if (opts?.dryRun === true) {\n return summary;\n }\n\n if ((direction === SyncDirection.Up || direction === SyncDirection.Both) && have.length > 0) {\n const found = await deps.storage.query([{ ids: have }]);\n const foundById = new Map(found.map((event) => [event.id, event]));\n for (const id of have) {\n if (!foundById.has(id)) {\n summary.sendFailures[id] = \"event not found in local store\";\n }\n }\n for (let i = 0; i < found.length; i += SYNC_UPLOAD_CONCURRENCY) {\n const chunk = found.slice(i, i + SYNC_UPLOAD_CONCURRENCY);\n // Upload chunks are rate-limited serially.\n // oxlint-disable-next-line no-await-in-loop\n await Promise.all(\n chunk.map(async (event) => {\n try {\n const results = await deps.pool.publish([url], event, {\n timeoutMs: opts?.timeoutMs,\n });\n const ok = results.some((r) => r.result?.ok === true);\n if (ok) {\n summary.sent.push(event.id);\n } else {\n summary.sendFailures[event.id] =\n results[0]?.error ?? results[0]?.result?.message ?? \"publish failed\";\n }\n } catch (error) {\n summary.sendFailures[event.id] = error instanceof Error ? error.message : String(error);\n }\n }),\n );\n }\n }\n\n if ((direction === SyncDirection.Down || direction === SyncDirection.Both) && need.length > 0) {\n const shouldObserve = deps.wantObserve(opts?.observe);\n for (let i = 0; i < need.length; i += SYNC_ID_BATCH) {\n const batch = need.slice(i, i + SYNC_ID_BATCH);\n deps.throwIfAborted(opts?.signal);\n // Every relay that delivered an event is recorded once it is ingested.\n const urlsById = new Map<string, string[]>();\n // Id batches are fetched serially so backpressure stays bounded.\n // oxlint-disable-next-line no-await-in-loop\n const events = await deps.pool.fetch([url], [{ ids: batch }], {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n onevent: shouldObserve\n ? (event, relayUrl) => {\n const urls = urlsById.get(event.id);\n if (urls === undefined) {\n urlsById.set(event.id, [relayUrl]);\n } else if (!urls.includes(relayUrl)) {\n urls.push(relayUrl);\n }\n }\n : undefined,\n });\n if (!shouldObserve) {\n for (const event of events) {\n summary.received.push(event.id);\n }\n continue;\n }\n const ingestAll = (event: Event): void => {\n const urls = urlsById.get(event.id) ?? [];\n deps.ingest(event, urls[0], { persist: false });\n for (const url of urls.slice(1)) {\n deps.markSeen(event.id, url);\n }\n };\n if (!deps.persistEvents) {\n for (const event of events) {\n ingestAll(event);\n summary.received.push(event.id);\n }\n continue;\n }\n // The awaited putMany reports persistFailures; accepted events then go\n // through the ingest path with persistence already done.\n let results: PutResult[];\n try {\n // Persisted serially per batch so failures land on the right ids.\n // oxlint-disable-next-line no-await-in-loop\n results = await deps.storage.putMany(events);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n for (const event of events) {\n summary.persistFailures[event.id] = message;\n }\n break;\n }\n for (const [j, event] of events.entries()) {\n if (results[j] === \"rejected\" || results[j] === \"invalid\") {\n continue;\n }\n ingestAll(event);\n summary.received.push(event.id);\n }\n }\n }\n\n return summary;\n}\n\n/**\n * NIP-77 sync against the given relays (or Client default relays). Independent sessions run in\n * parallel. Fulfilled summaries are merged; if every relay rejects, throws the first rejection in\n * URL order.\n */\nexport async function sync(\n deps: SyncDeps,\n filter: Filter,\n opts?: SyncOptions,\n): Promise<SyncSummary> {\n deps.assertAlive();\n const urls = deps.defaultRelays(opts?.relays ? [...opts.relays] : undefined);\n const results = await Promise.allSettled(\n urls.map(async (url) => syncToRelay(deps, url, filter, opts)),\n );\n let merged = emptySummary();\n let fulfilled = 0;\n let firstRejection: unknown;\n for (const result of results) {\n if (result.status === \"fulfilled\") {\n fulfilled += 1;\n merged = mergeSyncSummary(merged, result.value);\n } else {\n firstRejection ??= result.reason;\n }\n }\n if (urls.length > 0 && fulfilled === 0) {\n throw firstRejection;\n }\n return merged;\n}\n","import { throwIfAborted } from \"../core/abort.ts\";\nimport { EventBuilder } from \"../core/builder.ts\";\nimport { sortedEvents } from \"../core/event.ts\";\nimport type { Event, EventTemplate, UnsignedEvent } from \"../core/event.ts\";\nimport { canonicalizeFilters, matchFilters } from \"../core/filter.ts\";\nimport type { Filter } from \"../core/filter.ts\";\nimport { invokeSafely } from \"../core/report.ts\";\nimport { normalizeURL } from \"../core/util.ts\";\nimport { Gossip } from \"../gossip/index.ts\";\nimport { createLoaders, createOutboxFeed } from \"../loaders/index.ts\";\nimport type { Loaders, OutboxFeed } from \"../loaders/index.ts\";\nimport type { Recipient } from \"../nips/nip17.ts\";\nimport { isGiftWrapKind, requireNip59Crypto } from \"../nips/nip59.ts\";\nimport type { Nip59Crypto } from \"../nips/nip59.ts\";\nimport { Pool } from \"../relay/pool.ts\";\nimport type { PoolPublishResult } from \"../relay/pool.ts\";\nimport { NoSignerError } from \"../signer/error.ts\";\nimport type { NostrSigner } from \"../signer/types.ts\";\nimport { toStorageError } from \"../storage/error.ts\";\nimport type { StorageError } from \"../storage/error.ts\";\nimport { MemoryIndex } from \"../storage/memory-index.ts\";\nimport { MemoryEventStore } from \"../storage/memory.ts\";\nimport type { EventStore } from \"../storage/types.ts\";\nimport { ReactiveEventStore } from \"../store/reactive.ts\";\nimport { ClientBuilder } from \"./builder.ts\";\nimport {\n fetchPrivateMessages,\n giftWrapRelays,\n sendPrivateMessage,\n setDmRelays,\n subscribePrivateMessages,\n} from \"./dm.ts\";\nimport type { DmDeps } from \"./dm.ts\";\nimport { fetchGossip, subscribeGossip } from \"./gossip-io.ts\";\nimport { sync, syncToRelay } from \"./sync.ts\";\nimport type { SyncDeps } from \"./sync.ts\";\nimport { ClientError } from \"./types.ts\";\nimport type {\n ClientOptions,\n FetchEventsOptions,\n FetchPrivateMessagesOptions,\n PrivateMessageSendResult,\n PublishOptions,\n ReceivedPrivateMessage,\n SendPrivateMessageOptions,\n SubscribeOptions,\n SubscribePrivateMessagesOptions,\n SyncOptions,\n SyncSummary,\n} from \"./types.ts\";\n\n/** Layer-5 facade: signer + default relays + pool + loaders + gossip + event store. */\nexport class Client {\n readonly pool: Pool;\n readonly loaders: Loaders;\n readonly gossip: Gossip;\n readonly storage: EventStore;\n readonly index: ReactiveEventStore;\n onstorageerror: ((err: StorageError) => void) | undefined;\n #signer: NostrSigner | undefined;\n #relays: string[];\n #shutdown = false;\n readonly #persistEvents: boolean;\n readonly #persistQueue: Event[] = [];\n #flushing: Promise<void> | undefined;\n\n constructor(opts: ClientOptions = {}) {\n this.#signer = opts.signer;\n this.#relays = [];\n for (const raw of opts.relays ?? []) {\n const url = normalizeURL(raw);\n if (!this.#relays.includes(url)) {\n this.#relays.push(url);\n }\n }\n this.gossip = opts.gossip ?? new Gossip();\n this.storage = opts.storage ?? new MemoryEventStore();\n this.index = opts.index ?? new ReactiveEventStore();\n this.#persistEvents = opts.persistEvents ?? true;\n this.onstorageerror = opts.onstorageerror;\n this.pool = new Pool({\n websocketImplementation: opts.websocketImplementation,\n verifyEvent: opts.verifyEvent,\n enablePing: opts.enablePing,\n pingIntervalMs: opts.pingIntervalMs,\n pingTimeoutMs: opts.pingTimeoutMs,\n connectTimeoutMs: opts.connectTimeoutMs,\n publishTimeoutMs: opts.publishTimeoutMs,\n enableReconnect: opts.enableReconnect ?? true,\n allowInsecure: opts.allowInsecure,\n trustedInsecureUrls: opts.trustedInsecureUrls,\n idleTimeoutMs: opts.idleTimeoutMs,\n maxRelays: opts.maxRelays,\n pinnedUrls: opts.pinnedUrls,\n invalidEventPolicy: opts.invalidEventPolicy,\n onRelaySuspended: opts.onRelaySuspended,\n // The sign function resolves the signer at challenge time, so\n // setSigner() takes effect on already-connected relays; challenges\n // without a signer are ignored by the relay (NoSignerError).\n automaticallyAuth:\n (opts.automaticAuth ?? true)\n ? () => async (template) => {\n const signer = this.#signer;\n if (!signer) {\n throw new NoSignerError(\"no signer configured for AUTH\");\n }\n const pk = await signer.getPublicKey();\n return signer.signEvent({ ...template, pubkey: pk });\n }\n : undefined,\n });\n this.loaders = createLoaders({\n pool: this.pool,\n relays: this.#relays,\n index: this.index,\n ingest: (event, relayUrl) => this.#ingest(event, relayUrl),\n });\n }\n\n static builder(): ClientBuilder {\n return new ClientBuilder((opts) => new Client(opts));\n }\n\n get signer(): NostrSigner | undefined {\n return this.#signer;\n }\n\n get relays(): ReadonlyArray<string> {\n return this.#relays;\n }\n\n get isShutdown(): boolean {\n return this.#shutdown;\n }\n\n setSigner(signer: NostrSigner | undefined): void {\n this.#signer = signer;\n // Re-arm relays whose AUTH challenge was ignored or rejected.\n this.pool.resetAuth();\n }\n\n addRelay(url: string): void {\n const normalized = normalizeURL(url);\n if (!this.#relays.includes(normalized)) {\n this.#relays.push(normalized);\n this.loaders.addRelay(normalized);\n }\n }\n\n removeRelay(url: string): void {\n const normalized = normalizeURL(url);\n this.#relays = this.#relays.filter((r) => r !== normalized);\n this.loaders.removeRelay(normalized);\n this.pool.close([normalized]);\n }\n\n /** Connect all configured relays (best-effort; failures are ignored). */\n async connect(opts?: { signal?: AbortSignal }): Promise<void> {\n this.#assertAlive();\n await Promise.allSettled(\n this.#relays.map(async (url) => this.pool.ensureRelay(url, { signal: opts?.signal })),\n );\n }\n\n async shutdown(): Promise<void> {\n this.#shutdown = true;\n while (this.#flushing !== undefined) {\n // oxlint-disable-next-line no-await-in-loop -- waits for the current flush to settle before closing\n await this.#flushing;\n }\n this.pool.close();\n }\n\n #assertAlive(): void {\n if (this.#shutdown) {\n throw new ClientError(\"client is shut down\");\n }\n }\n\n #defaultRelays(urls?: string[]): string[] {\n const list = urls ?? this.#relays;\n if (list.length === 0) {\n throw new ClientError(\"no relays configured\");\n }\n return list;\n }\n\n #wantObserve(flag?: boolean): boolean {\n return flag !== false;\n }\n\n /** Ingest one inbound event: index (with relay URL) → meta → persistence. */\n observe(event: Event, relayUrl?: string): void {\n this.#ingest(event, relayUrl);\n }\n\n /** Ingest many events (deduped by id, order preserved) as one persist batch. */\n observeAll(events: ReadonlyArray<Event>): void {\n const seen = new Set<string>();\n for (const event of events) {\n if (seen.has(event.id)) {\n continue;\n }\n seen.add(event.id);\n this.#ingest(event);\n }\n }\n\n /**\n * The single inbound-event pipeline: reactive index add (with the source relay URL recorded in\n * seenOn), gossip routing meta, then the persistence decision. `persist: false` skips the storage\n * queue for callers that persist via their own awaited `putMany` (down-sync); `meta: false` is a\n * pure sighting/index add.\n */\n #ingest(event: Event, relayUrl?: string, opts?: { persist?: boolean; meta?: boolean }): void {\n this.index.add(event, relayUrl);\n if (opts?.meta !== false) {\n this.#ingestMeta(event);\n }\n if (opts?.persist === false || !this.#persistEvents) {\n return;\n }\n this.#persistQueue.push(event);\n this.#armFlush();\n }\n\n /** Record a relay sighting for an id already in the index. */\n #markSeen(id: string, relayUrl: string): void {\n this.index.markSeen(id, relayUrl);\n }\n\n #ingestMeta(event: Event): void {\n this.gossip.ingest(event);\n }\n\n #armFlush(): void {\n if (this.#flushing) {\n return;\n }\n this.#flushing = this.#flushLoop();\n }\n\n async #flushLoop(): Promise<void> {\n try {\n await new Promise<void>((resolve) => {\n queueMicrotask(resolve);\n });\n while (this.#persistQueue.length > 0) {\n const batch = this.#persistQueue.splice(0);\n try {\n // oxlint-disable-next-line no-await-in-loop -- queued batches are persisted in order\n await this.storage.putMany(batch);\n } catch (error) {\n invokeSafely(() => this.onstorageerror?.(toStorageError(error)));\n }\n }\n } finally {\n this.#flushing = undefined;\n if (this.#persistQueue.length > 0) {\n this.#armFlush();\n }\n }\n }\n\n /**\n * Explicitly load kind:10002 and kind:10050 lists for pubkeys and feed gossip. Does not\n * background-fetch unknown authors on every subscribe (predictable DX).\n */\n async hydrateGossip(\n pubkeys: ReadonlyArray<string>,\n opts?: { hints?: string[]; force?: boolean },\n ): Promise<void> {\n this.#assertAlive();\n await Promise.all(\n pubkeys.map(async (pk) => {\n const style = opts?.force === true ? \"force\" : \"default\";\n const [relayList, dmList] = await Promise.all([\n this.loaders.relayList(pk, { hints: opts?.hints, style }),\n this.loaders.dmRelayList(pk, { hints: opts?.hints, style }),\n ]);\n if (relayList.event) {\n this.observe(relayList.event);\n }\n if (dmList.event) {\n this.observe(dmList.event);\n }\n }),\n );\n }\n\n /**\n * Create an outbox-model feed for the given authors (NIP-65 write relays). Live events go through\n * {@link observe}; sync persists via applySync.\n */\n outbox(opts: {\n authors: ReadonlyArray<string>;\n kinds?: ReadonlyArray<number>;\n onEvent?: (event: Event) => void;\n maxRelaysPerAuthor?: number;\n }): OutboxFeed {\n this.#assertAlive();\n return createOutboxFeed({\n pool: this.pool,\n gossip: this.gossip,\n storage: this.storage,\n discoveryRelays: this.#relays,\n authors: opts.authors,\n kinds: opts.kinds,\n onEvent: opts.onEvent,\n maxRelaysPerAuthor: opts.maxRelaysPerAuthor,\n observe: (event, relayUrl) => this.observe(event, relayUrl),\n seen: (event, relayUrl) => this.#ingest(event, relayUrl, { persist: false, meta: false }),\n applySync: async (events) => {\n if (!this.#persistEvents) {\n for (const event of events) {\n this.#ingest(event, undefined, { persist: false });\n }\n return [];\n }\n const results = await this.storage.putMany(events);\n const applied: Event[] = [];\n for (const [i, event] of events.entries()) {\n const result = results[i];\n if (result === \"rejected\" || result === \"ephemeral\" || result === \"invalid\") {\n continue;\n }\n this.#ingest(event, undefined, { persist: false });\n applied.push(event);\n }\n return applied;\n },\n hydrate: async (pubkeys) => this.hydrateGossip(pubkeys),\n });\n }\n\n async getPublicKey(): Promise<string> {\n if (!this.#signer) {\n throw new ClientError(\"no signer configured\");\n }\n return this.#signer.getPublicKey();\n }\n\n async signEvent(unsigned: UnsignedEvent): Promise<Event> {\n if (!this.#signer) {\n throw new ClientError(\"no signer configured\");\n }\n return this.#signer.signEvent(unsigned);\n }\n\n async signEventBuilder(builder: EventBuilder): Promise<Event> {\n if (!this.#signer) {\n throw new ClientError(\"no signer configured\");\n }\n return builder.sign(this.#signer);\n }\n\n async signTemplate(template: EventTemplate): Promise<Event> {\n if (!this.#signer) {\n throw new ClientError(\"no signer configured\");\n }\n const pubkey = await this.#signer.getPublicKey();\n return this.#signer.signEvent({ ...template, pubkey });\n }\n\n /**\n * Sign (if builder) and publish to relays. With `gossip: true`, prefers the author's NIP-65\n * outbox relays, tagged `p` inboxes, and up to 5 `e`/`a` tag relay hints when known. On any\n * successful OK, observes the event into storage/gossip.\n */\n async publish(\n eventOrBuilder: Event | EventBuilder,\n opts?: PublishOptions,\n ): Promise<PoolPublishResult[]> {\n this.#assertAlive();\n const event =\n eventOrBuilder instanceof EventBuilder\n ? await this.signEventBuilder(eventOrBuilder)\n : eventOrBuilder;\n\n let relays = opts?.relays;\n if (!relays && isGiftWrapKind(event.kind)) {\n relays = giftWrapRelays(this.gossip, event);\n } else if (!relays && opts?.gossip === true) {\n const urls: string[] = [];\n const add = (list: ReadonlyArray<string>) => {\n for (const url of list) {\n if (!urls.includes(url)) {\n urls.push(url);\n }\n }\n };\n add(this.gossip.outboxRelays(event.pubkey));\n for (const tag of event.tags) {\n if (tag[0] === \"p\" && tag[1] !== undefined) {\n add(this.gossip.inboxRelays(tag[1]));\n }\n }\n let hintCount = 0;\n for (const tag of event.tags) {\n if (hintCount >= 5) {\n break;\n }\n if ((tag[0] !== \"e\" && tag[0] !== \"a\") || tag[2] === undefined) {\n continue;\n }\n try {\n const url = normalizeURL(tag[2]);\n if (!urls.includes(url)) {\n urls.push(url);\n hintCount += 1;\n }\n } catch {\n // not a relay URL\n }\n }\n if (urls.length > 0) {\n relays = urls;\n }\n }\n const results = await this.pool.publish(this.#defaultRelays(relays), event, {\n timeoutMs: opts?.timeoutMs,\n });\n\n const anyOk = results.some((r) => r.result?.ok === true);\n if (anyOk && this.#wantObserve(opts?.observe)) {\n this.observe(event);\n }\n return results;\n }\n\n /**\n * Fetch events. With `gossip: true`, routes filters by NIP-65 when possible. With `localFirst:\n * true`, merges storage hits with network results.\n */\n async fetchEvents(filter: Filter | Filter[], opts?: FetchEventsOptions): Promise<Event[]> {\n this.#assertAlive();\n const filters = canonicalizeFilters(Array.isArray(filter) ? filter : [filter]);\n const shouldObserve = this.#wantObserve(opts?.observe);\n const candidates: Event[] = [];\n\n if (opts?.localFirst === true) {\n // Persisted storage is merged with the index below; storage may hold a\n // stale replaceable version, so it is only a candidate — the index wins.\n try {\n const local = await this.storage.query(filters);\n for (const e of local) {\n candidates.push(e);\n if (shouldObserve) {\n this.#ingest(e, undefined, { persist: false, meta: false });\n }\n }\n } catch (error) {\n invokeSafely(() => this.onstorageerror?.(toStorageError(error)));\n }\n }\n\n // Every inbound event lands in the index (with its relay URL) before the\n // caller's onevent runs; the batch below re-ingests for persistence.\n const onevent = (event: Event, relayUrl: string) => {\n if (shouldObserve) {\n this.#ingest(event, relayUrl, { persist: false, meta: false });\n }\n opts?.onevent?.(event, relayUrl);\n };\n\n const batch =\n opts?.gossip !== true || opts.relays !== undefined\n ? await this.pool.fetch(this.#defaultRelays(opts?.relays), filters, {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n onevent,\n })\n : await fetchGossip(this.pool, this.gossip, filters, () => this.#defaultRelays(), {\n timeoutMs: opts?.timeoutMs,\n signal: opts?.signal,\n onevent,\n });\n for (const e of batch) {\n candidates.push(e);\n if (shouldObserve) {\n this.observe(e);\n }\n }\n\n // Merge through a scratch index so NIP-01 replaceable winners, kind-5\n // deletions, dedupe and per-filter limits all apply to the result.\n // Ephemeral kinds are never stored; matching ones join the result directly.\n const tmp = new MemoryIndex();\n const ephemeral = new Map<string, Event>();\n const merged =\n opts?.localFirst === true ? [...this.index.query(filters), ...candidates] : candidates;\n for (const e of merged) {\n if (tmp.put(e) === \"ephemeral\" && matchFilters(filters, e)) {\n ephemeral.set(e.id, e);\n }\n }\n return sortedEvents([...tmp.query(filters), ...ephemeral.values()]);\n }\n\n /** Query local storage only (no network). */\n async queryLocal(filter: Filter | Filter[]): Promise<Event[]> {\n this.#assertAlive();\n const filters = canonicalizeFilters(Array.isArray(filter) ? filter : [filter]);\n return this.storage.query(filters);\n }\n\n subscribe(\n filter: Filter | Filter[],\n opts?: SubscribeOptions,\n ): { close: (reason?: string) => void } {\n this.#assertAlive();\n const filters = canonicalizeFilters(Array.isArray(filter) ? filter : [filter]);\n const shouldObserve = this.#wantObserve(opts?.observe);\n\n const wrapEvent = (event: Event, relayUrl: string) => {\n if (shouldObserve) {\n this.observe(event, relayUrl);\n }\n opts?.onevent?.(event, relayUrl);\n };\n\n const wrapReceived = (id: string, relayUrl: string) => {\n if (shouldObserve) {\n this.#markSeen(id, relayUrl);\n }\n opts?.receivedEvent?.(id, relayUrl);\n };\n\n if (opts?.gossip !== true || opts.relays !== undefined) {\n return this.pool.subscribe(this.#defaultRelays(opts?.relays), filters, {\n onevent: wrapEvent,\n receivedEvent: wrapReceived,\n oneose: opts?.oneose,\n onclose: opts?.onclose,\n signal: opts?.signal,\n id: opts?.id,\n eoseTimeoutMs: opts?.eoseTimeoutMs,\n });\n }\n\n return subscribeGossip(this.pool, this.gossip, filters, () => this.#defaultRelays(), {\n onevent: wrapEvent,\n receivedEvent: wrapReceived,\n oneose: opts?.oneose,\n onclose: opts?.onclose,\n signal: opts?.signal,\n eoseTimeoutMs: opts?.eoseTimeoutMs,\n });\n }\n\n #requireNip59Crypto(): Nip59Crypto {\n if (!this.#signer) {\n throw new ClientError(\"no signer configured\");\n }\n return requireNip59Crypto(this.#signer);\n }\n\n #throwIfAborted(signal?: AbortSignal): void {\n throwIfAborted(signal);\n }\n\n #dmDeps(): DmDeps {\n return {\n pool: this.pool,\n gossip: this.gossip,\n hydrateGossip: async (pubkeys) => this.hydrateGossip(pubkeys),\n ingest: (event, relayUrl) => this.#ingest(event, relayUrl),\n markSeen: (id, relayUrl) => this.#markSeen(id, relayUrl),\n assertAlive: () => this.#assertAlive(),\n requireNip59Crypto: () => this.#requireNip59Crypto(),\n throwIfAborted: (signal) => this.#throwIfAborted(signal),\n wantObserve: (flag) => this.#wantObserve(flag),\n publish: async (eventOrBuilder, opts) => this.publish(eventOrBuilder, opts),\n };\n }\n\n #syncDeps(): SyncDeps {\n return {\n pool: this.pool,\n storage: this.storage,\n persistEvents: this.#persistEvents,\n assertAlive: () => this.#assertAlive(),\n throwIfAborted: (signal) => this.#throwIfAborted(signal),\n wantObserve: (flag) => this.#wantObserve(flag),\n ingest: (event, relayUrl, opts) => this.#ingest(event, relayUrl, opts),\n markSeen: (id, relayUrl) => this.#markSeen(id, relayUrl),\n defaultRelays: (urls) => this.#defaultRelays(urls),\n };\n }\n\n /** Publish a kind:10050 NIP-17 DM relay list (`relay` tags). Not kind 10002. */\n async setDmRelays(\n relays: ReadonlyArray<string>,\n opts?: PublishOptions,\n ): Promise<PoolPublishResult[]> {\n return setDmRelays(this.#dmDeps(), relays, opts);\n }\n\n async sendPrivateMessage(\n recipients: string | Recipient | ReadonlyArray<string | Recipient>,\n content: string,\n opts?: SendPrivateMessageOptions,\n ): Promise<PrivateMessageSendResult> {\n return sendPrivateMessage(this.#dmDeps(), recipients, content, opts);\n }\n\n async fetchPrivateMessages(\n opts?: FetchPrivateMessagesOptions,\n ): Promise<ReceivedPrivateMessage[]> {\n return fetchPrivateMessages(this.#dmDeps(), opts);\n }\n\n async subscribePrivateMessages(\n opts?: SubscribePrivateMessagesOptions,\n ): Promise<{ close: (reason?: string) => void }> {\n return subscribePrivateMessages(this.#dmDeps(), opts);\n }\n\n /**\n * NIP-77 sync against one relay: reconcile, then optionally upload local-only events and/or\n * download remote-only events. `observe: false` skips putMany and ingest; received ids are still\n * listed. `persistEvents: false` skips putMany, still ingests when observe is on.\n */\n async syncToRelay(\n url: string,\n filter: Filter,\n opts?: Omit<SyncOptions, \"relays\">,\n ): Promise<SyncSummary> {\n return syncToRelay(this.#syncDeps(), url, filter, opts);\n }\n\n /**\n * NIP-77 sync against the given relays (or Client default relays). Independent sessions run in\n * parallel. Fulfilled summaries are merged; if every relay rejects, throws the first rejection in\n * URL order.\n */\n async sync(filter: Filter, opts?: SyncOptions): Promise<SyncSummary> {\n return sync(this.#syncDeps(), filter, opts);\n }\n}\n"],"mappings":"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"}
|
package/dist/client.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as FetchEventsOptions, c as PublishOptions, d as SubscribeOptions, f as SubscribePrivateMessagesOptions, g as SyncSummary, h as SyncOptions, i as ClientOptions, l as ReceivedPrivateMessage, m as SyncDirectionName, n as ClientBuilder, o as FetchPrivateMessagesOptions, p as SyncDirection, r as ClientError, s as PrivateMessageSendResult, t as Client, u as SendPrivateMessageOptions } from "./index-
|
|
1
|
+
import { a as FetchEventsOptions, c as PublishOptions, d as SubscribeOptions, f as SubscribePrivateMessagesOptions, g as SyncSummary, h as SyncOptions, i as ClientOptions, l as ReceivedPrivateMessage, m as SyncDirectionName, n as ClientBuilder, o as FetchPrivateMessagesOptions, p as SyncDirection, r as ClientError, s as PrivateMessageSendResult, t as Client, u as SendPrivateMessageOptions } from "./index-BIhSADwK.mjs";
|
|
2
2
|
export { Client, ClientBuilder, ClientError, type ClientOptions, type FetchEventsOptions, type FetchPrivateMessagesOptions, type PrivateMessageSendResult, type PublishOptions, type ReceivedPrivateMessage, type SendPrivateMessageOptions, type SubscribeOptions, type SubscribePrivateMessagesOptions, SyncDirection, type SyncDirectionName, type SyncOptions, type SyncSummary };
|
package/dist/client.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as ClientBuilder, n as ClientError, r as SyncDirection, t as Client } from "./client-
|
|
1
|
+
import { i as ClientBuilder, n as ClientError, r as SyncDirection, t as Client } from "./client-5DhgYV7P.mjs";
|
|
2
2
|
export { Client, ClientBuilder, ClientError, SyncDirection };
|
|
@@ -3,7 +3,7 @@ import { n as ProfileMetadata } from "./builder-BWn55Paf.mjs";
|
|
|
3
3
|
import { t as RelayListItem } from "./nip65-DBfh2smt.mjs";
|
|
4
4
|
import { t as Gossip } from "./gossip-BRQwIM_q.mjs";
|
|
5
5
|
import { a as NostrError } from "./error-CR8Urc84.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { n as Pool } from "./pool-B7p_reAm.mjs";
|
|
7
7
|
import { r as OutboxBound, t as EventStore } from "./types-2pCUprRU.mjs";
|
|
8
8
|
import { t as ReactiveEventStore } from "./reactive-DFpqtIf_.mjs";
|
|
9
9
|
import { i as EventPointer, t as AddressPointer } from "./nip19-BevCJZsp.mjs";
|
|
@@ -204,4 +204,4 @@ type CreateLoadersOptions = {
|
|
|
204
204
|
declare function createLoaders(opts: CreateLoadersOptions): Loaders;
|
|
205
205
|
//#endregion
|
|
206
206
|
export { ReplaceableLoader as _, OutboxFeed as a, groupAuthorsByOutboxRelay as c, bareNostrUser as d, ListLoaders as f, ReplaceableLoadResult as g, LoadStyle as h, OutboxError as i, NostrUser as l, MutedEntity as m, Loaders as n, OutboxFeedOptions as o, ListResult as p, createLoaders as r, createOutboxFeed as s, CreateLoadersOptions as t, ProfileLoader as u, EventLoader as v, EventRef as y };
|
|
207
|
-
//# sourceMappingURL=index-
|
|
207
|
+
//# sourceMappingURL=index-B-CGNDPS.d.mts.map
|
|
@@ -3,11 +3,11 @@ import { t as EventBuilder } from "./builder-BWn55Paf.mjs";
|
|
|
3
3
|
import { t as Filter } from "./filter-C0IMByE3.mjs";
|
|
4
4
|
import { t as Gossip } from "./gossip-BRQwIM_q.mjs";
|
|
5
5
|
import { a as NostrError } from "./error-CR8Urc84.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { a as PoolPublishResult, n as Pool, t as InvalidEventPolicy } from "./pool-B7p_reAm.mjs";
|
|
7
7
|
import { t as WebSocketConstructor } from "./websocket-CmeqvAkp.mjs";
|
|
8
8
|
import { t as EventStore } from "./types-2pCUprRU.mjs";
|
|
9
9
|
import { t as ReactiveEventStore } from "./reactive-DFpqtIf_.mjs";
|
|
10
|
-
import { a as OutboxFeed, n as Loaders } from "./index-
|
|
10
|
+
import { a as OutboxFeed, n as Loaders } from "./index-B-CGNDPS.mjs";
|
|
11
11
|
import { a as Rumor } from "./nip59-zQgdLcEr.mjs";
|
|
12
12
|
import { i as ReplyTo, r as Recipient } from "./nip17-DNEkz8rM.mjs";
|
|
13
13
|
import { t as NostrSigner } from "./types-CRQEk0Oi.mjs";
|
|
@@ -76,6 +76,13 @@ type ClientOptions = {
|
|
|
76
76
|
maxRelays?: number;
|
|
77
77
|
/** Forwarded to the pool: relays never closed by idle cleanup or `maxRelays`. */
|
|
78
78
|
pinnedUrls?: ReadonlyArray<string>;
|
|
79
|
+
/**
|
|
80
|
+
* Forwarded to the pool: a relay exceeding `limit` verification failures inside `windowMs` is
|
|
81
|
+
* disconnected and suspended for `cooldownMs`.
|
|
82
|
+
*/
|
|
83
|
+
invalidEventPolicy?: InvalidEventPolicy;
|
|
84
|
+
/** Forwarded to the pool: fired once when a relay is suspended, with the lift time. */
|
|
85
|
+
onRelaySuspended?: (url: string, until: number) => void;
|
|
79
86
|
gossip?: Gossip;
|
|
80
87
|
/**
|
|
81
88
|
* Local event store. Defaults to {@link MemoryEventStore}. Browser apps that want persistence must
|
|
@@ -217,6 +224,10 @@ declare class ClientBuilder {
|
|
|
217
224
|
maxRelays(n: number): this;
|
|
218
225
|
/** Relays never closed by idle cleanup or `maxRelays` eviction. */
|
|
219
226
|
pinnedUrls(urls: ReadonlyArray<string>): this;
|
|
227
|
+
/** Forwarded to the pool: suspend a relay delivering too many invalid events. */
|
|
228
|
+
invalidEventPolicy(policy: InvalidEventPolicy): this;
|
|
229
|
+
/** Forwarded to the pool: fired once when a relay is suspended, with the lift time. */
|
|
230
|
+
onRelaySuspended(cb: (url: string, until: number) => void): this;
|
|
220
231
|
enableReconnect(enabled: boolean): this;
|
|
221
232
|
enablePing(enabled: boolean): this;
|
|
222
233
|
pingIntervalMs(ms: number): this;
|
|
@@ -325,4 +336,4 @@ declare class Client {
|
|
|
325
336
|
}
|
|
326
337
|
//#endregion
|
|
327
338
|
export { FetchEventsOptions as a, PublishOptions as c, SubscribeOptions as d, SubscribePrivateMessagesOptions as f, SyncSummary as g, SyncOptions as h, ClientOptions as i, ReceivedPrivateMessage as l, SyncDirectionName as m, ClientBuilder as n, FetchPrivateMessagesOptions as o, SyncDirection as p, ClientError as r, PrivateMessageSendResult as s, Client as t, SendPrivateMessageOptions as u };
|
|
328
|
-
//# sourceMappingURL=index-
|
|
339
|
+
//# sourceMappingURL=index-BIhSADwK.d.mts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as Event } from "./event-fU3Y01PR.mjs";
|
|
2
2
|
import { t as Filter } from "./filter-C0IMByE3.mjs";
|
|
3
3
|
import { a as NostrError } from "./error-CR8Urc84.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { n as Pool } from "./pool-B7p_reAm.mjs";
|
|
5
5
|
//#region src/relay/error.d.ts
|
|
6
6
|
/** Base class for relay errors; carries the relay URL when known. */
|
|
7
7
|
declare class RelayError extends NostrError {
|
|
@@ -25,6 +25,17 @@ declare class RelayClosedError extends RelayError {
|
|
|
25
25
|
declare class RelayTimeoutError extends RelayError {
|
|
26
26
|
name: string;
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* A relay was suspended for delivering more events that fail id/signature verification than its
|
|
30
|
+
* {@link PoolOptions.invalidEventPolicy} allows. Carries `until`, the epoch-ms time when the relay
|
|
31
|
+
* may connect again.
|
|
32
|
+
*/
|
|
33
|
+
declare class RelaySuspendedError extends RelayError {
|
|
34
|
+
name: string;
|
|
35
|
+
/** Epoch milliseconds when the suspension lifts. */
|
|
36
|
+
readonly until: number;
|
|
37
|
+
constructor(url: string, until: number);
|
|
38
|
+
}
|
|
28
39
|
//#endregion
|
|
29
40
|
//#region src/relay/url.d.ts
|
|
30
41
|
/**
|
|
@@ -69,5 +80,5 @@ declare function fetchRouted(pool: Pool, jobs: ReadonlyArray<RoutedJob>, opts?:
|
|
|
69
80
|
onevent?: ((event: Event, relayUrl: string) => void) | undefined;
|
|
70
81
|
}): Promise<Event[]>;
|
|
71
82
|
//#endregion
|
|
72
|
-
export { isInsecureRelayUrl as a, RelayError as c, fetchRouted as i, RelayPublishError as l, RoutedJob as n, RelayClosedError as o, fanIn as r, RelayConnectionError as s, FanInOptions as t,
|
|
73
|
-
//# sourceMappingURL=index-
|
|
83
|
+
export { isInsecureRelayUrl as a, RelayError as c, RelayTimeoutError as d, fetchRouted as i, RelayPublishError as l, RoutedJob as n, RelayClosedError as o, fanIn as r, RelayConnectionError as s, FanInOptions as t, RelaySuspendedError as u };
|
|
84
|
+
//# sourceMappingURL=index-D-TK_KJG.d.mts.map
|
|
@@ -124,19 +124,26 @@ type SqlValue = string | number | null | Uint8Array;
|
|
|
124
124
|
* start before the previous one settles, inside or outside a transaction — so drivers need not
|
|
125
125
|
* support overlapping statements on one connection.
|
|
126
126
|
*
|
|
127
|
-
* `expo-sqlite` maps onto this interface without changes to query code
|
|
127
|
+
* `expo-sqlite` maps onto this interface without changes to query code. Note that
|
|
128
|
+
* `withExclusiveTransactionAsync` resolves `void` and statements inside the exclusive transaction
|
|
129
|
+
* must run on the `txn` connection passed to its callback, not on `db` — the result is captured
|
|
130
|
+
* out-of-band:
|
|
128
131
|
*
|
|
129
132
|
* ```ts
|
|
130
|
-
* const
|
|
131
|
-
* const driver: SqlDriver = {
|
|
133
|
+
* const toDriver = (db: SQLiteDatabase): SqlDriver => ({
|
|
132
134
|
* exec: (sql) => db.execAsync(sql),
|
|
133
|
-
* run: async (sql, params = []) => {
|
|
134
|
-
*
|
|
135
|
-
*
|
|
135
|
+
* run: async (sql, params = []) => ({
|
|
136
|
+
* changes: (await db.runAsync(sql, [...params])).changes,
|
|
137
|
+
* }),
|
|
138
|
+
* all: (sql, params = []) => db.getAllAsync(sql, [...params]),
|
|
139
|
+
* transaction: async (fn) => {
|
|
140
|
+
* let result!: Awaited<ReturnType<typeof fn>>;
|
|
141
|
+
* await db.withExclusiveTransactionAsync(async (txn) => {
|
|
142
|
+
* result = await fn(toDriver(txn));
|
|
143
|
+
* });
|
|
144
|
+
* return result;
|
|
136
145
|
* },
|
|
137
|
-
*
|
|
138
|
-
* transaction: (fn) => db.withExclusiveTransactionAsync(() => fn(driver)),
|
|
139
|
-
* };
|
|
146
|
+
* });
|
|
140
147
|
* ```
|
|
141
148
|
*/
|
|
142
149
|
type SqlDriver = {
|
|
@@ -178,4 +185,4 @@ declare class SqliteEventStore implements EventStore {
|
|
|
178
185
|
}
|
|
179
186
|
//#endregion
|
|
180
187
|
export { IndexedDbEventStoreOptions as a, MemoryIndexOptions as c, IndexedDbEventStore as i, SqlValue as n, MemoryEventStore as o, SqliteEventStore as r, MemoryIndex as s, SqlDriver as t };
|
|
181
|
-
//# sourceMappingURL=index-
|
|
188
|
+
//# sourceMappingURL=index-D1bqFHKB.d.mts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -6,19 +6,19 @@ import { a as readRelays, c as writeRelays, i as parseRelayList, n as RelayMarke
|
|
|
6
6
|
import { i as RoutedFilter, r as PubkeyRoutes, t as Gossip } from "./gossip-BRQwIM_q.mjs";
|
|
7
7
|
import { a as assertSubscriptionId, c as encodeRelayMessage, d as parseRelayMessage, i as SubscriptionId, l as mergeCountHll, n as CountResult, o as createSubscriptionId, r as RelayMessage, s as encodeClientMessage, t as ClientMessage, u as parseClientMessage } from "./message-iGPIqnn0.mjs";
|
|
8
8
|
import { a as NostrError, i as MessageError, n as EventValidationError, o as UrlError, r as HexError, s as WasmVerifyPoisonedError, t as CryptoError } from "./error-CR8Urc84.mjs";
|
|
9
|
-
import { a as
|
|
9
|
+
import { a as PoolPublishResult, c as Relay, d as RelayStatusName, f as SubscribeOptions, i as PoolOptions, l as RelayOptions, n as Pool, o as PoolSubscribeOptions, r as PoolCountResult, s as PublishResult, t as InvalidEventPolicy, u as RelayStatus } from "./pool-B7p_reAm.mjs";
|
|
10
10
|
import { i as useWebSocketImplementation } from "./websocket-CmeqvAkp.mjs";
|
|
11
11
|
import { i as PutResult, n as NegentropyItem, r as OutboxBound, t as EventStore } from "./types-2pCUprRU.mjs";
|
|
12
12
|
import { n as ReactiveEventStoreOptions, r as Watch, t as ReactiveEventStore } from "./reactive-DFpqtIf_.mjs";
|
|
13
13
|
import { _ as neventEncode, a as NAddr, b as npubEncode, c as NPub, d as Note, f as ProfilePointer, g as naddrEncode, i as EventPointer, l as NSec, m as decodeNostrURI, n as Bech32MaxSize, o as NEvent, p as decode, r as DecodedResult, s as NProfile, t as AddressPointer, u as Nip19Error, v as noteEncode, x as nsecEncode, y as nprofileEncode } from "./nip19-BevCJZsp.mjs";
|
|
14
|
-
import { _ as ReplaceableLoader, a as OutboxFeed, c as groupAuthorsByOutboxRelay, d as bareNostrUser, g as ReplaceableLoadResult, h as LoadStyle, i as OutboxError, l as NostrUser, m as MutedEntity, n as Loaders, o as OutboxFeedOptions, p as ListResult, r as createLoaders, s as createOutboxFeed } from "./index-
|
|
14
|
+
import { _ as ReplaceableLoader, a as OutboxFeed, c as groupAuthorsByOutboxRelay, d as bareNostrUser, g as ReplaceableLoadResult, h as LoadStyle, i as OutboxError, l as NostrUser, m as MutedEntity, n as Loaders, o as OutboxFeedOptions, p as ListResult, r as createLoaders, s as createOutboxFeed } from "./index-B-CGNDPS.mjs";
|
|
15
15
|
import { t as NostrSigner } from "./types-CRQEk0Oi.mjs";
|
|
16
16
|
import { t as StorageError } from "./error-BW7re1UN.mjs";
|
|
17
|
-
import { f as SubscribePrivateMessagesOptions, g as SyncSummary, h as SyncOptions, i as ClientOptions, l as ReceivedPrivateMessage, m as SyncDirectionName, n as ClientBuilder, o as FetchPrivateMessagesOptions, p as SyncDirection, r as ClientError, s as PrivateMessageSendResult, t as Client, u as SendPrivateMessageOptions } from "./index-
|
|
17
|
+
import { f as SubscribePrivateMessagesOptions, g as SyncSummary, h as SyncOptions, i as ClientOptions, l as ReceivedPrivateMessage, m as SyncDirectionName, n as ClientBuilder, o as FetchPrivateMessagesOptions, p as SyncDirection, r as ClientError, s as PrivateMessageSendResult, t as Client, u as SendPrivateMessageOptions } from "./index-BIhSADwK.mjs";
|
|
18
18
|
import { _ as EVENT_ID_BYTES, a as classifyKind, b as SIGNATURE_BYTES, c as isRegularKind, f as bytesToHex, g as normalizeURL, h as isHex64, i as KnownKind, l as isReplaceableKind, m as isHex32, n as KindClassification, o as isAddressableKind, p as hexToBytes, r as KindName, s as isEphemeralKind, t as Kind, v as PUBLIC_KEY_BYTES, x as SUBSCRIPTION_ID_MAX_CHARS, y as SECRET_KEY_BYTES } from "./index-Boe8FshM.mjs";
|
|
19
19
|
import { a as Nip07Signer, c as isNip07Available, i as Nip46Transport, l as KeysSigner, n as Nip46SignerOptions, o as WindowNostr, r as Nip46SubscribeOptions, s as getWindowNostr, t as Nip46Signer, u as NoSignerError } from "./index-Ba7bJ7DD.mjs";
|
|
20
20
|
import { ReplyTagsOptions, ThreadReferences, buildReplyTags, parseThreadTags, replyTo } from "./nips/nip10.mjs";
|
|
21
21
|
import { isAuthRequired, makeAuthEvent } from "./nips/nip42.mjs";
|
|
22
|
-
import { a as isInsecureRelayUrl, c as RelayError, l as RelayPublishError, o as RelayClosedError, s as RelayConnectionError, u as
|
|
23
|
-
import { a as IndexedDbEventStoreOptions, i as IndexedDbEventStore, n as SqlValue, o as MemoryEventStore, r as SqliteEventStore, t as SqlDriver } from "./index-
|
|
24
|
-
export { type AddressPointer, Bech32MaxSize, Client, ClientBuilder, ClientError, type ClientMessage, type ClientOptions, type CountResult, CryptoError, type DecodedResult, EVENT_ID_BYTES, type Event, type EventAddress, EventBuilder, type EventPointer, type EventStore, type EventTemplate, EventValidationError, type FetchPrivateMessagesOptions, type Filter, Gossip, HexError, IndexedDbEventStore, type IndexedDbEventStoreOptions, Keys, KeysSigner, Kind, type KindClassification, type KindName, type KnownKind, type ListResult, type LoadStyle, type Loaders, MemoryEventStore, MessageError, type MutedEntity, type NAddr, type NEvent, type NProfile, type NPub, type NSec, type NegentropyItem, Nip07Signer, Nip19Error, Nip46Signer, type Nip46SignerOptions, type Nip46SubscribeOptions, type Nip46Transport, NoSignerError, NostrError, type NostrSigner, type NostrUser, type Note, type OutboxBound, OutboxError, OutboxFeed, type OutboxFeedOptions, PUBLIC_KEY_BYTES, Pool, type PoolCountResult, type PoolOptions, type PoolPublishResult, type PoolSubscribeOptions, type PrivateMessageSendResult, type ProfileMetadata, type ProfilePointer, type PubkeyRoutes, type PublicKey, type PublishResult, type PutResult, ReactiveEventStore, type ReactiveEventStoreOptions, type ReceivedPrivateMessage, Relay, RelayClosedError, RelayConnectionError, RelayError, type RelayListItem, type RelayMarker, type RelayMessage, type RelayOptions, RelayPublishError, RelayStatus, type RelayStatusName, RelayTimeoutError, type ReplaceableLoadResult, type ReplaceableLoader, type ReplyTagsOptions, type RoutedFilter, SECRET_KEY_BYTES, SIGNATURE_BYTES, SUBSCRIPTION_ID_MAX_CHARS, SecretKey, type SendPrivateMessageOptions, type SqlDriver, type SqlValue, SqliteEventStore, StorageError, type SubscribeOptions, type SubscribePrivateMessagesOptions, type SubscriptionId, SyncDirection, type SyncDirectionName, type SyncOptions, type SyncSummary, Tag, type TagInput, type ThreadReferences, type UnsignedEvent, UrlError, WasmVerifyPoisonedError, type Watch, type WindowNostr, assertSubscriptionId, bareNostrUser, buildReplyTags, bytesToHex, canonicalizeFilter, canonicalizeFilters, classifyKind, createLoaders, createOutboxFeed, createSubscriptionId, decodeNostrURI, encodeClientMessage, encodeRelayMessage, eventAddress, filterFingerprint, finalizeEvent, formatEventAddress, getDTag, getEventHash, getFilterLimit, getPublicKey, getWindowNostr, groupAuthorsByOutboxRelay, hexToBytes, isAddressableKind, isAuthRequired, isEphemeralKind, isHex32, isHex64, isInsecureRelayUrl, isNip07Available, isRegularKind, isReplaceableKind, isReplaceableWinner, isTag, itemCompare, makeAuthEvent, markerOf, matchFilter, matchFilters, mergeCountHll, mergeFilters, naddrEncode, neventEncode, decode as nip19Decode, normalizeURL, noteEncode, nprofileEncode, npubEncode, nsecEncode, parseClientMessage, parseEventAddress, parseRelayList, parseRelayMessage, parseThreadTags, publicKeyFromHex, readRelays, relayListEventBuilder, relayListToTags, replyTo, serializeEvent, signEvent, signedMatchesUnsigned, sortEvents, sortedEvents, tagName, tagValue, useWebSocketImplementation, validateEvent, validateSignedEvent, verifyEvent, writeRelays };
|
|
22
|
+
import { a as isInsecureRelayUrl, c as RelayError, d as RelayTimeoutError, l as RelayPublishError, o as RelayClosedError, s as RelayConnectionError, u as RelaySuspendedError } from "./index-D-TK_KJG.mjs";
|
|
23
|
+
import { a as IndexedDbEventStoreOptions, i as IndexedDbEventStore, n as SqlValue, o as MemoryEventStore, r as SqliteEventStore, t as SqlDriver } from "./index-D1bqFHKB.mjs";
|
|
24
|
+
export { type AddressPointer, Bech32MaxSize, Client, ClientBuilder, ClientError, type ClientMessage, type ClientOptions, type CountResult, CryptoError, type DecodedResult, EVENT_ID_BYTES, type Event, type EventAddress, EventBuilder, type EventPointer, type EventStore, type EventTemplate, EventValidationError, type FetchPrivateMessagesOptions, type Filter, Gossip, HexError, IndexedDbEventStore, type IndexedDbEventStoreOptions, type InvalidEventPolicy, Keys, KeysSigner, Kind, type KindClassification, type KindName, type KnownKind, type ListResult, type LoadStyle, type Loaders, MemoryEventStore, MessageError, type MutedEntity, type NAddr, type NEvent, type NProfile, type NPub, type NSec, type NegentropyItem, Nip07Signer, Nip19Error, Nip46Signer, type Nip46SignerOptions, type Nip46SubscribeOptions, type Nip46Transport, NoSignerError, NostrError, type NostrSigner, type NostrUser, type Note, type OutboxBound, OutboxError, OutboxFeed, type OutboxFeedOptions, PUBLIC_KEY_BYTES, Pool, type PoolCountResult, type PoolOptions, type PoolPublishResult, type PoolSubscribeOptions, type PrivateMessageSendResult, type ProfileMetadata, type ProfilePointer, type PubkeyRoutes, type PublicKey, type PublishResult, type PutResult, ReactiveEventStore, type ReactiveEventStoreOptions, type ReceivedPrivateMessage, Relay, RelayClosedError, RelayConnectionError, RelayError, type RelayListItem, type RelayMarker, type RelayMessage, type RelayOptions, RelayPublishError, RelayStatus, type RelayStatusName, RelaySuspendedError, RelayTimeoutError, type ReplaceableLoadResult, type ReplaceableLoader, type ReplyTagsOptions, type RoutedFilter, SECRET_KEY_BYTES, SIGNATURE_BYTES, SUBSCRIPTION_ID_MAX_CHARS, SecretKey, type SendPrivateMessageOptions, type SqlDriver, type SqlValue, SqliteEventStore, StorageError, type SubscribeOptions, type SubscribePrivateMessagesOptions, type SubscriptionId, SyncDirection, type SyncDirectionName, type SyncOptions, type SyncSummary, Tag, type TagInput, type ThreadReferences, type UnsignedEvent, UrlError, WasmVerifyPoisonedError, type Watch, type WindowNostr, assertSubscriptionId, bareNostrUser, buildReplyTags, bytesToHex, canonicalizeFilter, canonicalizeFilters, classifyKind, createLoaders, createOutboxFeed, createSubscriptionId, decodeNostrURI, encodeClientMessage, encodeRelayMessage, eventAddress, filterFingerprint, finalizeEvent, formatEventAddress, getDTag, getEventHash, getFilterLimit, getPublicKey, getWindowNostr, groupAuthorsByOutboxRelay, hexToBytes, isAddressableKind, isAuthRequired, isEphemeralKind, isHex32, isHex64, isInsecureRelayUrl, isNip07Available, isRegularKind, isReplaceableKind, isReplaceableWinner, isTag, itemCompare, makeAuthEvent, markerOf, matchFilter, matchFilters, mergeCountHll, mergeFilters, naddrEncode, neventEncode, decode as nip19Decode, normalizeURL, noteEncode, nprofileEncode, npubEncode, nsecEncode, parseClientMessage, parseEventAddress, parseRelayList, parseRelayMessage, parseThreadTags, publicKeyFromHex, readRelays, relayListEventBuilder, relayListToTags, replyTo, serializeEvent, signEvent, signedMatchesUnsigned, sortEvents, sortedEvents, tagName, tagValue, useWebSocketImplementation, validateEvent, validateSignedEvent, verifyEvent, writeRelays };
|
package/dist/index.mjs
CHANGED
|
@@ -11,14 +11,14 @@ import { Bech32MaxSize, Nip19Error, decode, decodeNostrURI, naddrEncode, neventE
|
|
|
11
11
|
import { buildReplyTags, parseThreadTags, replyTo } from "./nips/nip10.mjs";
|
|
12
12
|
import { isAuthRequired, makeAuthEvent } from "./nips/nip42.mjs";
|
|
13
13
|
import { markerOf, parseRelayList, readRelays, relayListEventBuilder, relayListToTags, writeRelays } from "./nips/nip65.mjs";
|
|
14
|
-
import { a as RelayStatus, c as isInsecureRelayUrl, d as RelayClosedError, f as RelayConnectionError,
|
|
14
|
+
import { a as RelayStatus, c as isInsecureRelayUrl, d as RelayClosedError, f as RelayConnectionError, g as RelayTimeoutError, h as RelaySuspendedError, i as Relay, m as RelayPublishError, p as RelayError, t as Pool, u as useWebSocketImplementation } from "./pool-C2xOcsun.mjs";
|
|
15
15
|
import "./relay.mjs";
|
|
16
16
|
import { t as Gossip } from "./gossip-oloDMWEj.mjs";
|
|
17
17
|
import { a as groupAuthorsByOutboxRelay, i as createOutboxFeed, n as OutboxError, o as bareNostrUser, r as OutboxFeed, t as createLoaders } from "./loaders-o6YXB_Ri.mjs";
|
|
18
18
|
import { t as StorageError } from "./error-DrEIM7FO.mjs";
|
|
19
19
|
import { t as MemoryEventStore } from "./memory-C2EVx0_O.mjs";
|
|
20
20
|
import { t as ReactiveEventStore } from "./reactive-DDlThDSZ.mjs";
|
|
21
|
-
import { i as ClientBuilder, n as ClientError, r as SyncDirection, t as Client } from "./client-
|
|
21
|
+
import { i as ClientBuilder, n as ClientError, r as SyncDirection, t as Client } from "./client-5DhgYV7P.mjs";
|
|
22
22
|
import { n as IndexedDbEventStore, t as SqliteEventStore } from "./storage-xXxLGCVb.mjs";
|
|
23
23
|
import "./store.mjs";
|
|
24
|
-
export { Bech32MaxSize, Client, ClientBuilder, ClientError, CryptoError, EVENT_ID_BYTES, EventBuilder, EventValidationError, Gossip, HexError, IndexedDbEventStore, Keys, KeysSigner, Kind, MemoryEventStore, MessageError, Nip07Signer, Nip19Error, Nip46Signer, NoSignerError, NostrError, OutboxError, OutboxFeed, PUBLIC_KEY_BYTES, Pool, ReactiveEventStore, Relay, RelayClosedError, RelayConnectionError, RelayError, RelayPublishError, RelayStatus, RelayTimeoutError, SECRET_KEY_BYTES, SIGNATURE_BYTES, SUBSCRIPTION_ID_MAX_CHARS, SecretKey, SqliteEventStore, StorageError, SyncDirection, Tag, UrlError, WasmVerifyPoisonedError, assertSubscriptionId, bareNostrUser, buildReplyTags, bytesToHex, canonicalizeFilter, canonicalizeFilters, classifyKind, createLoaders, createOutboxFeed, createSubscriptionId, decodeNostrURI, encodeClientMessage, encodeRelayMessage, eventAddress, filterFingerprint, finalizeEvent, formatEventAddress, getDTag, getEventHash, getFilterLimit, getPublicKey, getWindowNostr, groupAuthorsByOutboxRelay, hexToBytes, isAddressableKind, isAuthRequired, isEphemeralKind, isHex32, isHex64, isInsecureRelayUrl, isNip07Available, isRegularKind, isReplaceableKind, isReplaceableWinner, isTag, itemCompare, makeAuthEvent, markerOf, matchFilter, matchFilters, mergeCountHll, mergeFilters, naddrEncode, neventEncode, decode as nip19Decode, normalizeURL, noteEncode, nprofileEncode, npubEncode, nsecEncode, parseClientMessage, parseEventAddress, parseRelayList, parseRelayMessage, parseThreadTags, publicKeyFromHex, readRelays, relayListEventBuilder, relayListToTags, replyTo, serializeEvent, signEvent, signedMatchesUnsigned, sortEvents, sortedEvents, tagName, tagValue, useWebSocketImplementation, validateEvent, validateSignedEvent, verifyEvent, writeRelays };
|
|
24
|
+
export { Bech32MaxSize, Client, ClientBuilder, ClientError, CryptoError, EVENT_ID_BYTES, EventBuilder, EventValidationError, Gossip, HexError, IndexedDbEventStore, Keys, KeysSigner, Kind, MemoryEventStore, MessageError, Nip07Signer, Nip19Error, Nip46Signer, NoSignerError, NostrError, OutboxError, OutboxFeed, PUBLIC_KEY_BYTES, Pool, ReactiveEventStore, Relay, RelayClosedError, RelayConnectionError, RelayError, RelayPublishError, RelayStatus, RelaySuspendedError, RelayTimeoutError, SECRET_KEY_BYTES, SIGNATURE_BYTES, SUBSCRIPTION_ID_MAX_CHARS, SecretKey, SqliteEventStore, StorageError, SyncDirection, Tag, UrlError, WasmVerifyPoisonedError, assertSubscriptionId, bareNostrUser, buildReplyTags, bytesToHex, canonicalizeFilter, canonicalizeFilters, classifyKind, createLoaders, createOutboxFeed, createSubscriptionId, decodeNostrURI, encodeClientMessage, encodeRelayMessage, eventAddress, filterFingerprint, finalizeEvent, formatEventAddress, getDTag, getEventHash, getFilterLimit, getPublicKey, getWindowNostr, groupAuthorsByOutboxRelay, hexToBytes, isAddressableKind, isAuthRequired, isEphemeralKind, isHex32, isHex64, isInsecureRelayUrl, isNip07Available, isRegularKind, isReplaceableKind, isReplaceableWinner, isTag, itemCompare, makeAuthEvent, markerOf, matchFilter, matchFilters, mergeCountHll, mergeFilters, naddrEncode, neventEncode, decode as nip19Decode, normalizeURL, noteEncode, nprofileEncode, npubEncode, nsecEncode, parseClientMessage, parseEventAddress, parseRelayList, parseRelayMessage, parseThreadTags, publicKeyFromHex, readRelays, relayListEventBuilder, relayListToTags, replyTo, serializeEvent, signEvent, signedMatchesUnsigned, sortEvents, sortedEvents, tagName, tagValue, useWebSocketImplementation, validateEvent, validateSignedEvent, verifyEvent, writeRelays };
|
package/dist/loaders.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { r as OutboxBound } from "./types-2pCUprRU.mjs";
|
|
2
|
-
import { _ as ReplaceableLoader, a as OutboxFeed, c as groupAuthorsByOutboxRelay, d as bareNostrUser, f as ListLoaders, g as ReplaceableLoadResult, h as LoadStyle, i as OutboxError, l as NostrUser, m as MutedEntity, n as Loaders, o as OutboxFeedOptions, p as ListResult, r as createLoaders, s as createOutboxFeed, t as CreateLoadersOptions, u as ProfileLoader, v as EventLoader, y as EventRef } from "./index-
|
|
2
|
+
import { _ as ReplaceableLoader, a as OutboxFeed, c as groupAuthorsByOutboxRelay, d as bareNostrUser, f as ListLoaders, g as ReplaceableLoadResult, h as LoadStyle, i as OutboxError, l as NostrUser, m as MutedEntity, n as Loaders, o as OutboxFeedOptions, p as ListResult, r as createLoaders, s as createOutboxFeed, t as CreateLoadersOptions, u as ProfileLoader, v as EventLoader, y as EventRef } from "./index-B-CGNDPS.mjs";
|
|
3
3
|
export { CreateLoadersOptions, type EventLoader, type EventRef, type ListLoaders, type ListResult, type LoadStyle, Loaders, type MutedEntity, type NostrUser, type OutboxBound, OutboxError, OutboxFeed, type OutboxFeedOptions, type ProfileLoader, type ReplaceableLoadResult, type ReplaceableLoader, bareNostrUser, createLoaders, createOutboxFeed, groupAuthorsByOutboxRelay };
|
|
@@ -113,6 +113,11 @@ declare class Relay {
|
|
|
113
113
|
onauth: ((challenge: string) => void) | undefined;
|
|
114
114
|
/** Fired after a successful reconnect (not the initial connect). */
|
|
115
115
|
onreconnect: (() => void) | undefined;
|
|
116
|
+
/**
|
|
117
|
+
* Fired when a delivered EVENT fails id/signature verification, just before the event is dropped.
|
|
118
|
+
* A poisoned verifier dropping events does not fire it.
|
|
119
|
+
*/
|
|
120
|
+
oninvalidevent: (() => void) | undefined;
|
|
116
121
|
constructor(url: string, opts?: RelayOptions);
|
|
117
122
|
/** Latest NIP-42 challenge, if any. */
|
|
118
123
|
get challenge(): string | undefined;
|
|
@@ -133,6 +138,12 @@ declare class Relay {
|
|
|
133
138
|
}): Promise<void>;
|
|
134
139
|
/** Graceful shutdown: disables reconnect and closes all subscriptions. */
|
|
135
140
|
close(): void;
|
|
141
|
+
/**
|
|
142
|
+
* Sever the socket while keeping every subscription: unlike {@link close}, open REQ state is
|
|
143
|
+
* preserved and reconnect stays suppressed — the next {@link connect} re-subscribes all of them.
|
|
144
|
+
* {@link Pool} uses this to suspend a relay without tearing down its live subscriptions.
|
|
145
|
+
*/
|
|
146
|
+
disconnect(): void;
|
|
136
147
|
/** Low-level REQ with callbacks. Survives reconnect when enableReconnect is on. */
|
|
137
148
|
subscribe(filters: Filter[], opts?: SubscribeOptions): Subscription;
|
|
138
149
|
/** AsyncIterable of events for filters until the subscription is closed. */
|
|
@@ -191,6 +202,14 @@ declare class Relay {
|
|
|
191
202
|
}
|
|
192
203
|
//#endregion
|
|
193
204
|
//#region src/relay/pool.d.ts
|
|
205
|
+
/** Per-relay tolerance for events that fail id/signature verification. */
|
|
206
|
+
type InvalidEventPolicy = {
|
|
207
|
+
/** Invalid events tolerated within `windowMs`; exceeding it suspends the relay. */
|
|
208
|
+
limit: number;
|
|
209
|
+
windowMs: number;
|
|
210
|
+
/** How long a suspended relay stays closed before it may reconnect. */
|
|
211
|
+
cooldownMs: number;
|
|
212
|
+
};
|
|
194
213
|
/** Pool-wide options applied to every managed relay. */
|
|
195
214
|
type PoolOptions = {
|
|
196
215
|
websocketImplementation?: WebSocketConstructor | undefined;
|
|
@@ -223,6 +242,18 @@ type PoolOptions = {
|
|
|
223
242
|
maxRelays?: number | undefined;
|
|
224
243
|
/** Normalized URLs never closed by idle cleanup or `maxRelays` eviction. */
|
|
225
244
|
pinnedUrls?: ReadonlyArray<string> | undefined;
|
|
245
|
+
/**
|
|
246
|
+
* When set, a relay whose `limit`-th-plus-one EVENT fails id/signature verification inside a
|
|
247
|
+
* `windowMs` sliding window is disconnected and suspended for `cooldownMs`. During suspension
|
|
248
|
+
* `ensureRelay` rejects with {@link RelaySuspendedError}; once it lifts, live subscriptions resume
|
|
249
|
+
* through the normal reconnect path. Unset = events are dropped without counting, as before.
|
|
250
|
+
*/
|
|
251
|
+
invalidEventPolicy?: InvalidEventPolicy | undefined;
|
|
252
|
+
/**
|
|
253
|
+
* Fired once when a relay is suspended for exceeding `invalidEventPolicy`: `url` is the
|
|
254
|
+
* normalized relay URL and `until` the epoch-ms time the suspension lifts.
|
|
255
|
+
*/
|
|
256
|
+
onRelaySuspended?: ((url: string, until: number) => void) | undefined;
|
|
226
257
|
};
|
|
227
258
|
/** Per-relay publish outcome: the relay's OK reply or an error string. */
|
|
228
259
|
type PoolPublishResult = {
|
|
@@ -298,5 +329,5 @@ declare class Pool {
|
|
|
298
329
|
connectedUrls(): string[];
|
|
299
330
|
}
|
|
300
331
|
//#endregion
|
|
301
|
-
export {
|
|
302
|
-
//# sourceMappingURL=pool-
|
|
332
|
+
export { PoolPublishResult as a, Relay as c, RelayStatusName as d, SubscribeOptions as f, subscriptionToAsyncIterable as h, PoolOptions as i, RelayOptions as l, SubscriptionHandlers as m, Pool as n, PoolSubscribeOptions as o, Subscription as p, PoolCountResult as r, PublishResult as s, InvalidEventPolicy as t, RelayStatus as u };
|
|
333
|
+
//# sourceMappingURL=pool-B7p_reAm.d.mts.map
|