Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/khaki-terms-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nodesecure/scanner": minor
---

feat(scanner): emit stat event when api call is a success
18 changes: 17 additions & 1 deletion workspaces/scanner/docs/logger.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@ logger.on("error", (error: Error, phase?: string) => {
});
```

### stat

Emitted when a successful API call is made:

```ts
logger.on("stat", (stat: ApiStats) => {
console.log(`API call: ${stat.name}`);
console.log(`Duration: ${stat.executionTime}ms`);
console.log(`Start at: ${stat.startedAt}`);
});

### depWalkerFinished

Emitted when the dependency walker completes its analysis:
Expand Down Expand Up @@ -143,6 +154,10 @@ export interface LoggerEventData {
/** Count of triggered event */
count: number;
}

export type LoggerEventError = {
executionTime: number;
} & Error;
```

### LoggerEventsMap
Expand All @@ -153,6 +168,7 @@ export type LoggerEventsMap = {
tick: [eventName: string];
end: [eventName: string, data: LoggerEventData & { executionTime: number; }];
depWalkerFinished: [];
error: [error: Error, phase?: string];
error: [error: LoggerEventError, phase?: string];
stat: [stat: ApiStats];
};
```
43 changes: 33 additions & 10 deletions workspaces/scanner/src/class/StatsCollector.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,32 @@ import { isHTTPError } from "@openally/httpie";

// Import Internal Dependencies
import { SystemDateProvider, type DateProvider } from "./DateProvider.class.ts";
import type { LoggerEventsMap } from "./logger.class.ts";
import { type LoggerEventsMap, Logger } from "./logger.class.ts";
import type { ApiStats, Stats, Error } from "../types.ts";

export type Providers = {
dateProvider?: DateProvider;
logger?: EventEmitter<LoggerEventsMap>;
};

export type Options = {
isVerbose: boolean;
};

export class StatsCollector {
#logger: EventEmitter<LoggerEventsMap>;
#dateProvider: DateProvider;
#apiCalls: ApiStats[] = [];
#startedAt: number;
#errors: Error[] = [];
#isVerbose: boolean;

constructor(logger: EventEmitter<LoggerEventsMap>, dateProvider: DateProvider = new SystemDateProvider()) {
constructor(providers: Providers, options: Options) {
const { dateProvider = new SystemDateProvider(), logger = new Logger() } = providers;
this.#logger = logger;
this.#dateProvider = dateProvider;
this.#startedAt = this.#dateProvider.now();
this.#isVerbose = options.isVerbose;
}

track<T extends () => any>(name: string, phase: string, fn: T): ReturnType<T> {
Expand All @@ -29,7 +41,7 @@ export class StatsCollector {
if (result instanceof Promise) {
return result
.then((res: ReturnType<T>) => {
this.#addApiStat(name, startedAt, this.#calcExecutionTime(startedAt));
this.#addApiStatVerbose(name, startedAt, this.#calcExecutionTime(startedAt));

return res;
})
Expand All @@ -38,19 +50,26 @@ export class StatsCollector {
this.#addError({
name, err, executionTime, phase
});
this.#addApiStat(name, startedAt, executionTime);

this.#apiCalls.push({
name,
startedAt,
executionTime
});
throw err;
}) as ReturnType<T>;
}

this.#addApiStat(name, startedAt, this.#calcExecutionTime(startedAt));
this.#addApiStatVerbose(name, startedAt, this.#calcExecutionTime(startedAt));

return result;
}
catch (err) {
const executionTime = this.#calcExecutionTime(startedAt);
this.#addApiStat(name, startedAt, executionTime);
this.#apiCalls.push({
name,
startedAt,
executionTime
});
this.#addError({
name, err, executionTime, phase
});
Expand All @@ -62,12 +81,16 @@ export class StatsCollector {
return this.#dateProvider.now() - startedAt;
}

#addApiStat(name: string, startedAt: number, executionTime: number) {
this.#apiCalls.push({
#addApiStatVerbose(name: string, startedAt: number, executionTime: number) {
const stat = {
name,
startedAt,
executionTime
});
};
this.#apiCalls.push(stat);
if (this.#isVerbose) {
this.#logger.emit("stat", stat);
}
}

#addError(params: {
Expand Down
3 changes: 2 additions & 1 deletion workspaces/scanner/src/class/logger.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { EventEmitter } from "node:events";
import { performance } from "node:perf_hooks";

// Import Internal Dependencies
import type { Error } from "../types.ts";
import type { Error, ApiStats } from "../types.ts";

export const ScannerLoggerEvents = {
error: "error",
Expand Down Expand Up @@ -36,6 +36,7 @@ export type LoggerEventsMap = {
end: [eventName: string, data: LoggerEventData & { executionTime: number; }];
depWalkerFinished: [];
error: [error: LoggerEventError, phase?: string];
stat: [stat: ApiStats];
};

export class Logger extends EventEmitter<LoggerEventsMap> {
Expand Down
3 changes: 2 additions & 1 deletion workspaces/scanner/src/depWalker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export async function depWalker(
const {
scanRootNode = false,
includeDevDeps = false,
isVerbose = false,
packageLock,
maxDepth,
location,
Expand All @@ -123,7 +124,7 @@ export async function depWalker(
npmRcConfig
} = options;

const statsCollector = new StatsCollector(logger);
const statsCollector = new StatsCollector({ logger }, { isVerbose });

const collectables = kCollectableTypes.map((type) => new CollectableSet<Metadata>(type));

Expand Down
7 changes: 7 additions & 0 deletions workspaces/scanner/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,13 @@ export interface Options {
* @default true for cwd() API
*/
readonly scanRootNode?: boolean;

/**
* Enable verbose mode
*
* @default false
*/
isVerbose?: boolean;
}

export interface TokenStore {
Expand Down
Loading