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.2.2 to 0.3.0

doc/images/example-readfile.svg

31

index.d.ts

@@ -1,25 +0,28 @@

export interface CnysaOptions {
export declare type Serializable<T> = {
[P in keyof T]: T[P] extends string | number | boolean ? T[P] : T[P] extends Array<infer S> | IterableIterator<infer S> ? Array<Serializable<S>> : T[P] extends Function | RegExp ? string : T[P] extends {} ? Serializable<T[P]> : string;
};
export declare type Flexible<T> = Partial<T | Serializable<T>>;
export interface CnysaAsyncSnapshotOptions {
width: number;
ignoreTypes: RegExp | string;
highlightTypes: RegExp | string;
ignoreUnhighlighted: boolean;
ignoreTypes: RegExp;
roots: number[];
padding: number;
colors: Array<string>;
format: string;
}
export declare class Cnysa {
private width;
private ignoreTypes;
private highlightTypes;
private ignoreUnhighlighted;
private getColor;
private padding;
private static activeInstances;
private currentScopes;
private resources;
private events;
private hook;
private processOnExit;
private globalContinuationEnded;
static DEFAULTS: CnysaOptions;
constructor(options: Partial<CnysaOptions>);
static ASYNC_SNAPSHOT_DEFAULTS: CnysaAsyncSnapshotOptions;
static get(): Cnysa;
constructor();
enable(): void;
disable(): void;
private hasAncestor(resource, ancestor);
private canonicalizeAsyncSnapshotOptions(options?);
mark(name: string | number): void;
getAsyncSnapshot(options?: Flexible<CnysaAsyncSnapshotOptions>): string;
}

@@ -62,24 +62,8 @@ "use strict";

class Cnysa {
constructor(options) {
constructor() {
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.ignoreUnhighlighted = canonicalOpts.ignoreUnhighlighted;
this.padding = canonicalOpts.padding;
this.resources = {
1: { uid: 1, type: '(initial)' }
1: { uid: 1, type: '(initial)', parents: [], internal: false }
};
this.currentScopes = [1];
this.events = [{

@@ -91,7 +75,13 @@ timestamp: Date.now(),

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 };
init: (uid, type, triggerId) => {
const eid = ah.executionAsyncId();
if (type.startsWith('cnysa')) {
this.resources[uid] = { uid, type, parents: this.currentScopes.map(x => x), internal: true };
this.events.push({ timestamp: Date.now(), uid, type: 'internal' });
}
else {
this.resources[uid] = { uid, type, parents: this.currentScopes.map(x => x), internal: false };
if (triggerId !== eid) {
this.resources[uid].tid = triggerId;
}
this.events.push({ timestamp: Date.now(), uid, type: 'init' });

@@ -101,3 +91,3 @@ }

before: (uid) => {
if (this.resources[uid] && !this.resources[uid].type.match(this.ignoreTypes)) {
if (this.resources[uid] && !this.resources[uid].internal) {
if (!this.globalContinuationEnded) {

@@ -110,13 +100,16 @@ this.globalContinuationEnded = true;

});
this.currentScopes.pop();
}
this.events.push({ timestamp: Date.now(), uid, type: 'before' });
this.currentScopes.push(uid);
}
},
after: (uid) => {
if (this.resources[uid] && !this.resources[uid].type.match(this.ignoreTypes)) {
if (this.resources[uid] && !this.resources[uid].internal) {
this.events.push({ timestamp: Date.now(), uid, type: 'after' });
this.currentScopes.pop();
}
},
destroy: (uid) => {
if (this.resources[uid] && !this.resources[uid].type.match(this.ignoreTypes)) {
if (this.resources[uid] && !this.resources[uid].internal) {
this.events.push({ timestamp: Date.now(), uid, type: 'destroy' });

@@ -129,65 +122,120 @@ }

});
this.processOnExit = () => {
this.hook.disable();
const uncoloredLength = Object.keys(this.resources).reduce((acc, key) => {
Cnysa.activeInstances.push(this);
}
static get() {
if (Cnysa.activeInstances.length === 0) {
Cnysa.activeInstances.push(new Cnysa());
}
return Cnysa.activeInstances[Cnysa.activeInstances.length - 1];
}
enable() {
this.hook.enable();
}
disable() {
this.hook.disable();
}
hasAncestor(resource, ancestor) {
if (Array.isArray(ancestor) && ancestor.length === 0) {
return true;
}
let predicate;
if (ancestor instanceof RegExp) {
predicate = res => !!res.type.match(ancestor) || res.parents.some(parent => predicate(this.resources[parent]));
}
else {
predicate = res => ancestor.indexOf(res.uid) !== -1 || res.parents.some(parent => predicate(this.resources[parent]));
}
return predicate(resource);
}
canonicalizeAsyncSnapshotOptions(options = {}) {
const opts = Object.assign({}, Cnysa.ASYNC_SNAPSHOT_DEFAULTS, options);
opts.ignoreTypes = typeof opts.ignoreTypes === 'string' ?
new RegExp(opts.ignoreTypes) : opts.ignoreTypes;
return opts;
}
mark(name) {
new ah.AsyncResource(`cnysa(${name})`).emitDestroy();
}
getAsyncSnapshot(options = {}) {
// Initialize options.
const config = this.canonicalizeAsyncSnapshotOptions(options);
if (!this.globalContinuationEnded) {
this.globalContinuationEnded = true;
this.events.push({
timestamp: Date.now(),
uid: 1,
type: 'after'
});
}
const ignoredResources = Object.keys(this.resources).filter(key => {
const k = Number(key);
return this.resources[k].type.match(config.ignoreTypes) || !this.hasAncestor(this.resources[k], config.roots);
}).reduce((acc, key) => acc.add(Number(key)), new Set());
const maxLength = Object.keys(this.resources).reduce((acc, key) => {
const k = Number(key);
if (ignoredResources.has(k)) {
return acc;
}
const tidLength = this.resources[k].tid !== undefined ? (this.resources[k].uid.toString().length + 3) : 0;
const uidLength = this.resources[k].uid.toString().length + 1;
const typeLength = this.resources[k].type.length + 1;
return Math.max(acc, tidLength + uidLength + typeLength);
}, -Infinity);
const stack = [];
const paddedEvents = [];
for (const event of this.events) {
if (!ignoredResources.has(event.uid)) {
paddedEvents.push(event);
for (let i = 0; i < config.padding; i++) {
paddedEvents.push({ uid: -1, timestamp: 0, type: 'pad' });
}
}
}
const adjustedWidth = config.width - maxLength;
const eventStrings = {};
for (const event of paddedEvents) {
Object.keys(this.resources).forEach(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, `${chalk_1.default.magenta(this.resources[k].uid.toString())} ${chalk_1.default.yellow(this.resources[k].type)} `.length);
}, -Infinity);
const stack = [];
const paddedEvents = [];
for (const event of this.events) {
if (event.uid === 1 || !this.ignoreUnhighlighted || this.resources[event.uid].color) {
paddedEvents.push(event);
for (let i = 0; i < this.padding; i++) {
paddedEvents.push({ uid: -1, timestamp: 0, type: 'pad' });
}
if (!eventStrings[k]) {
eventStrings[k] = { alive: false, str: new StringRowsBuilder(adjustedWidth) };
}
}
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');
eventStrings[k].alive = true;
}
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 === 'before') {
eventStrings[k].str.appendChar('{', 'blue');
stack.push(k);
}
else if (event.type === 'after') {
eventStrings[k].str.appendChar('}', 'blue');
stack.pop();
}
else if (event.type === 'destroy') {
eventStrings[k].str.appendChar('*', 'red');
eventStrings[k].alive = false;
}
else if (event.type === 'promiseResolve') {
eventStrings[k].str.appendChar('*', 'gray');
eventStrings[k].alive = false;
}
else if (event.type === 'internal') {
eventStrings[k].str.appendChar('*', 'yellow');
}
else {
if (event.type === 'init' && stack.length > 0) {
eventStrings[k].str.appendChar('?', 'gray');
}
}
else {
const maybeVertical = (color) => {
if (stack.length > 0) {
if (event.uid > k) {
if (stack[stack.length - 1] < k) {
eventStrings[k].str.appendChar('|', 'green', this.resources[event.uid].color);
return;
eventStrings[k].str.appendChar('|', color);
return true;
}
else if (stack[stack.length - 1] === k) {
eventStrings[k].str.appendChar('.', 'green', this.resources[event.uid].color);
return;
eventStrings[k].str.appendChar('.', color);
return true;
}

@@ -197,61 +245,72 @@ }

if (stack[stack.length - 1] > k) {
eventStrings[k].str.appendChar('|', 'green', this.resources[k].color);
return;
eventStrings[k].str.appendChar('|', color);
return true;
}
else if (stack[stack.length - 1] === k) {
eventStrings[k].str.appendChar('.', 'green', this.resources[k].color);
return;
eventStrings[k].str.appendChar('.', color);
return true;
}
}
}
if (stack.indexOf(k) !== -1) {
eventStrings[k].str.appendChar('.', 'blue', this.resources[k].color);
return false;
};
if (event.type === 'internal' && maybeVertical('yellow')) {
return;
}
else if (event.type === 'init' && maybeVertical('green')) {
return;
}
if (stack.indexOf(k) !== -1) {
eventStrings[k].str.appendChar('.', 'blue');
}
else {
if (eventStrings[k].alive) {
eventStrings[k].str.appendChar('-', 'gray');
}
else {
if (eventStrings[k].alive) {
eventStrings[k].str.appendChar('-', 'gray', this.resources[k].color);
}
else {
eventStrings[k].str.appendChar(' ');
}
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.dirty) {
return '';
}
});
}
const separator = new Array(config.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.dirty) {
return '';
}
else {
if (this.resources[k].tid !== undefined) {
return leftPad(`${chalk_1.default.magenta(`${this.resources[k].uid}`)} (${chalk_1.default.green(`${this.resources[k].tid}`)}) ${chalk_1.default.yellow(this.resources[k].type)} `, maxLength + (chalk_1.default.magenta(' ').length - 1) * 3) + rhs.str;
}
else {
return leftPad(`${chalk_1.default.magenta(this.resources[k].uid.toString())} ${chalk_1.default.yellow(this.resources[k].type)} `, maxLength) + rhs.str;
return leftPad(`${chalk_1.default.magenta(`${this.resources[k].uid}`)} ${chalk_1.default.yellow(this.resources[k].type)} `, maxLength + (chalk_1.default.magenta(' ').length - 1) * 2) + rhs.str;
}
});
}), separator).filter(line => line.length > 0),
separator
].join('\n');
console.log(output);
};
}
});
}), separator).filter(line => line.length > 0),
separator
].join('\n');
const format = options && options.format ? options.format : config.format;
if (format === 'svg') {
const ansiToSvg = require('ansi-to-svg');
return ansiToSvg(output);
}
else {
return output;
}
}
enable() {
this.hook.enable();
process.on('exit', this.processOnExit);
}
disable() {
this.hook.disable();
process.removeListener('exit', this.processOnExit);
}
}
Cnysa.DEFAULTS = {
Cnysa.activeInstances = [];
Cnysa.ASYNC_SNAPSHOT_DEFAULTS = {
width: process.stdout.columns || 80,
ignoreTypes: / /,
highlightTypes: / /,
ignoreUnhighlighted: false,
roots: [],
padding: 1,
colors: ['bgMagenta', 'bgYellow', 'bgCyan']
format: 'default'
};
exports.Cnysa = Cnysa;
{
"name": "cnysa",
"version": "0.2.2",
"version": "0.3.0",
"description": "A tool for understanding async-hooks",

@@ -8,2 +8,3 @@ "main": "index.js",

"files": [
"doc",
"*.js",

@@ -24,2 +25,3 @@ "*.d.ts"

"dependencies": {
"ansi-to-svg": "^1.4.1",
"chalk": "^2.3.2",

@@ -29,4 +31,5 @@ "left-pad": "^1.2.0"

"devDependencies": {
"@types/node": "^9.6.5"
"@types/node": "^9.6.5",
"typescript": "^2.8.3"
}
}

@@ -26,16 +26,24 @@ `cnysa` (unpronouncible) is a module that allows you to see information about what the `async_hooks` module is doing under the covers.

* `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.
* `options.ignoreTypes`: A string or RegExp to filter out `AsyncResource` types.
* `options.highlightTypes`: A string or RegExp to highlight certain `AsyncResource` types and their descendants.
* `options.ignoreUnhighlighted`: Boolean to determine whether unhighlighted types should be ignored.
* `options.padding`: Number that represents the amount of space between each depicted event.
* `options.colors`: An array of strings that represent colors to use when highlighting.
* `options.format`: A string that represents how the output should be formatted. Currently, the available options are `'default'` and `'svg'` (which uses [`ansi-to-svg`](https://github.com/F1LT3R/ansi-to-svg)).
## `Cnysa#enable()`
Starts recording async events and registers a process exit hook.
Starts recording async events.
## `Cnysa#disable()`
Stops recording async events and unregisters the process exit hook.
Stops recording async events.
## `Cnysa#getAsyncSnapshot()`
Returns a formatted async ancestry tree.
# Understanding output
For each `AsyncResource`, a time line will be printed, with a number of colored symbols:
For each `AsyncResource`, a timeline will be printed, with a number of colored symbols:

@@ -52,1 +60,3 @@ * Green `*` represents the async resource creation.

```
![example-readfile.svg](./doc/images/example-readfile.svg)

@@ -14,3 +14,7 @@ "use strict";

finally {
new index_1.Cnysa(config).enable();
const cnysa = new index_1.Cnysa();
cnysa.enable();
process.once('exit', () => {
console.log(cnysa.getAsyncSnapshot(config));
});
}
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