Re-work address
This commit is contained in:
@@ -2,22 +2,12 @@ import {uniq, identity, flatten, pushToMapKey, intersection, tryCatch, now} from
|
|||||||
import type {TrustedEvent, Filter} from '@welshman/util'
|
import type {TrustedEvent, Filter} from '@welshman/util'
|
||||||
import {Tags, intersectFilters, getAddress, getIdFilters, unionFilters} from '@welshman/util'
|
import {Tags, intersectFilters, getAddress, getIdFilters, unionFilters} from '@welshman/util'
|
||||||
import type {CreatedAtItem, RequestItem, ListItem, WOTItem, DVMItem, Scope, Feed, FeedOptions} from './core'
|
import type {CreatedAtItem, RequestItem, ListItem, WOTItem, DVMItem, Scope, Feed, FeedOptions} from './core'
|
||||||
import {hasSubFeeds, getFeedArgs, feedsFromTags} from './utils'
|
import {getFeedArgs, feedsFromTags} from './utils'
|
||||||
import {FeedType} from './core'
|
import {FeedType} from './core'
|
||||||
|
|
||||||
export class FeedCompiler<E extends TrustedEvent> {
|
export class FeedCompiler<E extends TrustedEvent> {
|
||||||
constructor(readonly options: FeedOptions<E>) {}
|
constructor(readonly options: FeedOptions<E>) {}
|
||||||
|
|
||||||
walk(feed: Feed, visit: (feed: Feed) => void) {
|
|
||||||
visit(feed)
|
|
||||||
|
|
||||||
if (hasSubFeeds(feed)) {
|
|
||||||
for (const subFeed of getFeedArgs(feed)) {
|
|
||||||
this.walk(subFeed, visit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
canCompile(feed: Feed): boolean {
|
canCompile(feed: Feed): boolean {
|
||||||
switch(feed[0]) {
|
switch(feed[0]) {
|
||||||
case FeedType.Union:
|
case FeedType.Union:
|
||||||
@@ -240,6 +230,7 @@ export class FeedCompiler<E extends TrustedEvent> {
|
|||||||
|
|
||||||
if (event) {
|
if (event) {
|
||||||
for (const feed of feedsFromTags(Tags.fromEvent(event), mappings)) {
|
for (const feed of feedsFromTags(Tags.fromEvent(event), mappings)) {
|
||||||
|
|
||||||
feeds.push(feed)
|
feeds.push(feed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,3 +155,14 @@ export const feedsFromFilter = ({since, until, ...filter}: Filter) => {
|
|||||||
|
|
||||||
export const feedFromFilter = (filter: Filter) =>
|
export const feedFromFilter = (filter: Filter) =>
|
||||||
makeIntersectionFeed(...feedsFromFilter(filter))
|
makeIntersectionFeed(...feedsFromFilter(filter))
|
||||||
|
|
||||||
|
|
||||||
|
export const walkFeed = (feed: Feed, visit: (feed: Feed) => void) => {
|
||||||
|
visit(feed)
|
||||||
|
|
||||||
|
if (hasSubFeeds(feed)) {
|
||||||
|
for (const subFeed of getFeedArgs(feed)) {
|
||||||
|
walkFeed(subFeed, visit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ export const difference = <T>(a: T[], b: T[]) => {
|
|||||||
return a.filter(x => !s.has(x))
|
return a.filter(x => !s.has(x))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const remove = <T>(a: T, b: T[]) => b.filter(x => x !== a)
|
||||||
|
|
||||||
export const clamp = ([min, max]: [number, number], n: number) => Math.min(max, Math.max(min, n))
|
export const clamp = ([min, max]: [number, number], n: number) => Math.min(max, Math.max(min, n))
|
||||||
|
|
||||||
export const tryCatch = async <T>(f: () => Promise<T | void> | T | void, onError?: (e: Error) => void): Promise<T | void> => {
|
export const tryCatch = async <T>(f: () => Promise<T | void> | T | void, onError?: (e: Error) => void): Promise<T | void> => {
|
||||||
|
|||||||
+36
-27
@@ -1,27 +1,32 @@
|
|||||||
import type {UnsignedEvent} from 'nostr-tools'
|
|
||||||
import {nip19} from 'nostr-tools'
|
import {nip19} from 'nostr-tools'
|
||||||
import {GROUP, COMMUNITY} from './Kinds'
|
import {GROUP, COMMUNITY} from './Kinds'
|
||||||
|
|
||||||
export type Address = {
|
// Define this locally to avoid circular dependencies
|
||||||
kind: number,
|
type AddressableEvent = {
|
||||||
|
kind: number
|
||||||
pubkey: string
|
pubkey: string
|
||||||
identifier: string
|
tags: string[][]
|
||||||
relays: string[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plain text format
|
export class Address {
|
||||||
|
constructor(
|
||||||
|
readonly kind: number,
|
||||||
|
readonly pubkey: string,
|
||||||
|
readonly identifier: string,
|
||||||
|
readonly relays: string[] = []
|
||||||
|
) {}
|
||||||
|
|
||||||
export const decodeAddress = (a: string, relays: string[] = []): Address => {
|
static isAddress(address: string) {
|
||||||
const [kind, pubkey, identifier = ""] = a.split(":")
|
return Boolean(address.match(/^\d+:\w+:.*$/))
|
||||||
|
}
|
||||||
|
|
||||||
return {kind: parseInt(kind), pubkey, identifier, relays}
|
static from(address: string, relays: string[] = []) {
|
||||||
}
|
const [kind, pubkey, identifier = ""] = address.match(/^(\d+):(\w+):(.*)$/)!.slice(1)
|
||||||
|
|
||||||
export const encodeAddress = (a: Address) => [a.kind, a.pubkey, a.identifier].join(":")
|
return new Address(parseInt(kind), pubkey, identifier, relays)
|
||||||
|
}
|
||||||
|
|
||||||
// Naddr encoding
|
static fromNaddr(naddr: string) {
|
||||||
|
|
||||||
export const addressFromNaddr = (naddr: string): Address => {
|
|
||||||
let type
|
let type
|
||||||
let data = {} as any
|
let data = {} as any
|
||||||
try {
|
try {
|
||||||
@@ -37,22 +42,26 @@ export const addressFromNaddr = (naddr: string): Address => {
|
|||||||
throw new Error(`Invalid naddr ${naddr}`)
|
throw new Error(`Invalid naddr ${naddr}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return data
|
return new Address(data.kind, data.pubkey, data.identifier, data.relays)
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromEvent(event: AddressableEvent, relays: string[] = []) {
|
||||||
|
const identifier = event.tags.find(t => t[0] === "d")?.[1] || ""
|
||||||
|
|
||||||
|
return new Address(event.kind, event.pubkey, identifier, relays)
|
||||||
|
}
|
||||||
|
|
||||||
|
toString = () => [this.kind, this.pubkey, this.identifier].join(":")
|
||||||
|
|
||||||
|
toNaddr = () => nip19.naddrEncode(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const addressToNaddr = (a: Address): string => nip19.naddrEncode(a)
|
|
||||||
|
|
||||||
// Get from event, encode to filter
|
|
||||||
|
|
||||||
export const getIdentifier = (e: UnsignedEvent) => e.tags.find(t => t[0] === "d")?.[1] || ""
|
|
||||||
|
|
||||||
export const addressFromEvent = (e: UnsignedEvent, relays: string[] = []) =>
|
|
||||||
({kind: e.kind, pubkey: e.pubkey, identifier: getIdentifier(e), relays})
|
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
|
|
||||||
export const isGroupAddress = (a: Address) => a.kind === GROUP
|
export const getAddress = (e: AddressableEvent) => Address.fromEvent(e).toString()
|
||||||
|
|
||||||
export const isCommunityAddress = (a: Address) => a.kind === COMMUNITY
|
export const isGroupAddress = (a: string, ...args: unknown[]) => Address.from(a).kind === GROUP
|
||||||
|
|
||||||
export const isContextAddress = (a: Address) => [GROUP, COMMUNITY].includes(a.kind)
|
export const isCommunityAddress = (a: string, ...args: unknown[]) => Address.from(a).kind === COMMUNITY
|
||||||
|
|
||||||
|
export const isContextAddress = (a: string, ...args: unknown[]) => [GROUP, COMMUNITY].includes(Address.from(a).kind)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import {verifiedSymbol} from 'nostr-tools'
|
|||||||
import {verifyEvent, getEventHash} from 'nostr-tools'
|
import {verifyEvent, getEventHash} from 'nostr-tools'
|
||||||
import {cached, pick, now} from '@welshman/lib'
|
import {cached, pick, now} from '@welshman/lib'
|
||||||
import {Tags} from './Tags'
|
import {Tags} from './Tags'
|
||||||
import {addressFromEvent, encodeAddress} from './Address'
|
import {getAddress} from './Address'
|
||||||
import {isEphemeralKind, isReplaceableKind, isPlainReplaceableKind, isParameterizedReplaceableKind} from './Kinds'
|
import {isEphemeralKind, isReplaceableKind, isPlainReplaceableKind, isParameterizedReplaceableKind} from './Kinds'
|
||||||
|
|
||||||
export type EventTemplate = {
|
export type EventTemplate = {
|
||||||
@@ -98,8 +98,6 @@ export const hasValidSignature = cached<string, boolean, [SignedEvent]>({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export const getAddress = (e: HashedEvent) => encodeAddress(addressFromEvent(e))
|
|
||||||
|
|
||||||
export const getIdOrAddress = (e: HashedEvent) => isReplaceable(e) ? getAddress(e) : e.id
|
export const getIdOrAddress = (e: HashedEvent) => isReplaceable(e) ? getAddress(e) : e.id
|
||||||
|
|
||||||
export const getIdAndAddress = (e: HashedEvent) => isReplaceable(e) ? [e.id, getAddress(e)] : [e.id]
|
export const getIdAndAddress = (e: HashedEvent) => isReplaceable(e) ? [e.id, getAddress(e)] : [e.id]
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import {Event} from 'nostr-tools'
|
|||||||
import {matchFilter as nostrToolsMatchFilter} from 'nostr-tools'
|
import {matchFilter as nostrToolsMatchFilter} from 'nostr-tools'
|
||||||
import {prop, avg, hash, groupBy, randomId, uniq} from '@welshman/lib'
|
import {prop, avg, hash, groupBy, randomId, uniq} from '@welshman/lib'
|
||||||
import type {HashedEvent, TrustedEvent} from './Events'
|
import type {HashedEvent, TrustedEvent} from './Events'
|
||||||
import {decodeAddress, addressFromEvent, encodeAddress} from './Address'
|
|
||||||
import {isReplaceableKind} from './Kinds'
|
import {isReplaceableKind} from './Kinds'
|
||||||
|
import {Address, getAddress} from './Address'
|
||||||
|
|
||||||
export const EPOCH = 1609459200
|
export const EPOCH = 1609459200
|
||||||
|
|
||||||
@@ -133,8 +133,8 @@ export const getIdFilters = (idsOrAddresses: string[]) => {
|
|||||||
const aFilters = []
|
const aFilters = []
|
||||||
|
|
||||||
for (const idOrAddress of idsOrAddresses) {
|
for (const idOrAddress of idsOrAddresses) {
|
||||||
if (idOrAddress.includes(":")) {
|
if (Address.isAddress(idOrAddress)) {
|
||||||
const {kind, pubkey, identifier} = decodeAddress(idOrAddress)
|
const {kind, pubkey, identifier} = Address.from(idOrAddress)
|
||||||
|
|
||||||
if (identifier) {
|
if (identifier) {
|
||||||
aFilters.push({kinds: [kind], authors: [pubkey], "#d": [identifier]})
|
aFilters.push({kinds: [kind], authors: [pubkey], "#d": [identifier]})
|
||||||
@@ -163,7 +163,7 @@ export const getReplyFilters = (events: TrustedEvent[], filter: Filter) => {
|
|||||||
e.push(event.id)
|
e.push(event.id)
|
||||||
|
|
||||||
if (isReplaceableKind(event.kind)) {
|
if (isReplaceableKind(event.kind)) {
|
||||||
a.push(encodeAddress(addressFromEvent(event)))
|
a.push(getAddress(event))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.wrap) {
|
if (event.wrap) {
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import type {IReadable, Subscriber, Invalidator} from '@welshman/lib'
|
|||||||
import {Derived, Emitter, sortBy, customStore, inc, first, chunk, sleep, uniq, omit, now, range, identity} from '@welshman/lib'
|
import {Derived, Emitter, sortBy, customStore, inc, first, chunk, sleep, uniq, omit, now, range, identity} from '@welshman/lib'
|
||||||
import {DELETE} from './Kinds'
|
import {DELETE} from './Kinds'
|
||||||
import {EPOCH, matchFilter, getIdFilters, matchFilters} from './Filters'
|
import {EPOCH, matchFilter, getIdFilters, matchFilters} from './Filters'
|
||||||
import {isReplaceable, isTrustedEvent, getAddress} from './Events'
|
import {isReplaceable, isTrustedEvent} from './Events'
|
||||||
|
import {getAddress} from './Address'
|
||||||
import type {Filter} from './Filters'
|
import type {Filter} from './Filters'
|
||||||
import type {TrustedEvent} from './Events'
|
import type {TrustedEvent} from './Events'
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import {first, splitAt, identity, sortBy, uniq, shuffle, pushToMapKey} from '@welshman/lib'
|
import {first, splitAt, identity, sortBy, uniq, shuffle, pushToMapKey} from '@welshman/lib'
|
||||||
import {Tags, Tag} from '@welshman/util'
|
import {Tags, Tag} from '@welshman/util'
|
||||||
import type {TrustedEvent} from './Events'
|
import type {TrustedEvent} from './Events'
|
||||||
import {getAddress, isReplaceable} from './Events'
|
import {isReplaceable} from './Events'
|
||||||
import {isShareableRelayUrl} from './Relay'
|
import {isShareableRelayUrl} from './Relay'
|
||||||
import {addressFromEvent, decodeAddress, isCommunityAddress, isGroupAddress} from './Address'
|
import {getAddress, isCommunityAddress, isGroupAddress} from './Address'
|
||||||
|
|
||||||
export enum RelayMode {
|
export enum RelayMode {
|
||||||
Read = "read",
|
Read = "read",
|
||||||
@@ -246,11 +246,11 @@ export class Router {
|
|||||||
this.scenario(this.getContextSelections(Tags.wrap([["a", address]])))
|
this.scenario(this.getContextSelections(Tags.wrap([["a", address]])))
|
||||||
|
|
||||||
WithinContext = (address: string) => {
|
WithinContext = (address: string) => {
|
||||||
if (isGroupAddress(decodeAddress(address))) {
|
if (isGroupAddress(address)) {
|
||||||
return this.WithinGroup(address)
|
return this.WithinGroup(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCommunityAddress(decodeAddress(address))) {
|
if (isCommunityAddress(address)) {
|
||||||
return this.WithinCommunity(address)
|
return this.WithinCommunity(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,9 +294,6 @@ export class Router {
|
|||||||
|
|
||||||
return new Tags(tags)
|
return new Tags(tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
address = (event: TrustedEvent) =>
|
|
||||||
addressFromEvent(event, this.Event(event).redundancy(3).getUrls())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Router Scenario
|
// Router Scenario
|
||||||
@@ -352,7 +349,7 @@ export class RouterScenario {
|
|||||||
const relaySelections = this.router.relaySelectionsFromMap(valuesByRelay)
|
const relaySelections = this.router.relaySelectionsFromMap(valuesByRelay)
|
||||||
for (const {relay} of this.router.sortRelaySelections(relaySelections)) {
|
for (const {relay} of this.router.sortRelaySelections(relaySelections)) {
|
||||||
const values = new Set<string>()
|
const values = new Set<string>()
|
||||||
for (const value of valuesByRelay.get(relay) || []) {
|
for (const value of uniq(valuesByRelay.get(relay) || [])) {
|
||||||
const timesSeen = seen.get(value) || 0
|
const timesSeen = seen.get(value) || 0
|
||||||
|
|
||||||
if (timesSeen < adjustedRedundancy) {
|
if (timesSeen < adjustedRedundancy) {
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import {EventTemplate} from 'nostr-tools'
|
|||||||
import type {OmitStatics} from '@welshman/lib'
|
import type {OmitStatics} from '@welshman/lib'
|
||||||
import {Fluent, ensurePlural} from '@welshman/lib'
|
import {Fluent, ensurePlural} from '@welshman/lib'
|
||||||
import {isShareableRelayUrl, normalizeRelayUrl} from './Relay'
|
import {isShareableRelayUrl, normalizeRelayUrl} from './Relay'
|
||||||
import type {Address} from './Address'
|
|
||||||
import {encodeAddress, decodeAddress} from './Address'
|
|
||||||
import {GROUP, COMMUNITY} from './Kinds'
|
import {GROUP, COMMUNITY} from './Kinds'
|
||||||
|
|
||||||
export class Tag extends (Fluent<string> as OmitStatics<typeof Fluent<string>, 'from'>) {
|
export class Tag extends (Fluent<string> as OmitStatics<typeof Fluent<string>, 'from'>) {
|
||||||
@@ -17,7 +15,7 @@ export class Tag extends (Fluent<string> as OmitStatics<typeof Fluent<string>, '
|
|||||||
|
|
||||||
static fromPubkey = (pubkey: string) => new Tag(["p", pubkey])
|
static fromPubkey = (pubkey: string) => new Tag(["p", pubkey])
|
||||||
|
|
||||||
static fromAddress = (address: Address) => new Tag(["a", encodeAddress(address), address.relays[0] || ""])
|
static fromAddress = (address: string, relay = "") => new Tag(["a", address, relay])
|
||||||
|
|
||||||
key = () => this.xs[0]
|
key = () => this.xs[0]
|
||||||
|
|
||||||
@@ -29,8 +27,6 @@ export class Tag extends (Fluent<string> as OmitStatics<typeof Fluent<string>, '
|
|||||||
|
|
||||||
setValue = (v: string) => this.set(1, v)
|
setValue = (v: string) => this.set(1, v)
|
||||||
|
|
||||||
asAddress = () => decodeAddress(this.value())
|
|
||||||
|
|
||||||
isAddress = (kind?: number) => this.key() === "a" && this.value()?.startsWith(`${kind}:`)
|
isAddress = (kind?: number) => this.key() === "a" && this.value()?.startsWith(`${kind}:`)
|
||||||
|
|
||||||
isGroup = () => this.isAddress(GROUP)
|
isGroup = () => this.isAddress(GROUP)
|
||||||
|
|||||||
Reference in New Issue
Block a user