Initial OSS commit

This commit is contained in:
Aria Minaei 2021-06-18 13:05:06 +02:00
commit 4a7303f40a
391 changed files with 245738 additions and 0 deletions

View file

@ -0,0 +1,19 @@
import Emitter from './Emitter'
describe.only('DataVerse.Emitter', () => {
it('should work', () => {
const e: Emitter<string> = new Emitter()
e.emit('no one will see this')
e.emit('nor this')
const tappedEvents: string[] = []
const untap = e.tappable.tap((payload) => {
tappedEvents.push(payload)
})
e.emit('foo')
e.emit('bar')
untap()
e.emit('baz')
expect(tappedEvents).toMatchObject(['foo', 'bar'])
})
})

View file

@ -0,0 +1,55 @@
import Tappable from './Tappable'
type Tapper<V> = (v: V) => void
type Untap = () => void
export default class Emitter<V> {
private _tappers: Map<any, (v: V) => void>
private _lastTapperId: number
readonly tappable: Tappable<V>
private _onNumberOfTappersChangeListener: undefined | ((n: number) => void)
constructor() {
this._lastTapperId = 0
this._tappers = new Map()
this.tappable = new Tappable({
tapToSource: (cb: Tapper<V>) => {
return this._tap(cb)
},
})
}
_tap(cb: Tapper<V>): Untap {
const tapperId = this._lastTapperId++
this._tappers.set(tapperId, cb)
this._onNumberOfTappersChangeListener &&
this._onNumberOfTappersChangeListener(this._tappers.size)
return () => {
this._removeTapperById(tapperId)
}
}
_removeTapperById(id: number) {
const oldSize = this._tappers.size
this._tappers.delete(id)
const newSize = this._tappers.size
if (oldSize !== newSize) {
this._onNumberOfTappersChangeListener &&
this._onNumberOfTappersChangeListener(this._tappers.size)
}
}
emit(payload: V) {
this._tappers.forEach((cb) => {
cb(payload)
})
}
hasTappers() {
return this._tappers.size !== 0
}
onNumberOfTappersChange(cb: (n: number) => void) {
this._onNumberOfTappersChangeListener = cb
}
}

View file

@ -0,0 +1,56 @@
import forEach from 'lodash-es/forEach'
import without from 'lodash-es/without'
import type {$FixMe} from '../types'
type Listener = (v: $FixMe) => void
/**
* A simple barebones event emitter
*/
export default class EventEmitter {
_listenersByType: {[eventName: string]: Array<Listener>}
constructor() {
this._listenersByType = {}
}
addEventListener(eventName: string, listener: Listener) {
const listeners =
this._listenersByType[eventName] ||
(this._listenersByType[eventName] = [])
listeners.push(listener)
return this
}
removeEventListener(eventName: string, listener: Listener) {
const listeners = this._listenersByType[eventName]
if (listeners) {
const newListeners = without(listeners, listener)
if (newListeners.length === 0) {
delete this._listenersByType[eventName]
} else {
this._listenersByType[eventName] = newListeners
}
}
return this
}
emit(eventName: string, payload: unknown) {
const listeners = this.getListenersFor(eventName)
if (listeners) {
forEach(listeners, (listener) => {
listener(payload)
})
}
}
getListenersFor(eventName: string) {
return this._listenersByType[eventName]
}
hasListenersFor(eventName: string) {
return this.getListenersFor(eventName) ? true : false
}
}

View file

