288 lines
9.2 KiB
TypeScript
288 lines
9.2 KiB
TypeScript
import {ensureNumber} from "@welshman/lib"
|
|
import {Filter, unionFilters} from "@welshman/util"
|
|
import {getTagValues} from "@welshman/util"
|
|
import {
|
|
FeedType,
|
|
Feed,
|
|
AddressFeed,
|
|
AuthorFeed,
|
|
CreatedAtFeed,
|
|
DVMFeed,
|
|
DifferenceFeed,
|
|
IDFeed,
|
|
IntersectionFeed,
|
|
GlobalFeed,
|
|
KindFeed,
|
|
ListFeed,
|
|
LabelFeed,
|
|
WOTFeed,
|
|
RelayFeed,
|
|
ScopeFeed,
|
|
SearchFeed,
|
|
TagFeed,
|
|
UnionFeed,
|
|
TagFeedMapping,
|
|
Scope,
|
|
WOTItem,
|
|
DVMItem,
|
|
ListItem,
|
|
LabelItem,
|
|
CreatedAtItem,
|
|
} from "./core.js"
|
|
|
|
export const makeAddressFeed = (...addresses: string[]): AddressFeed => [
|
|
FeedType.Address,
|
|
...addresses,
|
|
]
|
|
export const makeAuthorFeed = (...pubkeys: string[]): AuthorFeed => [FeedType.Author, ...pubkeys]
|
|
export const makeCreatedAtFeed = (...items: CreatedAtItem[]): CreatedAtFeed => [
|
|
FeedType.CreatedAt,
|
|
...items,
|
|
]
|
|
export const makeDVMFeed = (...items: DVMItem[]): DVMFeed => [FeedType.DVM, ...items]
|
|
export const makeDifferenceFeed = (...feeds: Feed[]): DifferenceFeed => [
|
|
FeedType.Difference,
|
|
...feeds,
|
|
]
|
|
export const makeIDFeed = (...ids: string[]): IDFeed => [FeedType.ID, ...ids]
|
|
export const makeIntersectionFeed = (...feeds: Feed[]): IntersectionFeed => [
|
|
FeedType.Intersection,
|
|
...feeds,
|
|
]
|
|
export const makeGlobalFeed = (): GlobalFeed => [FeedType.Global]
|
|
export const makeKindFeed = (...kinds: number[]): KindFeed => [FeedType.Kind, ...kinds]
|
|
export const makeListFeed = (...items: ListItem[]): ListFeed => [FeedType.List, ...items]
|
|
export const makeLabelFeed = (...items: LabelItem[]): LabelFeed => [FeedType.Label, ...items]
|
|
export const makeWOTFeed = (...items: WOTItem[]): WOTFeed => [FeedType.WOT, ...items]
|
|
export const makeRelayFeed = (...urls: string[]): RelayFeed => [FeedType.Relay, ...urls]
|
|
export const makeScopeFeed = (...scopes: Scope[]): ScopeFeed => [FeedType.Scope, ...scopes]
|
|
export const makeSearchFeed = (...searches: string[]): SearchFeed => [FeedType.Search, ...searches]
|
|
export const makeTagFeed = (key: string, ...values: string[]): TagFeed => [
|
|
FeedType.Tag,
|
|
key,
|
|
...values,
|
|
]
|
|
export const makeUnionFeed = (...feeds: Feed[]): UnionFeed => [FeedType.Union, ...feeds]
|
|
|
|
export const isAddressFeed = (feed: Feed): feed is AddressFeed => feed[0] === FeedType.Address
|
|
export const isAuthorFeed = (feed: Feed): feed is AuthorFeed => feed[0] === FeedType.Author
|
|
export const isCreatedAtFeed = (feed: Feed): feed is CreatedAtFeed => feed[0] === FeedType.CreatedAt
|
|
export const isDVMFeed = (feed: Feed): feed is DVMFeed => feed[0] === FeedType.DVM
|
|
export const isDifferenceFeed = (feed: Feed): feed is DifferenceFeed =>
|
|
feed[0] === FeedType.Difference
|
|
export const isIDFeed = (feed: Feed): feed is IDFeed => feed[0] === FeedType.ID
|
|
export const isIntersectionFeed = (feed: Feed): feed is IntersectionFeed =>
|
|
feed[0] === FeedType.Intersection
|
|
export const isGlobalFeed = (feed: Feed): feed is GlobalFeed => feed[0] === FeedType.Global
|
|
export const isKindFeed = (feed: Feed): feed is KindFeed => feed[0] === FeedType.Kind
|
|
export const isListFeed = (feed: Feed): feed is ListFeed => feed[0] === FeedType.List
|
|
export const isLabelFeed = (feed: Feed): feed is LabelFeed => feed[0] === FeedType.Label
|
|
export const isWOTFeed = (feed: Feed): feed is WOTFeed => feed[0] === FeedType.WOT
|
|
export const isRelayFeed = (feed: Feed): feed is RelayFeed => feed[0] === FeedType.Relay
|
|
export const isScopeFeed = (feed: Feed): feed is ScopeFeed => feed[0] === FeedType.Scope
|
|
export const isSearchFeed = (feed: Feed): feed is SearchFeed => feed[0] === FeedType.Search
|
|
export const isTagFeed = (feed: Feed): feed is TagFeed => feed[0] === FeedType.Tag
|
|
export const isUnionFeed = (feed: Feed): feed is UnionFeed => feed[0] === FeedType.Union
|
|
|
|
export function getFeedArgs(feed: IntersectionFeed | UnionFeed | DifferenceFeed): Feed[]
|
|
export function getFeedArgs(
|
|
feed: AddressFeed | AuthorFeed | IDFeed | RelayFeed | SearchFeed,
|
|
): string[]
|
|
export function getFeedArgs(feed: CreatedAtFeed): CreatedAtItem[]
|
|
export function getFeedArgs(feed: ListFeed): ListItem[]
|
|
export function getFeedArgs(feed: LabelFeed): LabelItem[]
|
|
export function getFeedArgs(feed: DVMFeed): DVMItem[]
|
|
export function getFeedArgs(feed: WOTFeed): WOTItem[]
|
|
export function getFeedArgs(feed: ScopeFeed): Scope[]
|
|
export function getFeedArgs(feed: KindFeed): number[]
|
|
export function getFeedArgs(feed: TagFeed): [string, ...string[]]
|
|
export function getFeedArgs(feed: GlobalFeed): []
|
|
export function getFeedArgs(feed: Feed) {
|
|
switch (feed[0]) {
|
|
case FeedType.Intersection:
|
|
return feed.slice(1) as Feed[]
|
|
case FeedType.Union:
|
|
return feed.slice(1) as Feed[]
|
|
case FeedType.Difference:
|
|
return feed.slice(1) as Feed[]
|
|
case FeedType.Address:
|
|
return feed.slice(1) as string[]
|
|
case FeedType.Author:
|
|
return feed.slice(1) as string[]
|
|
case FeedType.ID:
|
|
return feed.slice(1) as string[]
|
|
case FeedType.Relay:
|
|
return feed.slice(1) as string[]
|
|
case FeedType.Search:
|
|
return feed.slice(1) as string[]
|
|
case FeedType.Tag:
|
|
return feed.slice(1) as [string, ...string[]]
|
|
case FeedType.CreatedAt:
|
|
return feed.slice(1) as CreatedAtItem[]
|
|
case FeedType.List:
|
|
return feed.slice(1) as ListItem[]
|
|
case FeedType.Label:
|
|
return feed.slice(1) as LabelItem[]
|
|
case FeedType.DVM:
|
|
return feed.slice(1) as DVMItem[]
|
|
case FeedType.WOT:
|
|
return feed.slice(1) as WOTItem[]
|
|
case FeedType.Scope:
|
|
return feed.slice(1) as Scope[]
|
|
case FeedType.Kind:
|
|
return feed.slice(1) as number[]
|
|
case FeedType.Global:
|
|
return feed.slice(1) as never[]
|
|
default:
|
|
throw new Error(`Invalid feed type ${feed[0]}`)
|
|
}
|
|
}
|
|
|
|
export const hasSubFeeds = (feed: Feed): feed is IntersectionFeed | UnionFeed | DifferenceFeed =>
|
|
[FeedType.Union, FeedType.Intersection, FeedType.Difference].includes(feed[0])
|
|
|
|
export const defaultTagFeedMappings: TagFeedMapping[] = [
|
|
["a", [FeedType.Address]],
|
|
["e", [FeedType.ID]],
|
|
["p", [FeedType.Author]],
|
|
["r", [FeedType.Relay]],
|
|
["t", [FeedType.Tag, "#t"]],
|
|
]
|
|
|
|
export const feedsFromTags = (tags: string[][], mappings?: TagFeedMapping[]) => {
|
|
const feeds = []
|
|
|
|
for (const [tagName, templateFeed] of mappings || defaultTagFeedMappings) {
|
|
let values: any[] = getTagValues(tagName, tags)
|
|
|
|
if (values.length > 0) {
|
|
if (isKindFeed(templateFeed)) {
|
|
values = values.map(ensureNumber) as number[]
|
|
}
|
|
|
|
feeds.push([...templateFeed, ...values] as Feed)
|
|
}
|
|
}
|
|
|
|
return feeds
|
|
}
|
|
|
|
export const feedFromTags = (tags: string[][], mappings?: TagFeedMapping[]) =>
|
|
makeIntersectionFeed(...feedsFromTags(tags, mappings))
|
|
|
|
export const feedsFromFilter = ({since, until, ...filter}: Filter) => {
|
|
const feeds = []
|
|
|
|
if (since && until) {
|
|
feeds.push(makeCreatedAtFeed({since, until}))
|
|
} else if (since) {
|
|
feeds.push(makeCreatedAtFeed({since}))
|
|
} else if (until) {
|
|
feeds.push(makeCreatedAtFeed({until}))
|
|
}
|
|
|
|
for (const [k, v] of Object.entries(filter)) {
|
|
if (k === "ids") feeds.push(makeIDFeed(...(v as string[])))
|
|
else if (k === "kinds") feeds.push(makeKindFeed(...(v as number[])))
|
|
else if (k === "authors") feeds.push(makeAuthorFeed(...(v as string[])))
|
|
else if (k.startsWith("#")) feeds.push(makeTagFeed(k as string, ...(v as string[])))
|
|
else throw new Error(`Unable to create feed from filter ${k}: ${v}`)
|
|
}
|
|
|
|
return feeds
|
|
}
|
|
|
|
export const feedFromFilter = (filter: Filter) => {
|
|
const feeds = feedsFromFilter(filter)
|
|
|
|
return feeds.length === 1 ? feeds[0] : makeIntersectionFeed(...feeds)
|
|
}
|
|
|
|
export const feedFromFilters = (filters: Filter[]) => {
|
|
const feeds = unionFilters(filters).map(filter => feedFromFilter(filter))
|
|
|
|
return feeds.length === 1 ? feeds[0] : makeUnionFeed(...feeds)
|
|
}
|
|
|
|
export const walkFeed = (feed: Feed, visit: (feed: Feed) => void) => {
|
|
visit(feed)
|
|
|
|
if (hasSubFeeds(feed)) {
|
|
for (const subFeed of getFeedArgs(feed)) {
|
|
walkFeed(subFeed, visit)
|
|
}
|
|
}
|
|
}
|
|
|
|
export const findFeed = (feed: Feed, match: (feed: Feed) => boolean): Feed | undefined => {
|
|
if (match(feed)) return feed
|
|
|
|
if (hasSubFeeds(feed)) {
|
|
for (const subFeed of getFeedArgs(feed)) {
|
|
const found = findFeed(subFeed, match)
|
|
|
|
if (found) {
|
|
return found
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export const simplifyFeed = (feed: Feed): Feed => {
|
|
if (isUnionFeed(feed)) {
|
|
const args = getFeedArgs(feed)
|
|
|
|
if (args.length === 1) return simplifyFeed(args[0])
|
|
|
|
const modified: Feed[] = []
|
|
|
|
for (const sub of args.map(simplifyFeed)) {
|
|
if (isUnionFeed(sub)) {
|
|
modified.push(...getFeedArgs(sub))
|
|
} else {
|
|
modified.push(sub)
|
|
}
|
|
}
|
|
|
|
return makeUnionFeed(...modified)
|
|
}
|
|
|
|
if (isIntersectionFeed(feed)) {
|
|
const args = getFeedArgs(feed)
|
|
|
|
if (args.length === 1) return simplifyFeed(args[0])
|
|
|
|
const modified: Feed[] = []
|
|
|
|
for (const sub of args.map(simplifyFeed)) {
|
|
if (isIntersectionFeed(sub)) {
|
|
modified.push(...getFeedArgs(sub))
|
|
} else {
|
|
modified.push(sub)
|
|
}
|
|
}
|
|
|
|
return makeIntersectionFeed(...modified)
|
|
}
|
|
|
|
if (isDifferenceFeed(feed)) {
|
|
const args = getFeedArgs(feed)
|
|
|
|
if (args.length === 1) return simplifyFeed(args[0])
|
|
|
|
const modified: Feed[] = []
|
|
|
|
for (const sub of args.map(simplifyFeed)) {
|
|
if (isDifferenceFeed(sub)) {
|
|
modified.push(...getFeedArgs(sub))
|
|
} else {
|
|
modified.push(sub)
|
|
}
|
|
}
|
|
|
|
return makeDifferenceFeed(...modified)
|
|
}
|
|
|
|
return feed
|
|
}
|