commit
191d1670be
|
|
@ -1,2 +1,4 @@
|
||||||
node_modules
|
node_modules
|
||||||
out
|
out
|
||||||
|
.system
|
||||||
|
*.tgz
|
||||||
|
|
@ -3,3 +3,5 @@ src
|
||||||
test
|
test
|
||||||
.editorconfig
|
.editorconfig
|
||||||
tsconfig.json
|
tsconfig.json
|
||||||
|
.system
|
||||||
|
*.tgz
|
||||||
|
|
@ -5,3 +5,4 @@ os:
|
||||||
- windows
|
- windows
|
||||||
- linux
|
- linux
|
||||||
- osx
|
- osx
|
||||||
|
env: YARN_GPG=no
|
||||||
|
|
@ -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",
|
||||||
|
|
@ -18,12 +18,15 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/debug": "^4.1.5",
|
"@types/debug": "^4.1.5",
|
||||||
|
"@types/fs-extra": "^9.0.11",
|
||||||
"@types/lodash": "^4.14.169",
|
"@types/lodash": "^4.14.169",
|
||||||
"@types/nearley": "^2.11.1",
|
"@types/nearley": "^2.11.1",
|
||||||
"@types/node": "^15.3.0",
|
"@types/node": "^15.3.0",
|
||||||
"@types/uglify-js": "^3.13.0",
|
"@types/uglify-js": "^3.13.0",
|
||||||
|
"@types/uuid": "^8.3.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"debug": "^4.3.1",
|
"debug": "^4.3.1",
|
||||||
|
"fs-extra": "^10.0.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"moo": "^0.5.1",
|
"moo": "^0.5.1",
|
||||||
"nearley": "^2.20.1",
|
"nearley": "^2.20.1",
|
||||||
|
|
@ -31,6 +34,7 @@
|
||||||
"supervisor": "^0.12.0",
|
"supervisor": "^0.12.0",
|
||||||
"typescript": "^4.2.4",
|
"typescript": "^4.2.4",
|
||||||
"uglify-js": "^3.13.5",
|
"uglify-js": "^3.13.5",
|
||||||
|
"uuid": "^8.3.2",
|
||||||
"yarn": "^1.22.10"
|
"yarn": "^1.22.10"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
152
src/Instance.ts
152
src/Instance.ts
|
|
@ -1,96 +1,196 @@
|
||||||
|
|
||||||
import Serializable from './Serializable.js';
|
|
||||||
import minify from './minify.js';
|
|
||||||
import debug from 'debug';
|
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 } from './Module.js';
|
import Module, { LinkDescription, Variable } from './Module.js';
|
||||||
import System from './System.js';
|
import System from './System.js';
|
||||||
import { KV } from './KV.js';
|
import * as uuid from 'uuid';
|
||||||
/**
|
/**
|
||||||
* @typedef {import('./System.js').default} System
|
* @typedef {import('./System.js').default} System
|
||||||
* @typedef {import('./Module.js').default} Module
|
* @typedef {import('./Module.js').default} Module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export type Link = any; // BUT PROXY
|
||||||
|
|
||||||
export default class Instance extends Serializable {
|
export type SerializedInstance = {
|
||||||
|
type: string,
|
||||||
|
links: {
|
||||||
|
[name: string]: string
|
||||||
|
},
|
||||||
|
members: {
|
||||||
|
[name: string]: any // SO LONG AS ITS SERIALIZABLE ._.*
|
||||||
|
},
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
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: KV = {};
|
|
||||||
|
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)
|
||||||
initialContext[variable.name] = null;
|
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)
|
for(const name in this.module.imports)
|
||||||
initialContext[name] = this.module.imports[name];
|
initialContext[name] = this.module.imports[name];
|
||||||
|
|
||||||
|
// instance defined functions
|
||||||
|
initialContext.sync = this.system.saveInstance.bind(this.system, this);
|
||||||
|
|
||||||
const context = vm.createContext(initialContext);
|
const context = vm.createContext(initialContext);
|
||||||
|
|
||||||
|
// 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();
|
||||||
vm.runInContext(injectedScript, context, {
|
// log('injecting function...')
|
||||||
|
// log(injectedScript)
|
||||||
});
|
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) {
|
||||||
super();
|
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 = 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 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 () {
|
||||||
return this._link;
|
return this._link;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toSerializableObject(): SerializedInstance {
|
||||||
|
const obj: any = {};
|
||||||
|
obj.type = this.module.name.full;
|
||||||
|
obj.links = Object.fromEntries(this.module.links.map((link: LinkDescription): [string, string] => {
|
||||||
|
const name = link.name;
|
||||||
|
const linkId = this.context?.[name]?.__link__;
|
||||||
|
return [name, linkId];
|
||||||
|
}));
|
||||||
|
obj.members = Object.fromEntries(
|
||||||
|
this.module.variables
|
||||||
|
.filter((member: Variable): boolean => {
|
||||||
|
return member.persist;
|
||||||
|
})
|
||||||
|
.map((member: Variable): [string, any] => {
|
||||||
|
const name = member.name;
|
||||||
|
const value = this.context?.[name];
|
||||||
|
return [name, value];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
obj.id = this._id;
|
||||||
|
|
||||||
|
return obj as SerializedInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
return this.module.name.full + '(' + this._id.substr(0, 4) + ')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function attachHookedProperty(target: any, name: string, initialValue: any, changedHook: () => void) {
|
||||||
|
const propId = uuid.v4();
|
||||||
|
target[propId] = initialValue;
|
||||||
|
// TODO if its an object, replace it with a dead simple proxy? for detecting internal changes...
|
||||||
|
Object.defineProperty(target, name, {
|
||||||
|
get() {
|
||||||
|
return target[propId];
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
target[propId] = value;
|
||||||
|
changedHook();
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -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]: {
|
||||||
|
|
|
||||||
|
|
@ -1,198 +0,0 @@
|
||||||
// import { Ubjson } from '@shelacek/ubjson';
|
|
||||||
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
||||||
import { KV } from './KV';
|
|
||||||
|
|
||||||
export default class Serializable {
|
|
||||||
|
|
||||||
constructor(...args: any[]) {}
|
|
||||||
|
|
||||||
// things that need to be stored only in cold
|
|
||||||
// storage are keyed with a special prefix
|
|
||||||
static CLASS_REFERENCE = '$$CLASS_NAME';
|
|
||||||
|
|
||||||
// things that need to be stored only at runtime
|
|
||||||
// are keyed with symbols to not interfere with
|
|
||||||
// user code.
|
|
||||||
static PERSIST_LOCATION = Symbol('PERSIST_LOCATION');
|
|
||||||
|
|
||||||
start() {}
|
|
||||||
|
|
||||||
// toUbj() {
|
|
||||||
// return Ubjson.encode(this.toSerializableObject());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// static fromUbj(buffer) {
|
|
||||||
// return this.fromSerializableObject(Ubjson.decode(buffer));
|
|
||||||
// }
|
|
||||||
|
|
||||||
toJson() {
|
|
||||||
return JSON.stringify(this.toSerializableObject(), null, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static serializationDependencies(): any[] {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(str: string) {
|
|
||||||
return this.fromSerializableObject(JSON.parse(str));
|
|
||||||
}
|
|
||||||
|
|
||||||
toSerializableObject() {
|
|
||||||
|
|
||||||
const transformValue = (val: any): any => {
|
|
||||||
if(Array.isArray(val)) {
|
|
||||||
return transformArray(val);
|
|
||||||
} else if (val === null || val === undefined) {
|
|
||||||
return val;
|
|
||||||
} else if(typeof val === 'object') {
|
|
||||||
return transformObject(val);
|
|
||||||
} else {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const transformObject = (obj: KV): KV => {
|
|
||||||
const clone: KV = {};
|
|
||||||
for(const prop of Object.keys(obj)) {
|
|
||||||
if(prop.startsWith('_')) continue;
|
|
||||||
|
|
||||||
clone[prop] = transformValue(obj[prop]);
|
|
||||||
}
|
|
||||||
if(obj instanceof Serializable) {
|
|
||||||
clone[Serializable.CLASS_REFERENCE] = obj.constructor.name;
|
|
||||||
}
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
const transformArray = (arr: any[]): any[] => {
|
|
||||||
const clone = [];
|
|
||||||
for(const item of arr) {
|
|
||||||
clone.push(transformValue(item));
|
|
||||||
}
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
return transformObject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromSerializableObject(obj: KV) {
|
|
||||||
if(obj[Serializable.CLASS_REFERENCE] !== this.name) return null;
|
|
||||||
|
|
||||||
const transformValue = (val: any): any => {
|
|
||||||
if(Array.isArray(val)) {
|
|
||||||
return transformArray(val);
|
|
||||||
} else if(val === null || val === undefined) {
|
|
||||||
return val;
|
|
||||||
} else if(typeof val === 'object') {
|
|
||||||
if(Serializable.CLASS_REFERENCE in val) {
|
|
||||||
const classes = this.serializationDependencies();
|
|
||||||
const matchingClasses = classes.filter((classObject) => {
|
|
||||||
classObject.name === val[Serializable.CLASS_REFERENCE]
|
|
||||||
});
|
|
||||||
if(matchingClasses.length === 1) {
|
|
||||||
return matchingClasses[0].fromSerializableObject(val);
|
|
||||||
} else {
|
|
||||||
return transformObject(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return transformObject(val);
|
|
||||||
} else {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const transformObject = (obj: KV): KV => {
|
|
||||||
const clone: KV = {};
|
|
||||||
for(const prop of Object.keys(obj)) {
|
|
||||||
if(prop.startsWith('_')) continue;
|
|
||||||
|
|
||||||
clone[prop] = transformValue(obj[prop]);
|
|
||||||
}
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
const transformArray = (arr: any[]): any[] => {
|
|
||||||
const clone = [];
|
|
||||||
for(const item of arr) {
|
|
||||||
clone.push(transformValue(item));
|
|
||||||
}
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
const clone = transformObject(obj);
|
|
||||||
if(Serializable.CLASS_REFERENCE in obj)
|
|
||||||
clone.__proto__ = this.prototype;
|
|
||||||
|
|
||||||
clone.restore();
|
|
||||||
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
serialize({
|
|
||||||
encoding = 'json'
|
|
||||||
} = {}) {
|
|
||||||
|
|
||||||
switch(encoding) {
|
|
||||||
case 'json': return this.toJson();
|
|
||||||
case 'ubjson':
|
|
||||||
// case 'ubj': return this.toUbj();
|
|
||||||
default: {
|
|
||||||
throw new TypeError('Unknown encoding: ' + encoding);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static deserialize(obj: any, {
|
|
||||||
encoding = 'json'
|
|
||||||
} = {}) {
|
|
||||||
|
|
||||||
switch(encoding) {
|
|
||||||
case 'json': return this.fromJson(obj);
|
|
||||||
case 'ubjson':
|
|
||||||
// case 'ubj': return this.fromUbj(obj);
|
|
||||||
default: {
|
|
||||||
throw new TypeError('Unknown encoding: ' + encoding);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async restore() {}
|
|
||||||
|
|
||||||
static createFromDisk(filename: string, ...args: any[]) {
|
|
||||||
if(existsSync(filename)) {
|
|
||||||
const instance = this.deserialize(readFileSync(createFilepath(filename)));
|
|
||||||
// TS is plain and simply wrong... symbols can be used to index object...
|
|
||||||
// @ts-ignore
|
|
||||||
instance[Serializable.PERSIST_LOCATION] = createFilepath(filename);
|
|
||||||
instance?.restore();
|
|
||||||
return instance;
|
|
||||||
} else {
|
|
||||||
const instance = new this(...args);
|
|
||||||
// again... TS is wrong...
|
|
||||||
// @ts-ignore
|
|
||||||
instance[Serializable.PERSIST_LOCATION] = createFilepath(filename);
|
|
||||||
instance?.updateDisk();
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDisk(filepath?: string) {
|
|
||||||
// if it hasnt yet been written to disk...
|
|
||||||
// this can happen if the contrustor
|
|
||||||
// was called outside of createFromDisk
|
|
||||||
if(filepath) {
|
|
||||||
// see above... TS7053 is just _wrong_. incorrect. thats not how JS works.
|
|
||||||
// @ts-ignore
|
|
||||||
this[Serializable.PERSIST_LOCATION] = createFilepath(filepath);
|
|
||||||
}
|
|
||||||
const data = this.serialize();
|
|
||||||
// this is getting annoying...
|
|
||||||
// @ts-ignore
|
|
||||||
writeFileSync(this[Serializable.PERSIST_LOCATION], data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createFilepath(path: string) {
|
|
||||||
return `data/${path}`;
|
|
||||||
}
|
|
||||||
122
src/System.ts
122
src/System.ts
|
|
@ -1,8 +1,10 @@
|
||||||
import Instance from './Instance.js';
|
import Instance, { Link, SerializedInstance } from './Instance.js';
|
||||||
import Serializable from './Serializable.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 { lstatSync, readdirSync, readFileSync, writeFileSync } from 'fs';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
import { ensureDirSync } from 'fs-extra';
|
||||||
const log = debug('vogue:system')
|
const log = debug('vogue:system')
|
||||||
|
|
||||||
const {get, set} = _;
|
const {get, set} = _;
|
||||||
|
|
@ -13,30 +15,102 @@ type ModuleNamespaceMap = {
|
||||||
|
|
||||||
type ModuleName = string;
|
type ModuleName = string;
|
||||||
|
|
||||||
class System extends Serializable {
|
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;
|
||||||
|
|
||||||
constructor(modules: Module[], rootDir: string) {
|
constructor(modules: Module[], rootDir: string) {
|
||||||
super();
|
this.rootDir = rootDir;
|
||||||
this.modules = modules;
|
this.modules = modules;
|
||||||
this.createNamespace();
|
this.createNamespace();
|
||||||
|
|
||||||
|
ensureDirSync(resolve(this.rootDir, '.system'));
|
||||||
|
|
||||||
|
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();
|
||||||
this.rootDir = rootDir;
|
|
||||||
|
|
||||||
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) => {
|
||||||
|
|
@ -49,8 +123,8 @@ class System extends Serializable {
|
||||||
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, {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,6 +153,16 @@ class System extends Serializable {
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveInstance(instance: Instance): void {
|
||||||
|
const path = resolve(this.rootDir, '.system');
|
||||||
|
ensureDirSync(path);
|
||||||
|
const file = instance._id + '.json';
|
||||||
|
const filepath = resolve(path, file);
|
||||||
|
log('saving ' + instance + '...');
|
||||||
|
const json = JSON.stringify(instance.toSerializableObject(), null, 2)
|
||||||
|
writeFileSync(filepath, json);
|
||||||
|
}
|
||||||
|
|
||||||
getModule(name: ModuleName): Module {
|
getModule(name: ModuleName): Module {
|
||||||
const module = get(this.namespace, name);
|
const module = get(this.namespace, name);
|
||||||
if(module instanceof Module) return module;
|
if(module instanceof Module) return module;
|
||||||
|
|
@ -86,10 +170,12 @@ class System extends Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
createInstance(name: ModuleName, args = {}) {
|
createInstance(name: ModuleName, args = {}) {
|
||||||
return 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 instance = this.createInstance(name, args);
|
||||||
const link = instance.link;
|
const link = instance.link;
|
||||||
instance.restore();
|
instance.restore();
|
||||||
|
|
@ -98,3 +184,13 @@ class System extends Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default System;
|
export default System;
|
||||||
|
|
||||||
|
// class SerializedInstanceInjector {
|
||||||
|
// system: System;
|
||||||
|
// serializedInstances: SerializedInstance[];
|
||||||
|
|
||||||
|
// constructor(serializedInstances: SerializedInstance[], system: System) {
|
||||||
|
// this.serializedInstances = serializedInstances;
|
||||||
|
// this.system = system;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
@ -4,3 +4,8 @@ Object.defineProperty(Array.prototype, 'empty', {
|
||||||
return this.length === 0;
|
return this.length === 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
process.on('unhandledRejection', (reason: Error, p) => {
|
||||||
|
console.log(reason.stack ?? reason.name + '\n\nStack trace unavailable...');
|
||||||
|
});
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node --enable-source-maps --unhandled-rejections=strict
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
const log = debug('vogue:cli');
|
const log = debug('vogue:cli');
|
||||||
const systemLocation = resolve(process.argv[2]);
|
const systemLocation = resolve(process.argv[2]);
|
||||||
|
|
@ -11,14 +11,11 @@ import Module from './Module.js';
|
||||||
import System from './System.js';
|
import System from './System.js';
|
||||||
import './extensions.js';
|
import './extensions.js';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
// globals inside grammar context
|
|
||||||
import minify from './minify';
|
|
||||||
|
|
||||||
const { get, set } = _;
|
const { get, set } = _;
|
||||||
const standardLibrary = resolve(fileURLToPath(dirname(import.meta.url)), '..', 'lib', 'vogue');
|
const standardLibrary = resolve(fileURLToPath(dirname(import.meta.url)), '..', 'lib', 'vogue');
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
// TODO simplify this line gaddam
|
|
||||||
const ignoreDeps = (path: string) => parse(path).name !== 'node_modules';
|
const ignoreDeps = (path: string) => parse(path).name !== 'node_modules';
|
||||||
|
|
||||||
const files = [
|
const files = [
|
||||||
|
|
@ -35,7 +32,7 @@ const standardLibrary = resolve(fileURLToPath(dirname(import.meta.url)), '..', '
|
||||||
const modules = await Promise.all(fullpaths.map(loc => Module.create(loc, systemLocation)));
|
const modules = await Promise.all(fullpaths.map(loc => Module.create(loc, systemLocation)));
|
||||||
|
|
||||||
const sys = new System(modules, systemLocation);
|
const sys = new System(modules, systemLocation);
|
||||||
})()
|
})();
|
||||||
|
|
||||||
function walkdirSync(root: string, filter: ((path: string) => boolean) = () => true): string[] {
|
function walkdirSync(root: string, filter: ((path: string) => boolean) = () => true): string[] {
|
||||||
log('reading', root, '...');
|
log('reading', root, '...');
|
||||||
|
|
|
||||||
|
|
@ -19,4 +19,6 @@ async restore {
|
||||||
// window.setScene()
|
// window.setScene()
|
||||||
|
|
||||||
// await counter.render();
|
// await counter.render();
|
||||||
|
|
||||||
|
sync();
|
||||||
}
|
}
|
||||||
BIN
vogue-0.0.1.tgz
BIN
vogue-0.0.1.tgz
Binary file not shown.
50
yarn.lock
50
yarn.lock
|
|
@ -6,6 +6,13 @@
|
||||||
version "4.1.5"
|
version "4.1.5"
|
||||||
resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz"
|
resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz"
|
||||||
|
|
||||||
|
"@types/fs-extra@^9.0.11":
|
||||||
|
version "9.0.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.11.tgz#8cc99e103499eab9f347dbc6ca4e99fb8d2c2b87"
|
||||||
|
integrity sha512-mZsifGG4QeQ7hlkhO56u7zt/ycBgGxSVsFI/6lGTU34VtwkiqrrSDgw0+ygs8kFGWcXnFQWMrzF2h7TtDFNixA==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/lodash@^4.14.169":
|
"@types/lodash@^4.14.169":
|
||||||
version "4.14.169"
|
version "4.14.169"
|
||||||
resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.169.tgz"
|
resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.169.tgz"
|
||||||
|
|
@ -14,6 +21,11 @@
|
||||||
version "2.11.1"
|
version "2.11.1"
|
||||||
resolved "https://registry.npmjs.org/@types/nearley/-/nearley-2.11.1.tgz"
|
resolved "https://registry.npmjs.org/@types/nearley/-/nearley-2.11.1.tgz"
|
||||||
|
|
||||||
|
"@types/node@*":
|
||||||
|
version "15.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.6.0.tgz#f0ddca5a61e52627c9dcb771a6039d44694597bc"
|
||||||
|
integrity sha512-gCYSfQpy+LYhOFTKAeE8BkyGqaxmlFxe+n4DKM6DR0wzw/HISUE/hAmkC/KT8Sw5PCJblqg062b3z9gucv3k0A==
|
||||||
|
|
||||||
"@types/node@^15.3.0":
|
"@types/node@^15.3.0":
|
||||||
version "15.3.0"
|
version "15.3.0"
|
||||||
resolved "https://registry.npmjs.org/@types/node/-/node-15.3.0.tgz"
|
resolved "https://registry.npmjs.org/@types/node/-/node-15.3.0.tgz"
|
||||||
|
|
@ -24,6 +36,11 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
source-map "^0.6.1"
|
source-map "^0.6.1"
|
||||||
|
|
||||||
|
"@types/uuid@^8.3.0":
|
||||||
|
version "8.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f"
|
||||||
|
integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==
|
||||||
|
|
||||||
async@0.2.10:
|
async@0.2.10:
|
||||||
version "0.2.10"
|
version "0.2.10"
|
||||||
resolved "https://registry.npmjs.org/async/-/async-0.2.10.tgz"
|
resolved "https://registry.npmjs.org/async/-/async-0.2.10.tgz"
|
||||||
|
|
@ -62,6 +79,20 @@ discontinuous-range@1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz"
|
||||||
|
|
||||||
|
fs-extra@^10.0.0:
|
||||||
|
version "10.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1"
|
||||||
|
integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==
|
||||||
|
dependencies:
|
||||||
|
graceful-fs "^4.2.0"
|
||||||
|
jsonfile "^6.0.1"
|
||||||
|
universalify "^2.0.0"
|
||||||
|
|
||||||
|
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
|
||||||
|
version "4.2.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
|
||||||
|
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
|
||||||
|
|
||||||
immediate@~3.0.5:
|
immediate@~3.0.5:
|
||||||
version "3.0.6"
|
version "3.0.6"
|
||||||
resolved "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz"
|
resolved "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz"
|
||||||
|
|
@ -70,6 +101,15 @@ isexe@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz"
|
resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz"
|
||||||
|
|
||||||
|
jsonfile@^6.0.1:
|
||||||
|
version "6.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
|
||||||
|
integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
|
||||||
|
dependencies:
|
||||||
|
universalify "^2.0.0"
|
||||||
|
optionalDependencies:
|
||||||
|
graceful-fs "^4.1.6"
|
||||||
|
|
||||||
lie@3.1.1:
|
lie@3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz"
|
resolved "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz"
|
||||||
|
|
@ -172,6 +212,16 @@ underscore@~1.4.4:
|
||||||
version "1.4.4"
|
version "1.4.4"
|
||||||
resolved "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz"
|
resolved "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz"
|
||||||
|
|
||||||
|
universalify@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
|
||||||
|
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
|
||||||
|
|
||||||
|
uuid@^8.3.2:
|
||||||
|
version "8.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||||
|
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||||
|
|
||||||
which@^2.0.1:
|
which@^2.0.1:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz"
|
resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz"
|
||||||
|
|
|
||||||
Reference in New Issue