@ -0,0 +1,133 @@
export type PathBasedReducer<S, ReturnType> = {
<
A0 extends keyof S,
A1 extends keyof S[A0],
A2 extends keyof S[A0][A1],
A3 extends keyof S[A0][A1][A2],
A4 extends keyof S[A0][A1][A2][A3],
A5 extends keyof S[A0][A1][A2][A3][A4],
A6 extends keyof S[A0][A1][A2][A3][A4][A5],
A7 extends keyof S[A0][A1][A2][A3][A4][A5][A6],
A8 extends keyof S[A0][A1][A2][A3][A4][A5][A6][A7],
A9 extends keyof S[A0][A1][A2][A3][A4][A5][A6][A7][A8],
A10 extends keyof S[A0][A1][A2][A3][A4][A5][A6][A7][A8][A9],
>(
addr: [A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10],
reducer: (
d: S[A0][A1][A2][A3][A4][A5][A6][A7][A8][A9][A10],
) => S[A0][A1][A2][A3][A4][A5][A6][A7][A8][A9][A10],
): ReturnType
<
A0 extends keyof S,
A1 extends keyof S[A0],
A2 extends keyof S[A0][A1],
A3 extends keyof S[A0][A1][A2],
A4 extends keyof S[A0][A1][A2][A3],
A5 extends keyof S[A0][A1][A2][A3][A4],
A6 extends keyof S[A0][A1][A2][A3][A4][A5],
A7 extends keyof S[A0][A1][A2][A3][A4][A5][A6],
A8 extends keyof S[A0][A1][A2][A3][A4][A5][A6][A7],
A9 extends keyof S[A0][A1][A2][A3][A4][A5][A6][A7][A8],
>(
addr: [A0, A1, A2, A3, A4, A5, A6, A7, A8, A9],
reducer: (
d: S[A0][A1][A2][A3][A4][A5][A6][A7][A8][A9],
) => S[A0][A1][A2][A3][A4][A5][A6][A7][A8][A9],
): ReturnType
<
A0 extends keyof S,
A1 extends keyof S[A0],
A2 extends keyof S[A0][A1],
A3 extends keyof S[A0][A1][A2],
A4 extends keyof S[A0][A1][A2][A3],
A5 extends keyof S[A0][A1][A2][A3][A4],
A6 extends keyof S[A0][A1][A2][A3][A4][A5],
A7 extends keyof S[A0][A1][A2][A3][A4][A5][A6],
A8 extends keyof S[A0][A1][A2][A3][A4][A5][A6][A7],
>(
addr: [A0, A1, A2, A3, A4, A5, A6, A7, A8],
reducer: (
d: S[A0][A1][A2][A3][A4][A5][A6][A7][A8],
) => S[A0][A1][A2][A3][A4][A5][A6][A7][A8],
): ReturnType
<
A0 extends keyof S,
A1 extends keyof S[A0],
A2 extends keyof S[A0][A1],
A3 extends keyof S[A0][A1][A2],
A4 extends keyof S[A0][A1][A2][A3],
A5 extends keyof S[A0][A1][A2][A3][A4],
A6 extends keyof S[A0][A1][A2][A3][A4][A5],
A7 extends keyof S[A0][A1][A2][A3][A4][A5][A6],
>(
addr: [A0, A1, A2, A3, A4, A5, A6, A7],
reducer: (
d: S[A0][A1][A2][A3][A4][A5][A6][A7],
) => S[A0][A1][A2][A3][A4][A5][A6][A7],
): ReturnType
<
A0 extends keyof S,
A1 extends keyof S[A0],
A2 extends keyof S[A0][A1],
A3 extends keyof S[A0][A1][A2],
A4 extends keyof S[A0][A1][A2][A3],
A5 extends keyof S[A0][A1][A2][A3][A4],
A6 extends keyof S[A0][A1][A2][A3][A4][A5],
>(
addr: [A0, A1, A2, A3, A4, A5, A6],
reducer: (
d: S[A0][A1][A2][A3][A4][A5][A6],
) => S[A0][A1][A2][A3][A4][A5][A6],
): ReturnType
<
A0 extends keyof S,
A1 extends keyof S[A0],
A2 extends keyof S[A0][A1],
A3 extends keyof S[A0][A1][A2],
A4 extends keyof S[A0][A1][A2][A3],
A5 extends keyof S[A0][A1][A2][A3][A4],
>(
addr: [A0, A1, A2, A3, A4, A5],
reducer: (d: S[A0][A1][A2][A3][A4][A5]) => S[A0][A1][A2][A3][A4][A5],
): ReturnType
<
A0 extends keyof S,
A1 extends keyof S[A0],
A2 extends keyof S[A0][A1],
A3 extends keyof S[A0][A1][A2],
A4 extends keyof S[A0][A1][A2][A3],
>(
addr: [A0, A1, A2, A3, A4],
reducer: (d: S[A0][A1][A2][A3][A4]) => S[A0][A1][A2][A3][A4],
): ReturnType
<
A0 extends keyof S,
A1 extends keyof S[A0],
A2 extends keyof S[A0][A1],
A3 extends keyof S[A0][A1][A2],
>(
addr: [A0, A1, A2, A3],
reducer: (d: S[A0][A1][A2][A3]) => S[A0][A1][A2][A3],
): ReturnType
<A0 extends keyof S, A1 extends keyof S[A0], A2 extends keyof S[A0][A1]>(
addr: [A0, A1, A2],
reducer: (d: S[A0][A1][A2]) => S[A0][A1][A2],
): ReturnType
<A0 extends keyof S, A1 extends keyof S[A0]>(
addr: [A0, A1],
reducer: (d: S[A0][A1]) => S[A0][A1],
): ReturnType
<A0 extends keyof S>(addr: [A0], reducer: (d: S[A0]) => S[A0]): ReturnType
(addr: undefined[], reducer: (d: S) => S): ReturnType
}

