import * as c from '@ts-std/codec' ; import { Result as _Result , Ok , Err } from '@ts-std/monads' ; export type Dict < T > = { [ key ] : T ; } ; export function tuple < L extends any [ ] > ( ... items ) { return items ; } export function exec < T > ( fn ) { return fn ( ) ; } export function setExtend < T , U > ( a , b ) { const union = new Set < T | U > ( a ) ; for ( const item of b ) union . add ( item ) ; return union ; } export type UnionKeys < T > = T extends T ? keyof T : never ; export type OmitVariants < U , K extends UnionKeys < U > , V extends U [ K ] > = U extends U ? U [ K ] extends V ? never : U : never ; export type PickVariants < U , K extends UnionKeys < U > , V extends U [ K ] > = U extends U ? U [ K ] extends V ? U : never : never ; export type UnboxArray < A extends unknown [ ] > = A extends ( infer T ) [ ] ? T : never ; export class Registry < T > { protected items : Dict < T > = { } ; peek ( key ) { return key in this . items ; } take ( key ) { const item = this . items [ key ] ; delete this . items [ key ] ; return item ; } put ( key , item ) { this . items [ key ] = item ; } } export class Cache < T > { protected map = new Map < string , T > ( ) ; has ( key ) { return this . map . has ( key ) ; } get ( key ) { return this . map . get ( key ) ; } set ( key , value ) { this . map . set ( key , value ) ; return value ; } } export function cachedLookup < T > ( fn ) { const cache = new Map < string , T > ( ) ; return ( key ) : T => { const item = cache . get ( key ) ; if ( item !== undefined ) return item ; const actual = fn ( key ) ; cache . set ( key , actual ) ; return actual ; } ; } export type NonEmpty < T > = [ T , ... T [ ] ] ; export namespace NonEmpty { export function decoder < T > ( decoder ) { const arrayDecoder = c . array ( decoder ) ; return c . wrap ( `NonEmpty<${ decoder . name }>` , input => { const result = arrayDecoder . decode ( input ) ; if ( result . is_err ( ) ) return result ; const values = result . value ; if ( values . length === 0 ) return Err ( `array empty, expected at least one item` ) ; return Ok ( [ values [ 0 ] , ... values . slice ( 1 ) ] ) ; } ) ; } export function flattenInto < T > ( item ) { return Array . isArray ( item ) ? item : [ item ] ; } } export type NonEmptyOrSingle < T > = T | NonEmpty < T > ; export namespace NonEmptyOrSingle { export function decoder < T > ( decoder ) { return c . union ( decoder , NonEmpty . decoder ( decoder ) ) ; } } export function longestMatchingStem ( value , stems ) { const longest = { length : 0 , stem : undefined as string | undefined } ; for ( const stem of stems ) { const stemLength = stem . length ; if ( stemLength <= longest . length || ! value . startsWith ( stem ) ) continue ; longest . stem = stem ; longest . length = stemLength ; } return longest . stem ; }