lots of potential issues, but definitely Closes #21
parent
7a12c1adb8
commit
24467f479a
|
|
@ -10,7 +10,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node --enable-source-maps --unhandled-rejections=strict out/run.js test",
|
"test": "node --enable-source-maps --unhandled-rejections=strict out/run.js test",
|
||||||
"debug": "cross-env DEBUG=vogue:* yarn 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",
|
"postinstall": "yarn compile && cd test && yarn",
|
||||||
"postcompile:watch": "echo DONE",
|
"postcompile:watch": "echo DONE",
|
||||||
"compile": "tsc",
|
"compile": "tsc",
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import debug from 'debug';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
const log = debug('vogue:instance');
|
const log = debug('vogue:instance');
|
||||||
import vm from 'vm';
|
import vm from 'vm';
|
||||||
import Module, { Link, Variable } from './Module.js';
|
import Module, { LinkDescription, Variable } from './Module.js';
|
||||||
import System from './System.js';
|
import System from './System.js';
|
||||||
import * as uuid from 'uuid';
|
import * as uuid from 'uuid';
|
||||||
/**
|
/**
|
||||||
|
|
@ -10,6 +10,8 @@ import * as uuid from 'uuid';
|
||||||
* @typedef {import('./Module.js').default} Module
|
* @typedef {import('./Module.js').default} Module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export type Link = any; // BUT PROXY
|
||||||
|
|
||||||
export type SerializedInstance = {
|
export type SerializedInstance = {
|
||||||
type: string,
|
type: string,
|
||||||
links: {
|
links: {
|
||||||
|
|
@ -25,38 +27,49 @@ export default class Instance {
|
||||||
module: Module;
|
module: Module;
|
||||||
links = {}
|
links = {}
|
||||||
system: System;
|
system: System;
|
||||||
context: vm.Context;
|
context: vm.Context | null = null;
|
||||||
locals = [];
|
locals = [];
|
||||||
internalFunctions = {};
|
internalFunctions = {};
|
||||||
_link: Instance;
|
_link: Instance;
|
||||||
location: string;
|
location: string;
|
||||||
_id: string;
|
_id: string;
|
||||||
|
initialContext: any = {};
|
||||||
|
|
||||||
|
get currentContext(): any {
|
||||||
|
return this.context ?? this.initialContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
sync() {
|
||||||
|
this.system.saveInstance(this);
|
||||||
|
}
|
||||||
|
|
||||||
createContext(): vm.Context {
|
createContext(): vm.Context {
|
||||||
if(this.context) return this.context;
|
if(this.context) return this.context;
|
||||||
|
|
||||||
|
|
||||||
const initialContext: any = {};
|
const initialContext: any = {};
|
||||||
|
|
||||||
// system globals!
|
// system globals!
|
||||||
// TODO turn this into its own vogue module! system.create/instance.create
|
// TODO turn this into its own vogue module! system.create/instance.create
|
||||||
// TODO request context from system...
|
// TODO request context from system...
|
||||||
initialContext.create = this.system.newInstance.bind(this.system);
|
initialContext.create = this.system.newLink.bind(this.system);
|
||||||
initialContext.process = process;
|
initialContext.process = process;
|
||||||
for(const name in this.system.staticInstances)
|
for(const name in this.system.staticLinks) {
|
||||||
initialContext[name] = this.system.staticInstances[name];
|
log('creating context with static link: ' + name);
|
||||||
|
initialContext[name] = this.system.staticLinks[name];
|
||||||
|
}
|
||||||
|
|
||||||
// local links!
|
// local links!
|
||||||
// optional arrays
|
// optional arrays
|
||||||
// TODO maybe make these property accessors to allow for some automation
|
// 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] = [];
|
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;
|
initialContext[link.name] = null;
|
||||||
for(const variable of this.module.variables) {
|
for(const variable of this.module.variables)
|
||||||
attachHookedProperty(initialContext, variable.name, null, () => {
|
attachHookedProperty(initialContext, variable.name, null, this.sync.bind(this))
|
||||||
this.system.saveInstance(this);
|
for(const name in this.initialContext)
|
||||||
})
|
initialContext[name] = this.initialContext[name]
|
||||||
}
|
|
||||||
for(const name in this.module.imports)
|
for(const name in this.module.imports)
|
||||||
initialContext[name] = this.module.imports[name];
|
initialContext[name] = this.module.imports[name];
|
||||||
|
|
||||||
|
|
@ -68,41 +81,70 @@ export default class Instance {
|
||||||
// user defined functions
|
// user defined functions
|
||||||
for(const name in this.module.functions) {
|
for(const name in this.module.functions) {
|
||||||
const { code, parameters, async } = this.module.functions[name];
|
const { code, parameters, async } = this.module.functions[name];
|
||||||
const injectedScript =
|
const injectedScript = `
|
||||||
`
|
var ${name} = ${async ? 'async ' : ''}function ${name}(${parameters.join(', ')}) ${code}
|
||||||
var ${name} = ${async ? 'async' : ''} function ${name}(${parameters.join(', ')}) ${code}
|
${name} = ${name}.bind(this);`.trim();
|
||||||
${name} = ${name}.bind(this);
|
// log('injecting function...')
|
||||||
`;
|
// log(injectedScript)
|
||||||
vm.runInContext(injectedScript, context, {});
|
vm.runInContext(injectedScript, context, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log('context created! ' + Object.keys(context));
|
||||||
|
// log(context);
|
||||||
|
|
||||||
return 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.module = module;
|
||||||
this.location = location;
|
this.location = location;
|
||||||
this.system = system;
|
this.system = system;
|
||||||
this.context = this.createContext();
|
// this.context = this.createContext();
|
||||||
this._id = uuid.v4();
|
this._id = options?.id ?? uuid.v4();
|
||||||
|
|
||||||
this._link = new Proxy(this, {
|
this._link = new Proxy(this, {
|
||||||
get(target: Instance, prop: string, receiver) {
|
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`;
|
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 === 'restore') throw new Error(DNEText);
|
||||||
if(prop === '__link__') return target._id;
|
if(prop === '__link__') return target._id;
|
||||||
|
|
||||||
if(prop in target.module.functions) {
|
if(prop in target.module.functions) {
|
||||||
return target.context[prop];
|
return target.context?.[prop];
|
||||||
}
|
}
|
||||||
throw new Error(DNEText);
|
throw new Error(DNEText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
log('created ' + this);
|
||||||
}
|
}
|
||||||
|
|
||||||
restore() {
|
restore() {
|
||||||
return this.context.restore?.();
|
if(this.context === null)
|
||||||
|
this.context = this.createContext();
|
||||||
|
|
||||||
|
return this.context?.restore?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
get link () {
|
get link () {
|
||||||
|
|
@ -112,9 +154,9 @@ ${name} = ${name}.bind(this);
|
||||||
toSerializableObject(): SerializedInstance {
|
toSerializableObject(): SerializedInstance {
|
||||||
const obj: any = {};
|
const obj: any = {};
|
||||||
obj.type = this.module.name.full;
|
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 name = link.name;
|
||||||
const linkId = this.context[name]?.__link__;
|
const linkId = this.context?.[name]?.__link__;
|
||||||
return [name, linkId];
|
return [name, linkId];
|
||||||
}));
|
}));
|
||||||
obj.members = Object.fromEntries(
|
obj.members = Object.fromEntries(
|
||||||
|
|
@ -124,7 +166,7 @@ ${name} = ${name}.bind(this);
|
||||||
})
|
})
|
||||||
.map((member: Variable): [string, any] => {
|
.map((member: Variable): [string, any] => {
|
||||||
const name = member.name;
|
const name = member.name;
|
||||||
const value = this.context[name];
|
const value = this.context?.[name];
|
||||||
return [name, value];
|
return [name, value];
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
@ -134,7 +176,7 @@ ${name} = ${name}.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
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';
|
import { pathToFileURL } from 'url';
|
||||||
const log = debug('vogue:module');
|
const log = debug('vogue:module');
|
||||||
|
|
||||||
export type Link = {
|
export type LinkDescription = {
|
||||||
name: string,
|
name: string,
|
||||||
array: boolean,
|
array: boolean,
|
||||||
required: boolean
|
required: boolean
|
||||||
|
|
@ -26,7 +26,7 @@ export type Variable = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Module {
|
export default class Module {
|
||||||
links: Link[] = [];
|
links: LinkDescription[] = [];
|
||||||
globals = [];
|
globals = [];
|
||||||
functions: {
|
functions: {
|
||||||
[name: string]: {
|
[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 _ from 'lodash';
|
||||||
import Module from './Module.js';
|
import Module from './Module.js';
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import { writeFileSync } from 'fs';
|
import { lstatSync, readdirSync, readFileSync, writeFileSync } from 'fs';
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
import { ensureDirSync } from 'fs-extra';
|
import { ensureDirSync } from 'fs-extra';
|
||||||
const log = debug('vogue:system')
|
const log = debug('vogue:system')
|
||||||
|
|
@ -16,11 +16,11 @@ type ModuleNamespaceMap = {
|
||||||
type ModuleName = string;
|
type ModuleName = string;
|
||||||
|
|
||||||
class System {
|
class System {
|
||||||
instances: Instance[] = [];
|
instances: Map<string, Instance> = new Map();
|
||||||
modules: Module[];
|
modules: Module[];
|
||||||
namespace: ModuleNamespaceMap = {};
|
namespace: ModuleNamespaceMap = {};
|
||||||
staticInstances: {
|
staticLinks: {
|
||||||
[key: string]: Instance
|
[key: string]: Link
|
||||||
} = {};
|
} = {};
|
||||||
rootDir: string;
|
rootDir: string;
|
||||||
|
|
||||||
|
|
@ -28,16 +28,87 @@ class System {
|
||||||
this.rootDir = rootDir;
|
this.rootDir = rootDir;
|
||||||
this.modules = modules;
|
this.modules = modules;
|
||||||
this.createNamespace();
|
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();
|
const bootModules = this.deriveBootModules();
|
||||||
this.createStaticInstances();
|
this.createStaticInstances();
|
||||||
|
|
||||||
log('instantiating boot modules...');
|
log('instantiating boot modules...');
|
||||||
for(const name of bootModules) {
|
for(const name of bootModules) {
|
||||||
log(' ' + name);
|
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() {
|
createStaticInstances() {
|
||||||
log('deriving static modules...');
|
log('deriving static modules...');
|
||||||
const staticModules = this.modules.filter((module) => {
|
const staticModules = this.modules.filter((module) => {
|
||||||
|
|
@ -50,8 +121,8 @@ class System {
|
||||||
log('instantiating static modules...');
|
log('instantiating static modules...');
|
||||||
for(const module of staticModules) {
|
for(const module of staticModules) {
|
||||||
log(' ' + module.static + ': ' + module.name.full);
|
log(' ' + module.static + ': ' + module.name.full);
|
||||||
this.staticInstances[module.static] =
|
this.staticLinks[module.static] =
|
||||||
this.newInstance(module.name.full, {});
|
this.newLink(module.name.full, {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,16 +152,13 @@ class System {
|
||||||
}
|
}
|
||||||
|
|
||||||
saveInstance(instance: Instance): void {
|
saveInstance(instance: Instance): void {
|
||||||
log('saving ' + instance)
|
|
||||||
const path = resolve(this.rootDir, '.system');
|
const path = resolve(this.rootDir, '.system');
|
||||||
ensureDirSync(path);
|
ensureDirSync(path);
|
||||||
const file = instance._id + '.json';
|
const file = instance._id + '.json';
|
||||||
const filepath = resolve(path, file);
|
const filepath = resolve(path, file);
|
||||||
log(filepath);
|
log('saving ' + instance + '...');
|
||||||
const json = JSON.stringify(instance.toSerializableObject(), null, 2)
|
const json = JSON.stringify(instance.toSerializableObject(), null, 2)
|
||||||
log(json);
|
|
||||||
writeFileSync(filepath, json);
|
writeFileSync(filepath, json);
|
||||||
log('synced ' + instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getModule(name: ModuleName): Module {
|
getModule(name: ModuleName): Module {
|
||||||
|
|
@ -100,12 +168,12 @@ class System {
|
||||||
}
|
}
|
||||||
|
|
||||||
createInstance(name: ModuleName, args = {}) {
|
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);
|
this.saveInstance(instance);
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
newInstance(name: ModuleName, args = {}) {
|
newLink(name: ModuleName, args = {}) {
|
||||||
const instance = this.createInstance(name, args);
|
const instance = this.createInstance(name, args);
|
||||||
const link = instance.link;
|
const link = instance.link;
|
||||||
instance.restore();
|
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