diff --git a/.changeset/odd-emus-drive.md b/.changeset/odd-emus-drive.md new file mode 100644 index 00000000..c83cb92f --- /dev/null +++ b/.changeset/odd-emus-drive.md @@ -0,0 +1,8 @@ +--- +"@nodesecure/tree-walker": major +"@nodesecure/npm-types": minor +"@nodesecure/scanner": minor +"@nodesecure/mama": minor +--- + +Implement ManifestManager class deep into scanner and tree-walker. Implement documentDigest into ManifestManager class and fix issue with pacote.manifest type. diff --git a/workspaces/mama/README.md b/workspaces/mama/README.md index 1003967b..a8e735ad 100644 --- a/workspaces/mama/README.md +++ b/workspaces/mama/README.md @@ -79,7 +79,7 @@ if (ManifestManager.isLocated(locatedManifest)) { } ``` -### constructor(document: ManifestManagerDocument, options?: ManifestManagerOptions) +### constructor(document: ManifestManagerDocument | AbbreviatedManifestDocument, options?: ManifestManagerOptions) document is described by the following type: ```ts @@ -157,6 +157,23 @@ If `dependencies` and `scripts` are missing, they are defaulted to an empty obje > [!CAUTION] > This is not available for Workspaces +### documentDigest + +Return an [SSRI](https://w3c.github.io/webappsec-subresource-integrity/) `sha512` hash of the **full serialized document** as a string, or `null` if the instance is a workspace. + +```ts +const mama = new ManifestManager({ + name: "foo", + version: "1.0.0" +}); +console.log(mama.documentDigest); // "sha512-" +``` + +Unlike [`integrity`](#integrity), which hashes a small subset of fields for change-detection purposes, `documentDigest` covers every field present in the document. This makes it suitable for content-addressable lookups and cache invalidation where an exact byte-level match is required. + +> [!IMPORTANT] +> Returns `null` for workspaces (whereas `integrity` throws). + ### author Return the author parsed as a **Contact** (or `null` if the property is missing). diff --git a/workspaces/mama/package.json b/workspaces/mama/package.json index d0ec1173..1293c5c9 100644 --- a/workspaces/mama/package.json +++ b/workspaces/mama/package.json @@ -40,7 +40,8 @@ "dependencies": { "@nodesecure/npm-types": "^1.3.0", "@nodesecure/utils": "^2.3.0", - "object-hash": "^3.0.0" + "object-hash": "^3.0.0", + "ssri": "13.0.1" }, "devDependencies": { "@types/object-hash": "^3.0.6" diff --git a/workspaces/mama/src/ManifestManager.class.ts b/workspaces/mama/src/ManifestManager.class.ts index b056493e..e463b8bf 100644 --- a/workspaces/mama/src/ManifestManager.class.ts +++ b/workspaces/mama/src/ManifestManager.class.ts @@ -9,8 +9,10 @@ import type { PackumentVersion, PackageJSON, WorkspacesPackageJSON, - Contact + Contact, + AbbreviatedManifestDocument } from "@nodesecure/npm-types"; +import { fromData } from "ssri"; // Import Internal Dependencies import { @@ -96,7 +98,7 @@ export class ManifestManager< }); constructor( - document: ManifestManagerDocument, + document: ManifestManagerDocument | AbbreviatedManifestDocument, options: ManifestManagerOptions = {} ) { const { location } = options; @@ -104,7 +106,7 @@ export class ManifestManager< this.document = Object.assign( { ...ManifestManager.Default }, structuredClone(document) - ); + ) as WithRequired; if (location) { this.location = location.endsWith("package.json") ? path.dirname(location) : @@ -120,6 +122,15 @@ export class ManifestManager< .some((script) => kUnsafeNPMScripts.has(script.toLowerCase())); } + get documentDigest() { + const isWorkspace = "workspaces" in this.document; + const data = JSON.stringify(this.document); + + return isWorkspace ? + null : + fromData(data, { algorithms: ["sha512"] }).toString(); + } + get name() { return this.document.name ?? "workspace"; } diff --git a/workspaces/mama/test/ManifestManager.spec.ts b/workspaces/mama/test/ManifestManager.spec.ts index 48ef796b..258629ac 100644 --- a/workspaces/mama/test/ManifestManager.spec.ts +++ b/workspaces/mama/test/ManifestManager.spec.ts @@ -603,6 +603,45 @@ describe("ManifestManager", () => { }); }); + describe("get documentDigest", () => { + test("Given a minimal PackageJSON, it must return a string starting with 'sha512-'", () => { + const mama = new ManifestManager(kMinimalPackageJSON); + + const digest = mama.documentDigest; + assert.ok(typeof digest === "string"); + assert.ok(digest!.startsWith("sha512-"), `Expected '${digest}' to start with 'sha512-'`); + }); + + test("Given a WorkspacesPackageJSON, it must return null", () => { + const mama = new ManifestManager({ + ...kMinimalPackageJSON, + workspaces: ["src/a"] + }); + + assert.strictEqual(mama.documentDigest, null); + }); + + test("Given two identical PackageJSON objects, the values must be strictly equal", () => { + const mamaA = new ManifestManager({ ...kMinimalPackageJSON }); + const mamaB = new ManifestManager({ ...kMinimalPackageJSON }); + + assert.strictEqual(mamaA.documentDigest, mamaB.documentDigest); + }); + + test("Given two different PackageJSON objects, the values must differ", () => { + const mamaA = new ManifestManager({ ...kMinimalPackageJSON }); + const mamaB = new ManifestManager({ ...kMinimalPackageJSON, description: "different" }); + + assert.notStrictEqual(mamaA.documentDigest, mamaB.documentDigest); + }); + + test("The value must be deterministic (two accesses on the same instance return the same string)", () => { + const mama = new ManifestManager(kMinimalPackageJSON); + + assert.strictEqual(mama.documentDigest, mama.documentDigest); + }); + }); + describe("get hasZeroSemver", () => { test("Given a PackageJSON with a semver higher than 1.x.x then it must return false", () => { const packageJSON: PackageJSON = { diff --git a/workspaces/npm-types/README.md b/workspaces/npm-types/README.md index 12c0c04a..9b40a453 100644 --- a/workspaces/npm-types/README.md +++ b/workspaces/npm-types/README.md @@ -95,6 +95,22 @@ Abbreviated package metadata format (corgi format). Lighter alternative to `Pack import type { Manifest } from "@nodesecure/npm-types"; ``` +#### `AbbreviatedManifestDocument` +Minimal manifest shape compatible with abbreviated registry manifests (e.g. `pacote.manifest()`). + +- No `[field: string]: unknown` index signature (unlike `BasePackageJSON`). +- Avoids the need for explicit casts when assigning `Pick`-based types like `pacote.AbbreviatedManifest & pacote.ManifestResult`. + +```ts +import type { AbbreviatedManifestDocument } from "@nodesecure/npm-types"; + +const doc: AbbreviatedManifestDocument = { + name: "my-package", + version: "1.0.0", + dependencies: { lodash: "^4.17.21" } +}; +``` + ### Utility Types #### `Contact` diff --git a/workspaces/npm-types/src/index.d.ts b/workspaces/npm-types/src/index.d.ts index dccdbdf0..9dd35473 100644 --- a/workspaces/npm-types/src/index.d.ts +++ b/workspaces/npm-types/src/index.d.ts @@ -274,3 +274,25 @@ export type PackTarball = { }[]; bundled: string[]; } + +/** + * Minimal manifest shape compatible with abbreviated registry manifests + * (e.g. `pacote.manifest()`). + * + * - No `[field: string]: unknown` index signature (unlike `BasePackageJSON`). + * - Avoids the need for explicit casts when assigning `Pick`-based types + * like `pacote.AbbreviatedManifest & pacote.ManifestResult`. + */ +export interface AbbreviatedManifestDocument { + name?: string; + version?: string; + dependencies?: Record; + devDependencies?: Record; + peerDependencies?: Record; + optionalDependencies?: Record; + bundledDependencies?: string[] | boolean; + scripts?: Record; + gypfile?: boolean; + bin?: Record; + engines?: Record; +} diff --git a/workspaces/scanner/package.json b/workspaces/scanner/package.json index 2f038708..f94024c1 100644 --- a/workspaces/scanner/package.json +++ b/workspaces/scanner/package.json @@ -82,7 +82,6 @@ "frequency-set": "^2.1.0", "pacote": "^21.0.0", "semver": "^7.5.4", - "ssri": "13.0.1", "type-fest": "^5.0.1" }, "devDependencies": { diff --git a/workspaces/scanner/src/depWalker.ts b/workspaces/scanner/src/depWalker.ts index d8bd6764..366aa9a3 100644 --- a/workspaces/scanner/src/depWalker.ts +++ b/workspaces/scanner/src/depWalker.ts @@ -11,12 +11,12 @@ import { import { DefaultCollectableSet } from "@nodesecure/js-x-ray"; import * as Vulnera from "@nodesecure/vulnera"; import { npm } from "@nodesecure/tree-walker"; -import { parseAuthor } from "@nodesecure/utils"; -import { parseNpmSpec } from "@nodesecure/mama"; -import type { ManifestVersion, PackageJSON, WorkspacesPackageJSON } from "@nodesecure/npm-types"; +import { + ManifestManager, + parseNpmSpec +} from "@nodesecure/mama"; import { getNpmRegistryURL } from "@nodesecure/npm-registry-sdk"; import type Config from "@npmcli/config"; -import { fromData } from "ssri"; import semver from "semver"; // Import Internal Dependencies @@ -28,11 +28,17 @@ import { NPM_TOKEN } from "./utils/index.ts"; import { getRegistryForPackage } from "./utils/npmrc.ts"; -import { NpmRegistryProvider, type NpmApiClient } from "./registry/NpmRegistryProvider.ts"; +import { + NpmRegistryProvider, + type NpmApiClient +} from "./registry/NpmRegistryProvider.ts"; import { StatsCollector } from "./class/StatsCollector.class.ts"; import { RegistryTokenStore } from "./registry/RegistryTokenStore.ts"; import { TempDirectory } from "./class/TempDirectory.class.ts"; -import { Logger, ScannerLoggerEvents } from "./class/logger.class.ts"; +import { + Logger, + ScannerLoggerEvents +} from "./class/logger.class.ts"; import { TarballScanner } from "./class/TarballScanner.class.ts"; import type { Dependency, @@ -109,7 +115,7 @@ type Metadata = { }; export async function depWalker( - manifest: PackageJSON | WorkspacesPackageJSON | ManifestVersion, + mama: ManifestManager, options: WalkerOptions, logger = new Logger() ): Promise { @@ -130,10 +136,15 @@ export async function depWalker( } = options; const statsCollector = new StatsCollector({ logger }, { isVerbose }); + const collectables = kCollectableTypes.map( + (type) => new DefaultCollectableSet(type) + ); - const collectables = kCollectableTypes.map((type) => new DefaultCollectableSet(type)); - - const tokenStore = new RegistryTokenStore(npmRcConfig, NPM_TOKEN.token, npmRcEntries); + const tokenStore = new RegistryTokenStore( + npmRcConfig, + NPM_TOKEN.token, + npmRcEntries + ); const npmProjectConfig = tokenStore.getConfig(registry); const pacoteScopedConfig = { @@ -164,8 +175,8 @@ export async function depWalker( const payload: InitialPayload = { id: tempDir.id, rootDependency: { - name: manifest.name ?? "workspace", - version: manifest.version ?? "0.0.0", + name: mama.name, + version: mama.version, integrity: null }, scannerVersion: packageVersion, @@ -231,7 +242,7 @@ export async function depWalker( packageLock }; try { - for await (const current of npmTreeWalker.walk(manifest, rootDepsOptions)) { + for await (const current of npmTreeWalker.walk(mama, rootDepsOptions)) { const { name, version, integrity, ...currentVersion } = current; const dependency: Dependency = { versions: { @@ -276,7 +287,7 @@ export async function depWalker( payload.rootDependency.integrity = integrity; } else if (isRoot) { - payload.rootDependency.integrity = manifestIntegrity ?? getManifestIntegrity(manifest); + payload.rootDependency.integrity = manifestIntegrity ?? mama.documentDigest; } // If the dependency is a DevDependencies we ignore it. @@ -315,7 +326,7 @@ export async function depWalker( version, ref: dependency.versions[version] as any, location, - isRootNode: scanRootNode && name === manifest.name, + isRootNode: scanRootNode && name === mama.name, registry }) ); @@ -383,16 +394,17 @@ export async function depWalker( ...addMissingVersionFlags(new Set(verDescriptor.flags), dependency) ); - if (isLocalManifest(verDescriptor, manifest, packageName)) { + if (isLocalManifest(verDescriptor, mama, packageName)) { + const author = mama.author; Object.assign(dependency.metadata, { - author: parseAuthor(manifest.author), - homepage: manifest.homepage + author, + homepage: mama.document.homepage }); Object.assign(verDescriptor, { - author: parseAuthor(manifest.author), - links: getManifestLinks(manifest), - repository: manifest.repository + author, + links: getManifestLinks(mama.document), + repository: mama.document.repository }); } @@ -431,17 +443,6 @@ export async function depWalker( } } -export function getManifestIntegrity( - manifest: PackageJSON | WorkspacesPackageJSON -): string | null { - const isWorkspace = "workspaces" in manifest; - const integrity = isWorkspace ? - null : - fromData(JSON.stringify(manifest), { algorithms: ["sha512"] }).toString(); - - return integrity; -} - function extractHighlightedIdentifiers( collectables: DefaultCollectableSet[], identifiersToHighlight: Set @@ -466,10 +467,10 @@ function extractHighlightedIdentifiers( function isLocalManifest( verDescriptor: DependencyVersion, - manifest: PackageJSON | WorkspacesPackageJSON | ManifestVersion, + mama: ManifestManager, packageName: string -): manifest is PackageJSON | WorkspacesPackageJSON { +): boolean { return verDescriptor.existOnRemoteRegistry === false && ( - packageName === manifest.name || manifest.name === undefined + packageName === mama.document.name || mama.document.name === undefined ); } diff --git a/workspaces/scanner/src/index.ts b/workspaces/scanner/src/index.ts index 0b8a3c06..469444f6 100644 --- a/workspaces/scanner/src/index.ts +++ b/workspaces/scanner/src/index.ts @@ -7,13 +7,13 @@ import os from "node:os"; import pacote from "pacote"; import { getLocalRegistryURL } from "@nodesecure/npm-registry-sdk"; import * as tarball from "@nodesecure/tarball"; +import { ManifestManager } from "@nodesecure/mama"; import type { PackageJSON } from "@nodesecure/npm-types"; import type Config from "@npmcli/config"; // Import Internal Dependencies import { - depWalker, - getManifestIntegrity + depWalker } from "./depWalker.ts"; import { NPM_TOKEN, @@ -84,15 +84,16 @@ export async function workingDir( logger.end(ScannerLoggerEvents.manifest.read); const packageJSON = JSON.parse(str) as PackageJSON; + const mama = new ManifestManager(packageJSON, { location }); - const integrity = getManifestIntegrity(packageJSON); + const integrity = mama.documentDigest; const cachedPayload = await options.cacheLookup?.(packageJSON, integrity); if (cachedPayload) { return cachedPayload; } return depWalker( - packageJSON, + mama, Object.assign(finalizedOptions, { integrity }), logger ); @@ -125,9 +126,10 @@ export async function from( return cachedPayload; } + const mama = new ManifestManager(manifest); + return depWalker( - // FIX: find a way to merge pacote & registry interfaces - manifest as pacote.AbbreviatedManifest, + mama, Object.assign(options, { registry }), logger ); diff --git a/workspaces/scanner/test/StatsCollector.spec.ts b/workspaces/scanner/test/StatsCollector.spec.ts index e8c3ea96..91a70652 100644 --- a/workspaces/scanner/test/StatsCollector.spec.ts +++ b/workspaces/scanner/test/StatsCollector.spec.ts @@ -378,10 +378,12 @@ describe("StatsCollectors", () => { }); class FakeDateProvider implements DateProvider { - #now: number; + #now: number = 0; + now(): number { return this.#now; } + oneYearAgo(): Date { return new Date(Date.now() - (365 * 24 * 60 * 60 * 1000)); } diff --git a/workspaces/scanner/test/depWalker.spec.ts b/workspaces/scanner/test/depWalker.spec.ts index 6e417c78..e236cca7 100644 --- a/workspaces/scanner/test/depWalker.spec.ts +++ b/workspaces/scanner/test/depWalker.spec.ts @@ -6,7 +6,10 @@ import assert from "node:assert"; // Import Third-party Dependencies import * as Vulnera from "@nodesecure/vulnera"; -import { getLocalRegistryURL } from "@nodesecure/npm-registry-sdk"; +import { ManifestManager } from "@nodesecure/mama"; +import { + getLocalRegistryURL +} from "@nodesecure/npm-registry-sdk"; // Import Internal Dependencies import { depWalker } from "../src/depWalker.ts"; @@ -80,7 +83,7 @@ describe("depWalker", { concurrency: 2 }, () => { t.after(() => logger.removeAllListeners()); const result = await depWalker( - is, + new ManifestManager(is), structuredClone(kDefaultWalkerOptions), logger ); @@ -98,7 +101,7 @@ describe("depWalker", { concurrency: 2 }, () => { t.after(() => logger.removeAllListeners()); const result = await depWalker( - config, + new ManifestManager(config), structuredClone(kDefaultWalkerOptions), logger ); @@ -136,7 +139,7 @@ describe("depWalker", { concurrency: 2 }, () => { t.after(() => logger.removeAllListeners()); const result = await depWalker( - pkgGitdeps, + new ManifestManager(pkgGitdeps), { ...structuredClone(kDefaultWalkerOptions), isVerbose: true @@ -191,7 +194,7 @@ describe("depWalker", { concurrency: 2 }, () => { t.after(() => logger.removeAllListeners()); const result = await depWalker( - pkgTypoSquatting, + new ManifestManager(pkgTypoSquatting), { ...structuredClone(kDefaultWalkerOptions), location: "", @@ -231,7 +234,7 @@ describe("depWalker", { concurrency: 2 }, () => { t.after(() => logger.removeAllListeners()); const result = await depWalker( - pkgTypoSquatting, + new ManifestManager(pkgTypoSquatting), { ...structuredClone(kDefaultWalkerOptions), isVerbose: true @@ -267,7 +270,7 @@ describe("depWalker", { concurrency: 2 }, () => { }; const result = await depWalker( - pkgHighlightedPackages, + new ManifestManager(pkgHighlightedPackages), structuredClone({ ...kDefaultWalkerOptions, highlight: { @@ -294,7 +297,7 @@ describe("depWalker", { concurrency: 2 }, () => { const hightlightPackages = ["zen-observable@0.8.14 || 0.8.15", "nanoid"]; const result = await depWalker( - pkgHighlightedPackages, + new ManifestManager(pkgHighlightedPackages), structuredClone({ ...kDefaultWalkerOptions, highlight: { diff --git a/workspaces/scanner/test/workingDir.spec.ts b/workspaces/scanner/test/workingDir.spec.ts index 8bd8b219..63aa5f08 100644 --- a/workspaces/scanner/test/workingDir.spec.ts +++ b/workspaces/scanner/test/workingDir.spec.ts @@ -118,7 +118,7 @@ describe("scanner.workingDir()", { concurrency: 2 }, () => { assert.deepStrictEqual(result.rootDependency, { name: "workspace", - version: "0.0.0", + version: "1.0.0", integrity: null }); }); diff --git a/workspaces/tree-walker/README.md b/workspaces/tree-walker/README.md index 88e58baf..f806927c 100644 --- a/workspaces/tree-walker/README.md +++ b/workspaces/tree-walker/README.md @@ -25,15 +25,17 @@ $ yarn add @nodesecure/tree-walker import os from "node:os"; import pacote from "pacote"; +import { ManifestManager } from "@nodesecure/mama"; import { npm } from "@nodesecure/tree-walker"; const manifest = await pacote.manifest("some-package@1.0.0", { cache: `${os.homedir()}/.npm` }); +const mama = new ManifestManager(manifest); const treeWalker = new npm.TreeWalker(); -for await (const dependency of treeWalker.walk(manifest)) { +for await (const dependency of treeWalker.walk(mama)) { console.log(dependency); } ``` @@ -74,7 +76,7 @@ interface TreeWalkerOptions { } ``` -#### *walk(manifest: PackageJSON | ManifestVersion, options: WalkOptions): AsyncIterableIterator< DependencyJSON > +#### *walk(mama: ManifestManager, options: WalkOptions): AsyncIterableIterator< DependencyJSON > The `walk` method processes package metadata from a given **package.json file** or a **Manifest** result from the [pacote](https://www.npmjs.com/package/pacote) library. diff --git a/workspaces/tree-walker/src/npm/walker.ts b/workspaces/tree-walker/src/npm/walker.ts index 863eac02..cedfbffc 100644 --- a/workspaces/tree-walker/src/npm/walker.ts +++ b/workspaces/tree-walker/src/npm/walker.ts @@ -10,7 +10,6 @@ import Arborist from "@npmcli/arborist"; // @ts-ignore import pickManifest from "npm-pick-manifest"; import { getNpmRegistryURL } from "@nodesecure/npm-registry-sdk"; -import type { PackageJSON, WorkspacesPackageJSON, ManifestVersion } from "@nodesecure/npm-types"; // Import Internal Dependencies import * as utils from "../utils/index.ts"; @@ -317,11 +316,9 @@ export class TreeWalker { } async* walk( - manifest: PackageJSON | WorkspacesPackageJSON | ManifestVersion, + mama: ManifestManager, options: WalkOptions = {} ): AsyncIterableIterator { - const mama = new ManifestManager(manifest); - this.relationsMap.clear(); const { maxDepth = Infinity, diff --git a/workspaces/tree-walker/test/npm/TreeWalker.spec.ts b/workspaces/tree-walker/test/npm/TreeWalker.spec.ts index 7c6d6bda..7f08d9e6 100644 --- a/workspaces/tree-walker/test/npm/TreeWalker.spec.ts +++ b/workspaces/tree-walker/test/npm/TreeWalker.spec.ts @@ -6,10 +6,17 @@ import path from "node:path"; // Import Third-party Dependencies import pacote from "pacote"; -import type { PackageJSON, WorkspacesPackageJSON } from "@nodesecure/npm-types"; +import { ManifestManager } from "@nodesecure/mama"; +import type { + PackageJSON, + WorkspacesPackageJSON +} from "@nodesecure/npm-types"; // Import Internal Dependencies -import { npm, type DependencyJSON } from "../../src/index.ts"; +import { + npm, + type DependencyJSON +} from "../../src/index.ts"; // CONSTANTS const kFixturesDir = path.join(import.meta.dirname, "..", "fixtures"); @@ -18,11 +25,12 @@ describe("npm.TreeWalker", () => { test("Given a fixed '@nodesecure/fs-walk' manifest then it must extract one root dependency", async() => { const spec = "@nodesecure/fs-walk@2.0.0"; const manifest = await pacote.manifest(spec); + const mama = new ManifestManager(manifest); const expectedIntegrity = manifest._integrity; const walker = new npm.TreeWalker(); - for await (const dependency of walker.walk(manifest as pacote.AbbreviatedManifest)) { + for await (const dependency of walker.walk(mama)) { assert.deepEqual( dependency, { @@ -47,7 +55,8 @@ describe("npm.TreeWalker", () => { test(`Given a manifest with only one dependency and a mocked pacote.manifest function that throw an Error then it's must set existOnRemoteRegistry to false on the root dependency`, async(t) => { const spec = "@nodesecure/fs-walk@2.0.0"; - const manifest = await pacote.manifest(spec) as pacote.AbbreviatedManifest; + const manifest = await pacote.manifest(spec); + const mama = new ManifestManager(manifest); const mockedPacoteProvider = { manifest: t.mock.fn(async function mock() { @@ -60,7 +69,7 @@ describe("npm.TreeWalker", () => { } }); - for await (const dependency of walker.walk(manifest)) { + for await (const dependency of walker.walk(mama)) { assert.deepEqual( dependency, { @@ -85,12 +94,13 @@ describe("npm.TreeWalker", () => { test(`Given a fixed 'fastify' manifest and maxDepth option equal to one then it must return only the package direct dependencies`, async() => { const spec = "fastify@4.28.1"; - const manifest = await pacote.manifest(spec) as pacote.AbbreviatedManifest; + const manifest = await pacote.manifest(spec); + const mama = new ManifestManager(manifest); const walker = new npm.TreeWalker(); const dependencies: DependencyJSON[] = []; - for await (const dependency of walker.walk(manifest, { maxDepth: 1 })) { + for await (const dependency of walker.walk(mama, { maxDepth: 1 })) { dependencies.push(dependency); } @@ -143,6 +153,7 @@ describe("npm.TreeWalker", () => { const manifest = ( await import(pathToFileURL(manifestLocation).href, { with: { type: "json" } }) ).default as WorkspacesPackageJSON; + const mama = new ManifestManager(manifest); const manifestWorkspaces = manifest.workspaces.map( (name) => "@nodesecure" + name.slice("workspaces".length) ); @@ -157,7 +168,7 @@ describe("npm.TreeWalker", () => { fetchManifest: false } }; - for await (const dependency of walker.walk(manifest, walkOptions)) { + for await (const dependency of walker.walk(mama, walkOptions)) { dependencies.push(dependency); } @@ -193,6 +204,7 @@ describe("npm.TreeWalker", () => { const manifest = ( await import(pathToFileURL(manifestLocation).href, { with: { type: "json" } }) ).default as PackageJSON; + const mama = new ManifestManager(manifest); const walker = new npm.TreeWalker(); const walkOptions: npm.WalkOptions = { @@ -204,7 +216,7 @@ describe("npm.TreeWalker", () => { }; const dependencies: DependencyJSON[] = []; - for await (const dependency of walker.walk(manifest, walkOptions)) { + for await (const dependency of walker.walk(mama, walkOptions)) { dependencies.push(dependency); } @@ -224,12 +236,13 @@ describe("npm.TreeWalker", () => { it("should always be cleared when triggering walk() and return an empty Map if the package has no dependencies", async() => { const manifest = await pacote.manifest( "@nodesecure/fs-walk@2.0.0" - ) as pacote.AbbreviatedManifest; + ); + const mama = new ManifestManager(manifest); const walker = new npm.TreeWalker(); walker.relationsMap.set("foo@1.5.0", new Set()); - for await (const _ of walker.walk(manifest)) { + for await (const _ of walker.walk(mama)) { // do nothing } @@ -274,8 +287,9 @@ describe("npm.TreeWalker", () => { } }); + const mama = new ManifestManager(rootManifest); const dependencies: DependencyJSON[] = []; - for await (const dependency of walker.walk(rootManifest as any, { maxDepth: 10 })) { + for await (const dependency of walker.walk(mama, { maxDepth: 10 })) { dependencies.push(dependency); } @@ -339,8 +353,10 @@ describe("npm.TreeWalker", () => { } }); + const mama = new ManifestManager(rootManifest); + const dependencies: DependencyJSON[] = []; - for await (const dependency of walker.walk(rootManifest, { maxDepth: 10 })) { + for await (const dependency of walker.walk(mama, { maxDepth: 10 })) { dependencies.push(dependency); } @@ -382,7 +398,8 @@ describe("npm.TreeWalker", () => { // This should complete without throwing and without leaking unhandled rejections const dependencies: DependencyJSON[] = []; - for await (const dependency of walker.walk(rootManifest, { maxDepth: 10 })) { + const mama = new ManifestManager(rootManifest); + for await (const dependency of walker.walk(mama, { maxDepth: 10 })) { dependencies.push(dependency); }