Socket
Socket
Sign inDemoInstall

impress

Package Overview
Dependencies
12
Maintainers
4
Versions
718
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.6.10 to 3.0.0-alpha.1

lib/planner.js

10

CHANGELOG.md

@@ -5,2 +5,9 @@ # Changelog

## [3.0.0-alpha.1][] - 2022-06-25
- Worker-based multitenancy implementation
- Update to metaschema v2.x
- Initial integration bus implementation (new place `application/bus`)
- Fix shutdown while initialization
## [2.6.10][] - 2022-05-09

@@ -270,3 +277,4 @@

[unreleased]: https://github.com/metarhia/impress/compare/v2.6.10...HEAD
[unreleased]: https://github.com/metarhia/impress/compare/v3.0.0-alpha.1...HEAD
[3.0.0-alpha.1]: https://github.com/metarhia/impress/compare/v2.6.10...v3.0.0-alpha.1
[2.6.10]: https://github.com/metarhia/impress/compare/v2.6.9...v2.6.10

@@ -273,0 +281,0 @@ [2.6.9]: https://github.com/metarhia/impress/compare/v2.6.8...v2.6.9

260

impress.js

@@ -5,2 +5,3 @@ 'use strict';

const fsp = require('fs').promises;
const { Worker } = require('worker_threads');

@@ -13,9 +14,7 @@ const path = require('path');

const { Logger } = require('metalog');
let logger = null;
const { Planner } = require('./lib/planner.js');
let finalization = false;
let initialization = true;
const CONFIG_SECTIONS = ['log', 'scale', 'server', 'sessions'];
const PATH = process.cwd();
const WORKER_PATH = path.join(__dirname, 'lib/worker.js');
const CFG_PATH = path.join(PATH, 'application/config');

@@ -25,6 +24,21 @@ const LOG_PATH = path.join(PATH, 'log');

