Comparing version 1.1.91 to 1.1.92
/** @module Basic **/ | ||
const Jest = require('jest'); | ||
exports = { | ||
Basic: { | ||
/** @type {import('../src').JSModuleFunction} **/ | ||
async main(env, context, parameters) { | ||
const test1 = 'TEST'; | ||
const test2 = `<div id="TEST"></div>`; | ||
module.exports = { | ||
/** @type {import('../src').JSModuleFunction} **/ | ||
async main(env, context, modules, parameters) { | ||
const test1 = Jest.getVersion(); | ||
const [test2, second] = await modules.Other.getMethod()(env, context, modules, parameters); | ||
const test3 = /*html*/`<div id="${test1}"></div>`; | ||
return [test1, test2, test3, second]; | ||
}, | ||
/** @type {import('../src').JSModuleFunction} **/ | ||
async second(env, context, modules, parameters) { | ||
return 'SECOND' | ||
return [test1, test2]; | ||
} | ||
} | ||
} |
@@ -1,47 +0,14 @@ | ||
/// <reference types="node" /> | ||
import Module from 'module'; | ||
import { Doer } from './doer'; | ||
import { ConnectorContext } from '../connectors/connector'; | ||
import { TaskConfig } from '../environment'; | ||
import { Environment, IMap } from '..'; | ||
export declare class JSDoer<C, S> extends Doer<any, IMap<string>> { | ||
modules: IMap<JSModule>; | ||
moduleErrors: IMap<any>; | ||
import { ConnectorContext, Environment } from '..'; | ||
export declare class JSDoer extends Doer<JSDoerConfig, any> { | ||
moduleContext: any; | ||
module: any; | ||
modules: any; | ||
setup(): Promise<void>; | ||
hasModule: (name: string) => boolean; | ||
getOrSetModule(name: string, code?: string): JSModule; | ||
main(context: ConnectorContext<any>, taskConfig: TaskConfig, config: JSTask): Promise<any>; | ||
getModule: <T>(modules: any, moduleName: string) => T; | ||
executeDoer: <T>(env: Environment, doerName: string, context: ConnectorContext<any>, coreConfig: T, runAsTask?: boolean, metadata?: any) => Promise<any>; | ||
loadCode(code: any): Promise<void>; | ||
main(context: any, taskConfig: any, config: any): Promise<any>; | ||
} | ||
export declare class JSModule { | ||
private _errors; | ||
private _path; | ||
private _code; | ||
private _name?; | ||
private _module?; | ||
get errors(): IMap<Error>; | ||
get path(): string; | ||
get code(): string; | ||
get exports(): any; | ||
get main(): any; | ||
get setup(): any; | ||
get hasErrors(): boolean; | ||
constructor(path: string, code: string); | ||
get module(): Module | undefined; | ||
get name(): string; | ||
getMethod(name?: string): any; | ||
export declare type JSModuleFunction = (env: Environment, context: ConnectorContext<any>, parameters: any) => Promise<any>; | ||
export interface JSDoerConfig { | ||
code: string; | ||
} | ||
export declare type JSModuleFunction = (env: Environment, context: ConnectorContext<any>, modules: IMap<JSModule>, parameters: any, utilities: JSModuleUtilities) => Promise<any>; | ||
export interface JSModuleUtilities { | ||
getModule: <T>(modules: any, moduleName: string) => any & { | ||
main?: JSModuleFunction; | ||
}; | ||
executeDoer: <T>(env: Environment, doerName: string, context: ConnectorContext<any>, coreConfig: T, runAsTask?: boolean, metadata?: any) => Promise<any>; | ||
} | ||
export interface JSTask { | ||
module: string; | ||
code?: string; | ||
parameters: IMap<any>; | ||
method?: string; | ||
} |
@@ -14,67 +14,61 @@ "use strict"; | ||
}; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const module_1 = __importDefault(require("module")); | ||
// import Module from 'module'; | ||
// import Url from 'url'; | ||
const vm_1 = __importDefault(require("vm")); | ||
const doer_1 = require("./doer"); | ||
const utilities_1 = require("../utilities"); | ||
class JSDoer extends doer_1.Doer { | ||
constructor() { | ||
super(...arguments); | ||
this.modules = {}; | ||
this.moduleErrors = {}; | ||
this.hasModule = (name) => !!this.modules[name]; | ||
this.getModule = (modules, moduleName) => { | ||
const module = modules[moduleName]; | ||
if (!module) | ||
throw new Error(`No JSModule with the name '${moduleName}' has been loaded`); | ||
return module.exports; | ||
}; | ||
this.executeDoer = (env, doerName, context, coreConfig, runAsTask = false, metadata = {}) => __awaiter(this, void 0, void 0, function* () { | ||
const doer = env.getDoer(doerName); | ||
const config = { name: `JSModule`, doer: doerName, config: coreConfig, metadata }; | ||
if (runAsTask) { | ||
return yield doer.execute(context, config); | ||
} | ||
else { | ||
return yield doer.main(context, config, coreConfig); | ||
} | ||
setup() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const code = typeof this.config.code === 'string' ? this.config.code : ''; | ||
yield this.loadCode(code); | ||
}); | ||
} | ||
setup() { | ||
loadCode(code) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const errors = []; | ||
for (const [name, code] of Object.entries(this.config)) { | ||
const newModule = new JSModule(name, code); | ||
if (newModule.setup) | ||
yield newModule.setup(); | ||
this.modules[newModule.name] = newModule; | ||
if (newModule.hasErrors) | ||
errors.push(newModule); | ||
} | ||
if (errors.length > 0) { | ||
console.error(`The following ${errors.length} JSDoer modules have errors: ${errors.map(error => error.name).join(', ')}`); | ||
} | ||
this.moduleContext = vm_1.default.createContext({ | ||
require: (filename) => { | ||
const module = require(filename); | ||
return module; | ||
}, | ||
exports: {} | ||
}); | ||
this.module = new vm_1.default.SourceTextModule(code, { | ||
context: this.moduleContext, | ||
initializeImportMeta(meta) { | ||
meta.url = __dirname; | ||
} | ||
}); | ||
yield this.module.link((specifier, referencingModule) => __awaiter(this, void 0, void 0, function* () { | ||
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { | ||
const module = yield Promise.resolve().then(() => __importStar(require(specifier))); | ||
const exportNames = Object.keys(module); | ||
const syntheticModule = new vm_1.default.SyntheticModule(exportNames, function () { | ||
exportNames.forEach(key => { | ||
this.setExport(key, module[key]); | ||
}); | ||
}, { context: this.moduleContext }); | ||
resolve(syntheticModule); | ||
})); | ||
})); | ||
yield this.module.evaluate(); | ||
this.modules = this.moduleContext.exports; | ||
}); | ||
} | ||
getOrSetModule(name, code) { | ||
if (this.hasModule(name)) | ||
return this.modules[name]; | ||
if (code) { | ||
const newModule = new JSModule(name, code); | ||
this.modules[newModule.name] = newModule; | ||
return this.modules[newModule.name]; | ||
} | ||
throw new Error(`A module with the name '${name}' has not been registered`); | ||
} | ||
main(context, taskConfig, config) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
try { | ||
const module = this.getOrSetModule(config.module, config.code); | ||
const method = module.getMethod(config.method); | ||
const output = yield method(this.env, context, this.modules, config.parameters, { | ||
getModule: this.getModule, executeDoer: this.executeDoer | ||
}); | ||
const module = this.modules[config.module]; | ||
const method = module[config.method || 'main']; | ||
const output = yield method(this.env, context, config.parameters); | ||
return output; | ||
} | ||
catch (err) { | ||
this.moduleErrors[config.module || 'unknown'] = err; | ||
throw err; | ||
@@ -86,70 +80,2 @@ } | ||
exports.JSDoer = JSDoer; | ||
class JSModule { | ||
constructor(path, code) { | ||
this._errors = {}; | ||
this._path = path; | ||
this._code = code; | ||
} | ||
get errors() { return this._errors; } | ||
get path() { return this._path; } | ||
get code() { return this._code; } | ||
get exports() { | ||
return (this.module ? | ||
this.module.exports : | ||
{}); | ||
} | ||
get main() { | ||
return (!this.module ? | ||
null : | ||
typeof this.module.exports === 'function' ? | ||
this.module.exports : | ||
typeof this.module.exports.main === 'function' ? | ||
this.module.exports.main : | ||
null); | ||
} | ||
get setup() { | ||
return (!this.module ? | ||
null : | ||
utilities_1.isObject(this.module.exports) && typeof this.module.exports.setup === 'function' ? | ||
this.module.exports.setup : | ||
null); | ||
} | ||
get hasErrors() { return Object.keys(this._errors).length > 0; } | ||
get module() { | ||
if (!this._module) { | ||
try { | ||
this._module = new module_1.default(''); | ||
this._module.paths = require.main.paths; | ||
this._module.path = this.path; | ||
this._module._compile(this.code, this.path); | ||
} | ||
catch (err) { | ||
this._errors.module = err; | ||
} | ||
} | ||
return this._module; | ||
} | ||
get name() { | ||
if (!this._name) { | ||
try { | ||
const found = this.code.match(/(?:\@module\s+)([\w]+)/); | ||
this._name = (found && found[1] || | ||
this.path.split('/').slice(-1)[0].split('.')[0] || | ||
this.path); | ||
} | ||
catch (err) { | ||
this._errors.name = err; | ||
} | ||
} | ||
return this._name || this.path; | ||
} | ||
getMethod(name = 'main') { | ||
if (name === 'main' && this.main) | ||
return this.main; | ||
if (typeof this.exports[name] === 'function') | ||
return this.exports[name]; | ||
throw Error(`No such method as '${name}' on module '${this.name}'`); | ||
} | ||
} | ||
exports.JSModule = JSModule; | ||
//# sourceMappingURL=js-doer.js.map |
@@ -5,3 +5,3 @@ export * from './cache-managers/cache-manager'; | ||
export * from './doers/doer'; | ||
export * from './doers/js-doer'; | ||
export * from './doers/js-doer.js'; | ||
export * from './doers/multi-doer'; | ||
@@ -8,0 +8,0 @@ export * from './environment'; |
@@ -10,3 +10,3 @@ "use strict"; | ||
__export(require("./doers/doer")); | ||
__export(require("./doers/js-doer")); | ||
__export(require("./doers/js-doer.js")); | ||
__export(require("./doers/multi-doer")); | ||
@@ -13,0 +13,0 @@ __export(require("./environment")); |
{ | ||
"name": "low", | ||
"version": "1.1.91", | ||
"version": "1.1.92", | ||
"description": "a templating driven low-code framework for rapid systems development", | ||
@@ -14,3 +14,3 @@ "main": "lib/index.js", | ||
"clean": "rm -rf docs/; rm -rf lib/; rm -rf coverage/", | ||
"test": "jest --coverage", | ||
"test": "node --experimental-vm-modules ./node_modules/.bin/jest --coverage", | ||
"build": "npm run clean && tsc && npm test" | ||
@@ -26,4 +26,5 @@ }, | ||
}, | ||
"gitHead": "7541785386fb6c0a69b171870635f52a469ba1ac", | ||
"gitHead": "11f8f1efb302e520ff0145a8af367d8f0096cf5e", | ||
"devDependencies": { | ||
"@babel/code-frame": "^7.21.4", | ||
"@types/jest": "^24.9.0", | ||
@@ -30,0 +31,0 @@ "jest": "^24.9.0", |
import { Environment } from '../environment'; | ||
import { ConnectorContext } from '../connectors/connector'; | ||
import { JSDoer } from '../doers/js-doer'; | ||
import { IMap } from '..'; | ||
import { JSDoer } from './js-doer'; | ||
import FS from 'fs'; | ||
@@ -13,29 +12,25 @@ import Path from 'path'; | ||
const context = createEmptyContext(env); | ||
console.log(`\n\nDOER:\n${JSON.stringify(doer.main, null, 2)}`); | ||
const [test1, test2] = await doer.modules.Basic.main(env, context, {}); | ||
const basicModule = doer.modules.Basic; | ||
const [test1, test2, test3, test4] = await basicModule.getMethod()(env, context, doer.modules, {}, {}); | ||
expect(test1).toEqual('24.9.0'); | ||
expect(test2).toEqual('24.9.0'); | ||
expect(test3).toEqual('<div id="24.9.0"></div>'); | ||
expect(test4).toEqual('SECOND'); | ||
expect(test1).toEqual('TEST'); | ||
expect(test2).toEqual(`<div id="TEST"></div>`); | ||
}); | ||
function getTestModulesConfig () { | ||
const config: IMap<string> = {}; | ||
const testModulesPath = Path.join(__dirname, '..', '..', 'js-doer-tests'); | ||
const modules = FS.readdirSync(testModulesPath); | ||
for (const filename of modules) { | ||
const path = Path.join(testModulesPath, filename); | ||
const code = FS.readFileSync(path).toString(); | ||
config[path] = code; | ||
} | ||
return config; | ||
const testModulePath = Path.join(__dirname, '..', '..', 'js-doer-tests', 'basic.js'); | ||
console.log(`\n\nLOADING TEST CODE FROM '${testModulePath}`); | ||
const module = { code: FS.readFileSync(testModulePath).toString() }; | ||
console.log(`\n\nCODE:\n${module.code}`); | ||
return module; | ||
} | ||
async function setupEnvironment(): Promise<[JSDoer<any, IMap<string>>, Environment]> { | ||
async function setupEnvironment(): Promise<[JSDoer, Environment]> { | ||
const doer = new JSDoer(); | ||
const modules = { doers: [doer] }; | ||
const config = { modules: { JSDoer: getTestModulesConfig() } }; | ||
console.log(`\n\nJSDOER TEST ENV CONFIG:\n${JSON.stringify(config)}`); | ||
const env = new Environment(modules, [], config); | ||
console.log(`\n\nJSDOER ENV MODULE:\n${JSON.stringify(env.config)}`) | ||
await env.init(); | ||
@@ -42,0 +37,0 @@ return [doer, env]; |
@@ -1,169 +0,79 @@ | ||
import Module from 'module'; | ||
// import Module from 'module'; | ||
// import Url from 'url'; | ||
import VM from 'vm'; | ||
import { Doer } from './doer'; | ||
import { ConnectorContext } from '../connectors/connector'; | ||
import { TaskConfig } from '../environment'; | ||
import { Environment, IMap } from '..'; | ||
import { isObject } from '../utilities'; | ||
import { ConnectorContext, Environment } from '..'; | ||
export class JSDoer extends Doer<JSDoerConfig, any> { | ||
moduleContext: any; | ||
module: any; | ||
modules: any; | ||
export class JSDoer<C, S> extends Doer<any, IMap<string>> { | ||
modules: IMap<JSModule> = {}; | ||
moduleErrors: IMap<any> = {}; | ||
async setup() { | ||
const errors = []; | ||
const code = typeof this.config.code === 'string' ? this.config.code : ''; | ||
await this.loadCode(code); | ||
} | ||
for (const [name, code] of Object.entries(this.config)) { | ||
const newModule = new JSModule(name, code as string); | ||
if (newModule.setup) await newModule.setup(); | ||
this.modules[newModule.name] = newModule; | ||
if (newModule.hasErrors) errors.push(newModule); | ||
} | ||
async loadCode (code) { | ||
this.moduleContext = VM.createContext({ | ||
require: (filename) => { | ||
const module = require(filename); | ||
return module; | ||
}, | ||
exports: {} | ||
}); | ||
if (errors.length > 0) { | ||
console.error(`The following ${errors.length} JSDoer modules have errors: ${errors.map(error => error.name).join(', ')}`); | ||
} | ||
} | ||
this.module = new (VM as any).SourceTextModule(code, { | ||
context: this.moduleContext, | ||
initializeImportMeta(meta) { | ||
meta.url = __dirname | ||
} | ||
}); | ||
hasModule = (name: string) => !!this.modules[name]; | ||
await this.module.link(async (specifier, referencingModule) => { | ||
return new Promise(async (resolve, reject) => { | ||
const module = await import(specifier); | ||
const exportNames = Object.keys(module); | ||
getOrSetModule(name: string, code?: string) { | ||
if (this.hasModule(name)) return this.modules[name]; | ||
if (code) { | ||
const newModule = new JSModule(name, code); | ||
this.modules[newModule.name] = newModule | ||
return this.modules[newModule.name]; | ||
} | ||
throw new Error(`A module with the name '${name}' has not been registered`); | ||
const syntheticModule = new (VM as any).SyntheticModule( | ||
exportNames, | ||
function () { | ||
exportNames.forEach(key => { | ||
(this as any).setExport(key, module[key]); | ||
}); | ||
}, { context: this.moduleContext } | ||
); | ||
resolve(syntheticModule); | ||
}); | ||
}); | ||
await this.module.evaluate(); | ||
this.modules = this.moduleContext.exports; | ||
} | ||
async main(context: ConnectorContext<any>, taskConfig: TaskConfig, config: JSTask): Promise<any> { | ||
async main(context, taskConfig, config) { | ||
try { | ||
const module = this.getOrSetModule(config.module, config.code); | ||
const method = module.getMethod(config.method); | ||
const output = await method(this.env, context, this.modules, config.parameters, { | ||
getModule: this.getModule, executeDoer: this.executeDoer | ||
}); | ||
const module = this.modules[config.module]; | ||
const method = module[config.method || 'main']; | ||
const output = await method(this.env, context, config.parameters); | ||
return output; | ||
} catch (err) { | ||
this.moduleErrors[config.module || 'unknown'] = err; | ||
throw err; | ||
} | ||
} | ||
getModule = <T>(modules: any, moduleName: string) => { | ||
const module = modules[moduleName]; | ||
if (!module) throw new Error(`No JSModule with the name '${moduleName}' has been loaded`); | ||
return module.exports as T; | ||
} | ||
executeDoer = async <T>(env: Environment, doerName: string, context: ConnectorContext<any>, coreConfig: T, runAsTask = false, metadata: any = {}) => { | ||
const doer = env.getDoer(doerName); | ||
const config = { name: `JSModule`, doer: doerName, config: coreConfig, metadata }; | ||
if (runAsTask) { | ||
return await doer.execute(context, config as TaskConfig); | ||
} else { | ||
return await doer.main(context, config as TaskConfig, coreConfig); | ||
} | ||
} | ||
} | ||
export class JSModule { | ||
private _errors: IMap<Error> = {}; | ||
private _path: string; | ||
private _code: string; | ||
private _name?: string; | ||
private _module?: Module; | ||
export type JSModuleFunction = (env: Environment, context: ConnectorContext<any>, parameters: any) => Promise<any>; | ||
get errors() { return this._errors; } | ||
get path() { return this._path; } | ||
get code() { return this._code; } | ||
// export interface JSTask { | ||
// module: string; | ||
// code?: string; | ||
// parameters: IMap<any>; | ||
// method?: string; | ||
// } | ||
get exports() { | ||
return ( | ||
this.module ? | ||
this.module.exports : | ||
{} | ||
); | ||
} | ||
get main() { | ||
return ( | ||
!this.module ? | ||
null : | ||
typeof this.module.exports === 'function' ? | ||
this.module.exports : | ||
typeof this.module.exports.main === 'function' ? | ||
this.module.exports.main : | ||
null | ||
); | ||
} | ||
get setup() { | ||
return ( | ||
!this.module ? | ||
null : | ||
isObject(this.module.exports) && typeof this.module.exports.setup === 'function' ? | ||
this.module.exports.setup : | ||
null | ||
); | ||
} | ||
get hasErrors() { return Object.keys(this._errors).length > 0; } | ||
constructor(path: string, code: string) { | ||
this._path = path; | ||
this._code = code; | ||
} | ||
get module() { | ||
if (!this._module) { | ||
try { | ||
this._module = new Module(''); | ||
this._module.paths = (require as any).main.paths; | ||
(this._module as any).path = this.path; | ||
(this._module as any)._compile(this.code, this.path); | ||
} catch (err) { | ||
this._errors.module = err as Error; | ||
} | ||
} | ||
return this._module; | ||
} | ||
get name() { | ||
if (!this._name) { | ||
try { | ||
const found = this.code.match(/(?:\@module\s+)([\w]+)/); | ||
this._name = ( | ||
found && found[1] || | ||
this.path.split('/').slice(-1)[0].split('.')[0] || | ||
this.path | ||
) | ||
} catch (err) { | ||
this._errors.name = err as Error; | ||
} | ||
} | ||
return this._name || this.path; | ||
} | ||
getMethod(name = 'main') { | ||
if (name === 'main' && this.main) return this.main; | ||
if (typeof this.exports[name] === 'function') return this.exports[name]; | ||
throw Error(`No such method as '${name}' on module '${this.name}'`); | ||
} | ||
} | ||
export type JSModuleFunction = (env: Environment, context: ConnectorContext<any>, modules: IMap<JSModule>, parameters: any, utilities: JSModuleUtilities) => Promise<any>; | ||
export interface JSModuleUtilities { | ||
getModule: <T>(modules: any, moduleName: string) => any & { main?: JSModuleFunction }; | ||
executeDoer: <T>(env: Environment, doerName: string, context: ConnectorContext<any>, coreConfig: T, runAsTask?: boolean, metadata?: any) => Promise<any> | ||
} | ||
export interface JSTask { | ||
module: string; | ||
code?: string; | ||
parameters: IMap<any>; | ||
method?: string; | ||
export interface JSDoerConfig { | ||
code: string; | ||
} |
@@ -5,3 +5,3 @@ export * from './cache-managers/cache-manager'; | ||
export * from './doers/doer'; | ||
export * from './doers/js-doer'; | ||
export * from './doers/js-doer.js'; | ||
export * from './doers/multi-doer'; | ||
@@ -8,0 +8,0 @@ export * from './environment'; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
513011
6
125
4875
5