252 lines
4.6 KiB
JavaScript
252 lines
4.6 KiB
JavaScript
import { assocPath, is } from '../fn.js'
|
|
|
|
class Value {
|
|
#value
|
|
|
|
constructor(value) {
|
|
this.#value = value
|
|
}
|
|
|
|
toString() {
|
|
return this.#value.toString()
|
|
}
|
|
|
|
get value() {
|
|
return this.#value?.value ?? this.#value
|
|
}
|
|
|
|
equals(other) {
|
|
if (other == null) {
|
|
return false
|
|
}
|
|
|
|
const value = this.value
|
|
return value === other
|
|
|| value === other.value
|
|
}
|
|
}
|
|
|
|
export class Identifier extends Value {
|
|
constructor(value) {
|
|
super(value)
|
|
}
|
|
|
|
from(component) {
|
|
return component
|
|
}
|
|
}
|
|
|
|
export class ObjectIdentifier extends Identifier {
|
|
constructor(value) {
|
|
super(value)
|
|
}
|
|
|
|
from(component) {
|
|
return new Proxy(component, {
|
|
get(target, entity, _receiver) {
|
|
return Object.fromEntries(
|
|
Object.entries(target).map(([k, v]) => [k, v[entity]])
|
|
)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
export class Property extends Identifier {
|
|
constructor(value) {
|
|
super(value)
|
|
}
|
|
|
|
from(component) {
|
|
return component[this.value]
|
|
}
|
|
}
|
|
|
|
export class Name extends Identifier {
|
|
constructor(value) {
|
|
super(value)
|
|
}
|
|
}
|
|
|
|
export class Label extends Identifier {
|
|
constructor(value) {
|
|
super(value)
|
|
}
|
|
}
|
|
|
|
export class SelectedGraph extends Identifier {
|
|
constructor(value) {
|
|
super(value)
|
|
}
|
|
}
|
|
|
|
export class Literal {
|
|
constructor(value) {
|
|
this.value = value
|
|
}
|
|
}
|
|
|
|
|
|
export class Alias extends Identifier {
|
|
constructor(value, alias) {
|
|
super(value)
|
|
this.alias = alias
|
|
}
|
|
}
|
|
|
|
export class ObjectPath extends Identifier {
|
|
constructor(value, ...path) {
|
|
super(value)
|
|
this.path = path
|
|
}
|
|
|
|
toString() {
|
|
return `${this.value}.${this.path.join('.')}`
|
|
}
|
|
|
|
from(component) {
|
|
return this.path.reduce((acc, v) => v.from(acc), component)
|
|
}
|
|
|
|
equals(value) {
|
|
if (is(ObjectPath, value)) {
|
|
return value != null
|
|
&& this.path.length === value.path.length
|
|
&& super.equals(value)
|
|
&& this.path.every((x, i) => x === value.path[i])
|
|
} else {
|
|
return super.equals(value)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class GraphObject {
|
|
constructor(name, label, properties = []) {
|
|
this.name = name
|
|
this.label = label
|
|
this.properties = properties
|
|
}
|
|
}
|
|
|
|
export class Component extends GraphObject {
|
|
constructor(name, label, properties) {
|
|
super(name, label, properties)
|
|
}
|
|
}
|
|
|
|
export class Node {
|
|
constructor(components) {
|
|
this.components = components
|
|
}
|
|
}
|
|
|
|
|
|
export const Direction = Object.freeze({
|
|
Left: 0,
|
|
Right: 1
|
|
})
|
|
|
|
export class Edge {
|
|
constructor(components) {
|
|
this.components = components
|
|
}
|
|
}
|
|
|
|
export class DirectedEdge extends Edge {
|
|
constructor(components, direction) {
|
|
super(components)
|
|
this.direction = direction
|
|
}
|
|
|
|
static fromEdge(edge, direction) {
|
|
return new DirectedEdge(edge.components, direction)
|
|
}
|
|
}
|
|
|
|
export class Relationship {
|
|
constructor(left, edge, right) {
|
|
const [from, to] = edge.direction === Direction.Right ? [left, right] : [right, left]
|
|
this.from = from
|
|
this.to = to
|
|
this.edge = edge
|
|
}
|
|
}
|
|
|
|
const isExactly = (Type, value) => Object.getPrototypeOf(value) === Type.prototype
|
|
|
|
export class ReturnValues {
|
|
constructor(...args) {
|
|
this.values = args.map(x => isExactly(Identifier, x) ? new ObjectIdentifier(x) : x)
|
|
}
|
|
|
|
#isTag(type) {
|
|
type && Object.getOwnPropertySymbols(type).find(
|
|
(s) => s.description === 'tagStore'
|
|
) || false
|
|
}
|
|
|
|
#forComponentType(value, type) {
|
|
if (this.#isTag(type)) {
|
|
return {}
|
|
} else if (typeof type === 'function') {
|
|
return type
|
|
} else {
|
|
return entity => value.from(type)[entity]
|
|
}
|
|
}
|
|
|
|
// TODO: adjust froms to accent entity, return data with built up objects as necessary
|
|
from(components) {
|
|
const values = components.map(cmp => ({ ...cmp, rv: this.findName(cmp.name) }))
|
|
.map(({ type, rv }) => new ReturnValue(rv, this.#forComponentType(rv, type)))
|
|
|
|
return entity => values.reduce((acc, rv) => Object.assign(acc, rv.withEntity(entity)), {})
|
|
}
|
|
|
|
find(fn) {
|
|
return this.values.find(fn)
|
|
}
|
|
|
|
findName(name) {
|
|
return this.find(value => value.equals(name))
|
|
}
|
|
|
|
filter(fn) {
|
|
return this.values.filter(fn)
|
|
}
|
|
|
|
includes(value) {
|
|
return this.find(x => x.equals(value))
|
|
}
|
|
}
|
|
|
|
class ReturnValue {
|
|
#value
|
|
#getter
|
|
|
|
constructor(value, getter) {
|
|
this.#value = value
|
|
this.#getter = getter
|
|
}
|
|
|
|
withEntity(entity) {
|
|
return assocPath(this.#value.toString(), this.#getter(entity), {})
|
|
}
|
|
}
|
|
|
|
|
|
export class KeyValuePair {
|
|
constructor(key, value) {
|
|
this.key = key
|
|
this.value = value
|
|
}
|
|
}
|
|
|
|
export class Query {
|
|
constructor(use, match, returnValues) {
|
|
this.use = use
|
|
this.match = match
|
|
this.returnValues = returnValues
|
|
}
|
|
}
|