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 } }