Add simplifyFeed

This commit is contained in:
Jon Staab
2025-04-30 16:43:23 -07:00
parent f20eab929e
commit 19f8e2ecb4
3 changed files with 117 additions and 58 deletions
+48 -53
View File
@@ -1,4 +1,4 @@
import {inc, memoize, omitVals, max, min, now} from "@welshman/lib" import {inc, defer, Deferred, memoize, omitVals, max, min, now} from "@welshman/lib"
import {EPOCH, trimFilters, guessFilterDelta, TrustedEvent, Filter} from "@welshman/util" import {EPOCH, trimFilters, guessFilterDelta, TrustedEvent, Filter} from "@welshman/util"
import {Feed, FeedType, RequestItem} from "./core.js" import {Feed, FeedType, RequestItem} from "./core.js"
import {FeedCompiler, FeedCompilerOptions} from "./compiler.js" import {FeedCompiler, FeedCompilerOptions} from "./compiler.js"
@@ -87,17 +87,17 @@ export class FeedController {
const minSince = sinces.length === filters.length ? min(sinces) : EPOCH const minSince = sinces.length === filters.length ? min(sinces) : EPOCH
const initialDelta = guessFilterDelta(filters) const initialDelta = guessFilterDelta(filters)
let loading = false let promise: Deferred<void> | undefined
let delta = initialDelta let delta = initialDelta
let since = this.options.useWindowing ? maxUntil - delta : minSince let since = this.options.useWindowing ? maxUntil - delta : minSince
let until = maxUntil let until = maxUntil
return async (limit: number) => { return async (limit: number) => {
if (loading) { if (promise) {
return return promise
} }
loading = true promise = defer()
const requestFilters = filters! const requestFilters = filters!
// Remove filters that don't fit our window // Remove filters that don't fit our window
@@ -148,32 +148,31 @@ export class FeedController {
onExhausted?.() onExhausted?.()
} }
loading = false promise.resolve()
promise = undefined
} }
} }
async _getDifferenceLoader(feeds: Feed[]) { _getDifferenceLoader(feeds: Feed[]) {
const exhausted = new Set<number>() const exhausted = new Set<number>()
const skip = new Set<string>() const skip = new Set<string>()
const events: TrustedEvent[] = [] const events: TrustedEvent[] = []
const seen = new Set() const seen = new Set()
const controllers = await Promise.all( const controllers = feeds.map(
feeds.map( (thisFeed: Feed, i: number) =>
(thisFeed: Feed, i: number) => new FeedController({
new FeedController({ ...this.options,
...this.options, feed: thisFeed,
feed: thisFeed, onExhausted: () => exhausted.add(i),
onExhausted: () => exhausted.add(i), onEvent: (event: TrustedEvent) => {
onEvent: (event: TrustedEvent) => { if (i === 0) {
if (i === 0) { events.push(event)
events.push(event) } else {
} else { skip.add(event.id)
skip.add(event.id) }
} },
}, }),
}),
),
) )
return async (limit: number) => { return async (limit: number) => {
@@ -200,25 +199,23 @@ export class FeedController {
} }
} }
async _getIntersectionLoader(feeds: Feed[]) { _getIntersectionLoader(feeds: Feed[]) {
const exhausted = new Set<number>() const exhausted = new Set<number>()
const counts = new Map<string, number>() const counts = new Map<string, number>()
const events: TrustedEvent[] = [] const events: TrustedEvent[] = []
const seen = new Set() const seen = new Set()
const controllers = await Promise.all( const controllers = feeds.map(
feeds.map( (thisFeed: Feed, i: number) =>
(thisFeed: Feed, i: number) => new FeedController({
new FeedController({ ...this.options,
...this.options, feed: thisFeed,
feed: thisFeed, onExhausted: () => exhausted.add(i),
onExhausted: () => exhausted.add(i), onEvent: (event: TrustedEvent) => {
onEvent: (event: TrustedEvent) => { events.push(event)
events.push(event) counts.set(event.id, inc(counts.get(event.id)))
counts.set(event.id, inc(counts.get(event.id))) },
}, }),
}),
),
) )
return async (limit: number) => { return async (limit: number) => {
@@ -245,25 +242,23 @@ export class FeedController {
} }
} }
async _getUnionLoader(feeds: Feed[]) { _getUnionLoader(feeds: Feed[]) {
const exhausted = new Set<number>() const exhausted = new Set<number>()
const seen = new Set() const seen = new Set()
const controllers = await Promise.all( const controllers = feeds.map(
feeds.map( (thisFeed: Feed, i: number) =>
(thisFeed: Feed, i: number) => new FeedController({
new FeedController({ ...this.options,
...this.options, feed: thisFeed,
feed: thisFeed, onExhausted: () => exhausted.add(i),
onExhausted: () => exhausted.add(i), onEvent: (event: TrustedEvent) => {
onEvent: (event: TrustedEvent) => { if (!seen.has(event.id)) {
if (!seen.has(event.id)) { this.options.onEvent?.(event)
this.options.onEvent?.(event) seen.add(event.id)
seen.add(event.id) }
} },
}, }),
}),
),
) )
return async (limit: number) => { return async (limit: number) => {
+11 -5
View File
@@ -1,4 +1,4 @@
import {now, uniq, displayList} from "@welshman/lib" import {now, uniq, displayList, formatTimestampAsDate, formatTimestampRelative} from "@welshman/lib"
import { import {
FeedType, FeedType,
Feed, Feed,
@@ -42,13 +42,19 @@ export const displayCreatedAtFeed = (feed: CreatedAtFeed) => {
const parts: string[] = [] const parts: string[] = []
if (since) { if (since) {
const timestamp = relative.includes("since") ? now() - since : since if (relative.includes("since")) {
parts.push(`after ${new Date(timestamp * 1000).toLocaleString()}`) parts.push(`after ${formatTimestampRelative(now() - since)}`)
} else {
parts.push(`after ${formatTimestampAsDate(since)}`)
}
} }
if (until) { if (until) {
const timestamp = relative.includes("until") ? now() - until : until if (relative.includes("until")) {
parts.push(`before ${new Date(timestamp * 1000).toLocaleString()}`) parts.push(`before ${formatTimestampRelative(now() - until)}`)
} else {
parts.push(`before ${formatTimestampAsDate(until)}`)
}
} }
if (parts.length > 0) { if (parts.length > 0) {
+58
View File
@@ -206,3 +206,61 @@ export const walkFeed = (feed: Feed, visit: (feed: Feed) => void) => {
} }
} }
} }
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 (let 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 (let 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 (let sub of args.map(simplifyFeed)) {
if (isDifferenceFeed(sub)) {
modified.push(...getFeedArgs(sub))
} else {
modified.push(sub)
}
}
return makeDifferenceFeed(...modified)
}
return feed
}