UNPKG

@tvkitchen/countertop

Version:

The entry point for developers who want to set up a TV Kitchen.

213 lines (196 loc) 7.53 kB
import { arraysHaveOverlap } from '.' import { sanitizeTopic } from './kafka' /** * Returns a list of stations that are considered sources. * * The only way a station can currently be a source is if it has no inputs. * This definition may expand over time. * * @param {CountertopStation[]} stations The stations being processed. * @return {CountertopStation[]} The source stations. */ export const getSourceStations = (stations) => stations.filter( (station) => station.getInputTypes().length === 0, ) /** * Returns the length (number of stations) of the longest CountertopStream in a set * of CountertopStreams. * * @param {CountertopStream[]} streams The streams being processed. * @return {Number} The longest length. */ export const getLongestStreamLength = (streams) => Math.max( ...streams.map((stream) => stream.getLength()), 0, ) /** * Returns a list of any type produced by any stream in the set. * * @param {CountertopStream[]} streams The CountertopStreams being processed. * @return {String[]} The types produced by any of those CountertopStreams. */ export const getCollectiveOutputTypes = (streams) => [ ...new Set(streams.flatMap((stream) => stream.getOutputTypes())), ] /** * Returns a subset of stations that consume any of the specified types * * @param {CountertopStation[]} stations The array of stations to search. * @param {String[]} types The types that are being searched for. * @return {CountertopStation[]} The filtered array of stations */ export const getStationsThatConsumeTypes = (stations, types) => stations.filter( (station) => arraysHaveOverlap( station.getInputTypes(), types, ), ) /** * Returns a subset of streams that produce any of the specified types. * * @param {CountertopStream[]} streams The array of streams to search. * @param {String[]} types The types that are being searched for. * @return {CountertopStream[]} The filtered array of streams */ export const getStreamsThatProduceTypes = (streams, types) => streams.filter( (stream) => arraysHaveOverlap( stream.getOutputTypes(), types, ), ) /** * Returns a subset of streams that do not contain a given station. * * @param {CountertopStream[]} streams The array of streams to search. * @param {CountertopStation} station The station being filtered out. * @return {CountertopStream[]} The filtered array of streams. */ export const filterStreamsContainingStation = (streams, station) => streams .filter((stream) => !stream.includesStation(station)) /** * Returns a subset of streams that do not contain a given stream. * Note that this will also filter the stream itself. * @param {CountertopStream[]} streams * @param {CountertopStream} filteredStream * @returns {CountertopStream[]} */ export const filterStreamsContainingStream = (streams, filteredStream) => streams .filter((stream) => !stream.includesStream(filteredStream) && stream !== filteredStream) /** * Generates a Map of type / stream[] pairs based on streams who output the types. * * @param {CountertopStream[]} streams The CountertopStreams being mapped. * @return {Map} The resulting map of types => streams */ export const getStreamOutputMap = (streams) => new Map( getCollectiveOutputTypes(streams).map( (type) => [ type, getStreamsThatProduceTypes(streams, [type]), ], ), ) /** * Get an array of distinct sources reflected inside of an Array of streams. * * This will return exactly one copy of any source represented in those streams. * * @param {CountetopStream[]} streams An array ofstreams * @return {CountertopStation[]} An array of distinct sources for the streams. */ export const getSourcesFromStreams = (streams) => [...new Set( streams.map( (stream) => stream.getSource(), ), )] /** * Get an array of unique streams represented in a map of streams. * * @param {Map<CountertopStream[]>} streamMap The stream map to be processed. * @return {CountertopStream[]} The unique streams contained in the map. */ export const getStreamsFromStreamMap = (streamMap) => [ ...new Set([...streamMap.values()].flat()), ] /** * Get an array of distinct sources reflected inside of a stream Map. * * This will return exactly one copy of any source represented in those streams. * * @param {Map<CountetopStream[]>} streamMap A Map containing streams * @return {CountertopStation[]} An array of distinct sources for the streams. */ export const getSourcesFromStreamMap = (streamMap) => getSourcesFromStreams( getStreamsFromStreamMap(streamMap), ) /** * Create all sets of valid stream combinations that can sufficiently fulfill a set of inputs * * This will only create combinations of streams that pull from the same source. * This will not return incomplete tributary maps (i.e. tributary maps will have exactly one stream * per input). * * @param {CountertopStation} station The station whose tributaries are being mapped. * @param {Map<CountertopStream[]>} streamOutputMap A map of of streams grouped by output type. * @return {Map<CountertopStream>[]} A map of tributary streams for a set of inputs. */ export const generateTributaryMaps = (station, streamOutputMap) => { const sources = getSourcesFromStreamMap(streamOutputMap) const tributarySeeds = sources.map((source) => new Map([['source', source]])) const inputTypes = station.getInputTypes() return inputTypes.reduce( (accumulator, type) => accumulator.flatMap( (tributarySet) => { const expandedTributarySets = (streamOutputMap.get(type) || []) .filter((stream) => stream.getSource() === tributarySet.get('source')) .map((stream) => tributarySet.set(type, stream)) return (expandedTributarySets.length > 0) ? expandedTributarySets : [tributarySet] }, ), tributarySeeds, ).map( (tributarySet) => { tributarySet.delete('source') // 'source' was an internal helper field return tributarySet }, ) } /** * Returns the kafka topic associated with a given stream + type pair. * * @param {String} dataType The data type. * @param {CountertopStream} stream The stream. * @return {String} The resulting kafka topic. */ export const getStreamTopic = (dataType, stream) => sanitizeTopic(`${dataType}::${stream.id}`) const isSubsetOf = (arr1, arr2) => arr1.every((value) => arr2.includes(value)) /** * Returns any streams with the same station whose mouth + source is the same and whose * tributaries are a direct subset (or equal set). * * @param {CountertopStream[]} currentStreams The base set of streams. * @param {CountertopStream[]} nextStreams The new streams to use to determine pruning. * @return {CountertopStream[]} The pruned subset of the currentStreams */ export const identifyObsoleteStreams = (currentStreams, nextStreams) => ( currentStreams.filter( (currentStream) => nextStreams.some( (nextStream) => ( currentStream.mouth.id === nextStream.mouth.id && currentStream.source.id === nextStream.source.id && isSubsetOf(currentStream.getTributaryArray(), nextStream.getTributaryArray()) ), ), ) ) export const pruneStreams = (currentStreams, removedStreams) => ( removedStreams.reduce( (remainingStreams, removedStream) => filterStreamsContainingStream( remainingStreams, removedStream, ), currentStreams, ) )