lots of potential issues, but definitely Closes #21
parent
7a12c1adb8
commit
24467f479a
|
|
@ -10,7 +10,7 @@
|
|||
"scripts": {
|
||||
"test": "node --enable-source-maps --unhandled-rejections=strict out/run.js test",
|
||||
"debug": "cross-env DEBUG=vogue:* yarn test",
|
||||
"debug:watch": "cross-env DEBUG=vogue:* supervisor -w out,test,lib -n exit --exec yarn -- test",
|
||||
"debug:watch": "cross-env DEBUG=vogue:* supervisor -w out,test/**/*.v,lib -n exit --exec yarn -- test",
|
||||
"postinstall": "yarn compile && cd test && yarn",
|
||||
"postcompile:watch": "echo DONE",
|
||||
"compile": "tsc",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import debug from 'debug';
|
|||
import _ from 'lodash';
|
||||
const log = debug('vogue:instance');
|
||||
import vm from 'vm';
|
||||
import Module, { Link, Variable } from './Module.js';
|
||||
import Module, { LinkDescription, Variable } from './Module.js';
|
||||
import System from './System.js';
|
||||
import * as uuid from 'uuid';
|
||||
/**
|
||||
|
|
@ -10,6 +10,8 @@ import * as uuid from 'uuid';
|
|||
* @typedef {import('./Module.js').default} Module
|
||||
*/
|
||||
|
||||
export type Link = any; // BUT PROXY
|
||||
|
||||
export type SerializedInstance = {
|
||||
type: string,
|
||||
links: {
|
||||
|
|
@ -25,38 +27,49 @@ export default class Instance {
|
|||
module: Module;
|
||||
links = {}
|
||||
system: System;
|
||||
context: vm.Context;
|
||||
context: vm.Context | null = null;
|
||||
locals = [];
|
||||
internalFunctions = {};
|
||||
_link: Instance;
|
||||
location: string;
|
||||
_id: string;
|
||||
initialContext: any = {};
|
||||
|
||||
get currentContext(): any {
|
||||
return this.context ?? this.initialContext;
|
||||
}
|
||||
|
||||
sync() {
|
||||
this.system.saveInstance(this);
|
||||
}
|
||||
|
||||
createContext(): vm.Context {
|
||||
if(this.context) return this.context;
|
||||
|
||||
|
||||
const initialContext: any = {};
|
||||
|
||||
// system globals!
|
||||
// TODO turn this into its own vogue module! system.create/instance.create
|
||||
// TODO request context from system...
|
||||
initialContext.create = this.system.newInstance.bind(this.system);
|
||||
initialContext.create = this.system.newLink.bind(this.system);
|
||||
initialContext.process = process;
|
||||
for(const name in this.system.staticInstances)
|
||||
initialContext[name] = this.system.staticInstances[name];
|
||||
for(const name in this.system.staticLinks) {
|
||||
log('creating context with static link: ' + name);
|
||||
initialContext[name] = this.system.staticLinks[name];
|
||||
}
|
||||
|
||||
// local links!
|
||||
// optional arrays
|
||||
// TODO maybe make these property accessors to allow for some automation
|
||||
for(const link of this.module.links.filter((v: Link) => v.array && !v.required))
|
||||
for(const link of this.module.links.filter((v: LinkDescription) => v.array && !v.required))
|
||||
initialContext[link.name] = [];
|
||||
for(const link of this.module.links.filter((v: Link) => !v.array && !v.required))
|
||||
for(const link of this.module.links.filter((v: LinkDescription) => !v.array && !v.required))
|
||||
initialContext[link.name] = null;
|
||||
for(const variable of this.module.variables) {
|
||||
attachHookedProperty(initialContext, variable.name, null, () => {
|
||||
this.system.saveInstance(this);
|
||||
})
|
||||
}
|
||||
for(const variable of this.module.variables)
|
||||
attachHookedProperty(initialContext, variable.name, null, this.sync.bind(this))
|
||||
for(const name in this.initialContext)
|
||||
initialContext[name] = this.initialContext[name]
|
||||
for(const name in this.module.imports)
|
||||
initialContext[name] = this.module.imports[name];
|
||||
|
||||
|
|
@ -68,41 +81,70 @@ export default class Instance {
|
|||
// user defined functions
|
||||
for(const name in this.module.functions) {
|
||||
const { code, parameters, async } = this.module.functions[name];
|
||||
const injectedScript =
|
||||
`
|
||||
var ${name} = ${async ? 'async' : ''} function ${name}(${parameters.join(', ')}) ${code}
|
||||
${name} = ${name}.bind(this);
|
||||
`;
|
||||
const injectedScript = `
|
||||
var ${name} = ${async ? 'async ' : ''}function ${name}(${parameters.join(', ')}) ${code}
|
||||
${name} = ${name}.bind(this);`.trim();
|
||||
// log('injecting function...')
|
||||
// log(injectedScript)
|
||||
vm.runInContext(injectedScript, context, {});
|
||||
}
|
||||
|
||||
log('context created! ' + Object.keys(context));
|
||||
// log(context);
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
constructor(module: Module, location: string, parameters: {[name: string]: any}, system: System) {
|
||||
setMember(name: string, value: any) {
|
||||
log('setMember: ' + this.toString() + '.' + name + ' => ' + value);
|
||||
this.currentContext[name] = value;
|
||||
}
|
||||
|
||||
setLink(name: string, value: Link) {
|
||||
log('setLink: ' + this.toString() + '.' + name + ' => ' + value.__link__);
|
||||
this.currentContext[name] = value;
|
||||
}
|
||||
|
||||
constructor(
|
||||
module: Module,
|
||||
location: string,
|
||||
parameters: {[name: string]: any},
|
||||
system: System,
|
||||
options?: {
|
||||
id?: string
|
||||
}
|
||||
) {
|
||||
this.module = module;
|
||||
this.location = location;
|
||||
this.system = system;
|
||||
this.context = this.createContext();
|
||||
this._id = uuid.v4();
|
||||
// this.context = this.createContext();
|
||||
this._id = options?.id ?? uuid.v4();
|
||||
|
||||
this._link = new Proxy(this, {
|
||||
get(target: Instance, prop: string, receiver) {
|
||||
log(`getting ${target.module.name.full}.${prop}: (${target.module.identifiers[prop]}|${typeof target.context[prop]})`);
|
||||
log(`getting ${target.module.name.full}.${prop.toString()}: (${target.module.identifiers[prop]}|${typeof target.context?.[prop]})`);
|
||||
|
||||
if(target.context === null)
|
||||
target.restore();
|
||||
|
||||
const DNEText = `${target.module.name.full}.${prop.toString()} either does not exist, or is not accessible`;
|
||||
if(prop === 'restore') throw new Error(DNEText);
|
||||
if(prop === '__link__') return target._id;
|
||||
|
||||
if(prop in target.module.functions) {
|
||||
return target.context[prop];
|
||||
return target.context?.[prop];
|
||||
}
|
||||
throw new Error(DNEText);
|
||||
}
|
||||
});
|
||||
log('created ' + this);
|
||||
}
|
||||
|
||||
restore() {
|
||||
return this.context.restore?.();
|
||||
if(this.context === null)
|
||||
this.context = this.createContext();
|
||||
|
||||
return this.context?.restore?.();
|
||||
}
|
||||
|
||||
get link () {
|
||||
|
|
@ -112,9 +154,9 @@ ${name} = ${name}.bind(this);
|
|||
toSerializableObject(): SerializedInstance {
|
||||
const obj: any = {};
|
||||
obj.type = this.module.name.full;
|
||||
obj.links = Object.fromEntries(this.module.links.map((link: Link): [string, string] => {
|
||||
obj.links = Object.fromEntries(this.module.links.map((link: LinkDescription): [string, string] => {
|
||||
const name = link.name;
|
||||
const linkId = this.context[name]?.__link__;
|
||||
const linkId = this.context?.[name]?.__link__;
|
||||
return [name, linkId];
|
||||
}));
|
||||
obj.members = Object.fromEntries(
|
||||
|
|
@ -124,7 +166,7 @@ ${name} = ${name}.bind(this);
|
|||
})
|
||||
.map((member: Variable): [string, any] => {
|
||||
const name = member.name;
|
||||
const value = this.context[name];
|
||||
const value = this.context?.[name];
|
||||
return [name, value];
|
||||
})
|
||||
);
|
||||
|
|
@ -134,7 +176,7 @@ ${name} = ${name}.bind(this);
|
|||
}
|
||||
|
||||
toString() {
|
||||
return this.module.name.full + '(' + this._id + ')';
|
||||
return this.module.name.full + '(' + this._id.substr(0, 4) + ')';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { createRequire } from 'module';
|
|||
import { pathToFileURL } from 'url';
|
||||
const log = debug('vogue:module');
|
||||
|
||||
export type Link = {
|
||||
export type LinkDescription = {
|
||||
name: string,
|
||||
array: boolean,
|
||||
required: boolean
|
||||
|
|
@ -26,7 +26,7 @@ export type Variable = {
|
|||
}
|
||||
|
||||
export default class Module {
|
||||
links: Link[] = [];
|
||||
links: LinkDescription[] = [];
|
||||
globals = [];
|
||||
functions: {
|
||||
[name: string]: {
|
||||
|
|
|
|||
108
src/System.ts
108
src/System.ts
|
|
@ -1,8 +1,8 @@
|
|||
import Instance from './Instance.js';
|
||||
import Instance, { Link, SerializedInstance } from './Instance.js';
|
||||
import _ from 'lodash';
|
||||
import Module from './Module.js';
|
||||
import debug from 'debug';
|
||||
import { writeFileSync } from 'fs';
|
||||
import { lstatSync, readdirSync, readFileSync, writeFileSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import { ensureDirSync } from 'fs-extra';
|
||||
const log = debug('vogue:system')
|
||||
|
|
@ -16,11 +16,11 @@ type ModuleNamespaceMap = {
|
|||
type ModuleName = string;
|
||||
|
||||
class System {
|
||||
instances: Instance[] = [];
|
||||
instances: Map<string, Instance> = new Map();
|
||||
modules: Module[];
|
||||
namespace: ModuleNamespaceMap = {};
|
||||
staticInstances: {
|
||||
[key: string]: Instance
|
||||
staticLinks: {
|
||||
[key: string]: Link
|
||||
} = {};
|
||||
rootDir: string;
|
||||
|
||||
|
|
@ -28,16 +28,87 @@ class System {
|
|||
this.rootDir = rootDir;
|
||||
this.modules = modules;
|
||||
this.createNamespace();
|
||||
|
||||
const vault = readdirSync(resolve(this.rootDir, '.system')).map(v => resolve(this.rootDir, '.system', v));
|
||||
const serializedInstances: SerializedInstance[] = vault.map((v) => JSON.parse(readFileSync(v).toString()));
|
||||
|
||||
log('injecting serialized instances...');
|
||||
for(const serializedInstance of serializedInstances)
|
||||
this.injectSerializedInstance(serializedInstance);
|
||||
log('linking serialized instances...');
|
||||
for(const serializedInstance of serializedInstances)
|
||||
this.linkSerializedInstance(serializedInstance);
|
||||
|
||||
log('restoring static instances...');
|
||||
for(const [,instance] of this.instances) {
|
||||
if(!!instance.module.static) {
|
||||
instance.restore();
|
||||
}
|
||||
}
|
||||
|
||||
log('restoring boot instances...');
|
||||
for(const [,instance] of this.instances) {
|
||||
if(!!instance.module.singleton) {
|
||||
instance.restore();
|
||||
}
|
||||
}
|
||||
// this.inject(serializedInstance);
|
||||
|
||||
|
||||
if (vault.length !== 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// TODO future workflow notes
|
||||
// pull jsons into boots
|
||||
// filter json boots
|
||||
// create static / singletons into boots
|
||||
// boot boots!
|
||||
|
||||
|
||||
const bootModules = this.deriveBootModules();
|
||||
this.createStaticInstances();
|
||||
|
||||
log('instantiating boot modules...');
|
||||
for(const name of bootModules) {
|
||||
log(' ' + name);
|
||||
this.newInstance(name);
|
||||
this.newLink(name);
|
||||
}
|
||||
}
|
||||
|
||||
linkSerializedInstance(serializedInstance: SerializedInstance): void {
|
||||
const instance = this.getInstanceById(serializedInstance.id);
|
||||
for(const name in serializedInstance.links) {
|
||||
const linkId = serializedInstance.links[name]
|
||||
const linkedInstance = this.getInstanceById(linkId);
|
||||
const linkedInstanceLink = linkedInstance.link;
|
||||
instance.setLink(name, linkedInstanceLink);
|
||||
}
|
||||
}
|
||||
|
||||
injectSerializedInstance(serializedInstance: SerializedInstance): void {
|
||||
const instance = new Instance(this.getModule(serializedInstance.type), this.rootDir, {}, this, {
|
||||
id: serializedInstance.id
|
||||
});
|
||||
this.instances.set(instance._id, instance);
|
||||
|
||||
for(const name in serializedInstance.members) {
|
||||
instance.setMember(name, serializedInstance.members[name]);
|
||||
}
|
||||
|
||||
if(instance.module.static) {
|
||||
log('injected static instance ' + instance.module.static + ': ' + instance.module.name.full);
|
||||
this.staticLinks[instance.module.static] = instance.link;
|
||||
}
|
||||
}
|
||||
|
||||
getInstanceById(id: string): Instance {
|
||||
if(!this.instances.has(id))
|
||||
throw new Error(`${id} is not a valid instance link id`);
|
||||
|
||||
return this.instances.get(id) as Instance;
|
||||
}
|
||||
|
||||
createStaticInstances() {
|
||||
log('deriving static modules...');
|
||||
const staticModules = this.modules.filter((module) => {
|
||||
|
|
@ -50,8 +121,8 @@ class System {
|
|||
log('instantiating static modules...');
|
||||
for(const module of staticModules) {
|
||||
log(' ' + module.static + ': ' + module.name.full);
|
||||
this.staticInstances[module.static] =
|
||||
this.newInstance(module.name.full, {});
|
||||
this.staticLinks[module.static] =
|
||||
this.newLink(module.name.full, {});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -81,16 +152,13 @@ class System {
|
|||
}
|
||||
|
||||
saveInstance(instance: Instance): void {
|
||||
log('saving ' + instance)
|
||||
const path = resolve(this.rootDir, '.system');
|
||||
ensureDirSync(path);
|
||||
const file = instance._id + '.json';
|
||||
const filepath = resolve(path, file);
|
||||
log(filepath);
|
||||
log('saving ' + instance + '...');
|
||||
const json = JSON.stringify(instance.toSerializableObject(), null, 2)
|
||||
log(json);
|
||||
writeFileSync(filepath, json);
|
||||
log('synced ' + instance);
|
||||
}
|
||||
|
||||
getModule(name: ModuleName): Module {
|
||||
|
|
@ -100,12 +168,12 @@ class System {
|
|||
}
|
||||
|
||||
createInstance(name: ModuleName, args = {}) {
|
||||
const instance = new Instance(this.getModule(name), '', args, this);
|
||||
const instance = new Instance(this.getModule(name), this.rootDir, args, this);
|
||||
this.saveInstance(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
newInstance(name: ModuleName, args = {}) {
|
||||
newLink(name: ModuleName, args = {}) {
|
||||
const instance = this.createInstance(name, args);
|
||||
const link = instance.link;
|
||||
instance.restore();
|
||||
|
|
@ -113,4 +181,14 @@ class System {
|
|||
}
|
||||
}
|
||||
|
||||
export default System;
|
||||
export default System;
|
||||
|
||||
// class SerializedInstanceInjector {
|
||||
// system: System;
|
||||
// serializedInstances: SerializedInstance[];
|
||||
|
||||
// constructor(serializedInstances: SerializedInstance[], system: System) {
|
||||
// this.serializedInstances = serializedInstances;
|
||||
// this.system = system;
|
||||
// }
|
||||
// }
|
||||
BIN
vogue-0.0.1.tgz
BIN
vogue-0.0.1.tgz
Binary file not shown.
Reference in New Issue