import type { EntityID } from '@wovin/core/applog' import type { IShare } from '@wovin/core/pubsub' import type { Thread, ThreadOnlyCurrentNoDeleted } from '@wovin/core/thread' import { joinThreads } from '@wovin/core/applog' import { lastWriteWins, query, threadFromMaybeArray, withoutDeleted } from '@wovin/core/query' import { getAgents } from '@wovin/core/thread' import { notNull } from '@wovin/utils/types' import { Logger } from 'besonders-logger' import { blockThreadWithRecursiveKids, getBlocksWithTags } from './block-utils-nowin' // import { notNull } from './Utils' const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.DEBUG) // eslint-disable-line unused-imports/no-unused-vars export function getShareThread(rootThread: Thread, share: IShare) { if (!share.selectors?.length) { return rootThread } const threads = share.selectors.map((selector) => { return filterThreadBySelector(rootThread, selector) }).filter(notNull) DEBUG(`[getShareData]`, { share, threads, rootThread }) // if (!threads.length) { // return null // ? EmptyThread // } let joinedThread = joinThreads(threads) const agentHashes = getAgents(joinedThread) const agentLogs = query(rootThread, { en: en => agentHashes.includes(en), vl: 'agent/appAgent' }).leafNodeLogs const agentLogsToAdd = agentLogs.filter(l => joinedThread.hasApplog(l, true)) if (agentLogsToAdd.length > 0) { joinedThread = joinThreads([joinedThread, threadFromMaybeArray(agentLogsToAdd, 'getShareThread.agents')]) } return joinedThread } function filterThreadBySelector(rootThread: Thread, selector: string): Thread { const filters = selector.split('|').map(s => s.trim()) const blocksMatch = filters[0].match(RE_SELECTOR_BLOCKS) const tagsMatch = filters[0].match(RE_SELECTOR_TAGS) let thread = rootThread const currentThread = withoutDeleted(lastWriteWins(thread)) as ThreadOnlyCurrentNoDeleted let blockIDs: EntityID[] if (tagsMatch) { filters.splice(0) // remove first filter const tags = tagsMatch[1].split(',') blockIDs = tags.flatMap((tag) => { return getBlocksWithTags(currentThread, [tag]) }) } if (blocksMatch) { filters.splice(0) // remove first filter blockIDs = blocksMatch[1].split(',') } if (blockIDs) { const blockThreads = blockIDs.map((blockID) => { // const rootVM = BlockVM.get(blockID, rootThread) // HACK: extract function from BlockVM // if (!rootVM.exists) { // return null // } const recursiveThread = blockThreadWithRecursiveKids(thread, blockID) if (VERBOSE.isEnabled) VERBOSE(`[filterThreadBySelector] block`, blockID, { logs: recursiveThread }) return recursiveThread }).filter(notNull) // if (!blockThreads.length) { // return null // ? EmptyThread // } thread = joinThreads(blockThreads) } if (filters[filters.length - 1] === 'lastWriteWins') { filters.splice(filters.length - 1) // remove last filter thread = lastWriteWins(thread) } DEBUG(`[filterThreadBySelector]`, { rootThread, selector, thread }) return thread } export const RE_SELECTOR_BLOCKS = /^blocks\(([^)]+)\)/ export const RE_SELECTOR_TAGS = /^tags\(([^)]+)\)/ export const RE_SELECTOR_WITHOUT_HISTORY = /(\| )?lastWriteWins/ export function makeBlocksSelector(blocks: string[], withoutHistory: boolean = false) { return `blocks(${blocks.join(',')})${withoutHistory ? ` | withoutHistory` : ''}` } export function makeTagSelector(tags: string[]) { return `tags(${tags.join(',')})` } export function toggleLastWriteWinsSelector(selector: string, lastWriteWins: boolean) { const match = selector.match(RE_SELECTOR_WITHOUT_HISTORY) if (lastWriteWins) { return match ? selector : `${selector} | lastWriteWins` } else { return match ? selector.replace(match[0], '').trim() : selector } }