Move plugins to a plugin directory

This commit is contained in:
Jon Staab
2026-06-18 10:31:48 -07:00
parent 72ab746254
commit fafa3b172e
32 changed files with 182 additions and 133 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ import {call} from "@welshman/lib"
import {Pool, Tracker, Repository, WrapManager} from "@welshman/net"
import type {NetContext, AdapterFactory} from "@welshman/net"
import type {User} from "./user.js"
import type {ClientPolicy} from "./policies.js"
import type {ClientPolicy} from "./policy.js"
export type ClientConfig = {
dufflepudUrl?: string
+1 -1
View File
@@ -1,6 +1,6 @@
import {Client} from "./client.js"
import type {ClientOptions} from "./client.js"
import {defaultClientPolicies} from "./policies.js"
import {defaultClientPolicies} from "./policy.js"
/**
* Creates a batteries-included client: a `Client` wired with the default client
+29 -29
View File
@@ -1,34 +1,34 @@
export * from "./client.js"
export * from "./policies.js"
export * from "./network.js"
export * from "./stores.js"
export * from "./clientData.js"
export * from "./policy.js"
export * from "./user.js"
export * from "./router.js"
export * from "./relays.js"
export * from "./relayStats.js"
export * from "./relayLists.js"
export * from "./blockedRelayLists.js"
export * from "./plaintext.js"
export * from "./profiles.js"
export * from "./follows.js"
export * from "./mutes.js"
export * from "./pins.js"
export * from "./blossom.js"
export * from "./messagingRelayLists.js"
export * from "./searchRelayLists.js"
export * from "./handles.js"
export * from "./zappers.js"
export * from "./topics.js"
export * from "./tags.js"
export * from "./session.js"
export * from "./logging.js"
export * from "./wot.js"
export * from "./feeds.js"
export * from "./search.js"
export * from "./sync.js"
export * from "./giftWraps.js"
export * from "./rooms.js"
export * from "./relayManagement.js"
export * from "./thunk.js"
export * from "./createApp.js"
export * from "./plugins/base.js"
export * from "./plugins/network.js"
export * from "./plugins/stores.js"
export * from "./plugins/router.js"
export * from "./plugins/relays.js"
export * from "./plugins/relayStats.js"
export * from "./plugins/relayLists.js"
export * from "./plugins/blockedRelayLists.js"
export * from "./plugins/plaintext.js"
export * from "./plugins/profiles.js"
export * from "./plugins/follows.js"
export * from "./plugins/mutes.js"
export * from "./plugins/pins.js"
export * from "./plugins/blossom.js"
export * from "./plugins/messagingRelayLists.js"
export * from "./plugins/searchRelayLists.js"
export * from "./plugins/handles.js"
export * from "./plugins/zappers.js"
export * from "./plugins/topics.js"
export * from "./plugins/tags.js"
export * from "./plugins/wot.js"
export * from "./plugins/feeds.js"
export * from "./plugins/search.js"
export * from "./plugins/sync.js"
export * from "./plugins/wraps.js"
export * from "./plugins/rooms.js"
export * from "./plugins/relayManagement.js"
export * from "./plugins/thunk.js"
@@ -4,7 +4,7 @@ import type {Maybe} from "@welshman/lib"
import type {Filter} from "@welshman/util"
import {deriveItems, getter, makeDeriveItem, makeLoadItem, makeForceLoadItem} from "@welshman/store"
import type {EventToItem, ItemsByKey, MakeLoadItemOptions} from "@welshman/store"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
import {Stores} from "./stores.js"
/**
@@ -17,6 +17,48 @@ export type Projection<T> = {
export const projection = <T>($: Readable<T>, get = getter($)) => ({$, get})
/**
* Synchronous read access to a keyed collection's current value. Shared by
* collections that own a map (`MapPlugin`) and those that are read-only views
* over the repository (`DerivedPlugin`).
*/
export interface ReadableMap<T> {
keys(): IterableIterator<string>
values(): IterableIterator<T>
get(key: string): Maybe<T>
}
/**
* Direct get/set access to a keyed collection the plugin owns. Extends the read
* accessors with mutation plus change notification. Only collections that own
* their map implement this repository-backed collections are read-only.
*/
export interface Mappable<T> extends ReadableMap<T> {
set(key: string, value: T): void
delete(key: string): void
clear(): void
onItem(subscriber: (key: string, value: Maybe<T>) => void): Unsubscriber
}
/**
* Lazy, async loading of items by key from the network, with per-key caching
* and backoff (`load`) or a cache-bypassing refresh (`forceLoad`).
*/
export interface Loadable<T> {
load(key: string, ...args: any[]): Promise<Maybe<T>>
forceLoad(key: string, ...args: any[]): Promise<Maybe<T>>
}
/**
* Reactive derivations over a keyed collection: the whole map (`index`), its
* values (`all`), and a per-key on-demand store (`one`).
*/
export interface Derivable<T> {
index: Projection<ItemsByKey<T>>
all: Projection<T[]>
one(key?: string, ...args: any[]): Readable<Maybe<T>>
}
/**
* Base class for a reactive, keyed collection of "local" (non-event) data
* things like relay stats or NIP-11 profiles that aren't backed by the
@@ -26,7 +68,7 @@ export const projection = <T>($: Readable<T>, get = getter($)) => ({$, get})
* snapshot via `.get()`. Per-key access is `one(key)`, a plain on-demand store
* (snapshot with svelte's `get(...)`, or read `get(key)` directly).
*/
export class ClientData<T> {
export class MapPlugin<T> implements Mappable<T>, Derivable<T> {
protected store = writable(new Map<string, T>())
index: Projection<ItemsByKey<T>>
all: Projection<T[]>
@@ -93,11 +135,11 @@ export class ClientData<T> {
}
/**
* A `ClientData` collection that knows how to lazily load items by key from the
* A `MapPlugin` collection that knows how to lazily load items by key from the
* network. Subclasses implement `fetch`; `load`/`forceLoad`/`one` are derived
* from it (with per-key caching and backoff via `makeLoadItem`).
*/
export abstract class LoadableData<T> extends ClientData<T> {
export abstract class LoadableMapPlugin<T> extends MapPlugin<T> implements Loadable<T> {
load: (key: string, ...args: any[]) => Promise<Maybe<T>>
forceLoad: (key: string, ...args: any[]) => Promise<Maybe<T>>
@@ -118,7 +160,7 @@ export abstract class LoadableData<T> extends ClientData<T> {
}
}
export type DerivedDataOptions<T> = {
export type DerivedPluginOptions<T> = {
filters: Filter[]
eventToItem: EventToItem<T>
getKey: (item: T) => string
@@ -135,7 +177,7 @@ export type DerivedDataOptions<T> = {
* `index` (map) and `all` (values) are `Projection`s subscribe via `.$`,
* snapshot via `.get()`. Per-key access is `one(key)`, a plain on-demand store.
*/
export abstract class DerivedData<T> {
export abstract class DerivedPlugin<T> implements ReadableMap<T>, Loadable<T>, Derivable<T> {
index: Projection<ItemsByKey<T>>
all: Projection<T[]>
one: (key?: string, ...args: any[]) => Readable<Maybe<T>>
@@ -146,7 +188,7 @@ export abstract class DerivedData<T> {
constructor(
protected readonly ctx: IClient,
options: DerivedDataOptions<T>,
options: DerivedPluginOptions<T>,
) {
const index = ctx.use(Stores).itemsByKey<T>({
filters: options.filters,
@@ -9,19 +9,19 @@ import {
removeFromList,
} from "@welshman/util"
import type {TrustedEvent} from "@welshman/util"
import {DerivedData} from "./clientData.js"
import {DerivedPlugin} from "./base.js"
import {Network} from "./network.js"
import {Router} from "./router.js"
import {User} from "./user.js"
import {User} from "../user.js"
import {Thunks} from "./thunk.js"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
/**
* Kind-10006 blocked-relay lists, keyed by pubkey. Loaded via the outbox model,
* so it depends on the relay-list collection. Feeds `RelayStats.getQuality` so
* blocked relays are never selected.
*/
export class BlockedRelayLists extends DerivedData<ReturnType<typeof readList>> {
export class BlockedRelayLists extends DerivedPlugin<ReturnType<typeof readList>> {
constructor(ctx: IClient) {
super(ctx, {
filters: [{kinds: [BLOCKED_RELAYS]}],
@@ -1,14 +1,14 @@
import {BLOSSOM_SERVERS, asDecryptedEvent, readList} from "@welshman/util"
import type {TrustedEvent} from "@welshman/util"
import {DerivedData} from "./clientData.js"
import {DerivedPlugin} from "./base.js"
import {Network} from "./network.js"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
/**
* Blossom server lists (kind 10063), keyed by pubkey. Loaded via the outbox
* model (the author's write relays), so it depends on the relay-list collection.
*/
export class BlossomServerLists extends DerivedData<ReturnType<typeof readList>> {
export class BlossomServerLists extends DerivedPlugin<ReturnType<typeof readList>> {
constructor(ctx: IClient) {
super(ctx, {
filters: [{kinds: [BLOSSOM_SERVERS]}],
@@ -1,7 +1,7 @@
import {Scope, FeedController} from "@welshman/feeds"
import type {FeedControllerOptions, Feed} from "@welshman/feeds"
import type {AdapterContext} from "@welshman/net"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
import {Router} from "./router.js"
import {Wot} from "./wot.js"
@@ -7,17 +7,17 @@ import {
removeFromList,
} from "@welshman/util"
import type {TrustedEvent} from "@welshman/util"
import {DerivedData} from "./clientData.js"
import {DerivedPlugin} from "./base.js"
import {Network} from "./network.js"
import {Thunks} from "./thunk.js"
import {User} from "./user.js"
import type {IClient} from "./client.js"
import {User} from "../user.js"
import type {IClient} from "../client.js"
/**
* Kind-3 follow lists, keyed by pubkey. Loaded via the outbox model (the
* author's write relays), so it depends on the relay-list collection.
*/
export class FollowLists extends DerivedData<ReturnType<typeof readList>> {
export class FollowLists extends DerivedPlugin<ReturnType<typeof readList>> {
constructor(ctx: IClient) {
super(ctx, {
filters: [{kinds: [FOLLOWS]}],
@@ -3,9 +3,9 @@ import type {Maybe} from "@welshman/lib"
import {queryProfile, displayNip05} from "@welshman/util"
import type {Handle} from "@welshman/util"
import {deriveDeduplicated} from "@welshman/store"
import {LoadableData, projection} from "./clientData.js"
import type {Projection} from "./clientData.js"
import type {IClient} from "./client.js"
import {LoadableMapPlugin, projection} from "./base.js"
import type {Projection} from "./base.js"
import type {IClient} from "../client.js"
import {Profiles} from "./profiles.js"
/**
@@ -15,7 +15,7 @@ import {Profiles} from "./profiles.js"
* user privacy). Depends on the profiles collection to resolve a pubkey's
* handle.
*/
export class Handles extends LoadableData<Handle> {
export class Handles extends LoadableMapPlugin<Handle> {
constructor(ctx: IClient) {
super(ctx)
}
@@ -8,19 +8,19 @@ import {
removeFromList,
} from "@welshman/util"
import type {TrustedEvent} from "@welshman/util"
import {DerivedData} from "./clientData.js"
import {DerivedPlugin} from "./base.js"
import {Network} from "./network.js"
import {Router} from "./router.js"
import {User} from "./user.js"
import {User} from "../user.js"
import {Thunks} from "./thunk.js"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
/**
* Kind-10050 messaging relay lists (NIP-17), keyed by pubkey. Loaded via the
* outbox model (the author's write relays), so it depends on the relay-list
* collection.
*/
export class MessagingRelayLists extends DerivedData<ReturnType<typeof readList>> {
export class MessagingRelayLists extends DerivedPlugin<ReturnType<typeof readList>> {
constructor(ctx: IClient) {
super(ctx, {
filters: [{kinds: [MESSAGING_RELAYS]}],
@@ -9,18 +9,18 @@ import {
updateList,
} from "@welshman/util"
import type {TrustedEvent, PublishedList} from "@welshman/util"
import {DerivedData} from "./clientData.js"
import type {IClient} from "./client.js"
import {DerivedPlugin} from "./base.js"
import type {IClient} from "../client.js"
import {Network} from "./network.js"
import {Thunks} from "./thunk.js"
import {Plaintext} from "./plaintext.js"
import {User} from "./user.js"
import {User} from "../user.js"
/**
* Kind-10000 mute lists, keyed by pubkey. Mute lists carry private entries in
* encrypted content, so decoding goes through the plaintext cache.
*/
export class MuteLists extends DerivedData<PublishedList> {
export class MuteLists extends DerivedPlugin<PublishedList> {
constructor(ctx: IClient) {
super(ctx, {
filters: [{kinds: [MUTES]}],
@@ -13,7 +13,7 @@ import type {
} from "@welshman/net"
import {Router, addMinimalFallbacks} from "./router.js"
import {RelayLists} from "./relayLists.js"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
/**
* Net utilities bound to the client's net context (its pool + repository). Reach
@@ -7,17 +7,17 @@ import {
removeFromList,
} from "@welshman/util"
import type {TrustedEvent} from "@welshman/util"
import {DerivedData} from "./clientData.js"
import {DerivedPlugin} from "./base.js"
import {Network} from "./network.js"
import {Thunks} from "./thunk.js"
import {User} from "./user.js"
import type {IClient} from "./client.js"
import {User} from "../user.js"
import type {IClient} from "../client.js"
/**
* NIP-51 pin lists (kind 10001), keyed by pubkey. Loaded via the outbox model
* (the author's write relays), so it depends on the relay-list collection.
*/
export class PinLists extends DerivedData<ReturnType<typeof readList>> {
export class PinLists extends DerivedPlugin<ReturnType<typeof readList>> {
constructor(ctx: IClient) {
super(ctx, {
filters: [{kinds: [PINS]}],
@@ -1,12 +1,12 @@
import {decrypt} from "@welshman/signer"
import type {Maybe} from "@welshman/lib"
import type {TrustedEvent} from "@welshman/util"
import {ClientData} from "./clientData.js"
import {MapPlugin} from "./base.js"
/**
* A cache of decrypted event content, keyed by event id.
*/
export class Plaintext extends ClientData<string> {
export class Plaintext extends MapPlugin<string> {
ensure = async (event: TrustedEvent): Promise<Maybe<string>> => {
if (this.ctx.user?.pubkey !== event.pubkey) return
@@ -10,18 +10,18 @@ import {
} from "@welshman/util"
import type {Profile} from "@welshman/util"
import type {Maybe} from "@welshman/lib"
import {DerivedData, projection} from "./clientData.js"
import type {Projection} from "./clientData.js"
import {DerivedPlugin, projection} from "./base.js"
import type {Projection} from "./base.js"
import {Network} from "./network.js"
import {Router} from "./router.js"
import {Thunks} from "./thunk.js"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
/**
* Kind-0 profiles, keyed by pubkey. Loaded via the outbox model (the author's
* write relays), resolved through the relay-list collection at fetch time.
*/
export class Profiles extends DerivedData<ReturnType<typeof readProfile>> {
export class Profiles extends DerivedPlugin<ReturnType<typeof readProfile>> {
constructor(ctx: IClient) {
super(ctx, {
filters: [{kinds: [PROFILE]}],
@@ -12,18 +12,18 @@ import {
makeEvent,
} from "@welshman/util"
import type {TrustedEvent, PublishedList} from "@welshman/util"
import {DerivedData} from "./clientData.js"
import {DerivedPlugin} from "./base.js"
import {Router, addMinimalFallbacks} from "./router.js"
import {Network} from "./network.js"
import {User} from "./user.js"
import {User} from "../user.js"
import {Thunks} from "./thunk.js"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
/**
* NIP-65 relay lists, keyed by pubkey. This is the routing substrate every other
* outbox-model load depends on (see `Network.loadUsingOutbox`).
*/
export class RelayLists extends DerivedData<PublishedList> {
export class RelayLists extends DerivedPlugin<PublishedList> {
constructor(ctx: IClient) {
super(ctx, {
filters: [{kinds: [RELAYS]}],
@@ -1,7 +1,7 @@
import {makeHttpAuth, sendManagementRequest} from "@welshman/util"
import type {ManagementRequest} from "@welshman/util"
import {User} from "./user.js"
import type {IClient} from "./client.js"
import {User} from "../user.js"
import type {IClient} from "../client.js"
/**
* NIP-86 relay management. Signs an HTTP-auth event as the client's user and
@@ -2,7 +2,7 @@ import {groupBy, batch, now, uniq, ago, DAY, HOUR, MINUTE} from "@welshman/lib"
import {isOnionUrl, isLocalUrl, isIPAddress, isRelayUrl} from "@welshman/util"
import {SocketStatus, SocketEvent} from "@welshman/net"
import type {ClientMessage, RelayMessage, Socket} from "@welshman/net"
import {ClientData} from "./clientData.js"
import {MapPlugin} from "./base.js"
import {BlockedRelayLists} from "./blockedRelayLists.js"
export type RelayStatsUpdate = [string, (stats: RelayStatsItem) => void]
@@ -56,7 +56,7 @@ export const makeRelayStatsItem = (url: string): RelayStatsItem => ({
* the router uses to rank relays. A pure store the socket wiring that fills it
* lives in `clientPolicyRelayStats`.
*/
export class RelayStats extends ClientData<RelayStatsItem> {
export class RelayStats extends MapPlugin<RelayStatsItem> {
getQuality = (url: string) => {
// Skip non-relays entirely
if (!isRelayUrl(url)) return 0
@@ -3,14 +3,14 @@ import {fetchJson} from "@welshman/lib"
import type {Maybe} from "@welshman/lib"
import {displayRelayUrl, displayRelayProfile} from "@welshman/util"
import type {RelayProfile} from "@welshman/util"
import {LoadableData, projection} from "./clientData.js"
import type {Projection} from "./clientData.js"
import {LoadableMapPlugin, projection} from "./base.js"
import type {Projection} from "./base.js"
/**
* NIP-11 relay profiles, keyed by url. A "local" loadable collection: items
* aren't nostr events, they're fetched over HTTP from each relay.
*/
export class Relays extends LoadableData<RelayProfile> {
export class Relays extends LoadableMapPlugin<RelayProfile> {
fetch = async (url: string): Promise<Maybe<RelayProfile>> => {
try {
const json = await fetchJson(url.replace(/^ws/, "http"), {
@@ -42,4 +42,17 @@ export class Relays extends LoadableData<RelayProfile> {
return projection(derived(this.one(url), read), () => read(this.get(url)))
}
hasNegentropy = async (url: string) => {
const relay = await this.load(url)
if (relay?.negentropy) return true
if (relay?.supported_nips?.includes("77")) return true
if (relay?.software?.includes?.("strfry") && !relay?.version?.match(/^0\./)) return true
return false
}
hasNip = async (url: string, nip: number | string) =>
(await this.load(url))?.supported_nips?.includes(String(nip)) ?? false
}
@@ -10,7 +10,7 @@ import {
import type {RoomMeta} from "@welshman/util"
import {Thunks} from "./thunk.js"
import type {ThunkOptions} from "./thunk.js"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
/**
* NIP-29 relay-based group (room) management. Each method publishes the relevant
@@ -1,7 +1,7 @@
import {Router as BaseRouter} from "@welshman/router"
import {RelayLists} from "./relayLists.js"
import {RelayStats} from "./relayStats.js"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
// Re-export the upstream router surface (scenarios, fallback policies,
// makeSelection, getFilterSelections, types). The local `Router` below shadows
@@ -7,7 +7,7 @@ import {dec, inc, sortBy} from "@welshman/lib"
import {PROFILE} from "@welshman/util"
import type {PublishedProfile, RelayProfile} from "@welshman/util"
import {throttled} from "@welshman/store"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
import {Network} from "./network.js"
import {Router} from "./router.js"
import {Profiles} from "./profiles.js"
@@ -8,19 +8,19 @@ import {
removeFromList,
} from "@welshman/util"
import type {TrustedEvent} from "@welshman/util"
import {DerivedData} from "./clientData.js"
import {DerivedPlugin} from "./base.js"
import {Network} from "./network.js"
import {Router} from "./router.js"
import {User} from "./user.js"
import {User} from "../user.js"
import {Thunks} from "./thunk.js"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
/**
* NIP-51 search relay lists (kind 10007), keyed by pubkey. Loaded via the
* outbox model (the author's write relays), so it depends on the relay-list
* collection.
*/
export class SearchRelayLists extends DerivedData<ReturnType<typeof readList>> {
export class SearchRelayLists extends DerivedPlugin<ReturnType<typeof readList>> {
constructor(ctx: IClient) {
super(ctx, {
filters: [{kinds: [SEARCH_RELAYS]}],
@@ -18,7 +18,7 @@ import type {
ItemsByKeyOptions,
} from "@welshman/store"
import type {TrustedEvent} from "@welshman/util"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
/**
* Store/derivation utilities bound to the client's repository and tracker. Reach
@@ -1,6 +1,6 @@
import {isSignedEvent} from "@welshman/util"
import type {Filter, SignedEvent} from "@welshman/util"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
import {Network} from "./network.js"
import {Relays} from "./relays.js"
@@ -21,25 +21,17 @@ export class Sync {
query = (filters: Filter[]) =>
this.ctx.repository.query(filters, {shouldSort: filters.every(f => f.limit === undefined)})
hasNegentropy = (url: string) => {
const relay = this.ctx.use(Relays).get(url)
if (relay?.negentropy) return true
if (relay?.supported_nips?.includes?.("77")) return true
if (relay?.software?.includes?.("strfry") && !relay?.version?.match(/^0\./)) return true
return false
}
pull = async ({relays, filters}: AppSyncOpts) => {
const net = this.ctx.use(Network)
const events = this.query(filters).filter(isSignedEvent)
await Promise.all(
relays.map(async relay => {
await (this.hasNegentropy(relay)
? net.pull({filters, events, relays: [relay]})
: net.request({filters, relays: [relay], autoClose: true}))
if (await this.ctx.use(Relays).hasNegentropy(relay)) {
await net.pull({filters, events, relays: [relay]})
} else {
await net.request({filters, relays: [relay], autoClose: true})
}
}),
)
}
@@ -50,9 +42,11 @@ export class Sync {
await Promise.all(
relays.map(async relay => {
await (this.hasNegentropy(relay)
? net.push({filters, events, relays: [relay]})
: Promise.all(events.map((event: SignedEvent) => net.publish({event, relays: [relay]}))))
if (await this.ctx.use(Relays).hasNegentropy(relay)) {
await net.push({filters, events, relays: [relay]})
} else {
await Promise.all(events.map((event: SignedEvent) => net.publish({event, relays: [relay]})))
}
}),
)
}
@@ -10,7 +10,7 @@ import {
import type {TrustedEvent} from "@welshman/util"
import {Router} from "./router.js"
import {Profiles} from "./profiles.js"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
/**
* Builders for nostr tags (p/e/a/q/zap/reply/comment/reaction). Needs the router
@@ -13,10 +13,10 @@ import {
} from "@welshman/util"
import {PublishStatus, PublishResult, PublishOptions, PublishResultsByRelay} from "@welshman/net"
import {Nip01Signer, Nip59} from "@welshman/signer"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
import {Network} from "./network.js"
import {Router, addMinimalFallbacks} from "./router.js"
import {User} from "./user.js"
import {User} from "../user.js"
export type ThunkOptions = Override<
PublishOptions,
@@ -4,7 +4,7 @@ import {on} from "@welshman/lib"
import {getTopicTagValues} from "@welshman/util"
import type {RepositoryUpdate} from "@welshman/net"
import {deriveItems} from "@welshman/store"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
export type Topic = {
name: string
@@ -2,9 +2,9 @@ import {readable, derived} from "svelte/store"
import {max, throttle, addToMapKey, inc, dec} from "@welshman/lib"
import {getListTags, getPubkeyTagValues} from "@welshman/util"
import type {List} from "@welshman/util"
import type {IClient} from "./client.js"
import {projection} from "./clientData.js"
import type {Projection} from "./clientData.js"
import type {IClient} from "../client.js"
import {projection} from "./base.js"
import type {Projection} from "./base.js"
import {FollowLists} from "./follows.js"
import {MuteLists} from "./mutes.js"
@@ -5,9 +5,9 @@ import type {TrustedEvent, SignedEvent, EventTemplate} from "@welshman/util"
import {Nip59} from "@welshman/signer"
import {MergedThunk, Thunks} from "./thunk.js"
import type {ThunkOptions} from "./thunk.js"
import {User} from "./user.js"
import {User} from "../user.js"
import {MessagingRelayLists} from "./messagingRelayLists.js"
import type {IClient} from "./client.js"
import type {IClient} from "../client.js"
export type SendWrappedOptions = Omit<
ThunkOptions,
@@ -18,13 +18,13 @@ export type SendWrappedOptions = Omit<
}
/**
* Per-client gift-wrap (NIP-59) state: the unwrap queue plus failure/dedup
* Per-client wrap (NIP-59) state: the unwrap queue plus failure/dedup
* tracking. Scoped to `ctx.user`, so a client only ever unwraps its own user's
* messages into its own repository which is what keeps DM history from being
* merged across identities. The repository subscription that feeds it lives in
* `clientPolicyGiftWraps`.
* `clientPolicyWraps`.
*/
export class GiftWraps {
export class Wraps {
failedUnwraps = new Set<string>()
queue: TaskQueue<TrustedEvent>
@@ -12,9 +12,9 @@ import type {Maybe} from "@welshman/lib"
import {getTagValue, getZapSplits, zapFromEvent} from "@welshman/util"
import type {Zapper, Zap, TrustedEvent} from "@welshman/util"
import {deriveDeduplicated, deriveDeduplicatedByValue} from "@welshman/store"
import {LoadableData, projection} from "./clientData.js"
import type {Projection} from "./clientData.js"
import type {IClient} from "./client.js"
import {LoadableMapPlugin, projection} from "./base.js"
import type {Projection} from "./base.js"
import type {IClient} from "../client.js"
import {Profiles} from "./profiles.js"
/**
@@ -23,7 +23,7 @@ import {Profiles} from "./profiles.js"
* lnurl, or via a dufflepud proxy to protect user privacy). Depends on the
* profiles collection to resolve a pubkey's lnurl.
*/
export class Zappers extends LoadableData<Zapper> {
export class Zappers extends LoadableMapPlugin<Zapper> {
fetch = batcher(800, async (lnurls: string[]) => {
const result = new Map<string, Zapper>()
const valid = lnurls.filter(lnurl => lnurl.startsWith("lnurl1"))
@@ -5,9 +5,9 @@ import type {TrustedEvent} from "@welshman/util"
import {SocketEvent, isRelayEvent, makeSocketPolicyAuth} from "@welshman/net"
import type {RelayMessage, Socket} from "@welshman/net"
import type {IClient} from "./client.js"
import {RelayStats} from "./relayStats.js"
import {GiftWraps} from "./giftWraps.js"
import {BlockedRelayLists} from "./blockedRelayLists.js"
import {RelayStats} from "./plugins/relayStats.js"
import {Wraps} from "./plugins/wraps.js"
import {BlockedRelayLists} from "./plugins/blockedRelayLists.js"
import {LoggingSigner} from "./logging.js"
import type {LogMessage} from "./logging.js"
@@ -103,17 +103,17 @@ export const clientPolicyRelayStats: ClientPolicy = client => {
* Watches the client's repository for gift wraps (existing and incoming) and
* feeds them to the unwrap queue.
*/
export const clientPolicyGiftWraps: ClientPolicy = client => {
const giftWraps = client.use(GiftWraps)
export const clientPolicyWraps: ClientPolicy = client => {
const wraps = client.use(Wraps)
for (const wrap of client.repository.query([{kinds: [WRAP]}])) {
giftWraps.enqueue(wrap)
wraps.enqueue(wrap)
}
return on(client.repository, "update", ({added}: {added: TrustedEvent[]}) => {
for (const event of added) {
if (event.kind === WRAP) {
giftWraps.enqueue(event)
wraps.enqueue(event)
}
}
})
@@ -139,6 +139,6 @@ export const makeClientPolicyLogger =
export const defaultClientPolicies: ClientPolicy[] = [
clientPolicyIngest,
clientPolicyRelayStats,
clientPolicyGiftWraps,
clientPolicyWraps,
clientPolicyAuthUnlessBlocked,
]