Remove Fluent, Tags
This commit is contained in:
@@ -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)
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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,7 +1,6 @@
|
||||
export * from './Context'
|
||||
export * from './Deferred'
|
||||
export * from './Emitter'
|
||||
export * from './Fluent'
|
||||
export * from './LRUCache'
|
||||
export * from './Tools'
|
||||
export * from './Worker'
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user