Move plugins to a plugin directory
This commit is contained in:
@@ -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,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
|
||||
|
||||
@@ -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,
|
||||
+4
-4
@@ -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)
|
||||
}
|
||||
+4
-4
@@ -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]}],
|
||||
+2
-2
@@ -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"
|
||||
+4
-4
@@ -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,
|
||||
]
|
||||
Reference in New Issue
Block a user