UNPKG

@aire-ux/aire-condensation

Version:

Client-side serialization library for Aire-UX

114 lines (113 loc) 4.01 kB
import TypeRegistry from "./type-registry"; import RemoteRegistry from "./remote-registry"; import { Dynamic, allocate, isPointer, Region, } from "./types"; import { BooleanDeserializer, NumberDeserializer, StringDeserializer, TypeRegistrationDeserializer, } from "./deserializer"; /** * root context for all operations */ export class Condensation { static get typeRegistry() { return Condensation.registry; } static deserializerFor(type) { const result = this.deserializerConfigurations.get(type); if (result) { return result; } const cfg = Condensation.registry.resolveConfiguration(type); return new TypeRegistrationDeserializer(type, cfg); } static newContext() { return new DefaultCondensationContext(); } static defaultContext() { if (!Condensation.context) { Condensation.context = Condensation.newContext(); } return Condensation.context; } } Condensation.deserializerConfigurations = new Map(); class DefaultCondensationContext { constructor(region = new Region("default")) { this.region = region; } create(t, ...args) { const actualParams = this.formalParams(t, "constructor", 'constructor', ...args); return allocate(new t(...actualParams), this.region); } addressOf(t) { return this.region.addressOf(t); } delete(address) { return this.region.delete(address); } invokeDirect(value, op, ...args) { const operation = value[op]; if (!operation) { throw new Error(`Type ${typeof value} has no method named '${op}'`); } const formals = this.formalParams(Object.getPrototypeOf(value).constructor, "method", op, ...args); return operation.apply(value, formals); } invoke(address, op, ...args) { let v; if (isPointer(address)) { v = address; } else { v = this.locate(address); } if (!v) { throw new Error(`Null pointer exception at ${address} while trying to invoke ${op}`); } const operation = v[op]; if (!operation) { throw new Error(`Type ${typeof v} has no method named '${op}'`); } const formals = this.formalParams(Object.getPrototypeOf(v).constructor, "method", op, ...args); return operation.apply(v, formals); } locate(address) { return this.region.values[address.value]; } move(address, target) { const v = this.delete(address); return allocate(v, target); } formalParams(t, type, operation, ...args) { const remotes = Condensation.remoteRegistry, remote = remotes.resolve(t), ctorArgs = remote.definitions.filter((definition) => definition.invocationType === type && definition.invocationTarget == operation); if (ctorArgs.length !== args.length) { throw new Error(`Error: ${type} argument count mismatch. Expected ${ctorArgs.length}, got ${args.length}`); } ctorArgs.sort((lhs, rhs) => lhs.index - rhs.index); return ctorArgs.map((def, idx) => { const doc = args[idx], jsonValue = JSON.parse(doc); if (def.type !== Dynamic) { const deserializer = Condensation.deserializerFor(def.type); return deserializer.read(jsonValue); } else { return jsonValue; } }); } } export function register(...registrations) { for (let reg of registrations) { Condensation.deserializerConfigurations.set(reg.type, reg.deserializer); } } Condensation.registry = new TypeRegistry(); Condensation.remoteRegistry = new RemoteRegistry(); register({ type: String, deserializer: new StringDeserializer(), }, { type: Boolean, deserializer: new BooleanDeserializer(), }, { type: Number, deserializer: new NumberDeserializer(), });