From 44f29c9da49b3a42ad4cf3e2e15307fa7eb6f1ad Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Thu, 5 Sep 2024 13:38:48 -0700 Subject: [PATCH] rework context --- .eslintrc.json | 1 + packages/app/src/context.ts | 45 +++++++++++++++++++++++ packages/app/src/core.ts | 20 +++-------- packages/app/src/handles.ts | 7 ++-- packages/app/src/index.ts | 32 +---------------- packages/app/src/relays.ts | 9 +++-- packages/app/src/router.ts | 39 +++++++++----------- packages/app/src/session.ts | 5 ++- packages/app/src/types.d.ts | 9 +++++ packages/app/src/zappers.ts | 7 ++-- packages/lib/src/Context.ts | 5 +++ packages/lib/src/Tools.ts | 2 ++ packages/lib/src/index.ts | 1 + packages/lib/src/types.d.ts | 3 ++ packages/net/src/Context.ts | 67 ++++++++++++++++------------------- packages/net/src/Executor.ts | 10 +++--- packages/net/src/Publish.ts | 5 ++- packages/net/src/Subscribe.ts | 20 ++++++----- packages/net/src/types.d.ts | 8 +++++ 19 files changed, 157 insertions(+), 138 deletions(-) create mode 100644 packages/app/src/context.ts create mode 100644 packages/app/src/types.d.ts create mode 100644 packages/lib/src/Context.ts create mode 100644 packages/lib/src/types.d.ts create mode 100644 packages/net/src/types.d.ts diff --git a/.eslintrc.json b/.eslintrc.json index d0c24a4..d52f86c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -20,6 +20,7 @@ "@typescript-eslint/no-non-null-assertion": "off", "@typescript-eslint/no-unused-vars": ["error", {"args": "none", "destructuredArrayIgnorePattern": "^_"}], "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-empty-interface": "off", "@typescript-eslint/ban-ts-comment": "off", "no-useless-escape": "off", "prefer-const": ["error", {"destructuring": "all"}], diff --git a/packages/app/src/context.ts b/packages/app/src/context.ts new file mode 100644 index 0000000..b2aa8c4 --- /dev/null +++ b/packages/app/src/context.ts @@ -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, +}) + diff --git a/packages/app/src/core.ts b/packages/app/src/core.ts index 0a5cb4d..603bd78 100644 --- a/packages/app/src/core.ts +++ b/packages/app/src/core.ts @@ -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() @@ -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(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)) diff --git a/packages/app/src/handles.ts b/packages/app/src/handles.ts index 7ad869b..2353a41 100644 --- a/packages/app/src/handles.ts +++ b/packages/app/src/handles.ts @@ -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([])) 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}) diff --git a/packages/app/src/index.ts b/packages/app/src/index.ts index 148c694..d916a5b 100644 --- a/packages/app/src/index.ts +++ b/packages/app/src/index.ts @@ -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(), -}) diff --git a/packages/app/src/relays.ts b/packages/app/src/relays.ts index c6ffdd3..c4d8616 100644 --- a/packages/app/src/relays.ts +++ b/packages/app/src/relays.ts @@ -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}) diff --git a/packages/app/src/router.ts b/packages/app/src/router.ts index b84e846..3215974 100644 --- a/packages/app/src/router.ts +++ b/packages/app/src/router.ts @@ -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 = {}) => // 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() @@ -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)) { diff --git a/packages/app/src/session.ts b/packages/app/src/session.ts index 0c7a9fc..e7bef4a 100644 --- a/packages/app/src/session.ts +++ b/packages/app/src/session.ts @@ -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 } diff --git a/packages/app/src/types.d.ts b/packages/app/src/types.d.ts new file mode 100644 index 0000000..5006653 --- /dev/null +++ b/packages/app/src/types.d.ts @@ -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 + } +} diff --git a/packages/app/src/zappers.ts b/packages/app/src/zappers.ts index 130dff4..f89350c 100644 --- a/packages/app/src/zappers.ts +++ b/packages/app/src/zappers.ts @@ -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([])) 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() diff --git a/packages/lib/src/Context.ts b/packages/lib/src/Context.ts new file mode 100644 index 0000000..84dc4a9 --- /dev/null +++ b/packages/lib/src/Context.ts @@ -0,0 +1,5 @@ +import type {Context} from '@welshman/lib' + +export const ctx: Context = {} + +export const setContext = (newCtx: Context) => Object.assign(ctx, newCtx) diff --git a/packages/lib/src/Tools.ts b/packages/lib/src/Tools.ts index acb21d4..460d697 100644 --- a/packages/lib/src/Tools.ts +++ b/packages/lib/src/Tools.ts @@ -13,6 +13,8 @@ export type Maybe = T | undefined export const now = () => Math.round(Date.now() / 1000) +export const noop = (...args: unknown[]) => undefined + export const first = (xs: T[], ...args: unknown[]) => xs[0] export const last = (xs: T[], ...args: unknown[]) => xs[xs.length - 1] diff --git a/packages/lib/src/index.ts b/packages/lib/src/index.ts index 86942bf..4a55cec 100644 --- a/packages/lib/src/index.ts +++ b/packages/lib/src/index.ts @@ -1,3 +1,4 @@ +export * from './Context' export * from './Deferred' export * from './Emitter' export * from './Fluent' diff --git a/packages/lib/src/types.d.ts b/packages/lib/src/types.d.ts new file mode 100644 index 0000000..0f66428 --- /dev/null +++ b/packages/lib/src/types.d.ts @@ -0,0 +1,3 @@ +declare module "@welshman/lib" { + export interface Context {} +} diff --git a/packages/net/src/Context.ts b/packages/net/src/Context.ts index eb0a860..b940a84 100644 --- a/packages/net/src/Context.ts +++ b/packages/net/src/Context.ts @@ -1,45 +1,40 @@ -import {uniq} from '@welshman/lib' +import {ctx, uniq, noop, always} from '@welshman/lib' import {matchFilters, unionFilters, isSignedEvent, hasValidSignature} from '@welshman/util' import type {Filter, TrustedEvent} from '@welshman/util' import {Pool} from "./Pool" import {Executor} from "./Executor" import {Relays} from "./target/Relays" -import type {Subscription} from "./Subscribe" +import type {Subscription, RelaysAndFilters} from "./Subscribe" -export const defaultPool = new Pool() - -export const defaultGetExecutor = (relays: string[]) => - new Executor(new Relays(relays.map((relay: string) => NetworkContext.pool.get(relay)))) - -const defaultOnEvent = (url: string, event: TrustedEvent) => null - -const defaultOnAuth = (url: string, challenge: string) => null - -const defaultOnOk = (url: string, id: string, ok: boolean, message: string) => null - -const defaultIsDeleted = (url: string, event: TrustedEvent) => false - -const defaultHasValidSignature = (url: string, event: TrustedEvent) => isSignedEvent(event) && hasValidSignature(event) - -const defaultMatchFilters = (url: string, filters: Filter[], event: TrustedEvent) => matchFilters(filters, event) - -export function* defaultOptimizeSubscriptions(subs: Subscription[]) { - for (const relay of uniq(subs.flatMap(sub => sub.request.relays || []))) { - const relaySubs = subs.filter(sub => sub.request.relays.includes(relay)) - const filters = unionFilters(relaySubs.flatMap(sub => sub.request.filters)) - - yield {relays: [relay], filters} - } +export type NetContext = { + pool: Pool + getExecutor: (relays: string[]) => Executor + onEvent: (url: string, event: TrustedEvent) => void + onAuth: (url: string, challenge: string) => void + onOk: (url: string, id: string, ok: boolean, message: string) => void + isDeleted: (url: string, event: TrustedEvent) => boolean + hasValidSignature: (url: string, event: TrustedEvent) => boolean + matchFilters: (url: string, filters: Filter[], event: TrustedEvent) => boolean + optimizeSubscriptions: (subs: Subscription[]) => RelaysAndFilters[] } -export const NetworkContext = { - pool: defaultPool, - getExecutor: defaultGetExecutor, - onEvent: defaultOnEvent, - onAuth: defaultOnAuth, - onOk: defaultOnOk, - isDeleted: defaultIsDeleted, - hasValidSignature: defaultHasValidSignature, - matchFilters: defaultMatchFilters, +export const defaultOptimizeSubscriptions = (subs: Subscription[]) => + uniq(subs.flatMap(sub => sub.request.relays || [])) + .map(relay => { + const relaySubs = subs.filter(sub => sub.request.relays.includes(relay)) + const filters = unionFilters(relaySubs.flatMap(sub => sub.request.filters)) + + return {relays: [relay], filters} + }) + +export const getDefaultNetContext = () => ({ + onOk: noop, + onAuth: noop, + onEvent: noop, + pool: new Pool(), + isDeleted: always(false), + getExecutor: (relays: string[]) => new Executor(new Relays(relays.map((relay: string) => ctx.net.pool.get(relay)))), + hasValidSignature: (url: string, event: TrustedEvent) => isSignedEvent(event) && hasValidSignature(event), + matchFilters: (url: string, filters: Filter[], event: TrustedEvent) => matchFilters(filters, event), optimizeSubscriptions: defaultOptimizeSubscriptions, -} +}) diff --git a/packages/net/src/Executor.ts b/packages/net/src/Executor.ts index df36d77..ae08936 100644 --- a/packages/net/src/Executor.ts +++ b/packages/net/src/Executor.ts @@ -1,8 +1,8 @@ +import {ctx} from '@welshman/lib' import type {Emitter} from '@welshman/lib' import type {SignedEvent, Filter} from '@welshman/util' import type {Message} from './Socket' import type {Connection} from './Connection' -import {NetworkContext} from './Context' export type Target = Emitter & { connections: Connection[] @@ -22,8 +22,8 @@ const createSubId = (prefix: string) => [prefix, Math.random().toString().slice( export class Executor { constructor(readonly target: Target) { - target.on('AUTH', NetworkContext.onAuth) - target.on('OK', NetworkContext.onOk) + target.on('AUTH', ctx.net.onAuth) + target.on('OK', ctx.net.onOk) } subscribe(filters: Filter[], {onEvent, onEose}: SubscribeOpts = {}) { @@ -33,7 +33,7 @@ export class Executor { const eventListener = (url: string, subid: string, e: SignedEvent) => { if (subid === id) { - NetworkContext.onEvent(url, e) + ctx.net.onEvent(url, e) onEvent?.(url, e) } } @@ -64,7 +64,7 @@ export class Executor { publish(event: SignedEvent, {verb = 'EVENT', onOk, onError}: PublishOpts = {}) { const okListener = (url: string, id: string, ...payload: any[]) => { if (id === event.id) { - NetworkContext.onEvent(url, event) + ctx.net.onEvent(url, event) onOk?.(url, id, ...payload) } } diff --git a/packages/net/src/Publish.ts b/packages/net/src/Publish.ts index 2a65106..c5a1c65 100644 --- a/packages/net/src/Publish.ts +++ b/packages/net/src/Publish.ts @@ -1,8 +1,7 @@ -import {Emitter, now, randomId, defer} from '@welshman/lib' +import {ctx, Emitter, now, randomId, defer} from '@welshman/lib' import type {Deferred} from '@welshman/lib' import {asSignedEvent} from '@welshman/util' import type {SignedEvent} from '@welshman/util' -import {NetworkContext} from './Context' export enum PublishStatus { Pending = "pending", @@ -44,7 +43,7 @@ export const makePublish = (request: PublishRequest) => { export const publish = (request: PublishRequest) => { const pub = makePublish(request) const event = asSignedEvent(request.event) - const executor = NetworkContext.getExecutor(request.relays) + const executor = ctx.net.getExecutor(request.relays) const abort = (reason: PublishStatus) => { for (const [url, status] of pub.status.entries()) { diff --git a/packages/net/src/Subscribe.ts b/packages/net/src/Subscribe.ts index ae4b378..3affd7a 100644 --- a/packages/net/src/Subscribe.ts +++ b/packages/net/src/Subscribe.ts @@ -1,16 +1,15 @@ -import {Emitter, max, chunk, randomId, once, groupBy, uniq} from '@welshman/lib' +import {ctx, Emitter, max, chunk, randomId, once, groupBy, uniq} from '@welshman/lib' import {matchFilters, unionFilters, TrustedEvent} from '@welshman/util' import type {Filter} from '@welshman/util' import {Tracker} from "./Tracker" import {Connection} from './Connection' -import {NetworkContext} from './Context' // `subscribe` is a super function that handles batching subscriptions by merging // them based on parameters (filters and subscribe opts), then splits them by relay. // This results in fewer REQs being opened per connection, fewer duplicate events // being downloaded, and therefore less signature validation. // -// Behavior can be further configured using NetworkContext. This can be useful for +// Behavior can be further configured using ctx.net. This can be useful for // adding support for querying a local cache like a relay, tracking deleted events, // and bypassing validation for trusted relays. // @@ -30,9 +29,12 @@ export enum SubscriptionEvent { InvalidSignature = "invalid-signature", } -export type SubscribeRequest = { +export type RelaysAndFilters = { relays: string[] filters: Filter[] +} + +export type SubscribeRequest = RelaysAndFilters & { delay?: number signal?: AbortSignal timeout?: number @@ -136,7 +138,7 @@ export const optimizeSubscriptions = (subs: Subscription[]) => { const eosedSubs = new Set() const mergedSubs = [] - for (const {relays, filters} of NetworkContext.optimizeSubscriptions(group)) { + for (const {relays, filters} of ctx.net.optimizeSubscriptions(group)) { const mergedSub = makeSubscription({filters, relays, timeout, @@ -212,7 +214,7 @@ export const optimizeSubscriptions = (subs: Subscription[]) => { export const executeSubscription = (sub: Subscription) => { const {request, emitter, tracker, controller} = sub const {filters, closeOnEose, relays, signal, timeout, authTimeout = 0} = request - const executor = NetworkContext.getExecutor(relays) + const executor = ctx.net.getExecutor(relays) const subs: {unsubscribe: () => void}[] = [] const completedRelays = new Set() const events: TrustedEvent[] = [] @@ -251,11 +253,11 @@ export const executeSubscription = (sub: Subscription) => { const onEvent = (url: string, event: TrustedEvent) => { if (tracker.track(event.id, url)) { emitter.emit(SubscriptionEvent.Duplicate, url, event) - } else if (NetworkContext.isDeleted(url, event)) { + } else if (ctx.net.isDeleted(url, event)) { emitter.emit(SubscriptionEvent.DeletedEvent, url, event) - } else if (!NetworkContext.matchFilters(url, filters, event)) { + } else if (!ctx.net.matchFilters(url, filters, event)) { emitter.emit(SubscriptionEvent.FailedFilter, url, event) - } else if (!NetworkContext.hasValidSignature(url, event)) { + } else if (!ctx.net.hasValidSignature(url, event)) { emitter.emit(SubscriptionEvent.InvalidSignature, url, event) } else { emitter.emit(SubscriptionEvent.Event, url, event) diff --git a/packages/net/src/types.d.ts b/packages/net/src/types.d.ts new file mode 100644 index 0000000..dc933cc --- /dev/null +++ b/packages/net/src/types.d.ts @@ -0,0 +1,8 @@ +import type {NetContext} from './Context' + + +declare module "@welshman/lib" { + interface Context { + net: NetContext + } +}