Remove Fluent, Tags

This commit is contained in:
Jon Staab
2024-12-13 15:18:33 -08:00
parent 6235c3f6bb
commit 760d6d07cd
7 changed files with 14 additions and 258 deletions
+4 -4
View File
@@ -1,6 +1,6 @@
import {uniq, identity, flatten, pushToMapKey, intersection, tryCatch, now} from '@welshman/lib'
import type {TrustedEvent, Filter} from '@welshman/util'
import {Tags, intersectFilters, matchFilter, getAddress, getIdFilters, unionFilters} from '@welshman/util'
import {intersectFilters, matchFilter, getAddress, getIdFilters, unionFilters} from '@welshman/util'
import type {CreatedAtItem, RequestItem, ListItem, LabelItem, WOTItem, DVMItem, Scope, Feed, FeedOptions} from './core'
import {getFeedArgs, feedsFromTags} from './utils'
import {FeedType} from './core'
@@ -110,7 +110,7 @@ export class FeedCompiler {
this.options.requestDVM({
...request,
onEvent: async (e: TrustedEvent) => {
const tags = Tags.wrap(await tryCatch(() => JSON.parse(e.content)) || [])
const tags = await tryCatch(() => JSON.parse(e.content)) || []
for (const feed of feedsFromTags(tags, mappings)) {
feeds.push(feed)
@@ -231,7 +231,7 @@ export class FeedCompiler {
const event = eventsByAddress.get(address)
if (event) {
for (const feed of feedsFromTags(Tags.fromEvent(event), mappings)) {
for (const feed of feedsFromTags(event.tags, mappings)) {
feeds.push(feed)
}
}
@@ -271,7 +271,7 @@ export class FeedCompiler {
}
}
return feedsFromTags(Tags.wrap(tags), mappings)
return feedsFromTags(tags, mappings)
})
)
)
+5 -7
View File
@@ -1,6 +1,6 @@
import {ensureNumber} from '@welshman/lib'
import type {Filter} from '@welshman/util'
import {Tags} from '@welshman/util'
import {getTagValues} from '@welshman/util'
import {
FeedType,
Feed,
@@ -110,15 +110,13 @@ export const defaultTagFeedMappings: TagFeedMapping[] = [
['t', [FeedType.Tag, '#t']],
]
export const feedsFromTags = (tags: Tags, mappings?: TagFeedMapping[]) => {
export const feedsFromTags = (tags: string[][], mappings?: TagFeedMapping[]) => {
const feeds = []
for (const [tagName, templateFeed] of mappings || defaultTagFeedMappings) {
const filterTags = tags.whereKey(tagName)
if (filterTags.exists()) {
let values: string[] | number[] = filterTags.values().valueOf()
let values: any[] = getTagValues(tagName, tags)
if (values.length > 0) {
if (isKindFeed(templateFeed)) {
values = values.map(ensureNumber) as number[]
}
@@ -130,7 +128,7 @@ export const feedsFromTags = (tags: Tags, mappings?: TagFeedMapping[]) => {
return feeds
}
export const feedFromTags = (tags: Tags, mappings?: TagFeedMapping[]) =>
export const feedFromTags = (tags: string[][], mappings?: TagFeedMapping[]) =>
makeIntersectionFeed(...feedsFromTags(tags, mappings))
export const feedsFromFilter = ({since, until, ...filter}: Filter) => {
-71
View File
@@ -1,71 +0,0 @@
import {last} from './Tools'
export class Fluent<T> {
constructor(readonly xs: T[]) {}
static create() {
return this.from([])
}
static from<T>(xs: Iterable<T>) {
return new Fluent<T>(Array.from(xs))
}
clone<K extends Fluent<T>>(this: K, xs: T[]): K {
return new (this.constructor as { new (xs: T[]): K })(xs)
}
valueOf = () => this.xs
first = () => this.xs[0]
nth = (i: number) => this.xs[i]
last = () => last(this.xs)
count = () => this.xs.length
exists = () => this.xs.length > 0
has = (v: T) => this.xs.includes(v)
every = (f: (t: T) => boolean) => this.xs.every(f)
some = (f: (t: T) => boolean) => this.xs.some(f)
find = (f: (t: T) => boolean) => this.xs.find(f)
uniq = () => this.clone(Array.from(new Set(this.xs)))
slice = (a: number, b?: number) => this.clone(this.xs.slice(a, b))
take = (n: number) => this.slice(0, n)
drop = (n: number) => this.slice(n)
filter = (f: (t: T) => boolean) => this.clone(this.xs.filter(f))
reject = (f: (t: T) => boolean) => this.clone(this.xs.filter(t => !f(t)))
keep = (xs: T[]) => this.filter(x => xs.includes(x))
without = (xs: T[]) => this.reject(x => xs.includes(x))
map = (f: (t: T) => T) => this.clone(this.xs.map(f))
mapTo = <U>(f: (t: T) => U) => Fluent.from(this.xs.map(f))
flatMap = <U>(f: (t: T) => U[]) => Fluent.from(this.xs.flatMap(f))
forEach = (f: (t: T, i: number) => void) => this.xs.forEach(f)
join = (s: string) => this.valueOf().join(s)
set = (i: number, x: T) => this.clone([...this.xs.slice(0, i), x, ...this.xs.slice(i + 1)])
concat = (xs: T[]) => this.clone(this.xs.concat(xs))
append = (x: T) => this.concat([x])
prepend = (x: T) => this.clone([x].concat(this.xs))
}
-7
View File
@@ -1088,10 +1088,3 @@ export const hexToBech32 = (prefix: string, hex: string) =>
*/
export const bech32ToHex = (b32: string) =>
utf8.encode(bech32.fromWords(bech32.decode(b32, false).words))
/** Extracts non-function property names from type */
// https://github.com/microsoft/TypeScript/issues/4628#issuecomment-1147905253
export type OmitStatics<T, S extends string> =
T extends {new(...args: infer A): infer R} ?
{new(...args: A): R}&Omit<T, S> :
Omit<T, S>;
-1
View File
@@ -1,7 +1,6 @@
export * from './Context'
export * from './Deferred'
export * from './Emitter'
export * from './Fluent'
export * from './LRUCache'
export * from './Tools'
export * from './Worker'
+3 -3
View File
@@ -1,6 +1,6 @@
import {verifiedSymbol, getEventHash, verifyEvent} from 'nostr-tools'
import {cached, pick, now} from '@welshman/lib'
import {Tags} from './Tags'
import {getAncestorTagValues} from './Tags'
import {getAddress} from './Address'
import {isEphemeralKind, isReplaceableKind, isPlainReplaceableKind, isParameterizedReplaceableKind} from './Kinds'
@@ -128,8 +128,8 @@ export const isPlainReplaceable = (e: EventTemplate) => isPlainReplaceableKind(e
export const isParameterizedReplaceable = (e: EventTemplate) => isParameterizedReplaceableKind(e.kind)
export const isChildOf = (child: EventContent, parent: HashedEvent) => {
const {roots, replies} = Tags.fromEvent(child).ancestors()
const parentIds = (replies.exists() ? replies : roots).values().valueOf()
const {roots, replies} = getAncestorTagValues(child.tags)
const parentIds = replies.length > 0 ? replies : roots
return getIdAndAddress(parent).some(x => parentIds.includes(x))
}
+2 -165
View File
@@ -1,170 +1,7 @@
import type {OmitStatics} from "@welshman/lib"
import {Fluent, uniq, uniqBy, mapVals, nth, nthEq, ensurePlural} from "@welshman/lib"
import {isRelayUrl, isShareableRelayUrl, normalizeRelayUrl} from "./Relay"
import {uniq, uniqBy, mapVals, nth, nthEq, ensurePlural} from "@welshman/lib"
import {isRelayUrl, isShareableRelayUrl} from "./Relay"
import {Address} from "./Address"
export class Tag extends (Fluent<string> as OmitStatics<typeof Fluent<string>, "from">) {
static from = (xs: Iterable<string>) => new Tag(Array.from(xs))
static fromId = (id: string) => new Tag(["e", id])
static fromIdentifier = (identifier: string) => new Tag(["d", identifier])
static fromTopic = (topic: string) => new Tag(["t", topic])
static fromPubkey = (pubkey: string) => new Tag(["p", pubkey])
static fromAddress = (address: string, relay = "") => new Tag(["a", address, relay])
key = () => this.xs[0]
value = () => this.xs[1]
entry = () => this.xs.slice(0, 2)
setKey = (k: string) => this.set(0, k)
setValue = (v: string) => this.set(1, v)
isAddress = (kind?: number) => this.key() === "a" && this.value()?.startsWith(`${kind}:`)
}
export class Tags extends (Fluent<Tag> as OmitStatics<typeof Fluent<Tag>, "from">) {
static from = (p: Iterable<Tag>) => new Tags(Array.from(p))
static wrap = (p: Iterable<string[]>) => new Tags(Array.from(p).map(Tag.from))
static fromEvent = (event: {tags: string[][]}) => Tags.wrap(event?.tags || [])
static fromEvents = (events: {tags: string[][]}[]) => Tags.wrap(events.flatMap(e => e.tags || []))
static fromIMeta = (imeta: string[]) => Tags.wrap(imeta.map((m: string) => m.split(" ")))
unwrap = () => this.xs.map(tag => tag.valueOf())
whereKey = (key: string) => this.filter(t => t.key() === key)
whereValue = (value: string) => this.filter(t => t.value() === value)
filterByKey = (keys: string[]) => this.filter(t => keys.includes(t.key()))
filterByValue = (values: string[]) => this.filter(t => values.includes(t.value()))
rejectByKey = (keys: string[]) => this.reject(t => keys.includes(t.key()))
rejectByValue = (values: string[]) => this.reject(t => values.includes(t.value()))
get = (key: string) => this.whereKey(key).first()
keys = () => this.mapTo(t => t.key())
values = (key?: string | string[]) =>
(key ? this.filterByKey(ensurePlural(key)) : this).mapTo(t => t.value())
entries = () => this.mapTo(t => t.entry())
relays = () =>
this.flatMap((t: Tag) =>
t
.valueOf()
.filter(isRelayUrl)
.map(url => normalizeRelayUrl(url))
).uniq()
topics = () =>
this.whereKey("t")
.values()
.map((t: string) => t.replace(/^#/, ""))
ancestors = (x?: boolean) => {
const {roots, replies, mentions} = getAncestorTags(this.unwrap())
return {
roots: Tags.wrap(roots),
replies: Tags.wrap(replies),
mentions: Tags.wrap(mentions),
}
}
roots = () => this.ancestors().roots
replies = () => this.ancestors().replies
mentions = () => this.ancestors().mentions
root = () => {
const roots = this.roots()
return roots.get("e") || roots.get("a")
}
reply = () => {
const replies = this.replies()
return replies.get("e") || replies.get("a")
}
parents = () => {
const {roots, replies} = this.ancestors()
return replies.exists() ? replies : roots
}
parent = () => {
const parents = this.parents()
return parents.get("e") || parents.get("a")
}
asObject = () => {
const result: Record<string, string> = {}
for (const t of this.xs) {
result[t.key()] = t.value()
}
return result
}
imeta = (url: string) => {
for (const tag of this.whereKey("imeta").xs) {
const tags = Tags.fromIMeta(tag.drop(1).valueOf())
if (tags.get("url")?.value() === url) {
return tags
}
}
return null
}
// Generic setters
addTag = (...args: string[]) => this.append(Tag.from(args))
setTag = (k: string, ...args: string[]) => this.rejectByKey([k]).addTag(k, ...args)
// Images
addImages = (imeta: Tags[]) =>
this.concat(imeta.map(tags => Tag.from(["image", tags.get("url").value()])))
removeImages = () => this.rejectByKey(["image"])
setImages = (imeta: Tags[]) => this.removeImages().addImages(imeta)
// IMeta
addIMeta = (imeta: Tags[]) =>
this.concat(imeta.map(tags => Tag.from(["imeta", ...tags.valueOf().map(xs => xs.join(" "))])))
removeIMeta = () => this.rejectByKey(["imeta"])
setIMeta = (imeta: Tags[]) => this.removeIMeta().addIMeta(imeta)
}
// New, simpler version
export const getTags = (types: string | string[], tags: string[][]) => {
types = ensurePlural(types)