View file

@ -0,0 +1,33 @@
interface Node<Data> {
next: undefined | Node<Data>
data: Data
}
/**
* Just a simple LinkedList
*/
export default class Stack<Data> {
_head: undefined | Node<Data>
constructor() {
this._head = undefined
}
peek() {
return this._head && this._head.data
}
pop() {
const head = this._head
if (!head) {
return undefined
}
this._head = head.next
return head.data
}
push(data: Data) {
const node = {next: this._head, data}
this._head = node
}
}

View file

@ -0,0 +1,91 @@
type Untap = () => void
type UntapFromSource = () => void
interface IProps<V> {
tapToSource: (cb: (payload: V) => void) => UntapFromSource
}
type Listener<V> = ((v: V) => void) | (() => void)
export default class Tappable<V> {
private _props: IProps<V>
private _tappers: Map<number, {bivarianceHack(v: V): void}['bivarianceHack']>
private _untapFromSource: null | UntapFromSource
private _lastTapperId: number
private _untapFromSourceTimeout: null | NodeJS.Timer = null
constructor(props: IProps<V>) {
this._lastTapperId = 0
this._untapFromSource = null
this._props = props
this._tappers = new Map()
}
private _check() {
if (this._untapFromSource) {
if (this._tappers.size === 0) {
this._scheduleToUntapFromSource()
/*
* this._untapFromSource()
* this._untapFromSource = null
*/
}
} else {
if (this._tappers.size !== 0) {
this._untapFromSource = this._props.tapToSource(this._cb)
}
}
}
private _scheduleToUntapFromSource() {
if (this._untapFromSourceTimeout !== null) return
this._untapFromSourceTimeout = setTimeout(() => {
this._untapFromSourceTimeout = null
if (this._tappers.size === 0) {
this._untapFromSource!()
this._untapFromSource = null
}
}, 0)
}
private _cb: any = (arg: any): void => {
this._tappers.forEach((cb) => {
cb(arg)
})
}
tap(cb: Listener<V>): Untap {
const tapperId = this._lastTapperId++
this._tappers.set(tapperId, cb)
this._check()
return () => {
this._removeTapperById(tapperId)
}
}
/*
* tapImmediate(cb: Listener<V>): Untap {
* const ret = this.tap(cb)
* return ret
* }
*/
private _removeTapperById(id: number) {
this._tappers.delete(id)
this._check()
}
// /**
// * @deprecated
// */
// map<T>(transform: {bivarianceHack(v: V): T}['bivarianceHack']): Tappable<T> {
// return new Tappable({
// tapToSource: (cb: (v: T) => void) => {
// return this.tap((v: $IntentionalAny) => {
// return cb(transform(v))
// })
// },
// })
// }
}

View file

@ -0,0 +1,12 @@
import type {$IntentionalAny} from '../types'
/**
* Useful in type tests, such as: const a: SomeType = _any
*/
export const _any: $IntentionalAny = null
/**
* Useful in typeTests. If you want to ensure that value v follows type V,
* just write `expectType<V>(v)`
*/
export const expectType = <T extends unknown>(v: T): T => v

View file

@ -0,0 +1,42 @@
import type {$FixMe, $IntentionalAny} from '../types'
export default function updateDeep<S>(
state: S,
path: (string | number | undefined)[],
reducer: (...args: $IntentionalAny[]) => $IntentionalAny,
): S {
if (path.length === 0) return reducer(state)
return hoop(state, path as $IntentionalAny, reducer)
}
const hoop = (
s: $FixMe,
path: (string | number)[],
reducer: $FixMe,
): $FixMe => {
if (path.length === 0) {
return reducer(s)
}
if (Array.isArray(s)) {
let [index, ...restOfPath] = path
index = parseInt(String(index), 10)
if (isNaN(index)) index = 0
const oldVal = s[index]
const newVal = hoop(oldVal, restOfPath, reducer)
if (oldVal === newVal) return s
const newS = [...s]
newS.splice(index, 1, newVal)
return newS
} else if (typeof s === 'object' && s !== null) {
const [key, ...restOfPath] = path
const oldVal = s[key]
const newVal = hoop(oldVal, restOfPath, reducer)
if (oldVal === newVal) return s
const newS = {...s, [key]: newVal}
return newS
} else {
const [key, ...restOfPath] = path
return {[key]: hoop(undefined, restOfPath, reducer)}
}
}