rework context
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import {partition} from "@welshman/lib"
|
||||
import {defaultOptimizeSubscriptions, getDefaultNetContext as originalGetDefaultNetContext} from "@welshman/net"
|
||||
import type {Subscription, RelaysAndFilters} from "@welshman/net"
|
||||
import {unionFilters, isSignedEvent, hasValidSignature} from "@welshman/util"
|
||||
import type {TrustedEvent} from "@welshman/util"
|
||||
import {tracker, repository} from './core'
|
||||
import {makeRouter, getFilterSelections} from './router'
|
||||
import {onAuth, getSession} from './session'
|
||||
import type {Router} from './router'
|
||||
|
||||
export type AppContext = {
|
||||
router: Router
|
||||
requestDelay: number
|
||||
requestTimeout: number
|
||||
dufflepudUrl?: string
|
||||
}
|
||||
|
||||
export const getDefaultNetContext = () => ({
|
||||
...originalGetDefaultNetContext(),
|
||||
onAuth: onAuth,
|
||||
onEvent: (url: string, event: TrustedEvent) => tracker.track(event.id, url),
|
||||
isDeleted: (url: string, event: TrustedEvent) => repository.isDeleted(event),
|
||||
hasValidSignature: (event: TrustedEvent) =>
|
||||
getSession(event.pubkey) || (isSignedEvent(event) && hasValidSignature(event)),
|
||||
optimizeSubscriptions: (subs: Subscription[]) => {
|
||||
const [withRelays, withoutRelays] = partition(sub => sub.request.relays.length > 0, subs)
|
||||
const filters = unionFilters(withoutRelays.flatMap(sub => sub.request.filters))
|
||||
const selections: RelaysAndFilters[] = defaultOptimizeSubscriptions(withRelays)
|
||||
|
||||
if (filters.length > 0) {
|
||||
for (const selection of getFilterSelections(filters)) {
|
||||
selections.push(selection)
|
||||
}
|
||||
}
|
||||
|
||||
return selections
|
||||
},
|
||||
})
|
||||
|
||||
export const getDefaultAppContext = () => ({
|
||||
router: makeRouter(),
|
||||
requestDelay: 50,
|
||||
requestTimeout: 3000,
|
||||
})
|
||||
|
||||
@@ -1,21 +1,9 @@
|
||||
import {isNil} from "@welshman/lib"
|
||||
import {ctx, isNil} from "@welshman/lib"
|
||||
import {Repository, Relay, LOCAL_RELAY_URL, getFilterResultCardinality} from "@welshman/util"
|
||||
import type {TrustedEvent, Filter} from "@welshman/util"
|
||||
import {Tracker, subscribe as baseSubscribe} from "@welshman/net"
|
||||
import type {SubscribeRequestWithHandlers} from "@welshman/net"
|
||||
import {createEventStore} from "@welshman/store"
|
||||
import type {Router} from './router'
|
||||
|
||||
export const AppContext: {
|
||||
router: Router,
|
||||
requestDelay: number
|
||||
requestTimeout: number
|
||||
dufflepudUrl?: string
|
||||
} = {
|
||||
router: undefined as unknown as Router,
|
||||
requestDelay: 50,
|
||||
requestTimeout: 3000,
|
||||
}
|
||||
|
||||
export const repository = new Repository<TrustedEvent>()
|
||||
|
||||
@@ -52,8 +40,8 @@ export const subscribe = (request: PartialSubscribeRequest) => {
|
||||
}
|
||||
|
||||
// Make sure to query our local relay too
|
||||
const delay = AppContext.requestDelay
|
||||
const timeout = AppContext.requestTimeout
|
||||
const delay = ctx.app.requestDelay
|
||||
const timeout = ctx.app.requestTimeout
|
||||
const sub = baseSubscribe({delay, authTimeout: timeout, relays: [], ...request})
|
||||
|
||||
sub.emitter.on("event", (url: string, e: TrustedEvent) => {
|
||||
@@ -72,7 +60,7 @@ export const subscribe = (request: PartialSubscribeRequest) => {
|
||||
|
||||
export const load = (request: PartialSubscribeRequest) =>
|
||||
new Promise<TrustedEvent[]>(resolve => {
|
||||
const sub = subscribe({closeOnEose: true, timeout: AppContext.requestTimeout, ...request})
|
||||
const sub = subscribe({closeOnEose: true, timeout: ctx.app.requestTimeout, ...request})
|
||||
const events: TrustedEvent[] = []
|
||||
|
||||
sub.emitter.on("event", (url: string, e: TrustedEvent) => events.push(e))
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import {writable, derived} from 'svelte/store'
|
||||
import {withGetter} from '@welshman/store'
|
||||
import {type SubscribeRequest} from "@welshman/net"
|
||||
import {uniq, uniqBy, batcher, postJson, last} from '@welshman/lib'
|
||||
import {AppContext} from './core'
|
||||
import {ctx, uniq, uniqBy, batcher, postJson, last} from '@welshman/lib'
|
||||
import {collection} from './collection'
|
||||
import {deriveProfile} from './profiles'
|
||||
|
||||
@@ -16,10 +15,10 @@ export type Handle = {
|
||||
export const handles = withGetter(writable<Handle[]>([]))
|
||||
|
||||
export const fetchHandles = (handles: string[]) => {
|
||||
const base = AppContext.dufflepudUrl!
|
||||
const base = ctx.app.dufflepudUrl!
|
||||
|
||||
if (!base) {
|
||||
throw new Error("AppContext.dufflepudUrl is required to fetch nip05 info")
|
||||
throw new Error("ctx.app.dufflepudUrl is required to fetch nip05 info")
|
||||
}
|
||||
|
||||
const res: any = postJson(`${base}/handle/info`, {handles})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './context'
|
||||
export * from './core'
|
||||
export * from './collection'
|
||||
export * from './freshness'
|
||||
@@ -15,34 +16,3 @@ export * from './thunk'
|
||||
export * from './topics'
|
||||
export * from './util'
|
||||
export * from './zappers'
|
||||
|
||||
import {partition} from "@welshman/lib"
|
||||
import {type Subscription, NetworkContext, defaultOptimizeSubscriptions} from "@welshman/net"
|
||||
import {type TrustedEvent, unionFilters, isSignedEvent, hasValidSignature} from "@welshman/util"
|
||||
import {tracker, repository, AppContext} from './core'
|
||||
import {makeRouter, getFilterSelections} from './router'
|
||||
import {onAuth, getSession} from './session'
|
||||
|
||||
export function* optimizeSubscriptions(subs: Subscription[]) {
|
||||
const [withRelays, withoutRelays] = partition(sub => sub.request.relays.length > 0, subs)
|
||||
const filters = unionFilters(withoutRelays.flatMap(sub => sub.request.filters))
|
||||
|
||||
yield* defaultOptimizeSubscriptions(withRelays)
|
||||
|
||||
if (filters.length > 0) {
|
||||
yield* getFilterSelections(filters)
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(NetworkContext, {
|
||||
onAuth,
|
||||
onEvent: (url: string, event: TrustedEvent) => tracker.track(event.id, url),
|
||||
isDeleted: (url: string, event: TrustedEvent) => repository.isDeleted(event),
|
||||
hasValidSignature: (event: TrustedEvent) =>
|
||||
getSession(event.pubkey) || (isSignedEvent(event) && hasValidSignature(event)),
|
||||
optimizeSubscriptions,
|
||||
})
|
||||
|
||||
Object.assign(AppContext, {
|
||||
router: makeRouter(),
|
||||
})
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import {writable, derived} from 'svelte/store'
|
||||
import {withGetter} from '@welshman/store'
|
||||
import {groupBy, indexBy, batch, now, uniq, uniqBy, batcher, postJson} from '@welshman/lib'
|
||||
import {type RelayProfile} from "@welshman/util"
|
||||
import {ctx, groupBy, indexBy, batch, now, uniq, uniqBy, batcher, postJson} from '@welshman/lib'
|
||||
import type {RelayProfile} from "@welshman/util"
|
||||
import {AuthStatus, asMessage, type Connection, type SocketMessage} from '@welshman/net'
|
||||
import {AppContext} from './core'
|
||||
import {createSearch} from './util'
|
||||
import {collection} from './collection'
|
||||
|
||||
@@ -45,10 +44,10 @@ export const relaysByPubkey = derived(relays, $relays =>
|
||||
)
|
||||
|
||||
export const fetchRelayProfiles = (urls: string[]) => {
|
||||
const base = AppContext.dufflepudUrl!
|
||||
const base = ctx.app.dufflepudUrl!
|
||||
|
||||
if (!base) {
|
||||
throw new Error("AppContext.dufflepudUrl is required to fetch relay metadata")
|
||||
throw new Error("ctx.app.dufflepudUrl is required to fetch relay metadata")
|
||||
}
|
||||
|
||||
const res: any = postJson(`${base}/relay/info`, {urls})
|
||||
|
||||
+17
-22
@@ -1,14 +1,14 @@
|
||||
import {
|
||||
intersection, first, switcher, throttleWithValue, clamp, last, splitAt, identity, sortBy, uniq, shuffle,
|
||||
pushToMapKey, now, assoc,
|
||||
pushToMapKey, now, assoc, ctx,
|
||||
} from '@welshman/lib'
|
||||
import {
|
||||
Tags, getFilterId, unionFilters, isShareableRelayUrl, isCommunityAddress, isGroupAddress, isContextAddress,
|
||||
PROFILE, RELAYS, INBOX_RELAYS, FOLLOWS,
|
||||
} from '@welshman/util'
|
||||
import type {TrustedEvent, Filter} from '@welshman/util'
|
||||
import {NetworkContext, ConnectionStatus} from '@welshman/net'
|
||||
import {AppContext} from './core'
|
||||
import {ConnectionStatus} from '@welshman/net'
|
||||
import type {RelaysAndFilters} from '@welshman/net'
|
||||
import {pubkey} from './session'
|
||||
import {relaySelectionsByPubkey, getReadRelayUrls, getWriteRelayUrls, getRelayUrls} from './relaySelections'
|
||||
import {relays, relaysByUrl} from './relays'
|
||||
@@ -396,7 +396,7 @@ export const getRelayQuality = (url: string) => {
|
||||
const relay = relaysByUrl.get().get(url)
|
||||
const connect_count = relay?.stats?.connect_count || 0
|
||||
const recent_errors = relay?.stats?.recent_errors || []
|
||||
const connection = NetworkContext.pool.get(url, {autoConnect: false})
|
||||
const connection = ctx.net.pool.get(url, {autoConnect: false})
|
||||
|
||||
// If we haven't connected, consult our relay record and see if there has
|
||||
// been a recent fault. If there has been, penalize the relay. If there have been several,
|
||||
@@ -467,11 +467,6 @@ export const makeRouter = (options: Partial<RouterOptions> = {}) =>
|
||||
|
||||
// Infer relay selections from filters
|
||||
|
||||
export type RelayFilters = {
|
||||
relays: string[]
|
||||
filters: Filter[]
|
||||
}
|
||||
|
||||
export type FilterSelection = {
|
||||
id: string,
|
||||
filter: Filter,
|
||||
@@ -492,8 +487,8 @@ export const getFilterSelectionsForSearch = (state: FilterSelectionRuleState) =>
|
||||
if (!state.filter.search) return false
|
||||
|
||||
const id = getFilterId(state.filter)
|
||||
const relays = AppContext.router.options.getSearchRelays?.() || []
|
||||
const scenario = AppContext.router.product([id], relays)
|
||||
const relays = ctx.app.router.options.getSearchRelays?.() || []
|
||||
const scenario = ctx.app.router.product([id], relays)
|
||||
|
||||
state.selections.push(makeFilterSelection(id, state.filter, scenario))
|
||||
|
||||
@@ -505,12 +500,12 @@ export const getFilterSelectionsForContext = (state: FilterSelectionRuleState) =
|
||||
|
||||
if (contexts.length === 0) return false
|
||||
|
||||
const scenario = AppContext.router.WithinMultipleContexts(contexts)
|
||||
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 = AppContext.router.product([id], [relay])
|
||||
const scenario = ctx.app.router.product([id], [relay])
|
||||
|
||||
state.selections.push(makeFilterSelection(id, contextFilter, scenario))
|
||||
}
|
||||
@@ -524,8 +519,8 @@ export const getFilterSelectionsForIndexedKinds = (state: FilterSelectionRuleSta
|
||||
if (kinds.length === 0) return false
|
||||
|
||||
const id = getFilterId({...state.filter, kinds})
|
||||
const relays = AppContext.router.options.getIndexerRelays?.() || []
|
||||
const scenario = AppContext.router.product([id], relays)
|
||||
const relays = ctx.app.router.options.getIndexerRelays?.() || []
|
||||
const scenario = ctx.app.router.product([id], relays)
|
||||
|
||||
state.selections.push(makeFilterSelection(id, state.filter, scenario))
|
||||
|
||||
@@ -536,7 +531,7 @@ export const getFilterSelectionsForAuthors = (state: FilterSelectionRuleState) =
|
||||
if (!state.filter.authors) return false
|
||||
|
||||
const id = getFilterId(state.filter)
|
||||
const scenario = AppContext.router.FromPubkeys(state.filter.authors!).update(assoc('value', id))
|
||||
const scenario = ctx.app.router.FromPubkeys(state.filter.authors!).update(assoc('value', id))
|
||||
|
||||
state.selections.push(makeFilterSelection(id, state.filter, scenario))
|
||||
|
||||
@@ -545,8 +540,8 @@ export const getFilterSelectionsForAuthors = (state: FilterSelectionRuleState) =
|
||||
|
||||
export const getFilterSelectionsForUser = (state: FilterSelectionRuleState) => {
|
||||
const id = getFilterId(state.filter)
|
||||
const relays = AppContext.router.ReadRelays().getUrls()
|
||||
const scenario = AppContext.router.product([id], relays)
|
||||
const relays = ctx.app.router.ReadRelays().getUrls()
|
||||
const scenario = ctx.app.router.product([id], relays)
|
||||
|
||||
state.selections.push(makeFilterSelection(id, state.filter, scenario))
|
||||
|
||||
@@ -561,7 +556,7 @@ export const defaultFilterSelectionRules = [
|
||||
getFilterSelectionsForUser,
|
||||
]
|
||||
|
||||
export const getFilterSelections = (filters: Filter[], rules: FilterSelectionRule[] = defaultFilterSelectionRules): RelayFilters[] => {
|
||||
export const getFilterSelections = (filters: Filter[], rules: FilterSelectionRule[] = defaultFilterSelectionRules): RelaysAndFilters[] => {
|
||||
const scenarios: RouterScenario[] = []
|
||||
const filtersById = new Map<string, Filter>()
|
||||
|
||||
@@ -584,7 +579,7 @@ export const getFilterSelections = (filters: Filter[], rules: FilterSelectionRul
|
||||
|
||||
|
||||
// Use low redundancy because filters will be very low cardinality
|
||||
const selections = AppContext.router
|
||||
const selections = ctx.app.router
|
||||
.merge(scenarios)
|
||||
.redundancy(1)
|
||||
.getSelections()
|
||||
@@ -594,8 +589,8 @@ export const getFilterSelections = (filters: Filter[], rules: FilterSelectionRul
|
||||
}))
|
||||
|
||||
// Pubkey-based selections can get really big. Use the most popular relays for the long tail
|
||||
const limit = AppContext.router.options.getLimit?.() || 8
|
||||
const redundancy = AppContext.router.options.getRedundancy?.() || 3
|
||||
const limit = ctx.app.router.options.getLimit?.() || 8
|
||||
const redundancy = ctx.app.router.options.getRedundancy?.() || 3
|
||||
const [keep, discard] = splitAt(limit, selections)
|
||||
|
||||
for (const target of keep.slice(0, redundancy)) {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import {derived} from "svelte/store"
|
||||
import {memoize, omit, equals, assoc} from "@welshman/lib"
|
||||
import {ctx, memoize, omit, equals, assoc} from "@welshman/lib"
|
||||
import {createEvent} from "@welshman/util"
|
||||
import {withGetter, synced} from "@welshman/store"
|
||||
import {type Nip46Handler} from "@welshman/signer"
|
||||
import {NetworkContext} from "@welshman/net"
|
||||
import {Nip46Broker, Nip46Signer, Nip07Signer, Nip01Signer} from "@welshman/signer"
|
||||
|
||||
export type Session = {
|
||||
@@ -76,7 +75,7 @@ export const onAuth = async (url: string, challenge: string) => {
|
||||
}),
|
||||
)
|
||||
|
||||
NetworkContext.pool.get(url).send(["AUTH", event])
|
||||
ctx.net.pool.get(url).send(["AUTH", event])
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
import type {NetContext} from '@welshman/net'
|
||||
import type {AppContext} from './context'
|
||||
|
||||
declare module "@welshman/lib" {
|
||||
interface Context {
|
||||
net: NetContext
|
||||
app: AppContext
|
||||
}
|
||||
}
|
||||
@@ -2,18 +2,17 @@ import {writable, derived} from 'svelte/store'
|
||||
import {withGetter} from '@welshman/store'
|
||||
import {type Zapper} from '@welshman/util'
|
||||
import {type SubscribeRequest} from "@welshman/net"
|
||||
import {uniq, identity, bech32ToHex, tryCatch, uniqBy, batcher, postJson} from '@welshman/lib'
|
||||
import {AppContext} from './core'
|
||||
import {ctx, uniq, identity, bech32ToHex, tryCatch, uniqBy, batcher, postJson} from '@welshman/lib'
|
||||
import {collection} from './collection'
|
||||
import {deriveProfile} from './profiles'
|
||||
|
||||
export const zappers = withGetter(writable<Zapper[]>([]))
|
||||
|
||||
export const fetchZappers = (lnurls: string[]) => {
|
||||
const base = AppContext.dufflepudUrl!
|
||||
const base = ctx.app.dufflepudUrl!
|
||||
|
||||
if (!base) {
|
||||
throw new Error("AppContext.dufflepudUrl is required to fetch zapper info")
|
||||
throw new Error("ctx.app.dufflepudUrl is required to fetch zapper info")
|
||||
}
|
||||
|
||||
const zappersByLnurl = new Map<string, Zapper>()
|
||||
|
||||
Reference in New Issue
Block a user