const LOG_OPTIONS = { path: LOG_PATH, home: PATH, workerId: 0 };
const CONTEXT = metavm.createContext({ process });
const CFG_OPTIONS = { mode: process.env.MODE, context: CONTEXT };
const exit = async (message = 'Can not start Application server') => {
console.error(metautil.replace(message, PATH, ''));
if (logger) await logger.close();
const impress = {
logger: null,
config: null,
planner: null,
finalized: () => {},
finalization: false,
initialization: true,
console,
applications: new Map(),
lastWorkerId: 0,
startTimer: null,
};
const exit = async (message) => {
impress.console.info(message);
if (impress.logger && impress.logger.active) await impress.logger.close();
process.exit(1);

@@ -35,14 +49,75 @@ };

const msg = err.stack || err.message || 'no stack trace';
console.error(`${type} error: ${msg}`);
if (finalization) return;
if (initialization) exit();
impress.console.error(`${type}: ${msg}`);
if (impress.finalization) return;
if (impress.initialization) exit('Can not start Application server');
};
process.on('uncaughtException', logError('uncaughtException'));
process.on('warning', logError('warning'));
process.on('unhandledRejection', logError('unhandledRejection'));
process.on('uncaughtException', logError('Uncaught exception'));
process.on('warning', logError('Warning'));
process.on('unhandledRejection', logError('Unhandled rejection'));
const startWorker = async (app, kind, port, id = ++impress.lastWorkerId) => {
const workerData = { id, kind, path: app.path, port };
const options = { trackUnmanagedFds: true, workerData };
const worker = new Worker(WORKER_PATH, options);
if (kind === 'worker') {
app.pool.add(worker);
await app.pool.capture();
}
app.threads.set(id, worker);
worker.on('exit', (code) => {
if (code !== 0) startWorker(app, kind, port, id);
else app.threads.delete(id);
if (app.threads.size === 0) {
impress.applications.delete(app.path);
if (impress.applications.size === 0) impress.finalized();
}
});
const handlers = {
started: ({ kind }) => {
app.ready++;
if (kind === 'worker') app.pool.release(worker);
if (app.threads.size === app.ready) {
clearTimeout(impress.startTimer);
impress.initialization = false;
impress.console.info(`App started: ${app.path}`);
}
},
task: async ({ action, port, task }) => {
const { planner } = impress;
task.app = app.path;
if (action === 'add') port.postMessage({ id: await planner.add(task) });
else if (action === 'remove') planner.remove(task.id);
else if (action === 'stop') planner.stop(task.name);
},
invoke: async (msg) => {
const { status, port, exclusive } = msg;
if (status === 'done') {
app.pool.release(worker);
return;
}
const promisedThread = exclusive ? app.pool.capture() : app.pool.next();
const next = await promisedThread.catch(() => {
const error = new Error('No thread available');
port.postMessage({ name: 'error', error });
return null;
});
if (!next) return;
next.postMessage(msg, [port]);
},
};
worker.on('message', (msg) => {
const handler = handlers[msg.name];
if (handler) handler(msg);
});
};
const validateConfig = async (config) => {
let valid = true;
const schemaPath = path.join(__dirname, 'schemas/config');
let valid = true;
for (const section of CONFIG_SECTIONS) {

@@ -54,3 +129,3 @@ const fileName = path.join(schemaPath, section + '.js');

for (const err of checkResult.errors) {
console.error(`${err} in application/config/${section}.js`);
impress.console.error(`${err} in application/config/${section}.js`);
}

@@ -60,107 +135,85 @@ valid = false;

}
if (!valid) exit();
if (!valid) exit('Application server configuration is invalid');
};
(async () => {
const context = metavm.createContext({ process });
const CFG_OPTIONS = { mode: process.env.MODE, context };
const config = await new Config(CFG_PATH, CFG_OPTIONS).catch((err) => {
const loadApplication = async (root) => {
impress.console.info(`Start: ${root}`);
const configPath = path.join(root, 'application/config');
const config = await new Config(configPath, CFG_OPTIONS).catch((err) => {
exit(`Can not read configuration: ${CFG_PATH}\n${err.stack}`);
});
await validateConfig(config);
logger = await new Logger({ ...LOG_OPTIONS, ...config.log });
logger.on('error', logError('logger error'));
if (logger.active) global.console = logger.console;
await validateConfig(config);
const { balancer, ports = [], workers = {} } = config.server;
const serversCount = ports.length + (balancer ? 1 : 0);
const schedulerCount = 1;
const schedulerId = serversCount;
const poolSize = workers.pool || 0;
const count = serversCount + schedulerCount + poolSize;
let startTimer = null;
let active = 0;
let starting = 0;
let scheduler = null;
const threads = new Array(count);
const threads = new Map();
const pool = new metautil.Pool({ timeout: workers.wait });
const stop = async () => {
finalization = true;
const closing = logger.close();
for (const worker of threads) {
worker.postMessage({ type: 'event', name: 'stop' });
}
await closing;
};
const app = { path: root, config, threads, pool, ready: 0 };
const start = (id) => {
const workerPath = path.join(__dirname, 'lib/worker.js');
const worker = new Worker(workerPath, { trackUnmanagedFds: true });
threads[id] = worker;
if (id === schedulerId) scheduler = worker;
else if (id > schedulerId) pool.add(worker);
if (balancer) await startWorker(app, 'balancer', balancer);
for (const port of ports) await startWorker(app, 'server', port);
const poolSize = workers.pool || 0;
for (let i = 0; i < poolSize; i++) await startWorker(app, 'worker');
worker.on('exit', (code) => {
active--;
if (code !== 0) start(id);
else if (active === 0) process.exit(0);
else if (active < 0 && id === 0) exit('Application server stopped');
});
impress.applications.set(root, app);
};
worker.on('online', () => {
if (++starting === count) {
startTimer = setTimeout(() => {
if (active !== count) {
console.warn(`Worker ${id} initialization timeout`);
}
}, config.server.timeouts.start);
}
});
const loadApplications = async () => {
const list = await fsp
.readFile('.applications', 'utf8')
.then((data) => data.split('\n').filter((s) => s.length !== 0))
.catch(() => [PATH]);
for (const path of list) {
await loadApplication(path);
}
};
const ITC = {
event: ({ name }) => {
if (name !== 'started') return;
active++;
if (active === count && startTimer) {
clearTimeout(startTimer);
startTimer = null;
}
},
const stopApplication = (root) => {
const app = impress.applications.get(root);
for (const thread of app.threads.values()) {
thread.postMessage({ name: 'stop' });
}
};
task: (msg) => {
if (msg.type !== 'task') return;
const transferList = msg.port ? [msg.port] : undefined;
scheduler.postMessage(msg, transferList);
},
const stop = async () => {
impress.finalization = true;
const logClosed = impress.logger.close();
const portsClosed = new Promise((resolve) => {
impress.finalized = resolve;
setTimeout(() => {
impress.console.error('Exit with graceful shutdown timeout');
resolve();
}, impress.config.server.timeouts.stop);
});
for (const app of impress.applications.values()) {
stopApplication(app.path);
}
await Promise.allSettled([logClosed, portsClosed]);
exit('Application server stopped');
};
invoke: async (msg) => {
const { name, port, exclusive } = msg;
if (name === 'done') {
if (exclusive) pool.release(worker);
return;
}
if (name !== 'request') return;
const promisedThread = exclusive ? pool.capture() : pool.next();
const next = await promisedThread.catch(() => {
port.postMessage({ error: new Error('No thread available') });
return null;
});
if (!next) return;
next.postMessage(msg, [port]);
},
};
(async () => {
const configPath = path.join(PATH, 'application/config');
const config = await new Config(configPath, CFG_OPTIONS).catch((err) => {
exit(`Can not read configuration: ${CFG_PATH}\n${err.stack}`);
});
await validateConfig(config);
impress.config = config;
const logger = await new Logger({ ...LOG_OPTIONS, ...config.log });
logger.on('error', logError('Logger'));
if (logger.active) impress.console = logger.console;
impress.logger = logger;
const tasksPath = path.join(PATH, 'application/tasks');
const tasksConfig = config.server.scheduler;
impress.planner = await new Planner(tasksPath, tasksConfig, impress);
worker.on('message', (msg) => {
const handler = ITC[msg.type];
if (handler) handler(msg);
});
};
for (let id = 0; id < count; id++) start(id);
process.on('SIGINT', stop);
process.on('SIGTERM', stop);
impress.startTimer = setTimeout(() => {
impress.console.warn(`Initialization timeout`);
}, config.server.timeouts.start);
await loadApplications();
if (process.stdin.isTTY) {

@@ -173,3 +226,2 @@ process.stdin.setRawMode(true);

}
initialization = false;
})().catch(logError('initialization'));
})().catch(logError('Initialization'));

@@ -7,8 +7,10 @@ 'use strict';

const fs = require('fs');
const { MessageChannel, parentPort, threadId } = require('worker_threads');
const wt = require('worker_threads');
const { MessageChannel, parentPort, threadId } = wt;
const workerData = wt.workerData || { path: process.cwd() };
const metavm = require('metavm');
const metawatch = require('metawatch');
const metautil = require('metautil');
const { Interfaces } = require('./interfaces.js');
const { Modules } = require('./modules.js');
const { Services } = require('./services.js');
const { Resources } = require('./resources.js');

@@ -40,6 +42,6 @@ const { Schemas } = require('./schemas.js');

super();
this.kind = '';
this.kind = workerData.kind;
this.initialization = true;
this.finalization = false;
this.root = process.cwd();
this.root = workerData.path;
this.path = path.join(this.root, 'application');

@@ -53,2 +55,3 @@

this.db = new Modules('db', this);
this.bus = new Services('bus', this);
this.domain = new Modules('domain', this);

@@ -72,4 +75,3 @@ this.scheduler = new Scheduler(this);

async init(kind) {
this.kind = kind;
async init() {
this.startWatch();

@@ -85,2 +87,3 @@ this.createSandbox();

await this.db.load();
await this.bus.load();
await this.domain.load();

@@ -91,5 +94,5 @@ })(),

await Promise.allSettled(this.starts.map((fn) => this.execute(fn)));
this.starts = [];
this.sandbox.application.emit('started');
await this.api.load();
if (kind === 'scheduler') await this.scheduler.load();
const { api } = this.sandbox;

@@ -106,3 +109,2 @@ if (api.auth) {

}
this.starts = [];
this.initialization = false;

@@ -122,2 +124,3 @@ this.sandbox.application.emit('initialized');

async stopPlace(name) {
if (!this.sandbox) return;
const place = this.sandbox[name];

@@ -141,2 +144,3 @@ for (const moduleName of Object.keys(place)) {

sandbox.db = this.db.tree;
sandbox.bus = this.bus.tree;
sandbox.domain = this.domain.tree;

@@ -169,3 +173,3 @@ this.sandbox = metavm.createContext(sandbox);

`Failed to execute method: ${err && err.message}`,
err.stack
err.stack,
);

@@ -226,3 +230,3 @@ });

const data = { method, args };
const msg = { type: 'invoke', name: 'request', exclusive, data, port };
const msg = { name: 'invoke', exclusive, data, port };
return new Promise((resolve, reject) => {

@@ -238,25 +242,2 @@ port2.on('message', ({ error, data }) => {

const application = new Application();
if (parentPort) {
parentPort.on('message', async ({ type, name, exclusive, data, port }) => {
if (type !== 'invoke' || name !== 'request') return;
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);
} finally {
parentPort.postMessage({ type: 'invoke', name: 'done', exclusive });
}
});
}
module.exports = application;
module.exports = new Application();

@@ -8,2 +8,3 @@ 'use strict';

constructor(place, application) {
this.place = place;
this.path = application.absolute(place);

@@ -18,3 +19,3 @@ this.application = application;

for (const file of files) {
if (file.name.startsWith('.')) continue;
if (file.name.startsWith('.') && !file.name.endsWith('.js')) continue;
const filePath = path.join(targetPath, file.name);

@@ -21,0 +22,0 @@ if (file.isDirectory()) await this.load(filePath);

@@ -31,3 +31,3 @@ 'use strict';

'/',
'-'
'-',
);

@@ -34,0 +34,0 @@ const camelKey = metautil.spinalToCamel(key);

@@ -59,3 +59,3 @@ 'use strict';

const iface = this.collection[iname];
if (!iface) return null;
if (!iface) return;
const methods = iface[version.toString()];

@@ -62,0 +62,0 @@ if (methods) delete methods[name];

@@ -28,2 +28,6 @@ 'use strict';

preprocess(iface) {
return iface;
}
set(relPath, iface) {

@@ -70,3 +74,3 @@ const names = parsePath(relPath);

const relPath = filePath.substring(this.path.length + 1);
this.set(relPath, exports);
this.set(relPath, this.preprocess(exports));
} catch (err) {

@@ -73,0 +77,0 @@ if (err.code !== 'ENOENT') {

'use strict';
const path = require('path');
const fsp = require('fs').promises;
const { MessageChannel, parentPort } = require('worker_threads');
const metautil = require('metautil');

@@ -11,200 +8,21 @@ class Scheduler {

this.application = application;
this.path = application.absolute('tasks');
this.tasks = new Map();
this.nextId = 0;
this.executor = false;
this.topics = new Map();
}
async load() {
this.executor = true;
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);
});
const now = metautil.nowDate();
try {
const files = await fsp.readdir(this.path, { withFileTypes: true });
for (const file of files) {
if (file.isDirectory()) continue;
const { name } = file;
if (!name.endsWith('.json') || name.startsWith('.')) continue;
const base = path.basename(name, '.json');
const [date, id] = metautil.split(base, '-id-');
if (date === now) {
const nextId = parseInt(id);
if (nextId > this.nextId) this.nextId = nextId;
}
const filePath = path.join(this.path, name);
const data = await fsp.readFile(filePath, 'utf8');
this.restore(JSON.parse(data));
}
} catch (err) {
this.application.console.error(err.stack);
}
}
restore(record) {
const { id, name, every, args, run } = record;
const data = {
id,
name,
every: metautil.parseEvery(every),
success: undefined,
result: null,
error: null,
lastStart: 0,
lastEnd: 0,
executing: false,
runCount: 0,
timer: null,
args,
run,
};
this.tasks.set(id, data);
return this.start(id);
}
async add(task) {
if (!this.executor) {
const { port1, port2 } = new MessageChannel();
const msg = { type: 'task', name: 'add', data: task, port: port1 };
return new Promise((resolve) => {
port2.on('message', ({ id }) => {
resolve(id);
});
parentPort.postMessage(msg, [port1]);
const { port1, port2 } = new MessageChannel();
return new Promise((resolve) => {
port2.on('message', ({ id }) => {
resolve(id);
});
}
const id = metautil.nowDate() + '-id-' + this.nextId.toString();
task.id = id;
this.nextId++;
const started = this.restore({ id, ...task });
if (!started) return id;
const filePath = path.join(this.path, id + '.json');
try {
const data = JSON.stringify(task);
await fsp.writeFile(filePath, data);
} catch (err) {
this.application.console.error(err.stack);
}
return id;
const msg = { name: 'task', action: 'add', port: port1, task };
parentPort.postMessage(msg, [port1]);
});
}
async remove(id) {
if (!this.executor) {
const msg = { type: 'task', name: 'remove', data: { id } };
parentPort.postMessage(msg);
return;
}
const task = this.tasks.get(id);
if (task) {
this.tasks.delete(id);
if (task.timer) {
clearTimeout(task.timer);
task.timer = null;
}
}
const filePath = path.join(this.path, id + '.json');
try {
await fsp.unlink(filePath);
} catch (err) {
if (err.code !== 'ENOENT') {
this.application.console.error(err.stack);
}
}
parentPort.postMessage({ name: 'task', action: 'remove', task: { id } });
}
start(id) {
const task = this.tasks.get(id);
if (!task || task.timer) return false;
const next = metautil.nextEvent(task.every);
if (next === -1) {
this.remove(id);
return false;
}
if (next === 0) {
this.execute(task, true);
return true;
}
task.timer = setTimeout(() => {
const once = task.every.interval === 0;
this.execute(task, once);
}, next);
return true;
}
async enter(name) {
let semaphore = this.topics.get(name);
if (!semaphore) {
const config = this.application.config.server.scheduler;
const { concurrency, size, timeout } = config;
semaphore = new metautil.Semaphore(concurrency, size, timeout);
this.topics.set(name, semaphore);
}
return semaphore.enter();
}
leave(name) {
const semaphore = this.topics.get(name);
if (!semaphore) return;
if (semaphore.empty) {
this.topics.delete(name);
}
semaphore.leave();
}
async execute(task, once = false) {
if (task.executing) {
this.fail(task, 'Already started task');
return;
}
task.lastStart = Date.now();
task.executing = true;
try {
await this.enter(task.name);
task.result = await this.application.invoke({
method: task.run,
args: task.args,
});
} catch (err) {
task.error = err;
if (err.message === 'Semaphore timeout') {
this.fail(task, 'Scheduler queue is full');
} else {
this.application.console.error(err.stack);
}
} finally {
this.leave(task.name);
}
task.success = !task.error;
task.lastEnd = Date.now();
task.executing = false;
task.runCount++;
task.timer = null;
if (once) this.remove(task.id);
else this.start(task.id);
}
fail(task, reason) {
const { id, name, run, args } = task;
const target = `${name} (${id}) ${run}(${JSON.stringify(args)})`;
const msg = `${reason}, can't execute: ${target}`;
this.application.console.error(msg);
}
stop(name = '') {
if (!this.executor) {
const msg = { type: 'task', name: 'stop', data: { name } };
parentPort.postMessage(msg);
return;
}
for (const task of this.tasks.values()) {
if (name !== '' && name !== task.name) continue;
this.remove(task.id);
}
parentPort.postMessage({ name: 'task', action: 'stop', task: { name } });
}

@@ -211,0 +29,0 @@ }

'use strict';
const path = require('path');
const { Model, loadSchema } = require('metaschema');
const { loadModel, loadSchema } = require('metaschema');
const { Cache } = require('./cache.js');

@@ -15,3 +15,3 @@

await super.load(targetPath);
this.model = await Model.load(targetPath);
this.model = await loadModel(targetPath);
}

@@ -18,0 +18,0 @@

@@ -5,3 +5,3 @@ 'use strict';

const fsp = require('fs').promises;
const { parentPort, threadId } = require('worker_threads');
const { parentPort, threadId, workerData } = require('worker_threads');
const { Config } = require('metaconfiguration');

@@ -11,2 +11,3 @@ const { Logger } = require('metalog');

const metavm = require('metavm');
const metautil = require('metautil');
const { notLoaded } = require('./dependencies.js');

@@ -20,3 +21,4 @@ const application = require('./application.js');

if (application.initialization) {
console.info(`Can not start Application in worker ${threadId}`);
console.info(`Initialization error in worker ${threadId}`);
await application.shutdown();
process.exit(0);

@@ -62,20 +64,11 @@ }

const { balancer, ports = [] } = config.server;
const servingThreads = ports.length + (balancer ? 1 : 0);
await application.init();
let kind = 'worker';
if (threadId <= servingThreads) kind = 'server';
if (threadId === servingThreads + 1) kind = 'scheduler';
await application.init(kind);
if (kind === 'server') {
application.server = new Server(config.server, application);
const { kind, port } = workerData;
if (kind === 'server' || kind === 'balancer') {
const options = { ...config.server, port, kind };
application.server = new Server(options, application);
}
console.info(`Application started in worker ${threadId}`);
parentPort.postMessage({ type: 'event', name: 'started' });
parentPort.on('message', async ({ type, name }) => {
if (type !== 'event' || name !== 'stop') return;
const stop = async () => {
if (application.finalization) return;

@@ -85,3 +78,33 @@ console.info(`Graceful shutdown in worker ${threadId}`);

process.exit(0);
};
const invoke = async ({ exclusive, data, port }) => {
const { method, args } = data;
const handler = metautil.namespaceByPath(application.sandbox, method);
if (!handler) {
const error = new Error('Handler not found');
port.postMessage({ name: 'error', error });
return;
}
const msg = { name: 'invoke', status: 'done' };
try {
const result = await handler(args);
port.postMessage({ ...msg, data: result });
} catch (error) {
port.postMessage({ name: 'error', error });
application.console.error(error.stack);
} finally {
if (exclusive) parentPort.postMessage(msg);
}
};
const handlers = { stop, invoke };
parentPort.on('message', async (msg) => {
const handler = handlers[msg.name];
if (handler) handler(msg);
});
console.info(`Application started in worker ${threadId}`);
parentPort.postMessage({ name: 'started', kind });
})().catch(logError(`Can not start Application in worker ${threadId}`));
{
"name": "impress",
"version": "2.6.10",
"version": "3.0.0-alpha.1",
"author": "Timur Shemsedinov <timur.shemsedinov@gmail.com>",

@@ -62,22 +62,22 @@ "description": "Enterprise application server for Node.js",

"types": "tsc -p types/tsconfig.json",
"lint": "eslint . && prettier -c \"**/*.js\" \"**/*.json\" \"**/*.md\" \"**/*.yml\" \"**/*.ts\"",
"fmt": "prettier --write \"**/*.js\" \"**/*.json\" \"**/*.md\" \"**/*.yml\" \"**/*.ts\""
"lint": "eslint . && prettier -c \"**/*.js\" \"**/*.json\" \"**/*.md\" \"**/*.ts\"",
"fmt": "prettier --write \"**/*.js\" \"**/*.json\" \"**/*.md\" \"**/*.ts\""
},
"engines": {
"node": "^12.9 || 14 || 16 || 17 || 18"
"node": "14 || 16 || 18"
},
"dependencies": {
"metacom": "^2.0.7",
"metaconfiguration": "^2.1.6",
"metacom": "^3.0.0-alpha.1",
"metaconfiguration": "^2.1.7",
"metalog": "^3.1.8",
"metaschema": "^1.4.1",
"metaschema": "^2.0.2",
"metautil": "^3.5.20",
"metavm": "^1.1.0",
"metavm": "^1.2.0",
"metawatch": "^1.0.5"
},
"devDependencies": {
"@types/node": "^17.0.31",
"@types/node": "^18.0.0",
"@types/ws": "^8.5.3",
"eslint": "^8.15.0",
"eslint-config-metarhia": "^7.0.1",
"eslint": "^8.18.0",
"eslint-config-metarhia": "^8.1.0",
"eslint-config-prettier": "^8.5.0",

@@ -87,5 +87,5 @@ "eslint-plugin-import": "^2.26.0",

"metatests": "^0.8.2",
"prettier": "^2.6.2",
"typescript": "^4.6.4"
"prettier": "^2.7.1",
"typescript": "^4.7.4"
}
}
({
caption: { type: 'string', required: false },
description: { type: 'string', required: false },
access: { type: 'Access', required: false },
parameters: { type: 'Schema', required: false },
validate: { type: 'function', required: false },
access: '?Access',
parameters: '?Schema',
validate: '?Function',
timeout: { type: 'number', required: false },
queue: { type: 'QueueParameters', required: false },
serializer: { type: 'Serializer', required: false },
protocols: { array: 'Protocols', required: false },
queue: '?QueueParameters',
serializer: '?Serializer',
protocols: '?Protocols',
deprecated: { type: 'boolean', required: false },
method: 'AsyncFuction',
returns: { type: 'Schema', required: false },
assert: { type: 'function', required: false },
script: { type: 'function', required: false },
examples: { array: 'Example', required: false },
application: { type: 'Application', required: false },
returns: '?Schema',
assert: '?Function',
script: '?Function',
examples: '?Example',
application: '?Application',
});

@@ -12,3 +12,3 @@ import { Schema } from 'metaschema';

type Protocols = 'http' | 'https' | 'ws' | 'wss';
type AsyncFuction = (...args: Array<any>) => Promise<any>;
type AsyncFunction = (...args: Array<any>) => Promise<any>;
type Example = {

@@ -23,3 +23,3 @@ parameters: object;

application: Application;
method?: AsyncFuction;
method?: AsyncFunction;
parameters?: Schema;

@@ -26,0 +26,0 @@ returns?: Schema;

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc