diff --git a/packages/app/src/commands.ts b/packages/app/src/commands.ts index 7f93243..c358c12 100644 --- a/packages/app/src/commands.ts +++ b/packages/app/src/commands.ts @@ -9,27 +9,27 @@ export const unfollow = async (value: string) => { const list = get(userFollows) || makeList({kind: FOLLOWS}) const event = await removeFromList(list, value).reconcile(nip44EncryptToSelf) - return publishThunk({event, relays: ctx.app.router.WriteRelays().getUrls()}) + return publishThunk({event, relays: ctx.app.router.FromUser().getUrls()}) } export const follow = async (tag: string[]) => { const list = get(userFollows) || makeList({kind: FOLLOWS}) const event = await addToListPublicly(list, tag).reconcile(nip44EncryptToSelf) - return publishThunk({event, relays: ctx.app.router.WriteRelays().getUrls()}) + return publishThunk({event, relays: ctx.app.router.FromUser().getUrls()}) } export const unmute = async (value: string) => { const list = get(userMutes) || makeList({kind: MUTES}) const event = await removeFromList(list, value).reconcile(nip44EncryptToSelf) - return publishThunk({event, relays: ctx.app.router.WriteRelays().getUrls()}) + return publishThunk({event, relays: ctx.app.router.FromUser().getUrls()}) } export const mute = async (tag: string[]) => { const list = get(userMutes) || makeList({kind: MUTES}) const event = await addToListPublicly(list, tag).reconcile(nip44EncryptToSelf) - return publishThunk({event, relays: ctx.app.router.WriteRelays().getUrls()}) + return publishThunk({event, relays: ctx.app.router.FromUser().getUrls()}) } diff --git a/packages/app/src/feeds.ts b/packages/app/src/feeds.ts index e12b26d..7a0acae 100644 --- a/packages/app/src/feeds.ts +++ b/packages/app/src/feeds.ts @@ -25,7 +25,7 @@ export const feedLoader = new FeedLoader({ const event = await $signer.sign(createEvent(kind, {tags})) const relays = request.relays - ? ctx.app.router.fromRelays(request.relays).getUrls() + ? ctx.app.router.FromRelays(request.relays).getUrls() : ctx.app.router.FromPubkeys(getPubkeyTagValues(tags)).getUrls() const req = makeDvmRequest({event, relays}) diff --git a/packages/app/src/router.ts b/packages/app/src/router.ts index 6b4eda4..07ff6e6 100644 --- a/packages/app/src/router.ts +++ b/packages/app/src/router.ts @@ -114,6 +114,7 @@ export type RelayValues = { export type ValueRelays = { value: string relays: string[] + weight: number } // Fallback policies @@ -131,24 +132,22 @@ export class Router { // Utilities derived from options - getPubkeySelection = (pubkey: string, mode?: RelayMode) => - this.selection(pubkey, this.options.getPubkeyRelays?.(pubkey, mode) || []) + getRelaysForPubkey = (pubkey: string, mode?: RelayMode) => + this.options.getPubkeyRelays?.(pubkey, mode) || [] - getPubkeySelections = (pubkeys: string[], mode?: RelayMode) => - pubkeys.map(pubkey => this.getPubkeySelection(pubkey, mode)) + getRelaysForPubkeys = (pubkeys: string[], mode?: RelayMode) => + pubkeys.map(pubkey => this.getRelaysForPubkey(pubkey, mode)) - getUserSelections = (mode?: RelayMode) => - this.getPubkeySelections([this.options.getUserPubkey?.()].filter(identity) as string[], mode) + getRelaysForUser = (mode?: RelayMode) => { + const pubkey = this.options.getUserPubkey?.() + + return pubkey ? this.getRelaysForPubkey(pubkey) : [] + } // Utilities for creating ValueRelays - selection = (value: string, relays: Iterable) => ({value, relays: Array.from(relays)}) - - selections = (values: string[], relays: string[]) => - values.map(value => this.selection(value, relays)) - - forceValue = (value: string, selections: ValueRelays[]) => - selections.map(({relays}) => this.selection(value, relays)) + selection = (value: string, relays: Iterable, weight = 1): ValueRelays => + ({value, relays: Array.from(relays), weight}) // Utilities for processing hints @@ -161,8 +160,8 @@ export class Router { })) ) - scoreRelaySelection = ({values, relay}: RelayValues) => - values.length * (this.options.getRelayQuality?.(relay) || 1) + scoreRelaySelection = ({values, relay, weight}: RelayValues) => + values.length * (this.options.getRelayQuality?.(relay) || 1) * weight sortRelaySelections = (relaySelections: RelayValues[]) => { const scores = new Map() @@ -182,53 +181,63 @@ export class Router { merge = (scenarios: RouterScenario[]) => this.scenario(scenarios.flatMap((scenario: RouterScenario) => scenario.selections)) - product = (values: string[], relays: string[]) => this.scenario(this.selections(values, relays)) - - fromRelays = (relays: string[]) => this.scenario([this.selection("", relays)]) - // Routing scenarios - User = () => this.scenario(this.getUserSelections()) - ReadRelays = () => this.scenario(this.getUserSelections(RelayMode.Read)) + FromRelays = (relays: string[], id = "") => + this.scenario([this.selection(id, relays)]) - WriteRelays = () => this.scenario(this.getUserSelections(RelayMode.Write)) + ForPubkey = (pubkey: string) => + this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Read)) - InboxRelays = () => this.scenario(this.getUserSelections(RelayMode.Inbox)).policy(addNoFallbacks) + FromPubkey = (pubkey: string) => + this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Write)) - Messages = (pubkeys: string[]) => - this.scenario([ - ...this.getUserSelections(RelayMode.Inbox), - ...this.getPubkeySelections(pubkeys, RelayMode.Inbox), - ]) + PubkeyInbox = (pubkey: string) => + this.FromRelays(this.getRelaysForPubkey(pubkey, RelayMode.Inbox)).policy(addNoFallbacks) - PublishMessage = (pubkey: string) => - this.scenario([ - ...this.getUserSelections(RelayMode.Inbox), - this.getPubkeySelection(pubkey, RelayMode.Inbox), - ]).policy(addMinimalFallbacks) + ForUser = () => + this.FromRelays(this.getRelaysForUser(RelayMode.Read)) + + FromUser = () => + this.FromRelays(this.getRelaysForUser(RelayMode.Write)) + + UserInbox = () => + this.FromRelays(this.getRelaysForUser(RelayMode.Inbox)).policy(addNoFallbacks) + + ForPubkeys = (pubkeys: string[]) => + this.merge(pubkeys.map(pubkey => this.ForPubkey(pubkey))) + + FromPubkeys = (pubkeys: string[]) => + this.merge(pubkeys.map(pubkey => this.FromPubkey(pubkey))) + + PubkeyInboxes = (pubkeys: string[]) => + this.merge(pubkeys.map(pubkey => this.PubkeyInbox(pubkey))) Event = (event: TrustedEvent) => - this.scenario( - this.forceValue(event.id, [this.getPubkeySelection(event.pubkey, RelayMode.Write)]) - ) + this.FromRelays(this.getRelaysForPubkey(event.pubkey, RelayMode.Write), event.id) EventChildren = (event: TrustedEvent) => - this.scenario( - this.forceValue(event.id, [this.getPubkeySelection(event.pubkey, RelayMode.Read)]) - ) + this.FromRelays(this.getRelaysForPubkey(event.pubkey, RelayMode.Read), event.id) EventAncestors = (event: TrustedEvent, type: "mentions" | "replies" | "roots") => { - const tags = Tags.fromEvent(event) - const ancestors = tags.ancestors()[type] - const pubkeys = tags.values("p").valueOf() - const relays = uniq([ - ...(this.options.getPubkeyRelays?.(event.pubkey, RelayMode.Read) || []), - ...pubkeys.flatMap((k: string) => this.options.getPubkeyRelays?.(k, RelayMode.Write) || []), - ...ancestors.relays().valueOf(), - ]) + return this.scenario( + getAncestorTags(event.tags)[type].flatMap( + ([_, value, relay, pubkey]) => { + const tagScenarios = [this.selection(value, this.ForUser().getUrls(), 0.5)] - return this.product(ancestors.values().valueOf(), relays) + if (pubkey) { + tagScenarios.push(this.selection(value, this.FromPubkey(pubkey).getUrls())) + } + + if (relay) { + tagScenarios.push(this.selection(value, [relay], 0.9)) + } + + return tagScenarios + } + ) + ) } EventMentions = (event: TrustedEvent) => this.EventAncestors(event, "mentions") @@ -238,25 +247,13 @@ export class Router { EventRoots = (event: TrustedEvent) => this.EventAncestors(event, "roots") PublishEvent = (event: TrustedEvent) => { - const tags = Tags.fromEvent(event) - const mentions = tags.values("p").valueOf() + const pubkeys = getPubkeyTagValues(event.tags) - return this.scenario( - this.forceValue(event.id, [ - this.getPubkeySelection(event.pubkey, RelayMode.Write), - ...this.getPubkeySelections(mentions, RelayMode.Read), - ]) - ) + return this.scenario([ + this.selection(event.id, this.FromPubkey(event.pubkey).getUrls()), + ...pubkeys.map(pubkey => this.selection(event.id, this.ForPubkey(event.pubkey).getUrls(), 0.5)), + ]) } - - FromPubkeys = (pubkeys: string[]) => - this.scenario(this.getPubkeySelections(pubkeys, RelayMode.Write)) - - ForPubkeys = (pubkeys: string[]) => - this.scenario(this.getPubkeySelections(pubkeys, RelayMode.Read)) - - Search = (term: string, relays: string[] = []) => - this.product([term], uniq(relays.concat(this.options.getSearchRelays?.() || []))) } // Router Scenario @@ -490,7 +487,7 @@ export const makeFilterSelection = (id: string, filter: Filter, scenario: Router export const getFilterSelectionsForLocalRelay = (state: FilterSelectionRuleState) => { const id = getFilterId(state.filter) - const scenario = ctx.app.router.product([id], [LOCAL_RELAY_URL]) + const scenario = ctx.app.router.FromRelays([LOCAL_RELAY_URL], id) state.selections.push(makeFilterSelection(id, state.filter, scenario)) @@ -502,36 +499,18 @@ export const getFilterSelectionsForSearch = (state: FilterSelectionRuleState) => const id = getFilterId(state.filter) const relays = ctx.app.router.options.getSearchRelays?.() || [] - const scenario = ctx.app.router.product([id], relays) + const scenario = ctx.app.router.FromRelays(relays, id) state.selections.push(makeFilterSelection(id, state.filter, scenario)) return true } -export const getFilterSelectionsForContext = (state: FilterSelectionRuleState) => { - const contexts = state.filter["#a"]?.filter(isContextAddress) || [] - - if (contexts.length === 0) return false - - const scenario = ctx.app.router.WithinMultipleContexts(contexts) - - for (const {relay, values} of scenario.getSelections()) { - const contextFilter = {...state.filter, "#a": Array.from(values)} - const id = getFilterId(contextFilter) - const scenario = ctx.app.router.product([id], [relay]) - - state.selections.push(makeFilterSelection(id, contextFilter, scenario)) - } - - return true -} - export const getFilterSelectionsForWraps = (state: FilterSelectionRuleState) => { if (!state.filter.kinds?.includes(WRAP) || state.filter.authors) return false const id = getFilterId({...state.filter, kinds: [WRAP]}) - const scenario = ctx.app.router.InboxRelays().update(assoc("value", id)) + const scenario = ctx.app.router.UserInbox().update(assoc('value', id)) state.selections.push(makeFilterSelection(id, state.filter, scenario)) @@ -545,7 +524,7 @@ export const getFilterSelectionsForIndexedKinds = (state: FilterSelectionRuleSta const id = getFilterId({...state.filter, kinds}) const relays = ctx.app.router.options.getIndexerRelays?.() || [] - const scenario = ctx.app.router.product([id], relays) + const scenario = ctx.app.router.FromRelays(relays, id) state.selections.push(makeFilterSelection(id, state.filter, scenario)) @@ -567,8 +546,8 @@ export const getFilterSelectionsForAuthors = (state: FilterSelectionRuleState) = export const getFilterSelectionsForUser = (state: FilterSelectionRuleState) => { const id = getFilterId(state.filter) - const relays = ctx.app.router.ReadRelays().getUrls() - const scenario = ctx.app.router.product([id], relays) + const relays = ctx.app.router.ForUser().getUrls() + const scenario = ctx.app.router.FromRelays(relays, id) state.selections.push(makeFilterSelection(id, state.filter, scenario)) @@ -578,7 +557,6 @@ export const getFilterSelectionsForUser = (state: FilterSelectionRuleState) => { export const defaultFilterSelectionRules = [ getFilterSelectionsForLocalRelay, getFilterSelectionsForSearch, - getFilterSelectionsForContext, getFilterSelectionsForWraps, getFilterSelectionsForIndexedKinds, getFilterSelectionsForAuthors, diff --git a/packages/app/src/tags.ts b/packages/app/src/tags.ts index f4125c2..f6cf102 100644 --- a/packages/app/src/tags.ts +++ b/packages/app/src/tags.ts @@ -7,14 +7,14 @@ import {pubkey} from './session' export const tagZapSplit = (pubkey: string, split = 1) => [ "zap", pubkey, - ctx.app.router.FromPubkeys([pubkey]).getUrl(), + ctx.app.router.FromPubkey(pubkey).getUrl(), String(split), ] export const tagPubkey = (pubkey: string, ...args: unknown[]) => [ "p", pubkey, - ctx.app.router.FromPubkeys([pubkey]).getUrl(), + ctx.app.router.FromPubkey(pubkey).getUrl(), displayProfileByPubkey(pubkey), ] diff --git a/packages/app/tsc-multi.json b/packages/app/tsc-multi.json index 6c37019..dd7b078 100644 --- a/packages/app/tsc-multi.json +++ b/packages/app/tsc-multi.json @@ -1,6 +1,5 @@ { "targets": [ - {"extname": ".cjs", "module": "commonjs"}, {"extname": ".mjs", "module": "esnext", "moduleResolution": "node"} ], "projects": ["tsconfig.json"]