Compile intersections
This commit is contained in:
Generated
+3
-3
@@ -3064,7 +3064,7 @@
|
|||||||
"version": "0.0.2",
|
"version": "0.0.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@welshman/util": "0.0.2"
|
"@welshman/util": "0.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"gts": "^5.0.1",
|
"gts": "^5.0.1",
|
||||||
@@ -3102,7 +3102,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@welshman/lib": "0.0.2",
|
"@welshman/lib": "0.0.2",
|
||||||
"@welshman/util": "0.0.2",
|
"@welshman/util": "0.0.3",
|
||||||
"isomorphic-ws": "^5.0.0",
|
"isomorphic-ws": "^5.0.0",
|
||||||
"ws": "^8.16.0"
|
"ws": "^8.16.0"
|
||||||
},
|
},
|
||||||
@@ -3115,7 +3115,7 @@
|
|||||||
},
|
},
|
||||||
"packages/util": {
|
"packages/util": {
|
||||||
"name": "@welshman/util",
|
"name": "@welshman/util",
|
||||||
"version": "0.0.2",
|
"version": "0.0.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@welshman/lib": "0.0.2",
|
"@welshman/lib": "0.0.2",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {uniq, tryCatch, now, isNil} from '@welshman/lib'
|
import {uniq, tryCatch, now, isNil} from '@welshman/lib'
|
||||||
import type {Rumor, Filter} from '@welshman/util'
|
import type {Rumor, Filter} from '@welshman/util'
|
||||||
import {Tags, getIdFilters, mergeFilters} from '@welshman/util'
|
import {Tags, intersectFilters, BOGUS_RELAY_URL, getIdFilters, unionFilters} from '@welshman/util'
|
||||||
import type {RequestItem, DVMItem, Scope, Feed, DynamicFilter, FeedOptions} from './core'
|
import type {RequestItem, DVMItem, Scope, Feed, DynamicFilter, FeedOptions} from './core'
|
||||||
import {FeedType, getSubFeeds} from './core'
|
import {FeedType, getSubFeeds} from './core'
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@ export class FeedCompiler<E extends Rumor> {
|
|||||||
switch(type) {
|
switch(type) {
|
||||||
case FeedType.Relay:
|
case FeedType.Relay:
|
||||||
case FeedType.Union:
|
case FeedType.Union:
|
||||||
|
case FeedType.Intersection:
|
||||||
return getSubFeeds([type, ...feed] as Feed).every(this.canCompile)
|
return getSubFeeds([type, ...feed] as Feed).every(this.canCompile)
|
||||||
case FeedType.Filter:
|
case FeedType.Filter:
|
||||||
case FeedType.List:
|
case FeedType.List:
|
||||||
@@ -33,6 +34,8 @@ export class FeedCompiler<E extends Rumor> {
|
|||||||
switch(type) {
|
switch(type) {
|
||||||
case FeedType.Union:
|
case FeedType.Union:
|
||||||
return await this._compileUnion(feed as Feed[])
|
return await this._compileUnion(feed as Feed[])
|
||||||
|
case FeedType.Intersection:
|
||||||
|
return await this._compileIntersection(feed as Feed[])
|
||||||
case FeedType.Relay:
|
case FeedType.Relay:
|
||||||
/* eslint no-case-declarations: 0 */
|
/* eslint no-case-declarations: 0 */
|
||||||
const {relays, filters} = await this._compileUnion(feed.slice(1) as Feed[])
|
const {relays, filters} = await this._compileUnion(feed.slice(1) as Feed[])
|
||||||
@@ -72,10 +75,30 @@ export class FeedCompiler<E extends Rumor> {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
relays: uniq(relays),
|
relays: uniq(relays),
|
||||||
filters: mergeFilters(filters),
|
filters: unionFilters(filters),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _compileIntersection(feeds: Feed[]): Promise<RequestItem> {
|
||||||
|
const items = await Promise.all(feeds.map(this.compile))
|
||||||
|
const filters = intersectFilters(items.map(item => item.filters))
|
||||||
|
|
||||||
|
let relays = uniq(items.flatMap(item => item.relays))
|
||||||
|
let hasRelays = relays.length > 0
|
||||||
|
|
||||||
|
items.forEach((item, i) => {
|
||||||
|
if (item.relays.length > 0) {
|
||||||
|
relays = relays.filter(relay => item.relays.includes(relay))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (hasRelays && relays.length === 0) {
|
||||||
|
relays.push(BOGUS_RELAY_URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {relays, filters}
|
||||||
|
}
|
||||||
|
|
||||||
async _compileLists(addresses: string[]): Promise<RequestItem> {
|
async _compileLists(addresses: string[]): Promise<RequestItem> {
|
||||||
const events: E[] = []
|
const events: E[] = []
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,6 @@
|
|||||||
"typescript": "~5.1.6"
|
"typescript": "~5.1.6"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@welshman/util": "0.0.2"
|
"@welshman/util": "0.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type {Event} from 'nostr-tools'
|
import type {Event} from 'nostr-tools'
|
||||||
import {Emitter, randomId, groupBy, batch, defer, uniq, uniqBy} from '@welshman/lib'
|
import {Emitter, randomId, groupBy, batch, defer, uniq, uniqBy} from '@welshman/lib'
|
||||||
import type {Deferred} from '@welshman/lib'
|
import type {Deferred} from '@welshman/lib'
|
||||||
import {matchFilters, mergeFilters} from '@welshman/util'
|
import {matchFilters, unionFilters} from '@welshman/util'
|
||||||
import type {Filter} from '@welshman/util'
|
import type {Filter} from '@welshman/util'
|
||||||
import {Tracker} from "./Tracker"
|
import {Tracker} from "./Tracker"
|
||||||
import {Connection} from './Connection'
|
import {Connection} from './Connection'
|
||||||
@@ -83,7 +83,7 @@ export const mergeSubscriptions = (subs: Subscription[]) => {
|
|||||||
const mergedSub = makeSubscription({
|
const mergedSub = makeSubscription({
|
||||||
relays: [relay],
|
relays: [relay],
|
||||||
timeout: callerSubs[0].request.timeout,
|
timeout: callerSubs[0].request.timeout,
|
||||||
filters: mergeFilters(callerSubs.flatMap((sub: Subscription) => sub.request.filters)),
|
filters: unionFilters(callerSubs.flatMap((sub: Subscription) => sub.request.filters)),
|
||||||
})
|
})
|
||||||
|
|
||||||
for (const {id, emitter} of callerSubs) {
|
for (const {id, emitter} of callerSubs) {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@welshman/lib": "0.0.2",
|
"@welshman/lib": "0.0.2",
|
||||||
"@welshman/util": "0.0.2",
|
"@welshman/util": "0.0.3",
|
||||||
"isomorphic-ws": "^5.0.0",
|
"isomorphic-ws": "^5.0.0",
|
||||||
"ws": "^8.16.0"
|
"ws": "^8.16.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export const calculateFilterGroup = ({since, until, limit, search, ...filter}: F
|
|||||||
return group.sort().join("-")
|
return group.sort().join("-")
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mergeFilters = (filters: Filter[]) => {
|
export const unionFilters = (filters: Filter[]) => {
|
||||||
const result = []
|
const result = []
|
||||||
|
|
||||||
for (const group of Object.values(groupBy(calculateFilterGroup, filters))) {
|
for (const group of Object.values(groupBy(calculateFilterGroup, filters))) {
|
||||||
@@ -96,6 +96,38 @@ export const mergeFilters = (filters: Filter[]) => {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const intersectFilters = (groups: Filter[][]) => {
|
||||||
|
let result = groups[0]
|
||||||
|
|
||||||
|
for (const filters of groups.slice(1)) {
|
||||||
|
result = result.flatMap(f1 => {
|
||||||
|
return filters.map(f2 => {
|
||||||
|
const f3: Filter = {}
|
||||||
|
|
||||||
|
for (const k of uniq([...Object.keys(f1), ...Object.keys(f2)]) as (keyof Filter)[]) {
|
||||||
|
if (k === 'since' || k === 'limit') {
|
||||||
|
f3[k] = Math.max(f1[k] || 0, f2[k] || 0)
|
||||||
|
} else if (k === 'until') {
|
||||||
|
f3[k] = Math.min(f1[k] || f2[k] || 0, f2[k] || f1[k] || 0)
|
||||||
|
} else if (k === 'search') {
|
||||||
|
if (f1[k] && f2[k] && f1[k] !== f2[k]) {
|
||||||
|
f3[k] = [f1[k], f2[k]].join(' ')
|
||||||
|
} else {
|
||||||
|
f3[k] = f1[k] || f2[k]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f3[k] = uniq([...(f1[k] || []), ...(f2[k] || [])]) as any[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return f3
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return unionFilters(result)
|
||||||
|
}
|
||||||
|
|
||||||
export const getIdFilters = (idsOrAddresses: Iterable<string>) => {
|
export const getIdFilters = (idsOrAddresses: Iterable<string>) => {
|
||||||
const ids = []
|
const ids = []
|
||||||
const aFilters = []
|
const aFilters = []
|
||||||
@@ -114,7 +146,7 @@ export const getIdFilters = (idsOrAddresses: Iterable<string>) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const filters = mergeFilters(aFilters)
|
const filters = unionFilters(aFilters)
|
||||||
|
|
||||||
if (ids.length > 0) {
|
if (ids.length > 0) {
|
||||||
filters.push({ids})
|
filters.push({ids})
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import {normalizeUrl, stripProtocol} from '@welshman/lib'
|
import {normalizeUrl, stripProtocol} from '@welshman/lib'
|
||||||
|
|
||||||
|
export const LOCAL_RELAY_URL = "local://welshman.relay"
|
||||||
|
|
||||||
|
export const BOGUS_RELAY_URL = "bogus://welshman.relay"
|
||||||
|
|
||||||
export const isShareableRelayUrl = (url: string) =>
|
export const isShareableRelayUrl = (url: string) =>
|
||||||
Boolean(
|
Boolean(
|
||||||
typeof url === 'string' &&
|
typeof url === 'string' &&
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@welshman/util",
|
"name": "@welshman/util",
|
||||||
"version": "0.0.2",
|
"version": "0.0.3",
|
||||||
"author": "hodlbod",
|
"author": "hodlbod",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"description": "A collection of nostr-related utilities.",
|
"description": "A collection of nostr-related utilities.",
|
||||||
|
|||||||
Reference in New Issue
Block a user