Initial commit
This commit is contained in:
commit
6c4696227b
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.idea*
|
5
di/module.ts
Normal file
5
di/module.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export { container, make } from './src/global.ts'
|
||||||
|
export { Inject, Injectable } from './src/decorator/Injection.ts'
|
||||||
|
export { Service } from './src/decorator/Service.ts'
|
||||||
|
export { default as AbstractFactory } from './src/factory/AbstractFactory.ts'
|
||||||
|
export { Container, InvalidDependencyKeyError, DuplicateFactoryKeyError, MaybeDependency, MaybeFactory } from './src/Container.ts'
|
152
di/src/Container.ts
Executable file
152
di/src/Container.ts
Executable file
@ -0,0 +1,152 @@
|
|||||||
|
import AbstractFactory from './factory/AbstractFactory.ts'
|
||||||
|
import Factory from './factory/Factory.ts'
|
||||||
|
import SingletonFactory from './factory/SingletonFactory.ts'
|
||||||
|
import FunctionFactory from './factory/FunctionFactory.ts'
|
||||||
|
import NamedFactory from './factory/NamedFactory.ts'
|
||||||
|
import { DependencyKey } from './type/DependencyKey.ts'
|
||||||
|
import { collect, Collection } from '../../lib/src/collection/Collection.ts'
|
||||||
|
import Instantiable, {isInstantiable} from './type/Instantiable.ts'
|
||||||
|
|
||||||
|
type MaybeFactory = AbstractFactory | undefined
|
||||||
|
type MaybeDependency = any | undefined
|
||||||
|
type ResolvedDependency = { param_index: number, key: DependencyKey, resolved: any }
|
||||||
|
|
||||||
|
interface InstanceRef {
|
||||||
|
key: DependencyKey,
|
||||||
|
value: any,
|
||||||
|
}
|
||||||
|
|
||||||
|
class DuplicateFactoryKeyError extends Error {
|
||||||
|
constructor(key: DependencyKey) {
|
||||||
|
super(`A factory definition already exists with the key for ${key}.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InvalidDependencyKeyError extends Error {
|
||||||
|
constructor(key: DependencyKey) {
|
||||||
|
super(`No such dependency is registered with this container: ${key}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Container {
|
||||||
|
private factories: Collection<AbstractFactory> = new Collection<AbstractFactory>()
|
||||||
|
private instances: Collection<InstanceRef> = new Collection<InstanceRef>()
|
||||||
|
|
||||||
|
register(dependency: Instantiable<any>) {
|
||||||
|
if ( this.resolve(dependency) )
|
||||||
|
throw new DuplicateFactoryKeyError(dependency)
|
||||||
|
|
||||||
|
const factory = new Factory(dependency)
|
||||||
|
this.factories.push(factory)
|
||||||
|
}
|
||||||
|
|
||||||
|
register_producer(name: string, producer: () => any) {
|
||||||
|
if ( this.resolve(name) )
|
||||||
|
throw new DuplicateFactoryKeyError(name)
|
||||||
|
|
||||||
|
const factory = new FunctionFactory(name, producer)
|
||||||
|
this.factories.push(factory)
|
||||||
|
}
|
||||||
|
|
||||||
|
register_named(name: string, dependency: Instantiable<any>) {
|
||||||
|
if ( this.resolve(name) )
|
||||||
|
throw new DuplicateFactoryKeyError(name)
|
||||||
|
|
||||||
|
const factory = new NamedFactory(name, dependency)
|
||||||
|
this.factories.push(factory)
|
||||||
|
}
|
||||||
|
|
||||||
|
register_singleton(key: string, value: any) {
|
||||||
|
if ( this.resolve(key) )
|
||||||
|
throw new DuplicateFactoryKeyError(key)
|
||||||
|
|
||||||
|
this.factories.push(new SingletonFactory(value, key))
|
||||||
|
}
|
||||||
|
|
||||||
|
register_factory(factory: AbstractFactory) {
|
||||||
|
if ( !this.factories.includes(factory) )
|
||||||
|
this.factories.push(factory)
|
||||||
|
}
|
||||||
|
|
||||||
|
has_instance(key: DependencyKey): boolean {
|
||||||
|
return this.instances.where('key', '=', key).isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
has_key(key: DependencyKey): boolean {
|
||||||
|
return !!this.resolve(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
get_existing_instance(key: DependencyKey): MaybeDependency {
|
||||||
|
const instances = this.instances.where('key', '=', key)
|
||||||
|
if ( instances.isNotEmpty() ) return instances.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(key: DependencyKey): MaybeFactory {
|
||||||
|
const factory = this.factories.firstWhere(item => item.match(key))
|
||||||
|
if ( factory ) return factory
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_and_create(key: DependencyKey, ...parameters: any[]): any {
|
||||||
|
// If we've already instantiated this, just return that
|
||||||
|
const instance = this.get_existing_instance(key)
|
||||||
|
if ( typeof instance !== 'undefined' ) return instance.value
|
||||||
|
|
||||||
|
// Otherwise, attempt to create it
|
||||||
|
const factory = this.resolve(key)
|
||||||
|
if ( !factory )
|
||||||
|
throw new InvalidDependencyKeyError(key)
|
||||||
|
|
||||||
|
// Produce and store a new instance
|
||||||
|
const new_instance = this.produce_factory(factory, parameters)
|
||||||
|
this.instances.push({
|
||||||
|
key,
|
||||||
|
value: new_instance,
|
||||||
|
})
|
||||||
|
|
||||||
|
return new_instance
|
||||||
|
}
|
||||||
|
|
||||||
|
protected produce_factory(factory: AbstractFactory, parameters: any[]) {
|
||||||
|
// Create the dependencies for the factory
|
||||||
|
const keys = factory.get_dependency_keys().filter(req => this.has_key(req.key))
|
||||||
|
const dependencies = keys.map<ResolvedDependency>(req => {
|
||||||
|
return {
|
||||||
|
param_index: req.param_index,
|
||||||
|
key: req.key,
|
||||||
|
resolved: this.resolve_and_create(req.key),
|
||||||
|
}
|
||||||
|
}).sortBy('param_index')
|
||||||
|
|
||||||
|
// Build the arguments for the factory, using dependencies in the
|
||||||
|
// correct param_index positions, or parameters of we don't have
|
||||||
|
// the dependency.
|
||||||
|
const construction_args = []
|
||||||
|
let params = collect(parameters).reverse()
|
||||||
|
for ( let i = 0; i <= dependencies.max('param_index'); i++ ) {
|
||||||
|
const dep = dependencies.firstWhere('param_index', '=', i)
|
||||||
|
if ( dep ) construction_args.push(dep.resolved)
|
||||||
|
else construction_args.push(params.pop())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Produce a new instance
|
||||||
|
return factory.produce(construction_args, params.reverse().all())
|
||||||
|
}
|
||||||
|
|
||||||
|
make<T>(target: Instantiable<T>|DependencyKey, ...parameters: any[]): T {
|
||||||
|
if ( isInstantiable(target) )
|
||||||
|
return this.produce_factory(new Factory(target), parameters)
|
||||||
|
else
|
||||||
|
return this.resolve_and_create(target, ...parameters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Container,
|
||||||
|
InvalidDependencyKeyError,
|
||||||
|
DuplicateFactoryKeyError,
|
||||||
|
MaybeDependency,
|
||||||
|
MaybeFactory,
|
||||||
|
ResolvedDependency,
|
||||||
|
InstanceRef,
|
||||||
|
}
|
73
di/src/decorator/Injection.ts
Executable file
73
di/src/decorator/Injection.ts
Executable file
@ -0,0 +1,73 @@
|
|||||||
|
import {Reflect} from '../../../lib/src/external/reflect.ts'
|
||||||
|
import {DEPENDENCY_KEYS_METADATA_KEY, DependencyKey} from '../type/DependencyKey.ts'
|
||||||
|
import {collect, Collection} from '../../../lib/src/collection/Collection.ts'
|
||||||
|
import {DependencyRequirement} from '../type/DependencyRequirement.ts'
|
||||||
|
|
||||||
|
const initDependencyMetadata = (target: Object): Collection<DependencyRequirement> => {
|
||||||
|
const param_types = Reflect.getMetadata('design:paramtypes', target)
|
||||||
|
return collect<DependencyKey>(param_types).map<DependencyRequirement>((type, index) => {
|
||||||
|
return {
|
||||||
|
param_index: index,
|
||||||
|
key: type,
|
||||||
|
overridden: false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const Injectable = (): ClassDecorator => {
|
||||||
|
return (target) => {
|
||||||
|
const meta = initDependencyMetadata(target)
|
||||||
|
const existing = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, target)
|
||||||
|
const new_meta = new Collection<DependencyRequirement>()
|
||||||
|
|
||||||
|
if ( existing ) {
|
||||||
|
const max_new = meta.max('param_index')
|
||||||
|
const max_existing = existing.max('param_index')
|
||||||
|
for ( let i = 0; i <= Math.max(max_new, max_existing); i++ ) {
|
||||||
|
const existing_dr = existing.firstWhere('param_index', '=', i)
|
||||||
|
const new_dr = meta.firstWhere('param_index', '=', i)
|
||||||
|
|
||||||
|
if ( existing_dr && !new_dr ) {
|
||||||
|
new_meta.push(existing_dr)
|
||||||
|
} else if ( new_dr && !existing_dr ) {
|
||||||
|
new_meta.push(new_dr)
|
||||||
|
} else if ( new_dr && existing_dr ) {
|
||||||
|
if ( existing_dr.overridden ) {
|
||||||
|
new_meta.push(existing_dr)
|
||||||
|
} else {
|
||||||
|
new_meta.push(new_dr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
new_meta.concat(meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
Reflect.defineMetadata(DEPENDENCY_KEYS_METADATA_KEY, new_meta, target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Inject = (key: DependencyKey): ParameterDecorator => {
|
||||||
|
return (target, property, param_index) => {
|
||||||
|
if ( !Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, target) ) {
|
||||||
|
Reflect.defineMetadata(DEPENDENCY_KEYS_METADATA_KEY, initDependencyMetadata(target), target)
|
||||||
|
}
|
||||||
|
|
||||||
|
const meta = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, target)
|
||||||
|
const req = meta.firstWhere('param_index', '=', param_index)
|
||||||
|
if ( req ) {
|
||||||
|
req.key = key
|
||||||
|
req.overridden = true
|
||||||
|
} else {
|
||||||
|
meta.push({
|
||||||
|
param_index,
|
||||||
|
key,
|
||||||
|
overridden: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Reflect.defineMetadata(DEPENDENCY_KEYS_METADATA_KEY, meta, target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Inject, Injectable, initDependencyMetadata }
|
13
di/src/decorator/Service.ts
Executable file
13
di/src/decorator/Service.ts
Executable file
@ -0,0 +1,13 @@
|
|||||||
|
import { container } from '../global.ts'
|
||||||
|
import { isInstantiable } from '../type/Instantiable.ts'
|
||||||
|
|
||||||
|
const Service = (name?: string): ClassDecorator => {
|
||||||
|
return (target) => {
|
||||||
|
if ( isInstantiable(target) ) {
|
||||||
|
if ( name ) container.register_named(name, target)
|
||||||
|
else container.register(target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Service }
|
12
di/src/factory/AbstractFactory.ts
Normal file
12
di/src/factory/AbstractFactory.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import {Collection} from '../../../lib/src/collection/Collection.ts'
|
||||||
|
import {DependencyRequirement} from '../type/DependencyRequirement.ts'
|
||||||
|
|
||||||
|
export default abstract class AbstractFactory {
|
||||||
|
protected constructor(
|
||||||
|
protected token: any
|
||||||
|
) {}
|
||||||
|
|
||||||
|
abstract produce(dependencies: any[], parameters: any[]): any
|
||||||
|
abstract match(something: any): boolean
|
||||||
|
abstract get_dependency_keys(): Collection<DependencyRequirement>
|
||||||
|
}
|
28
di/src/factory/Factory.ts
Executable file
28
di/src/factory/Factory.ts
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
import Instantiable from '../type/Instantiable.ts'
|
||||||
|
import { DEPENDENCY_KEYS_METADATA_KEY } from '../type/DependencyKey.ts'
|
||||||
|
import { Reflect } from '../../../lib/src/external/reflect.ts'
|
||||||
|
import { Collection } from '../../../lib/src/collection/Collection.ts'
|
||||||
|
import { DependencyRequirement } from '../type/DependencyRequirement.ts'
|
||||||
|
import AbstractFactory from './AbstractFactory.ts'
|
||||||
|
|
||||||
|
export default class Factory extends AbstractFactory {
|
||||||
|
constructor(
|
||||||
|
protected token: Instantiable<any>
|
||||||
|
) {
|
||||||
|
super(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
produce(dependencies: any[], parameters: any[]) {
|
||||||
|
return new this.token(...dependencies, ...parameters)
|
||||||
|
}
|
||||||
|
|
||||||
|
match(something: any) {
|
||||||
|
return something === this.token || (something.toString && String(something) === this.token.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
get_dependency_keys(): Collection<DependencyRequirement> {
|
||||||
|
const meta = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, this.token)
|
||||||
|
if ( meta ) return meta
|
||||||
|
return new Collection<DependencyRequirement>()
|
||||||
|
}
|
||||||
|
}
|
24
di/src/factory/FunctionFactory.ts
Normal file
24
di/src/factory/FunctionFactory.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import AbstractFactory from './AbstractFactory.ts'
|
||||||
|
import {DependencyRequirement} from '../type/DependencyRequirement.ts'
|
||||||
|
import {Collection} from '../../../lib/src/collection/Collection.ts'
|
||||||
|
|
||||||
|
export default class FunctionFactory extends AbstractFactory {
|
||||||
|
constructor(
|
||||||
|
protected name: string,
|
||||||
|
protected token: () => any,
|
||||||
|
) {
|
||||||
|
super(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
get_dependency_keys(): Collection<DependencyRequirement> {
|
||||||
|
return new Collection<DependencyRequirement>()
|
||||||
|
}
|
||||||
|
|
||||||
|
match(something: any) {
|
||||||
|
return something === this.name
|
||||||
|
}
|
||||||
|
|
||||||
|
produce(dependencies: any[], parameters: any[]): any {
|
||||||
|
return this.token()
|
||||||
|
}
|
||||||
|
}
|
15
di/src/factory/NamedFactory.ts
Normal file
15
di/src/factory/NamedFactory.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import Factory from './Factory.ts'
|
||||||
|
import Instantiable from "../type/Instantiable.ts";
|
||||||
|
|
||||||
|
export default class NamedFactory extends Factory {
|
||||||
|
constructor(
|
||||||
|
protected name: string,
|
||||||
|
protected token: Instantiable<any>,
|
||||||
|
) {
|
||||||
|
super(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
match(something: any) {
|
||||||
|
return something === this.name
|
||||||
|
}
|
||||||
|
}
|
24
di/src/factory/SingletonFactory.ts
Normal file
24
di/src/factory/SingletonFactory.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import Factory from './Factory.ts'
|
||||||
|
import {Collection} from '../../../lib/src/collection/Collection.ts'
|
||||||
|
import {DependencyRequirement} from '../type/DependencyRequirement.ts'
|
||||||
|
|
||||||
|
export default class SingletonFactory extends Factory {
|
||||||
|
constructor(
|
||||||
|
protected token: FunctionConstructor,
|
||||||
|
protected key: string,
|
||||||
|
) {
|
||||||
|
super(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
produce(dependencies: any[], parameters: any[]) {
|
||||||
|
return this.token
|
||||||
|
}
|
||||||
|
|
||||||
|
match(something: any) {
|
||||||
|
return something === this.key
|
||||||
|
}
|
||||||
|
|
||||||
|
get_dependency_keys(): Collection<DependencyRequirement> {
|
||||||
|
return new Collection<DependencyRequirement>()
|
||||||
|
}
|
||||||
|
}
|
7
di/src/global.ts
Normal file
7
di/src/global.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { Container } from './Container.ts'
|
||||||
|
import Instantiable from './type/Instantiable.ts'
|
||||||
|
import { DependencyKey } from './type/DependencyKey.ts'
|
||||||
|
|
||||||
|
const container = new Container()
|
||||||
|
const make = <T>(target: Instantiable<T>|DependencyKey, ...parameters: any[]) => container.make(target, ...parameters)
|
||||||
|
export { container, make }
|
4
di/src/type/DependencyKey.ts
Normal file
4
di/src/type/DependencyKey.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import Instantiable from './Instantiable.ts'
|
||||||
|
const DEPENDENCY_KEYS_METADATA_KEY = 'daton:di:dependencyKeys.ts'
|
||||||
|
type DependencyKey = Instantiable<any> | string
|
||||||
|
export { DependencyKey, DEPENDENCY_KEYS_METADATA_KEY }
|
9
di/src/type/DependencyRequirement.ts
Normal file
9
di/src/type/DependencyRequirement.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { DependencyKey } from './DependencyKey.ts'
|
||||||
|
|
||||||
|
interface DependencyRequirement {
|
||||||
|
param_index: number,
|
||||||
|
key: DependencyKey,
|
||||||
|
overridden: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
export { DependencyRequirement }
|
9
di/src/type/Instantiable.ts
Normal file
9
di/src/type/Instantiable.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export default interface Instantiable<T> {
|
||||||
|
new(...args: any[]): T
|
||||||
|
}
|
||||||
|
|
||||||
|
const isInstantiable = (what: any): what is Instantiable<any> => {
|
||||||
|
return (typeof what === 'object' || typeof what === 'function') && 'constructor' in what && typeof what.constructor === 'function'
|
||||||
|
}
|
||||||
|
|
||||||
|
export { isInstantiable }
|
595
lib/src/collection/Collection.ts
Executable file
595
lib/src/collection/Collection.ts
Executable file
@ -0,0 +1,595 @@
|
|||||||
|
type CollectionItem<T> = T
|
||||||
|
type MaybeCollectionItem<T> = CollectionItem<T> | undefined
|
||||||
|
type KeyFunction<T, T2> = (item: CollectionItem<T>, index: number) => CollectionItem<T2>
|
||||||
|
type KeyReducerFunction<T, T2> = (current: any, item: CollectionItem<T>, index: number) => T2
|
||||||
|
type CollectionFunction<T, T2> = (items: Collection<T>) => T2
|
||||||
|
type KeyOperator<T, T2> = string | KeyFunction<T, T2>
|
||||||
|
type AssociatedCollectionItem<T2, T> = { key: T2, item: CollectionItem<T> }
|
||||||
|
type CollectionComparable<T> = CollectionItem<T>[] | Collection<T>
|
||||||
|
type DeterminesEquality<T> = (item: CollectionItem<T>, other: any) => boolean
|
||||||
|
type CollectionIndex = number
|
||||||
|
type MaybeCollectionIndex = CollectionIndex | undefined
|
||||||
|
type ComparisonFunction<T> = (item: CollectionItem<T>, other_item: CollectionItem<T>) => number
|
||||||
|
|
||||||
|
import { WhereOperator, applyWhere, whereMatch } from './Where.ts'
|
||||||
|
|
||||||
|
const collect = <T>(items: CollectionItem<T>[]): Collection<T> => Collection.collect(items)
|
||||||
|
export {
|
||||||
|
collect,
|
||||||
|
Collection,
|
||||||
|
|
||||||
|
// Types
|
||||||
|
CollectionItem,
|
||||||
|
MaybeCollectionItem,
|
||||||
|
KeyFunction,
|
||||||
|
KeyReducerFunction,
|
||||||
|
CollectionFunction,
|
||||||
|
KeyOperator,
|
||||||
|
AssociatedCollectionItem,
|
||||||
|
CollectionComparable,
|
||||||
|
DeterminesEquality,
|
||||||
|
CollectionIndex,
|
||||||
|
MaybeCollectionIndex,
|
||||||
|
ComparisonFunction,
|
||||||
|
}
|
||||||
|
|
||||||
|
class Collection<T> {
|
||||||
|
private _items: CollectionItem<T>[] = []
|
||||||
|
|
||||||
|
public static collect<T>(items: CollectionItem<T>[]): Collection<T> {
|
||||||
|
return new Collection(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static size(size: number): Collection<undefined> {
|
||||||
|
const arr = Array(size).fill(undefined)
|
||||||
|
return new Collection<undefined>(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fill<T>(size: number, item: T): Collection<T> {
|
||||||
|
const arr = Array(size).fill(item)
|
||||||
|
return new Collection<T>(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
items?: CollectionItem<T>[]
|
||||||
|
) {
|
||||||
|
if ( items )
|
||||||
|
this._items = items
|
||||||
|
}
|
||||||
|
|
||||||
|
private _all<T2>(key: KeyOperator<T, T2>): CollectionItem<T2>[] {
|
||||||
|
let items: CollectionItem<T2>[] = []
|
||||||
|
if ( typeof key === 'function' ) {
|
||||||
|
items = this._items.map(key)
|
||||||
|
} else if ( typeof key === 'string' ) {
|
||||||
|
items = this._items.map((item: CollectionItem<T>) => (<any>item)[key])
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
private _all_numbers<T2>(key: KeyOperator<T, T2>): number[] {
|
||||||
|
return this._all(key).map(value => Number(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
private _all_associate<T2>(key: KeyOperator<T, T2>): AssociatedCollectionItem<T2, T>[] {
|
||||||
|
const assoc_items: AssociatedCollectionItem<T2, T>[] = []
|
||||||
|
let items = [...this._items]
|
||||||
|
if ( typeof key === 'function' ) {
|
||||||
|
items.map((item, index) => {
|
||||||
|
const key_item = key(item, index)
|
||||||
|
assoc_items.push({ key: key_item, item })
|
||||||
|
})
|
||||||
|
} else if ( typeof key === 'string' ) {
|
||||||
|
items.map((item, index) => {
|
||||||
|
assoc_items.push({ key: (<any>item)[key], item })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return assoc_items
|
||||||
|
}
|
||||||
|
|
||||||
|
all(): CollectionItem<T>[] {
|
||||||
|
return [...this._items]
|
||||||
|
}
|
||||||
|
|
||||||
|
average<T2>(key?: KeyOperator<T, T2>): number {
|
||||||
|
let items
|
||||||
|
if ( key ) items = this._all_numbers(key)
|
||||||
|
else items = this._items.map(x => Number(x))
|
||||||
|
if ( items.length === 0 ) return 0
|
||||||
|
|
||||||
|
let sum = items.reduce((prev, curr) => prev + curr)
|
||||||
|
return sum / items.length
|
||||||
|
}
|
||||||
|
|
||||||
|
median<T2>(key?: KeyOperator<T, T2>): number {
|
||||||
|
let items
|
||||||
|
if ( key ) items = this._all_numbers(key).sort((a, b) => a - b)
|
||||||
|
else items = this._items.map(x => Number(x)).sort((a, b) => a - b)
|
||||||
|
|
||||||
|
const middle = Math.floor((items.length - 1) / 2)
|
||||||
|
if ( items.length % 2 ) return items[middle]
|
||||||
|
else return (items[middle] + items[middle + 1]) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
mode<T2>(key?: KeyOperator<T, T2>): number {
|
||||||
|
let items
|
||||||
|
if ( key ) items = this._all_numbers(key).sort((a, b) => a - b)
|
||||||
|
else items = this._items.map(x => Number(x)).sort((a, b) => a - b)
|
||||||
|
|
||||||
|
let counts: any = {}
|
||||||
|
for ( const item of items ) {
|
||||||
|
counts[item] = (counts[item] ?? -1) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.max(...Object.values(counts).map(Number))
|
||||||
|
}
|
||||||
|
|
||||||
|
collapse(): Collection<any> {
|
||||||
|
const new_items: CollectionItem<T>[] = []
|
||||||
|
const items = [...this._items]
|
||||||
|
const get_layer = (current: CollectionItem<T>|CollectionItem<T>[]) => {
|
||||||
|
if ( typeof (<any>current)[Symbol.iterator] === 'function' ) {
|
||||||
|
// @ts-ignore
|
||||||
|
// TODO fix this
|
||||||
|
for (const item of current) {
|
||||||
|
if (Array.isArray(item)) {
|
||||||
|
get_layer(item)
|
||||||
|
} else {
|
||||||
|
new_items.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get_layer(items)
|
||||||
|
return new Collection(new_items)
|
||||||
|
}
|
||||||
|
|
||||||
|
contains<T2>(key: KeyOperator<T, T2>, operator: WhereOperator, operand?: any): boolean {
|
||||||
|
const associate = this._all_associate(key)
|
||||||
|
const matches = applyWhere(associate, operator, operand)
|
||||||
|
return matches.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO crossJoin
|
||||||
|
|
||||||
|
diff<T2>(items: CollectionComparable<T|T2>): Collection<T> {
|
||||||
|
const exclude = items instanceof Collection ? items.all() : items
|
||||||
|
const matches = []
|
||||||
|
for ( const item of [...this._items] ) {
|
||||||
|
if ( !exclude.includes(item) ) matches.push(item)
|
||||||
|
}
|
||||||
|
return new Collection(matches)
|
||||||
|
}
|
||||||
|
|
||||||
|
diffUsing<T2>(items: CollectionComparable<T|T2>, compare: DeterminesEquality<T>): Collection<T> {
|
||||||
|
const exclude = items instanceof Collection ? items.all() : items
|
||||||
|
const matches = []
|
||||||
|
for ( const item of [...this._items] ) {
|
||||||
|
if ( !exclude.some(exc => compare(item, exc)) )
|
||||||
|
matches.push(item)
|
||||||
|
}
|
||||||
|
return new Collection(matches)
|
||||||
|
}
|
||||||
|
|
||||||
|
each<T2>(func: KeyFunction<T, T2>): Collection<T2> {
|
||||||
|
return new Collection(this._items.map(func))
|
||||||
|
}
|
||||||
|
|
||||||
|
map<T2>(func: KeyFunction<T, T2>): Collection<T2> {
|
||||||
|
return this.each(func)
|
||||||
|
}
|
||||||
|
|
||||||
|
every<T2>(func: KeyFunction<T, T2>): boolean {
|
||||||
|
return this._items.every(func)
|
||||||
|
}
|
||||||
|
|
||||||
|
everyWhere<T2>(key: KeyOperator<T, T2>, operator: WhereOperator, operand?: any): boolean {
|
||||||
|
const items = this._all_associate(key)
|
||||||
|
return items.every(item => whereMatch(item, operator, operand))
|
||||||
|
}
|
||||||
|
|
||||||
|
filter<T2>(func: KeyFunction<T, T2>): Collection<T> {
|
||||||
|
return new Collection(this._items.filter(func))
|
||||||
|
}
|
||||||
|
|
||||||
|
when<T2>(bool: boolean, then: CollectionFunction<T, T2>): Collection<T> {
|
||||||
|
if ( bool ) then(this)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
unless<T2>(bool: boolean, then: CollectionFunction<T, T2>): Collection<T> {
|
||||||
|
if ( !bool ) then(this)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
where<T2>(key: KeyOperator<T, T2>, operator: WhereOperator, operand?: any): Collection<T> {
|
||||||
|
const items = this._all_associate(key)
|
||||||
|
return new Collection(applyWhere(items, operator, operand))
|
||||||
|
}
|
||||||
|
|
||||||
|
whereNot<T2>(key: KeyOperator<T, T2>, operator: WhereOperator, operand?: any): Collection<T> {
|
||||||
|
return this.diff(this.where(key, operator, operand))
|
||||||
|
}
|
||||||
|
|
||||||
|
whereIn<T2>(key: KeyOperator<T, T2>, items: CollectionComparable<T2>): Collection<T> {
|
||||||
|
const allowed = items instanceof Collection ? items.all() : items
|
||||||
|
const matches = []
|
||||||
|
for ( const { key: search, item } of this._all_associate(key) ) {
|
||||||
|
if ( allowed.includes(search) ) matches.push(item)
|
||||||
|
}
|
||||||
|
return new Collection(matches)
|
||||||
|
}
|
||||||
|
|
||||||
|
whereNotIn<T2>(key: KeyOperator<T, T2>, items: CollectionComparable<T2>): Collection<T> {
|
||||||
|
return this.diff(this.whereIn(key, items))
|
||||||
|
}
|
||||||
|
|
||||||
|
first(): MaybeCollectionItem<T> {
|
||||||
|
if ( this.length > 0 ) return this._items[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
firstWhere<T2>(key: KeyOperator<T, T2>, operator: WhereOperator = '=', operand: any = true): MaybeCollectionItem<T> {
|
||||||
|
const items = this.where(key, operator, operand).all()
|
||||||
|
if ( items.length > 0 ) return items[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
firstWhereNot<T2>(key: KeyOperator<T, T2>, operator: WhereOperator, operand?: any): MaybeCollectionItem<T> {
|
||||||
|
const items = this.whereNot(key, operator, operand).all()
|
||||||
|
if ( items.length > 0 ) return items[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
get length() {
|
||||||
|
return this._items.length
|
||||||
|
}
|
||||||
|
|
||||||
|
count() {
|
||||||
|
return this._items.length
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO flatten - depth
|
||||||
|
|
||||||
|
get(index: number, fallback?: any) {
|
||||||
|
if ( this.length > index ) return this._items[index]
|
||||||
|
else return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
at(index: number): MaybeCollectionItem<T> {
|
||||||
|
return this.get(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
groupBy<T2>(key: KeyOperator<T, T2>): any {
|
||||||
|
const items = this._all_associate(key)
|
||||||
|
const groups: any = {}
|
||||||
|
for ( const item of items ) {
|
||||||
|
const key = String(item.key)
|
||||||
|
if ( !groups[key] ) groups[key] = []
|
||||||
|
groups[key].push(item.item)
|
||||||
|
}
|
||||||
|
return groups
|
||||||
|
}
|
||||||
|
|
||||||
|
associate<T2>(key: KeyOperator<T, T2>): any {
|
||||||
|
const items = this._all_associate(key)
|
||||||
|
const values: any = {}
|
||||||
|
for ( const item of items ) {
|
||||||
|
values[String(item.key)] = item.item
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
join(delimiter: string): string {
|
||||||
|
return this._items.join(delimiter)
|
||||||
|
}
|
||||||
|
|
||||||
|
implode(delimiter: string): string {
|
||||||
|
return this.join(delimiter)
|
||||||
|
}
|
||||||
|
|
||||||
|
intersect(items: CollectionComparable<T>, key?: KeyOperator<T, T>): Collection<T> {
|
||||||
|
const compare = items instanceof Collection ? items.all() : items
|
||||||
|
const intersect = []
|
||||||
|
let all_items
|
||||||
|
if ( key ) all_items = this._all_associate(key)
|
||||||
|
else all_items = this._items.map(item => {
|
||||||
|
return { key: item, item }
|
||||||
|
})
|
||||||
|
|
||||||
|
for ( const item of all_items ) {
|
||||||
|
if ( compare.includes(item.key) )
|
||||||
|
intersect.push(item.item)
|
||||||
|
}
|
||||||
|
return new Collection(intersect)
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmpty(): boolean {
|
||||||
|
return this.length < 1
|
||||||
|
}
|
||||||
|
|
||||||
|
isNotEmpty(): boolean {
|
||||||
|
return this.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
last(): MaybeCollectionItem<T> {
|
||||||
|
if ( this.length > 0 ) return this._items.reverse()[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
lastWhere<T2>(key: KeyOperator<T, T2>, operator: WhereOperator, operand?: any): MaybeCollectionItem<T> {
|
||||||
|
const items = this.where(key, operator, operand).all()
|
||||||
|
if ( items.length > 0 ) return items.reverse()[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
lastWhereNot<T2>(key: KeyOperator<T, T2>, operator: WhereOperator, operand?: any): MaybeCollectionItem<T> {
|
||||||
|
const items = this.whereNot(key, operator, operand).all()
|
||||||
|
if ( items.length > 0 ) return items.reverse()[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
pluck<T2>(key: KeyOperator<T, T2>): Collection<T2> {
|
||||||
|
return new Collection<T2>(this._all(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
max<T2>(key: KeyOperator<T, T2>): number {
|
||||||
|
const values = this._all_numbers(key)
|
||||||
|
return Math.max(...values)
|
||||||
|
}
|
||||||
|
|
||||||
|
whereMax<T2>(key: KeyOperator<T, T2>): Collection<T> {
|
||||||
|
return this.where(key, '=', this.max(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
min<T2>(key: KeyOperator<T, T2>): number {
|
||||||
|
const values = this._all_numbers(key)
|
||||||
|
return Math.min(...values)
|
||||||
|
}
|
||||||
|
|
||||||
|
whereMin<T2>(key: KeyOperator<T, T2>): Collection<T> {
|
||||||
|
return this.where(key, '=', this.min(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
merge<T2>(items: CollectionComparable<T2>): Collection<T|T2> {
|
||||||
|
const merge = items instanceof Collection ? items.all() : items
|
||||||
|
return new Collection([...this._items, ...merge])
|
||||||
|
}
|
||||||
|
|
||||||
|
nth(n: number): Collection<T> {
|
||||||
|
const matches: CollectionItem<T>[] = []
|
||||||
|
let current = 1
|
||||||
|
this._items.forEach((item, index) => {
|
||||||
|
if ( current === 1 ) matches.push(item)
|
||||||
|
current += 1
|
||||||
|
if ( current > n ) current = 1
|
||||||
|
})
|
||||||
|
return new Collection(matches)
|
||||||
|
}
|
||||||
|
|
||||||
|
forPage(page: number, perPage: number) {
|
||||||
|
const start = page * perPage - perPage
|
||||||
|
const end = page * perPage
|
||||||
|
return new Collection(this._items.slice(start, end))
|
||||||
|
}
|
||||||
|
|
||||||
|
pipe<T2>(func: CollectionFunction<T, T2>): any {
|
||||||
|
return func(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
pop(): MaybeCollectionItem<T> {
|
||||||
|
if ( this.length > 0 ) {
|
||||||
|
return this._items.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prepend(item: CollectionItem<T>): Collection<T> {
|
||||||
|
this._items = [item, ...this._items]
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
push(item: CollectionItem<T>): Collection<T> {
|
||||||
|
this._items.push(item)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
concat(items: CollectionComparable<T>): Collection<T> {
|
||||||
|
const concats = items instanceof Collection ? items.all() : items
|
||||||
|
for ( const item of concats ) {
|
||||||
|
this._items.push(item)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
pull(index: number, fallback: any): MaybeCollectionItem<T> {
|
||||||
|
let value
|
||||||
|
const new_items = []
|
||||||
|
this._items.forEach((item, item_index) => {
|
||||||
|
if ( item_index !== index ) new_items.push(item)
|
||||||
|
else value = item
|
||||||
|
})
|
||||||
|
return value ?? fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
put(index: number, item: CollectionItem<T>): Collection<T> {
|
||||||
|
const new_items = []
|
||||||
|
let inserted = false
|
||||||
|
this._items.forEach((existing, existing_index) => {
|
||||||
|
if ( existing_index === index ) {
|
||||||
|
new_items.push(item)
|
||||||
|
inserted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
new_items.push(existing)
|
||||||
|
})
|
||||||
|
|
||||||
|
if ( !inserted ) new_items.push(item)
|
||||||
|
return new Collection(new_items)
|
||||||
|
}
|
||||||
|
|
||||||
|
random(n: number): Collection<T> {
|
||||||
|
const random_items: CollectionItem<T>[] = []
|
||||||
|
const all = this._items
|
||||||
|
if ( n > this.length ) n = this.length
|
||||||
|
while ( random_items.length < n ) {
|
||||||
|
const item = all[Math.floor(Math.random() * all.length)]
|
||||||
|
if ( !random_items.includes(item) ) random_items.push(item)
|
||||||
|
}
|
||||||
|
return new Collection(random_items)
|
||||||
|
}
|
||||||
|
|
||||||
|
reduce<T2>(reducer: KeyReducerFunction<T, T2>, initial_value?: T2): T2 | undefined {
|
||||||
|
let current_value = initial_value
|
||||||
|
this._items.forEach((item, index) => {
|
||||||
|
current_value = reducer(current_value, item, index)
|
||||||
|
})
|
||||||
|
return current_value
|
||||||
|
}
|
||||||
|
|
||||||
|
reject<T2>(truth_test: KeyFunction<T, T2>): Collection<T> {
|
||||||
|
const rejected = this._items.filter((item, index) => {
|
||||||
|
return !truth_test(item, index)
|
||||||
|
})
|
||||||
|
return new Collection(rejected)
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse(): Collection<T> {
|
||||||
|
return new Collection([...this._items.reverse()])
|
||||||
|
}
|
||||||
|
|
||||||
|
search(item: CollectionItem<T>): MaybeCollectionIndex {
|
||||||
|
let found_index
|
||||||
|
this._items.some((possible_item, index) => {
|
||||||
|
if ( possible_item === item ) {
|
||||||
|
found_index = index
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return found_index
|
||||||
|
}
|
||||||
|
|
||||||
|
shift(): MaybeCollectionItem<T> {
|
||||||
|
if ( this.length > 0 ) {
|
||||||
|
return this._items.shift()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shuffle(): Collection<T> {
|
||||||
|
const items = [...this._items]
|
||||||
|
for ( let i = items.length - 1; i > 0; i-- ) {
|
||||||
|
const j = Math.floor(Math.random() * (i + 1))
|
||||||
|
;[items[i], items[j]] = [items[j], items[i]]
|
||||||
|
}
|
||||||
|
return new Collection(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
slice(start: number, end: number) {
|
||||||
|
return new Collection(this._items.slice(start, end))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO split
|
||||||
|
// TODO chunk
|
||||||
|
|
||||||
|
sort(compare_func?: ComparisonFunction<T>): Collection<T> {
|
||||||
|
const items = this._items
|
||||||
|
if ( compare_func ) items.sort(compare_func)
|
||||||
|
else items.sort()
|
||||||
|
return new Collection(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
sortBy<T2>(key?: KeyOperator<T, T2>): Collection<T> {
|
||||||
|
let items: any[]
|
||||||
|
if ( key ) items = this._all_associate(key)
|
||||||
|
else items = this._items.map(item => {
|
||||||
|
return { key: item, item }
|
||||||
|
})
|
||||||
|
|
||||||
|
items.sort((a: any, b: any) => {
|
||||||
|
if ( a.key > b.key ) return 1
|
||||||
|
else if ( a.key < b.key ) return -1
|
||||||
|
else return 0
|
||||||
|
})
|
||||||
|
return new Collection(items.map((item: AssociatedCollectionItem<T2, T>) => item.item))
|
||||||
|
}
|
||||||
|
|
||||||
|
sortDesc(compare_func?: ComparisonFunction<T>): Collection<T> {
|
||||||
|
return this.sort(compare_func).reverse()
|
||||||
|
}
|
||||||
|
|
||||||
|
sortByDesc<T2>(key?: KeyOperator<T, T2>): Collection<T> {
|
||||||
|
return this.sortBy(key).reverse()
|
||||||
|
}
|
||||||
|
|
||||||
|
splice(start: CollectionIndex, deleteCount?: number): Collection<T> {
|
||||||
|
return new Collection([...this._items].splice(start, deleteCount))
|
||||||
|
}
|
||||||
|
|
||||||
|
sum<T2>(key?: KeyOperator<T, T2>): number {
|
||||||
|
let items
|
||||||
|
if ( key ) items = this._all_numbers(key)
|
||||||
|
else items = this._items.map(x => Number(x))
|
||||||
|
return items.reduce((prev, curr) => prev + curr)
|
||||||
|
}
|
||||||
|
|
||||||
|
take(limit: number): Collection<T> {
|
||||||
|
if ( limit === 0 ) return new Collection()
|
||||||
|
else if ( limit > 0 ) {
|
||||||
|
return new Collection(this._items.slice(0, limit))
|
||||||
|
} else {
|
||||||
|
return new Collection(this._items.reverse().slice(0, -1 * limit).reverse())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tap<T2>(func: CollectionFunction<T, T2>): Collection<T> {
|
||||||
|
func(this)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
unique<T2>(key?: KeyOperator<T, T2>): Collection<T|T2> {
|
||||||
|
const has: CollectionItem<T|T2>[] = []
|
||||||
|
let items
|
||||||
|
if ( key ) items = this._all<T2>(key)
|
||||||
|
else items = [...this._items]
|
||||||
|
for ( const item of items ) {
|
||||||
|
if ( !has.includes(item) ) has.push(item)
|
||||||
|
}
|
||||||
|
return new Collection(has)
|
||||||
|
}
|
||||||
|
|
||||||
|
includes(item: CollectionItem<T>): boolean {
|
||||||
|
return this._items.includes(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
pad(length: number, value: CollectionItem<T>): Collection<T> {
|
||||||
|
const items = [...this._items]
|
||||||
|
while ( items.length < length ) items.push(value)
|
||||||
|
return new Collection(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
toArray(): any[] {
|
||||||
|
const returns: any = []
|
||||||
|
for ( const item of this._items ) {
|
||||||
|
if ( item instanceof Collection ) returns.push(item.toArray())
|
||||||
|
else returns.push(item)
|
||||||
|
}
|
||||||
|
return returns
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(replacer = undefined, space = 4): string {
|
||||||
|
return JSON.stringify(this.toArray(), replacer, space)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO getIterator
|
||||||
|
// TODO getCachingIterator
|
||||||
|
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
const items = this._items
|
||||||
|
return {
|
||||||
|
current_index: 0,
|
||||||
|
next() {
|
||||||
|
if ( items.length < 1 || this.current_index + 1 >= items.length ) {
|
||||||
|
return { done: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = items[this.current_index]
|
||||||
|
this.current_index += 1
|
||||||
|
return { done: false, value: item }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
lib/src/collection/Where.ts
Executable file
75
lib/src/collection/Where.ts
Executable file
@ -0,0 +1,75 @@
|
|||||||
|
type WhereOperator = '&' | '>' | '>=' | '<' | '<=' | '!=' | '<=>' | '%' | '|' | '!' | '~' | '=' | '^'
|
||||||
|
type AssociatedSearchItem = { key: any, item: any }
|
||||||
|
type WhereResult = any[]
|
||||||
|
|
||||||
|
// bitwise and
|
||||||
|
// greater than
|
||||||
|
// greater than equal
|
||||||
|
// less than
|
||||||
|
// less than equal
|
||||||
|
// not equal
|
||||||
|
// null-safe equal
|
||||||
|
// modulo
|
||||||
|
// bitwise or
|
||||||
|
// not
|
||||||
|
// bitwise negation
|
||||||
|
// equal
|
||||||
|
|
||||||
|
const whereMatch = (item: AssociatedSearchItem, operator: WhereOperator, operand?: any): boolean => {
|
||||||
|
switch ( operator ) {
|
||||||
|
case '&':
|
||||||
|
if ( item.key & operand ) return true
|
||||||
|
break
|
||||||
|
case '>':
|
||||||
|
if ( item.key > operand ) return true
|
||||||
|
break
|
||||||
|
case '>=':
|
||||||
|
if ( item.key >= operand ) return true
|
||||||
|
break
|
||||||
|
case '<':
|
||||||
|
if ( item.key < operand ) return true
|
||||||
|
break
|
||||||
|
case '<=':
|
||||||
|
if ( item.key <= operand ) return true
|
||||||
|
break
|
||||||
|
case '!=':
|
||||||
|
if ( item.key !== operand ) return true
|
||||||
|
break
|
||||||
|
case '<=>':
|
||||||
|
if ( item.key === operand && typeof item.key !== 'undefined' && item.key !== null )
|
||||||
|
return true
|
||||||
|
break
|
||||||
|
case '%':
|
||||||
|
if ( item.key % operand ) return true
|
||||||
|
break
|
||||||
|
case '|':
|
||||||
|
if ( item.key | operand ) return true
|
||||||
|
break
|
||||||
|
case '!':
|
||||||
|
if ( !item.key ) return true
|
||||||
|
break
|
||||||
|
case '~':
|
||||||
|
if ( ~item.key ) return true
|
||||||
|
break
|
||||||
|
case '=':
|
||||||
|
if ( item.key === operand ) return true
|
||||||
|
break
|
||||||
|
case '^':
|
||||||
|
if ( item.key ^ operand ) return true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const applyWhere = (items: AssociatedSearchItem[], operator: WhereOperator, operand?: any): WhereResult => {
|
||||||
|
const matches: WhereResult = []
|
||||||
|
for ( const item of items ) {
|
||||||
|
if ( whereMatch(item, operator, operand) )
|
||||||
|
matches.push(item.item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches
|
||||||
|
}
|
||||||
|
|
||||||
|
export { WhereOperator, WhereResult, AssociatedSearchItem, applyWhere, whereMatch }
|
2155
lib/src/external/reflect.ts
vendored
Normal file
2155
lib/src/external/reflect.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
15
lib/src/type/UUID.ts
Executable file
15
lib/src/type/UUID.ts
Executable file
@ -0,0 +1,15 @@
|
|||||||
|
type UUID = string
|
||||||
|
|
||||||
|
const isUUID = (possible: any): boolean => {
|
||||||
|
return typeof possible === 'string' && /[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}/.test(possible)
|
||||||
|
}
|
||||||
|
|
||||||
|
const uuid = (): UUID => {
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||||
|
const r = Math.random() * 16 | 0
|
||||||
|
const v = c == 'x' ? r : (r & 0x3 | 0x8)
|
||||||
|
return v.toString(16)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export { UUID, isUUID, uuid }
|
9
tsconfig.json
Executable file
9
tsconfig.json
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"target": "es2017"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user