impress
Advanced tools
Comparing version 2.6.0 to 2.6.1
@@ -5,2 +5,9 @@ # Changelog | ||
## [2.6.1][] - 2021-09-10 | ||
- Quick (not optimal) implementation of `application.invoke` | ||
- Refactor impress internal dependencies loading | ||
- Remove access to `worker_threads` from application | ||
- Execute tasks in thread pool | ||
## [2.6.0][] - 2021-09-08 | ||
@@ -208,3 +215,4 @@ | ||
[unreleased]: https://github.com/metarhia/impress/compare/v2.6.0...HEAD | ||
[unreleased]: https://github.com/metarhia/impress/compare/v2.6.1...HEAD | ||
[2.6.1]: https://github.com/metarhia/impress/compare/v2.6.0...v2.6.1 | ||
[2.6.0]: https://github.com/metarhia/impress/compare/v2.5.3...v2.6.0 | ||
@@ -211,0 +219,0 @@ [2.5.3]: https://github.com/metarhia/impress/compare/v2.5.2...v2.5.3 |
@@ -7,3 +7,2 @@ 'use strict'; | ||
const path = require('path'); | ||
const { Config } = require('metaconfiguration'); | ||
@@ -61,2 +60,3 @@ const metavm = require('metavm'); | ||
let starting = 0; | ||
let next = schedulerId + 1; | ||
const threads = new Array(count); | ||
@@ -92,14 +92,29 @@ | ||
worker.on('message', (data) => { | ||
if (data.type === 'event') { | ||
if (data.name === 'started') active++; | ||
if (data.name.startsWith('task:')) { | ||
const transferList = data.port ? [data.port] : undefined; | ||
scheduler.postMessage(data, transferList); | ||
worker.on('message', (msg) => { | ||
const { type, name, port } = msg; | ||
if (type === 'event') { | ||
if (name === 'started') { | ||
active++; | ||
if (active === count && startTimer) { | ||
clearTimeout(startTimer); | ||
startTimer = null; | ||
} | ||
} | ||
return; | ||
} | ||
if (active === count && startTimer) { | ||
clearTimeout(startTimer); | ||
startTimer = null; | ||
const transferList = port ? [port] : undefined; | ||
if (type === 'task') { | ||
scheduler.postMessage(msg, transferList); | ||
return; | ||
} | ||
if (type === 'invoke') { | ||
if (next === count) { | ||
port.postMessage({ error: new Error('No thread available') }); | ||
return; | ||
} | ||
threads[next].postMessage(msg, transferList); | ||
next++; | ||
if (next === count) next = schedulerId + 1; | ||
return; | ||
} | ||
}); | ||
@@ -106,0 +121,0 @@ }; |
'use strict'; | ||
const { node, npm, metarhia } = require('./dependencies.js'); | ||
const { path, events, fs } = node; | ||
const { metavm, metawatch } = metarhia; | ||
const path = require('path'); | ||
const events = require('events'); | ||
const fs = require('fs'); | ||
const { MessageChannel, parentPort, threadId } = require('worker_threads'); | ||
const metavm = require('metavm'); | ||
const metawatch = require('metawatch'); | ||
const { Interfaces } = require('./interfaces.js'); | ||
@@ -107,6 +111,7 @@ const { Modules } = require('./modules.js'); | ||
const { server: { host, port, protocol } = {} } = this; | ||
const worker = { id: 'W' + node.worker.threadId.toString() }; | ||
const worker = { id: 'W' + threadId.toString() }; | ||
const server = { host, port, protocol }; | ||
const application = { worker, server, resources, schemas, scheduler }; | ||
application.introspect = async (interfaces) => this.introspect(interfaces); | ||
application.invoke = async (call) => this.invoke(call); | ||
const sandbox = { ...SANDBOX, console, application, config }; | ||
@@ -159,3 +164,3 @@ sandbox.api = {}; | ||
} | ||
if (node.worker.threadId === 1) { | ||
if (threadId === 1) { | ||
this.console.debug('Reload: /' + relPath); | ||
@@ -172,3 +177,3 @@ } | ||
this[place].delete(filePath); | ||
if (node.worker.threadId === 1) { | ||
if (threadId === 1) { | ||
this.console.debug('Deleted: /' + relPath); | ||
@@ -194,4 +199,16 @@ } | ||
} | ||
async invoke({ method, args }) { | ||
const { port1, port2 } = new MessageChannel(); | ||
const msg = { type: 'invoke', data: { method, args }, port: port1 }; | ||
return new Promise((resolve, reject) => { | ||
port2.on('message', ({ error, data }) => { | ||
if (error) reject(error); | ||
else resolve(data); | ||
}); | ||
parentPort.postMessage(msg, [port1]); | ||
}); | ||
} | ||
} | ||
module.exports = new Application(); |
'use strict'; | ||
const { metautil } = require('./dependencies.js').metarhia; | ||
const metautil = require('metautil'); | ||
@@ -5,0 +5,0 @@ const accounts = new Map(); |
'use strict'; | ||
const { fsp, path } = require('./dependencies.js').node; | ||
const path = require('path'); | ||
const fsp = require('fs').promises; | ||
@@ -5,0 +6,0 @@ class Cache { |
@@ -7,3 +7,3 @@ 'use strict'; | ||
const system = ['util', 'child_process', 'worker_threads', 'os', 'v8', 'vm']; | ||
const system = ['util', 'child_process', 'os', 'v8', 'vm']; | ||
const tools = ['path', 'url', 'string_decoder', 'querystring', 'assert']; | ||
@@ -16,6 +16,3 @@ const streams = ['stream', 'fs', 'crypto', 'zlib', 'readline']; | ||
const ORG_LENGTH = '@metarhia/'.length; | ||
const metalibs = ['metaconfiguration']; | ||
const metacore = ['metautil', 'metavm', 'metacom', 'metalog', 'metawatch']; | ||
const metaoptional = ['metaschema', 'metasql']; | ||
const metapkg = [...metalibs, ...metacore, ...metaoptional]; | ||
const metapkg = ['metautil', 'metacom', 'metaschema', 'metasql']; | ||
@@ -54,3 +51,2 @@ const npmpkg = ['ws']; | ||
node.asyncHooks = node['async_hooks']; | ||
node.worker = node['worker_threads']; | ||
node.fsp = node.fs.promises; | ||
@@ -57,0 +53,0 @@ |
'use strict'; | ||
const { node, metarhia } = require('./dependencies.js'); | ||
const { fs, fsp, path } = node; | ||
const { metautil } = metarhia; | ||
const path = require('path'); | ||
const fs = require('fs'); | ||
const fsp = fs.promises; | ||
const metautil = require('metautil'); | ||
const application = require('./application.js'); | ||
@@ -7,0 +8,0 @@ |
'use strict'; | ||
const { node, metarhia, npm } = require('./dependencies.js'); | ||
const { fsp, path } = node; | ||
const { metavm, metautil } = metarhia; | ||
const path = require('path'); | ||
const fsp = require('fs').promises; | ||
const metavm = require('metavm'); | ||
const metautil = require('metautil'); | ||
const { metarhia, npm } = require('./dependencies.js'); | ||
const { Procedure } = require('./procedure.js'); | ||
@@ -7,0 +9,0 @@ const { Cache } = require('./cache.js'); |
'use strict'; | ||
const { node, metarhia } = require('./dependencies.js'); | ||
const { path } = node; | ||
const { metavm } = metarhia; | ||
const path = require('path'); | ||
const metavm = require('metavm'); | ||
const { Cache } = require('./cache.js'); | ||
@@ -7,0 +6,0 @@ |
'use strict'; | ||
const { metautil, metaschema } = require('./dependencies.js').metarhia; | ||
const metautil = require('metautil'); | ||
const { Semaphore, createAbortController } = metautil; | ||
const { Schema } = metaschema; | ||
const { Schema } = require('metaschema'); | ||
@@ -7,0 +7,0 @@ const EMPTY_CONTEXT = Object.freeze({}); |
'use strict'; | ||
const { node, metarhia } = require('./dependencies.js'); | ||
const { fsp, path } = node; | ||
const { metautil } = metarhia; | ||
const path = require('path'); | ||
const fsp = require('fs').promises; | ||
const metautil = require('metautil'); | ||
const { Cache } = require('./cache.js'); | ||
@@ -7,0 +7,0 @@ |
'use strict'; | ||
const { node, metarhia } = require('./dependencies.js'); | ||
const { fsp, path, worker } = node; | ||
const { metautil } = metarhia; | ||
const path = require('path'); | ||
const fsp = require('fs').promises; | ||
const { MessageChannel, parentPort } = require('worker_threads'); | ||
const metautil = require('metautil'); | ||
const findHandler = (sandbox, name) => { | ||
const [key, rest] = metautil.split(name, '.'); | ||
const element = sandbox[key]; | ||
if (element) { | ||
if (rest === '') return element; | ||
return findHandler(element, rest); | ||
} | ||
return null; | ||
}; | ||
class Scheduler { | ||
@@ -30,7 +21,7 @@ constructor(application) { | ||
worker.parentPort.on('message', async ({ type, name, task, port }) => { | ||
if (type !== 'event') return; | ||
if (name === 'task:add') port.postMessage({ id: await this.add(task) }); | ||
else if (name === 'task:remove') this.remove(task.id); | ||
else if (name === 'task:stop') this.stop(task.name); | ||
parentPort.on('message', async ({ type, name, data, port }) => { | ||
if (type !== 'task') return; | ||
if (name === 'add') port.postMessage({ id: await this.add(data) }); | ||
else if (name === 'remove') this.remove(data.id); | ||
else if (name === 'stop') this.stop(data.name); | ||
}); | ||
@@ -62,3 +53,3 @@ | ||
const { id, name, every, args, run } = record; | ||
const task = { | ||
const data = { | ||
id, | ||
@@ -77,5 +68,4 @@ name, | ||
run, | ||
handler: findHandler(this.application.sandbox, run), | ||
}; | ||
this.tasks.set(id, task); | ||
this.tasks.set(id, data); | ||
return this.start(id); | ||
@@ -86,4 +76,4 @@ } | ||
if (!this.executor) { | ||
const { port1, port2 } = new worker.MessageChannel(); | ||
const msg = { type: 'event', name: 'task:add', task, port: port1 }; | ||
const { port1, port2 } = new MessageChannel(); | ||
const msg = { type: 'task', name: 'add', data: task, port: port1 }; | ||
return new Promise((resolve) => { | ||
@@ -93,3 +83,3 @@ port2.on('message', ({ id }) => { | ||
}); | ||
worker.parentPort.postMessage(msg, [port1]); | ||
parentPort.postMessage(msg, [port1]); | ||
}); | ||
@@ -113,4 +103,4 @@ } | ||
if (!this.executor) { | ||
const msg = { type: 'event', name: 'task:remove', task: { id } }; | ||
worker.parentPort.postMessage(msg); | ||
const msg = { type: 'task', name: 'remove', data: { id } }; | ||
parentPort.postMessage(msg); | ||
return; | ||
@@ -184,3 +174,6 @@ } | ||
await this.enter(task.name); | ||
task.result = await task.handler(task.args); | ||
task.result = await this.application.invoke({ | ||
method: task.run, | ||
args: task.args, | ||
}); | ||
} catch (err) { | ||
@@ -214,4 +207,4 @@ task.error = err; | ||
if (!this.executor) { | ||
const msg = { type: 'event', name: 'task:stop', task: { name } }; | ||
worker.parentPort.postMessage(msg); | ||
const msg = { type: 'task', name: 'stop', data: { name } }; | ||
parentPort.postMessage(msg); | ||
return; | ||
@@ -218,0 +211,0 @@ } |
'use strict'; | ||
const { node, metarhia } = require('./dependencies.js'); | ||
const { path } = node; | ||
const { Model, loadSchema } = metarhia.metaschema; | ||
const path = require('path'); | ||
const { Model, loadSchema } = require('metaschema'); | ||
const { Cache } = require('./cache.js'); | ||
@@ -7,0 +6,0 @@ |
'use strict'; | ||
const { node, metarhia, notLoaded } = require('./dependencies.js'); | ||
const { worker, fsp, path } = node; | ||
const path = require('path'); | ||
const fsp = require('fs').promises; | ||
const { parentPort, threadId } = require('worker_threads'); | ||
const { Config } = require('metaconfiguration'); | ||
const { Logger } = require('metalog'); | ||
const { Server } = require('metacom'); | ||
const metavm = require('metavm'); | ||
const metautil = require('metautil'); | ||
const { notLoaded } = require('./dependencies.js'); | ||
const application = require('./application.js'); | ||
const { Config } = metarhia.metaconfiguration; | ||
const { Logger } = metarhia.metalog; | ||
const { Server } = metarhia.metacom; | ||
(async () => { | ||
const configPath = path.join(application.path, 'config'); | ||
const context = metarhia.metavm.createContext({ process }); | ||
const context = metavm.createContext({ process }); | ||
const options = { mode: process.env.MODE, context }; | ||
@@ -17,3 +21,2 @@ const config = await new Config(configPath, options); | ||
const home = application.root; | ||
const { threadId } = worker; | ||
const logger = await new Logger({ | ||
@@ -75,12 +78,30 @@ path: logPath, | ||
console.info(`Application started in worker ${threadId}`); | ||
worker.parentPort.postMessage({ type: 'event', name: 'started' }); | ||
parentPort.postMessage({ type: 'event', name: 'started' }); | ||
worker.parentPort.on('message', async (data) => { | ||
if (data.type === 'event' && data.name === 'stop') { | ||
if (application.finalization) return; | ||
console.info(`Graceful shutdown in worker ${threadId}`); | ||
await application.shutdown(); | ||
process.exit(0); | ||
parentPort.on('message', async ({ type, name, data, port }) => { | ||
if (type === 'event') { | ||
if (name === 'stop') { | ||
if (application.finalization) return; | ||
console.info(`Graceful shutdown in worker ${threadId}`); | ||
await application.shutdown(); | ||
process.exit(0); | ||
} | ||
return; | ||
} | ||
if (type === 'invoke') { | ||
const { method, args } = data; | ||
const handler = metautil.namespaceByPath(application.sandbox, method); | ||
if (!handler) { | ||
port.postMessage({ error: new Error('Handler not found') }); | ||
return; | ||
} | ||
try { | ||
const result = await handler(args); | ||
port.postMessage({ data: result }); | ||
} catch (err) { | ||
port.postMessage({ error: err }); | ||
application.console.error(err.stack); | ||
} | ||
} | ||
}); | ||
})(); |
{ | ||
"name": "impress", | ||
"version": "2.6.0", | ||
"version": "2.6.1", | ||
"author": "Timur Shemsedinov <timur.shemsedinov@gmail.com>", | ||
@@ -71,5 +71,5 @@ "description": "Enterprise application server for Node.js", | ||
"metaconfiguration": "^2.1.5", | ||
"metalog": "^3.1.3", | ||
"metaschema": "^1.3.3", | ||
"metautil": "^3.5.10", | ||
"metalog": "^3.1.4", | ||
"metaschema": "^1.3.4", | ||
"metautil": "^3.5.11", | ||
"metavm": "^1.0.3", | ||
@@ -79,3 +79,3 @@ "metawatch": "^1.0.4" | ||
"devDependencies": { | ||
"@types/node": "^16.6.2", | ||
"@types/node": "^16.9.1", | ||
"@types/ws": "^7.4.7", | ||
@@ -85,8 +85,8 @@ "eslint": "^7.32.0", | ||
"eslint-config-prettier": "^8.3.0", | ||
"eslint-plugin-import": "^2.24.0", | ||
"eslint-plugin-prettier": "^3.4.0", | ||
"eslint-plugin-import": "^2.24.2", | ||
"eslint-plugin-prettier": "^4.0.0", | ||
"metatests": "^0.7.2", | ||
"prettier": "^2.3.2", | ||
"typescript": "^4.3.5" | ||
"prettier": "^2.4.0", | ||
"typescript": "^4.4.2" | ||
} | ||
} |
@@ -14,2 +14,7 @@ export interface Task { | ||
export interface InvokeTarget { | ||
method: string; | ||
args: object; | ||
} | ||
export interface Application { | ||
@@ -21,2 +26,3 @@ worker: object; | ||
introspect: () => Promise<any>; | ||
invoke: (target: InvokeTarget) => Promise<any>; | ||
scheduler: Scheduler; | ||
@@ -23,0 +29,0 @@ } |
@@ -6,3 +6,2 @@ import { LogConfig, ScaleConfig, ServerConfig, SessionsConfig } from './config'; | ||
import * as _cp from 'child_process'; | ||
import * as _wt from 'worker_threads'; | ||
import * as _os from 'os'; | ||
@@ -69,4 +68,2 @@ import * as _v8 from 'v8'; | ||
const childProcess: typeof _cp; | ||
const worker_threads: typeof _wt; | ||
const worker: typeof _wt; | ||
const os: typeof _os; | ||
@@ -73,0 +70,0 @@ const v8: typeof _v8; |
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
59573
1356
11
Updatedmetalog@^3.1.4
Updatedmetaschema@^1.3.4
Updatedmetautil@^3.5.11