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/colorless-green-ideas.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nodesecure/scanner": minor
---

feat(extractor): Extends eventTarget instead of eventEmitter for browser compatibility
41 changes: 37 additions & 4 deletions workspaces/scanner/src/extractors/payload.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
// Import Node.js Dependencies
import { EventEmitter } from "node:events";

// Import Third-party Dependencies
import type { Simplify } from "type-fest";
// @ts-ignore
Expand Down Expand Up @@ -53,7 +50,7 @@ export interface ManifestProbeExtractor<Defs> extends ProbeExtractor<Defs> {
next: ManifestProbeNextCallback;
}

export class Payload<T extends ProbeExtractor<any>[]> extends EventEmitter {
export class Payload<T extends ProbeExtractor<any>[]> extends EventTarget {
private dependencies: Scanner.Payload["dependencies"];
private probes: Record<ProbeExtractorLevel, T>;
private cachedResult: ExtractProbeResult<T>;
Expand Down Expand Up @@ -82,6 +79,7 @@ export class Payload<T extends ProbeExtractor<any>[]> extends EventEmitter {
for (const [name, dependency] of Object.entries(this.dependencies)) {
this.probes.packument.forEach((probe) => probe.next(name, dependency));
this.emit("packument", name, dependency);

if (this.probes.manifest.length > 0) {
for (const [spec, depVersion] of Object.entries(dependency.versions)) {
this.probes.manifest.forEach((probe) => probe.next(spec, depVersion, { name, dependency }));
Expand All @@ -103,6 +101,29 @@ export class Payload<T extends ProbeExtractor<any>[]> extends EventEmitter {
...this.extract()
) as unknown as MergedExtractProbeResult<T>;
}

emit<T extends ProbeExtractorLevel>(
event: T,
...extractionDetails: unknown[]
) {
const customEvent = new CustomEvent(event, {
detail: extractionDetails
});
this.dispatchEvent(customEvent);
}

on<T extends ProbeExtractorLevel>(
e: T,
listener: ExtractorListener<T>
): this {
function wrappedListener(event: Event) {
const customEvent = event as CustomEvent<ExtractorCallbackParams<T>>;
listener(...customEvent.detail);
}
this.addEventListener(e, wrappedListener);

return this;
}
}

export const Callbacks = {
Expand All @@ -126,6 +147,18 @@ export const Callbacks = {
}
} as const;

type ExtractorCallback<T extends ProbeExtractorLevel> = Parameters<
(typeof Callbacks)[T]
>[0];

export type ExtractorCallbackParams<T extends ProbeExtractorLevel> = Parameters<
ExtractorCallback<T>
>;

export type ExtractorListener<T extends ProbeExtractorLevel> = (
...events: CustomEvent<ExtractorCallbackParams<T>>["detail"]
) => void;

function noop() {
return void 0;
}
74 changes: 29 additions & 45 deletions workspaces/scanner/test/extractors/payload.spec.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
// Import Node.js Dependencies
import { describe, it } from "node:test";
import assert from "node:assert";
import path from "node:path";
import fs from "node:fs";
import path from "node:path";
import { describe, it } from "node:test";

// Import Internal Dependencies
import {
Extractors,
type ManifestProbeNextCallback,
type PackumentProbeNextCallback,
type Payload
} from "../../src/index.js";
import { type ExtractorCallbackParams } from "../../src/extractors/payload.js";
import { Extractors, type Payload } from "../../src/index.js";

// CONSTANTS
const kFixturePath = path.join("fixtures", "extractors");
Expand Down Expand Up @@ -220,30 +216,23 @@ describe("Extractors.Probes", () => {
}
};

const extractor = new Extractors.Payload(
fakePayload,
[
new Extractors.Probes.Vulnerabilities()
]
);
const extractor = new Extractors.Payload(fakePayload, [
new Extractors.Probes.Vulnerabilities()
]);

const {
vulnerabilities
} = extractor.extractAndMerge();
const { vulnerabilities } = extractor.extractAndMerge();

assert.strictEqual(vulnerabilities.length, 2);
assert.deepEqual(vulnerabilities, ["foo", "bar"]);
});
});

it("should extract data with multiple extractors in once", () => {
const extractor = new Extractors.Payload(
expressNodesecurePayload,
[
new Extractors.Probes.Size(),
new Extractors.Probes.Contacts(),
new Extractors.Probes.Licenses()
]
const extractor = new Extractors.Payload(expressNodesecurePayload, [
new Extractors.Probes.Size(),
new Extractors.Probes.Contacts(),
new Extractors.Probes.Licenses()
]
);

const arrResult = extractor.extract();
Expand All @@ -258,9 +247,6 @@ describe("Extractors.Probes", () => {
});

describe("Extractors.Payload events", () => {
type ManifestEvent = Parameters<ManifestProbeNextCallback>;
type PackumentEvent = Parameters<PackumentProbeNextCallback>;

it("should emits packument and manifest events", () => {
const extractor = new Extractors.Payload(
expressNodesecurePayload,
Expand All @@ -270,14 +256,14 @@ describe("Extractors.Payload events", () => {
]
);

const manifestEvents: ManifestEvent[] = [];
const packumentEvents: PackumentEvent[] = [];
const manifestEvents: ExtractorCallbackParams<"manifest">[] = [];
const packumentEvents: ExtractorCallbackParams<"packument">[] = [];

extractor.on("manifest", (...event: ManifestEvent) => {
extractor.on("manifest", (...event) => {
manifestEvents.push(event);
});

extractor.on("packument", (...event: PackumentEvent) => {
extractor.on("packument", (...event) => {
packumentEvents.push(event);
});

Expand All @@ -301,20 +287,18 @@ describe("Extractors.Callbacks", () => {
it("should extract name and versions for all packages", () => {
const packages = new Map<string, string[]>();

const extractor = new Extractors.Payload(
expressNodesecurePayload,
[
Extractors.Callbacks.packument((name) => {
if (!packages.has(name)) {
packages.set(name, []);
}
}),
Extractors.Callbacks.manifest((spec, _, parent) => {
if (packages.has(parent.name)) {
packages.get(parent.name)!.push(spec);
}
})
]
const extractor = new Extractors.Payload(expressNodesecurePayload, [
Extractors.Callbacks.packument((name) => {
if (!packages.has(name)) {
packages.set(name, []);
}
}),
Extractors.Callbacks.manifest((spec, _, parent) => {
if (packages.has(parent.name)) {
packages.get(parent.name)!.push(spec);
}
})
]
);

extractor.extract();
Expand Down