Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

cnysa

Package Overview
Dependencies
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cnysa - npm Package Compare versions

Comparing version 0.1.0 to 0.2.0

index.d.ts

337

index.js

@@ -1,122 +0,229 @@

const leftPad = require('left-pad');
const chalk = require('chalk').default;
const fs = require('fs');
const asyncHooks = require('async_hooks');
class Cnysa {
constructor(options = {}) {
if (options.typeFilter) {
this.typeFilter = new RegExp(options.typeFilter);
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const ah = require("async_hooks");
require("colors");
const leftPad = require("left-pad");
/**
* Flatten an array of arrays, but in "column-major" order
*/
const interleave = (input, separator) => {
const result = [];
const maxLength = input.reduce((acc, next) => Math.max(next.length, acc), -Infinity);
for (let i = 0; i < maxLength; i++) {
if (separator !== undefined && i > 0) {
result.push(separator);
}
for (let j = 0; j < input.length; j++) {
input[j][i] !== undefined && result.push(input[j][i]);
}
}
this.start = Math.floor(Date.now() / 1000) % 1000;
this.typesTable = {};
this.promiseTable = new Map();
this.indent = '';
}
_init(id, type, trigger, resource) {
if (this.typeFilter && !this.typeFilter.exec(type)) {
return;
return result;
};
const until = (array, predicate) => {
return array.slice(0, array.findIndex(e => !predicate(e)));
};
class StringRowsBuilder {
constructor(maxLength) {
this.data = [];
this.currentLength = 0;
this.maxLength = maxLength;
}
this.typesTable[id] = type;
if (type === 'PROMISE') {
this.promiseTable.set(resource.promise, id);
// treats as if one char
appendChar(char, ...styles) {
if (this.currentLength === 0) {
this.data.push('');
}
this.data[this.data.length - 1] += styles.reduce((acc, next) => next ? acc[next] : acc, char);
if (++this.currentLength === this.maxLength) {
this.currentLength = 0;
}
}
this._write(this.indent);
this._write('+ [', 'green');
this._write(leftPad(this._getTime(), 3));
this._write(`] ${this.typesTable[id]} `, 'green');
this._write(id, 'yellow');
if (asyncHooks.executionAsyncId() !== trigger) {
this._write(':', 'green');
this._write(trigger, 'magenta');
getData() {
return this.data;
}
this._write('\n');
}
_before(id) {
if (!this.typesTable[id]) return;
this._write(this.indent);
this._write('* [', 'blue');
this._write(leftPad(this._getTime(), 3));
this._write(`] ${this.typesTable[id]} `, 'blue');
this._write(`${id}`, 'yellow');
this._write(' {\n', 'blue');
this.indent += ' ';
}
_after(id) {
if (!this.typesTable[id]) return;
this.indent = this.indent.slice(2);
this._write(this.indent);
this._write('}', 'blue');
this._write('\n');
}
_destroy(id) {
if (!this.typesTable[id]) return;
this._write(this.indent);
this._write('- [', 'red');
this._write(leftPad(this._getTime(), 3));
this._write(`] ${this.typesTable[id]} `, 'red');
this._write(`${id}`, 'yellow');
this._write('\n');
}
_promiseResolve(id) {
if (!this.typesTable[id]) return;
this._write(this.indent);
this._write(': [', 'gray');
this._write(leftPad(this._getTime(), 3));
this._write(`] ${this.typesTable[id]} `, 'gray');
this._write(`${id}`, 'yellow');
this._write('\n');
}
_getTime() {
const s = (Math.floor(Date.now() / 1000) % 1000) - this.start;
if (s < 0) s += 1000;
return s;
}
_write(str, color) {
if (color) {
fs.writeSync(1, chalk[color]('' + str));
} else {
fs.writeSync(1, '' + str);
}
class Cnysa {
constructor(options) {
this.globalContinuationEnded = false;
// Initialize options.
const canonicalOpts = Object.assign({}, Cnysa.DEFAULTS, options);
this.width = canonicalOpts.width;
this.ignoreTypes = typeof canonicalOpts.ignoreTypes === 'string' ?
new RegExp(canonicalOpts.ignoreTypes) : canonicalOpts.ignoreTypes;
this.highlightTypes = typeof canonicalOpts.highlightTypes === 'string' ?
new RegExp(canonicalOpts.highlightTypes) : canonicalOpts.highlightTypes;
this.getColor = (function* () {
const colors = canonicalOpts.colors;
while (true) {
for (const color of colors) {
yield color;
}
}
})();
this.resources = {
1: { uid: 1, type: '(initial)' }
};
this.events = [{
timestamp: Date.now(),
uid: 1,
type: 'before'
}];
this.hook = ah.createHook({
init: (uid, type) => {
if (!type.match(this.ignoreTypes)) {
const eid = ah.executionAsyncId();
const color = (this.resources[eid] && this.resources[eid].color) || (type.match(this.highlightTypes) ? this.getColor.next().value : undefined);
this.resources[uid] = { uid, type, color };
this.events.push({ timestamp: Date.now(), uid, type: 'init' });
}
},
before: (uid) => {
if (this.resources[uid] && !this.resources[uid].type.match(this.ignoreTypes)) {
if (!this.globalContinuationEnded) {
this.globalContinuationEnded = true;
this.events.push({
timestamp: Date.now(),
uid: 1,
type: 'after'
});
}
this.events.push({ timestamp: Date.now(), uid, type: 'before' });
}
},
after: (uid) => {
if (this.resources[uid] && !this.resources[uid].type.match(this.ignoreTypes)) {
this.events.push({ timestamp: Date.now(), uid, type: 'after' });
}
},
destroy: (uid) => {
if (this.resources[uid] && !this.resources[uid].type.match(this.ignoreTypes)) {
this.events.push({ timestamp: Date.now(), uid, type: 'destroy' });
}
},
promiseResolve: (uid) => {
this.events.push({ timestamp: Date.now(), uid, type: 'promiseResolve' });
}
});
this.processOnExit = () => {
this.hook.disable();
const uncoloredLength = Object.keys(this.resources).reduce((acc, key) => {
const k = Number(key);
return Math.max(acc, `${this.resources[k].uid.toString()} ${this.resources[k].type} `.length);
}, -Infinity);
const maxLength = Object.keys(this.resources).reduce((acc, key) => {
const k = Number(key);
return Math.max(acc, `${this.resources[k].uid.toString().magenta} ${this.resources[k].type.yellow} `.length);
}, -Infinity);
const stack = [];
const paddedEvents = [];
for (const event of this.events) {
// if (resources[event.uid].color) {
paddedEvents.push(event);
paddedEvents.push({ uid: -1, timestamp: 0, type: 'pad' });
// }
}
const adjustedWidth = this.width - uncoloredLength;
const eventStrings = {};
for (const event of paddedEvents) {
Object.keys(this.resources).forEach(key => {
const k = Number(key);
if (!eventStrings[k]) {
eventStrings[k] = { alive: false, str: new StringRowsBuilder(adjustedWidth) };
}
if (event.uid === k) {
if (event.type === 'init') {
eventStrings[k].str.appendChar('*', 'green', this.resources[k].color);
eventStrings[k].alive = true;
}
else if (event.type === 'before') {
eventStrings[k].str.appendChar('{', 'blue', this.resources[k].color);
stack.push(k);
}
else if (event.type === 'after') {
eventStrings[k].str.appendChar('}', 'blue', this.resources[k].color);
stack.pop();
}
else if (event.type === 'destroy') {
eventStrings[k].str.appendChar('*', 'red', this.resources[k].color);
eventStrings[k].alive = false;
}
else if (event.type === 'promiseResolve') {
eventStrings[k].str.appendChar('*', 'gray', this.resources[k].color);
eventStrings[k].alive = false;
}
else {
eventStrings[k].str.appendChar('?', 'gray', this.resources[k].color);
}
}
else {
if (event.type === 'init' && stack.length > 0) {
if (event.uid > k) {
if (stack[stack.length - 1] < k) {
eventStrings[k].str.appendChar('|', 'green', this.resources[event.uid].color);
return;
}
else if (stack[stack.length - 1] === k) {
eventStrings[k].str.appendChar('.', 'green', this.resources[event.uid].color);
return;
}
}
else if (event.uid < k) {
if (stack[stack.length - 1] > k) {
eventStrings[k].str.appendChar('|', 'green', this.resources[k].color);
return;
}
else if (stack[stack.length - 1] === k) {
eventStrings[k].str.appendChar('.', 'green', this.resources[k].color);
return;
}
}
}
if (stack.indexOf(k) !== -1) {
eventStrings[k].str.appendChar('.', 'blue', this.resources[k].color);
}
else {
if (eventStrings[k].alive) {
eventStrings[k].str.appendChar('-', 'gray', this.resources[k].color);
}
else {
eventStrings[k].str.appendChar(' ');
}
}
}
});
}
const separator = new Array(this.width).fill(':').join('');
const output = [
separator,
...interleave(Object.keys(this.resources).map(key => {
const k = Number(key);
return eventStrings[k].str.getData().map(rhs => {
if (rhs.strip.match(/^(\s|\|)+$/)) {
return '';
}
else {
return leftPad(`${this.resources[k].uid.toString().magenta} ${this.resources[k].type.yellow} `, maxLength) + rhs;
}
});
}), separator).filter(line => line.length > 0),
separator
].join('\n');
console.log(output);
};
}
}
enable() {
this.hook = asyncHooks.createHook({
init: this._init.bind(this),
before: this._before.bind(this),
after: this._after.bind(this),
destroy: this._destroy.bind(this),
promiseResolve: this._promiseResolve.bind(this)
}).enable();
return this;
}
disable() {
this.hook.disable();
return this;
}
print(p, alias) {
this._write(this.indent);
this._write('> [', 'white');
this._write(leftPad(this._getTime(), 3));
this._write(`] PROMISE `, 'white');
this._write(`${this.promiseTable.get(p)||'?'}`, 'yellow');
if (alias) {
this._write(' aka ', 'white');
this._write(alias, 'cyan');
enable() {
this.hook.enable();
process.on('exit', this.processOnExit);
}
this._write('\n');
return p;
}
disable() {
this.hook.disable();
process.removeListener('exit', this.processOnExit);
}
}
module.exports.Cnysa = Cnysa;
Cnysa.DEFAULTS = {
width: process.stdout.columns || 80,
ignoreTypes: / /,
highlightTypes: / /,
colors: ['bgMagenta', 'bgYellow', 'bgCyan']
};
exports.Cnysa = Cnysa;
{
"name": "cnysa",
"version": "0.1.0",
"version": "0.2.0",
"description": "A tool for understanding async-hooks",
"main": "index.js",
"types": "index.d.ts",
"files": [
"*.js",
"*.d.ts"
],
"keywords": [
"async",
"async_hooks"
"async_hooks",
"visualization"
],
"scripts": {
"prepack": "tsc"
},
"author": "Kelvin Jin (kelvinjin@google.com)",

@@ -14,5 +23,9 @@ "repository": "https://github.com/kjin/cnysa",

"dependencies": {
"chalk": "^2.3.0",
"colors": "^1.2.1",
"left-pad": "^1.2.0"
},
"devDependencies": {
"@types/colors": "^1.2.1",
"@types/node": "^9.6.5"
}
}
`cnysa` (unpronouncible) is a module that allows you to see information about what the `async_hooks` module is doing under the covers.
__Note: This module currently uses the `colors` module.__
# Pre-Require Hook

@@ -23,31 +25,23 @@

* `options.typeFilter`: String or RegExp to use as a filter for `AsyncResource` types.
* `options.width`: Maximum number of characters to print on a single line before wrapping. Defaults to the current terminal width.
* `options.ignoreTypes`: String or RegExp to filter out `AsyncResource` types.
* `options.highlightTypes`: String or RegExp to highlight certain `AsyncResource` types and their descendants.
## `Cnysa#enable()`
Starts emitting output.
Starts recording async events and registers a process exit hook.
## `Cnysa#disable()`
Stops emitting output.
Stops recording async events and unregisters the process exit hook.
# Understanding output
For each `init`, `before`, `promiseResolve`, and `destroy` event, a line will be printed.
For each `AsyncResource`, a time line will be printed, with a number of colored symbols:
Each of those lines start with a symbol:
* Green `*` represents the async resource creation.
* Red `*` represents its destruction.
* Blue `{...}` represent running in an async scope.
* Gray `-` indicates the lifetime of the resource creation, and is bookended by `*` symbols.
* `+` Init
* `*` Before
* `-` Destroy
* `:` Promise Fulfillment
The number that follows is the current time in seconds, mod 1000. This number might be useful in distinguishing "clusters" of async events.
A string after represents the async resource type.
The async resource's execution ID follows. For `init` events, if the trigger ID is different than the current execution ID, it is displayed as well.
`before`-`after` pairs are visualized as indents.
## Examples

@@ -58,7 +52,1 @@

```
```bash
(sleep 2 && curl localhost:8080) & node --require cnysa/register -e "http.createServer((req, res) => res.send('hi')).listen(8080)"
```
https://gist.github.com/kjin/a499bfcb7c5e12a80f8c6ad66a30b740

@@ -1,12 +0,15 @@

const { Cnysa } = require('.');
const fs = require('fs');
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const index_1 = require("./index");
const fs = require("fs");
const expectedConfigPath = `${process.cwd()}/cnysa.json`;
let config = {};
try {
fs.statSync(expectedConfigPath);
const configJson = fs.readFileSync(expectedConfigPath, 'utf8');
config = JSON.parse(configJson);
} catch (e) {} finally {
new Cnysa(config).enable();
fs.statSync(expectedConfigPath);
const configJson = fs.readFileSync(expectedConfigPath, 'utf8');
config = JSON.parse(configJson);
}
catch (e) { }
finally {
new index_1.Cnysa(config).enable();
}
SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc