mirror of
https://github.com/pnpm/pnpm.git
synced 2026-04-11 10:40:53 -04:00
Replaces the dual `authConfig` (raw .npmrc) + `authInfos` (parsed auth) + `sslConfigs` (parsed SSL) pattern with a single structured `configByUri: Record<string, RegistryConfig>` field on Config.
### New types (`@pnpm/types`)
- **`RegistryConfig`** — per-registry config: `{ creds?: Creds, tls?: TlsConfig }`
- **`Creds`** — auth credentials: `{ authToken?, basicAuth?, tokenHelper? }`
- **`TlsConfig`** — TLS config: `{ cert?, key?, ca? }`
### Key changes
- Rewrite `createGetAuthHeaderByURI` to accept `Record<string, RegistryConfig>` instead of raw .npmrc key-value pairs
- Eliminate duplicate auth parsing between `getAuthHeadersFromConfig` and `getNetworkConfigs`
- Remove `authConfig` from the install pipeline (`StrictInstallOptions`, `HeadlessOptions`), replaced by `configByUri`
- Remove `sslConfigs` from Config — SSL fields now live in `configByUri[uri].tls`
- Remove `authConfig['registry']` mutation in `extendInstallOptions` (default registry now passed directly to `createGetAuthHeaderByURI`)
- `authConfig` remains on Config only for raw .npmrc access (config commands, error reporting, config inheritance)
### Security
- tokenHelper in project .npmrc now throws instead of being silently stripped
- tokenHelper execution uses `shell: false` to prevent shell metacharacter injection
- Basic auth uses `Buffer.from().toString('base64')` instead of `btoa()` for Unicode safety
- Dispatcher only creates custom agents when entries actually have TLS fields
1263 lines
38 KiB
TypeScript
1263 lines
38 KiB
TypeScript
/// <reference path="../../../__typings__/index.d.ts" />
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
|
|
import { jest } from '@jest/globals'
|
|
import { depPathToFilename } from '@pnpm/deps.path'
|
|
import { createClient } from '@pnpm/installing.client'
|
|
import { createPackageRequester, type PackageResponse } from '@pnpm/installing.package-requester'
|
|
import { streamParser } from '@pnpm/logger'
|
|
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
|
|
import type { PackageFilesIndex } from '@pnpm/store.cafs'
|
|
import type { PkgRequestFetchResult, PkgResolutionId, RequestPackageOptions } from '@pnpm/store.controller-types'
|
|
import { createCafsStore } from '@pnpm/store.create-cafs-store'
|
|
import { StoreIndex } from '@pnpm/store.index'
|
|
import { fixtures } from '@pnpm/test-fixtures'
|
|
import { setupMockAgent, teardownMockAgent } from '@pnpm/testing.mock-agent'
|
|
import { restartWorkerPool } from '@pnpm/worker'
|
|
import delay from 'delay'
|
|
import normalize from 'normalize-path'
|
|
import { temporaryDirectory } from 'tempy'
|
|
|
|
const registry = `http://localhost:${REGISTRY_MOCK_PORT}`
|
|
const f = fixtures(import.meta.dirname)
|
|
const IS_POSITIVE_TARBALL = f.find('is-positive-1.0.0.tgz')
|
|
|
|
const registries = { default: registry }
|
|
|
|
const storeIndexes: StoreIndex[] = []
|
|
afterAll(() => {
|
|
for (const si of storeIndexes) si.close()
|
|
})
|
|
|
|
const topStoreIndex = new StoreIndex('.store')
|
|
storeIndexes.push(topStoreIndex)
|
|
|
|
const { resolve, fetchers } = createClient({
|
|
configByUri: {},
|
|
cacheDir: '.store',
|
|
storeDir: '.store',
|
|
registries,
|
|
storeIndex: topStoreIndex,
|
|
})
|
|
|
|
function createFetchersForStore (storeDir: string) {
|
|
const si = new StoreIndex(storeDir)
|
|
storeIndexes.push(si)
|
|
return createClient({
|
|
configByUri: {},
|
|
cacheDir: storeDir,
|
|
storeDir,
|
|
registries,
|
|
storeIndex: si,
|
|
}).fetchers
|
|
}
|
|
|
|
afterEach(async () => {
|
|
await teardownMockAgent()
|
|
})
|
|
|
|
test('request package', async () => {
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
expect(typeof requestPackage).toBe('function')
|
|
|
|
const projectDir = temporaryDirectory()
|
|
const pkgResponse = await requestPackage({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {
|
|
downloadPriority: 0,
|
|
lockfileDir: projectDir,
|
|
preferredVersions: {},
|
|
projectDir,
|
|
})
|
|
|
|
expect(pkgResponse).toBeTruthy()
|
|
expect(pkgResponse.body).toBeTruthy()
|
|
|
|
expect(pkgResponse.body.id).toBe('is-positive@1.0.0')
|
|
expect(pkgResponse.body.resolvedVia).toBe('npm-registry')
|
|
expect(pkgResponse.body.isLocal).toBe(false)
|
|
expect(typeof pkgResponse.body.latest).toBe('string')
|
|
expect(pkgResponse.body.manifest?.name).toBe('is-positive')
|
|
expect(!pkgResponse.body.normalizedBareSpecifier).toBeTruthy()
|
|
expect(pkgResponse.body.resolution).toStrictEqual({
|
|
integrity: 'sha512-xxzPGZ4P2uN6rROUa5N9Z7zTX6ERuE0hs6GUOc/cKBLF2NqKc16UwqHMt3tFg4CO6EBTE5UecUasg+3jZx3Ckg==',
|
|
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`,
|
|
})
|
|
|
|
const { files } = await pkgResponse.fetching!()
|
|
expect(Array.from(files.filesMap.keys()).sort((a, b) => a.localeCompare(b))).toStrictEqual(['package.json', 'index.js', 'license', 'readme.md'].sort((a, b) => a.localeCompare(b)))
|
|
expect(files.resolvedFrom).toBe('remote')
|
|
})
|
|
|
|
test('request package but skip fetching', async () => {
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
expect(typeof requestPackage).toBe('function')
|
|
|
|
const projectDir = temporaryDirectory()
|
|
const pkgResponse = await requestPackage({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {
|
|
downloadPriority: 0,
|
|
lockfileDir: projectDir,
|
|
preferredVersions: {},
|
|
projectDir,
|
|
skipFetch: true,
|
|
})
|
|
|
|
expect(pkgResponse).toBeTruthy()
|
|
expect(pkgResponse.body).toBeTruthy()
|
|
|
|
expect(pkgResponse.body.id).toBe('is-positive@1.0.0')
|
|
expect(pkgResponse.body.isLocal).toBe(false)
|
|
expect(typeof pkgResponse.body.latest).toBe('string')
|
|
expect(pkgResponse.body.manifest?.name).toBe('is-positive')
|
|
expect(!pkgResponse.body.normalizedBareSpecifier).toBeTruthy()
|
|
expect(pkgResponse.body.resolution).toStrictEqual({
|
|
integrity: 'sha512-xxzPGZ4P2uN6rROUa5N9Z7zTX6ERuE0hs6GUOc/cKBLF2NqKc16UwqHMt3tFg4CO6EBTE5UecUasg+3jZx3Ckg==',
|
|
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`,
|
|
})
|
|
|
|
expect(pkgResponse.fetching).toBeFalsy()
|
|
})
|
|
|
|
test('request package but skip fetching, when resolution is already available', async () => {
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
expect(typeof requestPackage).toBe('function')
|
|
|
|
const projectDir = temporaryDirectory()
|
|
const pkgResponse = await requestPackage({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {
|
|
currentPkg: {
|
|
id: 'is-positive@1.0.0' as PkgResolutionId,
|
|
resolution: {
|
|
integrity: 'sha512-xxzPGZ4P2uN6rROUa5N9Z7zTX6ERuE0hs6GUOc/cKBLF2NqKc16UwqHMt3tFg4CO6EBTE5UecUasg+3jZx3Ckg==',
|
|
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`,
|
|
},
|
|
},
|
|
downloadPriority: 0,
|
|
lockfileDir: projectDir,
|
|
preferredVersions: {},
|
|
projectDir,
|
|
skipFetch: true,
|
|
update: false,
|
|
}) as PackageResponse & {
|
|
body: {
|
|
manifest: { name: string }
|
|
}
|
|
}
|
|
|
|
expect(pkgResponse).toBeTruthy()
|
|
expect(pkgResponse.body).toBeTruthy()
|
|
|
|
expect(pkgResponse.body.id).toBe('is-positive@1.0.0')
|
|
expect(pkgResponse.body.isLocal).toBe(false)
|
|
// latest may be undefined when the resolver's fast path resolves from the store cache
|
|
expect(pkgResponse.body.manifest.name).toBe('is-positive')
|
|
expect(!pkgResponse.body.normalizedBareSpecifier).toBeTruthy()
|
|
expect(pkgResponse.body.resolution).toStrictEqual({
|
|
integrity: 'sha512-xxzPGZ4P2uN6rROUa5N9Z7zTX6ERuE0hs6GUOc/cKBLF2NqKc16UwqHMt3tFg4CO6EBTE5UecUasg+3jZx3Ckg==',
|
|
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`,
|
|
})
|
|
|
|
expect(pkgResponse.fetching).toBeFalsy()
|
|
})
|
|
|
|
test('refetch local tarball if its integrity has changed', async () => {
|
|
const projectDir = temporaryDirectory()
|
|
const tarballPath = path.join(projectDir, 'tarball.tgz')
|
|
const tarballRelativePath = path.relative(projectDir, tarballPath)
|
|
f.copy('pnpm-package-requester-0.8.1.tgz', tarballPath)
|
|
const tarball = `file:${tarballRelativePath}`
|
|
const wantedPackage = { bareSpecifier: tarball }
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
const localFetchers = createFetchersForStore(storeDir)
|
|
const pkgId = `file:${normalize(tarballRelativePath)}`
|
|
const requestPackageOpts = {
|
|
downloadPriority: 0,
|
|
lockfileDir: projectDir,
|
|
preferredVersions: {},
|
|
projectDir,
|
|
skipFetch: true,
|
|
update: false,
|
|
} satisfies RequestPackageOptions
|
|
|
|
{
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers: localFetchers,
|
|
cafs,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const response = await requestPackage(wantedPackage, {
|
|
...requestPackageOpts,
|
|
currentPkg: {
|
|
id: pkgId as PkgResolutionId,
|
|
resolution: {
|
|
integrity: 'sha512-lqODmYcc/FKOGROEUByd5Sbugqhzgkv+Hij9PXH0sZVQsU2npTQ0x3L81GCtHilFKme8lhBtD31Vxg/AKYrAvg==',
|
|
tarball,
|
|
},
|
|
},
|
|
}) as PackageResponse & {
|
|
fetching: () => Promise<PkgRequestFetchResult>
|
|
}
|
|
const { files, bundledManifest } = await response.fetching()
|
|
|
|
expect(response.body.updated).toBeFalsy()
|
|
expect(files.resolvedFrom).toBe('remote')
|
|
expect(bundledManifest).toBeTruthy()
|
|
}
|
|
|
|
f.copy('pnpm-package-requester-4.1.2.tgz', tarballPath)
|
|
await delay(50)
|
|
|
|
{
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers: localFetchers,
|
|
cafs,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const response = await requestPackage(wantedPackage, {
|
|
...requestPackageOpts,
|
|
currentPkg: {
|
|
id: pkgId as PkgResolutionId,
|
|
resolution: {
|
|
integrity: 'sha512-lqODmYcc/FKOGROEUByd5Sbugqhzgkv+Hij9PXH0sZVQsU2npTQ0x3L81GCtHilFKme8lhBtD31Vxg/AKYrAvg==',
|
|
tarball,
|
|
},
|
|
},
|
|
})
|
|
const { files, bundledManifest } = await response.fetching!()
|
|
|
|
expect(response.body.updated).toBeTruthy()
|
|
expect(files.resolvedFrom).toBe('remote')
|
|
expect(bundledManifest).toBeTruthy()
|
|
}
|
|
|
|
{
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers: localFetchers,
|
|
cafs,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const response = await requestPackage(wantedPackage, {
|
|
...requestPackageOpts,
|
|
currentPkg: {
|
|
id: pkgId as PkgResolutionId,
|
|
resolution: {
|
|
integrity: 'sha512-v3uhYkN+Eh3Nus4EZmegjQhrfpdPIH+2FjrkeBc6ueqZJWWRaLnSYIkD0An6m16D3v+6HCE18ox6t95eGxj5Pw==',
|
|
tarball,
|
|
},
|
|
},
|
|
}) as PackageResponse & {
|
|
fetching: () => Promise<PkgRequestFetchResult>
|
|
}
|
|
const { files, bundledManifest } = await response.fetching()
|
|
|
|
expect(response.body.updated).toBeFalsy()
|
|
expect(files.resolvedFrom).toBe('store')
|
|
expect(bundledManifest).toBeTruthy()
|
|
}
|
|
})
|
|
|
|
test('refetch local tarball if its integrity has changed. The requester does not know the correct integrity', async () => {
|
|
const projectDir = temporaryDirectory()
|
|
const tarballPath = path.join(projectDir, 'tarball.tgz')
|
|
f.copy('pnpm-package-requester-0.8.1.tgz', tarballPath)
|
|
const tarball = `file:${tarballPath}`
|
|
const wantedPackage = { bareSpecifier: tarball }
|
|
const storeDir = path.join(projectDir, 'store')
|
|
const cafs = createCafsStore(storeDir)
|
|
const localFetchers = createFetchersForStore(storeDir)
|
|
const requestPackageOpts = {
|
|
downloadPriority: 0,
|
|
lockfileDir: projectDir,
|
|
preferredVersions: {},
|
|
projectDir,
|
|
update: false,
|
|
} satisfies RequestPackageOptions
|
|
|
|
{
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers: localFetchers,
|
|
cafs,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const response = await requestPackage(wantedPackage, requestPackageOpts) as PackageResponse & {
|
|
fetching: () => Promise<PkgRequestFetchResult>
|
|
}
|
|
const { files, bundledManifest } = await response.fetching()
|
|
|
|
expect(response.body.updated).toBeTruthy()
|
|
expect(files.resolvedFrom).toBe('remote')
|
|
expect(bundledManifest).toBeTruthy()
|
|
}
|
|
|
|
f.copy('pnpm-package-requester-4.1.2.tgz', tarballPath)
|
|
await delay(50)
|
|
|
|
{
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers: localFetchers,
|
|
cafs,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const response = await requestPackage(wantedPackage, requestPackageOpts) as PackageResponse & {
|
|
fetching: () => Promise<PkgRequestFetchResult>
|
|
}
|
|
const { files, bundledManifest } = await response.fetching()
|
|
|
|
expect(response.body.updated).toBeTruthy()
|
|
expect(files.resolvedFrom).toBe('remote')
|
|
expect(bundledManifest).toBeTruthy()
|
|
}
|
|
|
|
{
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers: localFetchers,
|
|
cafs,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const response = await requestPackage(wantedPackage, requestPackageOpts) as PackageResponse & {
|
|
fetching: () => Promise<PkgRequestFetchResult>
|
|
}
|
|
const { files, bundledManifest } = await response.fetching()
|
|
|
|
expect(files.resolvedFrom).toBe('store')
|
|
expect(bundledManifest).toBeTruthy()
|
|
}
|
|
})
|
|
|
|
test('force fetch when resolution integrity differs from current package integrity', async () => {
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
const projectDir = temporaryDirectory()
|
|
|
|
// Create a custom resolver that returns a different integrity than the current package
|
|
const customResolve: typeof resolve = async () => {
|
|
// Return a resolution with a different integrity than what's in currentPkg
|
|
return {
|
|
id: 'is-positive@1.0.0' as PkgResolutionId,
|
|
latest: '1.0.0',
|
|
resolution: {
|
|
integrity: 'sha512-xxzPGZ4P2uN6rROUa5N9Z7zTX6ERuE0hs6GUOc/cKBLF2NqKc16UwqHMt3tFg4CO6EBTE5UecUasg+3jZx3Ckg==',
|
|
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`,
|
|
},
|
|
manifest: {
|
|
name: 'is-positive',
|
|
version: '1.0.0',
|
|
},
|
|
resolvedVia: 'npm-registry',
|
|
}
|
|
}
|
|
|
|
const requestPackage = createPackageRequester({
|
|
resolve: customResolve,
|
|
fetchers,
|
|
cafs,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
// Request with a currentPkg that has a different integrity
|
|
const response = await requestPackage({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {
|
|
currentPkg: {
|
|
id: 'is-positive@1.0.0' as PkgResolutionId,
|
|
resolution: {
|
|
// Different valid integrity than what the resolver returns
|
|
integrity: 'sha512-AvAi2XyFuGzKkv+hij9PXH0sZVQsU2npTQ0x3L81GCtHilFKme8lhBtD31Vxg/AKYrAvg==',
|
|
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`,
|
|
},
|
|
},
|
|
downloadPriority: 0,
|
|
lockfileDir: projectDir,
|
|
preferredVersions: {},
|
|
projectDir,
|
|
skipFetch: false,
|
|
update: false,
|
|
}) as PackageResponse & {
|
|
fetching: () => Promise<PkgRequestFetchResult>
|
|
}
|
|
|
|
// The package should be marked as updated because the integrity changed
|
|
expect(response.body.updated).toBe(true)
|
|
|
|
// Fetching should occur because integrity changed
|
|
const { files } = await response.fetching()
|
|
expect(files.resolvedFrom).toBe('remote')
|
|
})
|
|
|
|
test('fetchPackageToStore()', async () => {
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
const localFetchers = createFetchersForStore(storeDir)
|
|
const packageRequester = createPackageRequester({
|
|
resolve,
|
|
fetchers: localFetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const pkgId = 'is-positive@1.0.0'
|
|
const fetchResult = packageRequester.fetchPackageToStore({
|
|
force: false,
|
|
lockfileDir: temporaryDirectory(),
|
|
pkg: {
|
|
name: 'is-positive',
|
|
version: '1.0.0',
|
|
id: pkgId,
|
|
resolution: {
|
|
integrity: 'sha512-xxzPGZ4P2uN6rROUa5N9Z7zTX6ERuE0hs6GUOc/cKBLF2NqKc16UwqHMt3tFg4CO6EBTE5UecUasg+3jZx3Ckg==',
|
|
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`,
|
|
},
|
|
},
|
|
})
|
|
|
|
const { files, bundledManifest } = await fetchResult.fetching()
|
|
expect(bundledManifest).toBeTruthy() // we always read the bundled manifest
|
|
expect(Array.from(files.filesMap.keys()).sort((a, b) => a.localeCompare(b))).toStrictEqual(['package.json', 'index.js', 'license', 'readme.md'].sort((a, b) => a.localeCompare(b)))
|
|
expect(files.resolvedFrom).toBe('remote')
|
|
|
|
const storeIndex = new StoreIndex(storeDir)
|
|
storeIndexes.push(storeIndex)
|
|
const indexFile = storeIndex.get(fetchResult.filesIndexFile) as PackageFilesIndex
|
|
expect(indexFile).toBeTruthy()
|
|
expect(typeof indexFile.files.get('package.json')!.checkedAt).toBeTruthy()
|
|
|
|
const fetchResult2 = packageRequester.fetchPackageToStore({
|
|
fetchRawManifest: true,
|
|
force: false,
|
|
lockfileDir: temporaryDirectory(),
|
|
pkg: {
|
|
name: 'is-positive',
|
|
version: '1.0.0',
|
|
id: pkgId,
|
|
resolution: {
|
|
integrity: 'sha512-xxzPGZ4P2uN6rROUa5N9Z7zTX6ERuE0hs6GUOc/cKBLF2NqKc16UwqHMt3tFg4CO6EBTE5UecUasg+3jZx3Ckg==',
|
|
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`,
|
|
},
|
|
},
|
|
})
|
|
|
|
// This verifies that when a package has been cached with no full manifest
|
|
// the full manifest is requested and added to the cache
|
|
expect(
|
|
(await fetchResult2.fetching()).bundledManifest
|
|
).toStrictEqual(
|
|
{
|
|
devDependencies: { ava: '^0.0.4' },
|
|
engines: { node: '>=0.10.0' },
|
|
name: 'is-positive',
|
|
version: '1.0.0',
|
|
}
|
|
)
|
|
})
|
|
|
|
test('fetchPackageToStore() concurrency check', async () => {
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
const packageRequester = createPackageRequester({
|
|
resolve,
|
|
fetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const pkgId = 'is-positive@1.0.0'
|
|
const projectDir1 = temporaryDirectory()
|
|
const projectDir2 = temporaryDirectory()
|
|
const fetchResults = await Promise.all([
|
|
packageRequester.fetchPackageToStore({
|
|
force: false,
|
|
lockfileDir: projectDir1,
|
|
pkg: {
|
|
name: 'is-positive',
|
|
version: '1.0.0',
|
|
id: pkgId,
|
|
resolution: {
|
|
integrity: 'sha512-xxzPGZ4P2uN6rROUa5N9Z7zTX6ERuE0hs6GUOc/cKBLF2NqKc16UwqHMt3tFg4CO6EBTE5UecUasg+3jZx3Ckg==',
|
|
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`,
|
|
},
|
|
},
|
|
}),
|
|
packageRequester.fetchPackageToStore({
|
|
force: false,
|
|
lockfileDir: projectDir2,
|
|
pkg: {
|
|
name: 'is-positive',
|
|
version: '1.0.0',
|
|
id: pkgId,
|
|
resolution: {
|
|
integrity: 'sha512-xxzPGZ4P2uN6rROUa5N9Z7zTX6ERuE0hs6GUOc/cKBLF2NqKc16UwqHMt3tFg4CO6EBTE5UecUasg+3jZx3Ckg==',
|
|
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`,
|
|
},
|
|
},
|
|
}),
|
|
])
|
|
|
|
let ino1!: number
|
|
let ino2!: number
|
|
|
|
{
|
|
const fetchResult = fetchResults[0]
|
|
const { files } = await fetchResult.fetching()
|
|
|
|
ino1 = fs.statSync(files.filesMap.get('package.json') as string).ino
|
|
|
|
expect(Array.from(files.filesMap.keys()).sort((a, b) => a.localeCompare(b))).toStrictEqual(['package.json', 'index.js', 'license', 'readme.md'].sort((a, b) => a.localeCompare(b)))
|
|
expect(files.resolvedFrom).toBe('remote')
|
|
}
|
|
|
|
{
|
|
const fetchResult = fetchResults[1]
|
|
const { files } = await fetchResult.fetching()
|
|
|
|
ino2 = fs.statSync(files.filesMap.get('package.json') as string).ino
|
|
|
|
expect(Array.from(files.filesMap.keys()).sort((a, b) => a.localeCompare(b))).toStrictEqual(['package.json', 'index.js', 'license', 'readme.md'].sort((a, b) => a.localeCompare(b)))
|
|
expect(files.resolvedFrom).toBe('remote')
|
|
}
|
|
|
|
expect(ino1).toBe(ino2)
|
|
})
|
|
|
|
test('fetchPackageToStore() does not cache errors', async () => {
|
|
const agent = await setupMockAgent()
|
|
const mockPool = agent.get(registry)
|
|
// First request returns 404
|
|
mockPool.intercept({ path: '/is-positive/-/is-positive-1.0.0.tgz', method: 'GET' }).reply(404, {})
|
|
// Second request returns the tarball
|
|
const tarballContent = fs.readFileSync(IS_POSITIVE_TARBALL)
|
|
mockPool.intercept({ path: '/is-positive/-/is-positive-1.0.0.tgz', method: 'GET' }).reply(200, tarballContent, {
|
|
headers: { 'content-length': String(tarballContent.length) },
|
|
})
|
|
|
|
const noRetryStoreIndex = new StoreIndex('.store')
|
|
storeIndexes.push(noRetryStoreIndex)
|
|
const noRetry = createClient({
|
|
configByUri: {},
|
|
retry: { retries: 0 },
|
|
cacheDir: '.pnpm',
|
|
storeDir: '.store',
|
|
registries,
|
|
storeIndex: topStoreIndex,
|
|
})
|
|
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
const packageRequester = createPackageRequester({
|
|
resolve: noRetry.resolve,
|
|
fetchers: noRetry.fetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const pkgId = 'is-positive@1.0.0'
|
|
|
|
const badRequest = packageRequester.fetchPackageToStore({
|
|
force: false,
|
|
lockfileDir: temporaryDirectory(),
|
|
pkg: {
|
|
name: 'is-positive',
|
|
version: '1.0.0',
|
|
id: pkgId,
|
|
resolution: {
|
|
integrity: 'sha512-xxzPGZ4P2uN6rROUa5N9Z7zTX6ERuE0hs6GUOc/cKBLF2NqKc16UwqHMt3tFg4CO6EBTE5UecUasg+3jZx3Ckg==',
|
|
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`,
|
|
},
|
|
},
|
|
})
|
|
await expect(badRequest.fetching()).rejects.toThrow()
|
|
|
|
const fetchResult = packageRequester.fetchPackageToStore({
|
|
force: false,
|
|
lockfileDir: temporaryDirectory(),
|
|
pkg: {
|
|
name: 'is-positive',
|
|
version: '1.0.0',
|
|
id: pkgId,
|
|
resolution: {
|
|
integrity: 'sha512-xxzPGZ4P2uN6rROUa5N9Z7zTX6ERuE0hs6GUOc/cKBLF2NqKc16UwqHMt3tFg4CO6EBTE5UecUasg+3jZx3Ckg==',
|
|
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`,
|
|
},
|
|
},
|
|
})
|
|
const { files } = await fetchResult.fetching()
|
|
expect(Array.from(files.filesMap.keys()).sort((a, b) => a.localeCompare(b))).toStrictEqual(['package.json', 'index.js', 'license', 'readme.md'].sort((a, b) => a.localeCompare(b)))
|
|
expect(files.resolvedFrom).toBe('remote')
|
|
|
|
await teardownMockAgent()
|
|
})
|
|
|
|
// This test was added to cover the issue described here: https://github.com/pnpm/supi/issues/65
|
|
test('always return a package manifest in the response', async () => {
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
expect(typeof requestPackage).toBe('function')
|
|
const projectDir = temporaryDirectory()
|
|
|
|
{
|
|
const pkgResponse = await requestPackage({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {
|
|
downloadPriority: 0,
|
|
lockfileDir: projectDir,
|
|
preferredVersions: {},
|
|
projectDir,
|
|
}) as PackageResponse & { body: { manifest: { name: string } } }
|
|
|
|
expect(pkgResponse.body).toBeTruthy()
|
|
expect(pkgResponse.body.manifest.name).toBeTruthy()
|
|
}
|
|
|
|
{
|
|
const pkgResponse = await requestPackage({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {
|
|
currentPkg: {
|
|
id: 'is-positive@1.0.0' as PkgResolutionId,
|
|
resolution: {
|
|
integrity: 'sha512-xxzPGZ4P2uN6rROUa5N9Z7zTX6ERuE0hs6GUOc/cKBLF2NqKc16UwqHMt3tFg4CO6EBTE5UecUasg+3jZx3Ckg==',
|
|
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`,
|
|
},
|
|
},
|
|
downloadPriority: 0,
|
|
lockfileDir: projectDir,
|
|
preferredVersions: {},
|
|
projectDir,
|
|
}) as PackageResponse & { fetching: () => Promise<PkgRequestFetchResult> }
|
|
|
|
expect(pkgResponse.body).toBeTruthy()
|
|
expect(
|
|
(await pkgResponse.fetching()).bundledManifest
|
|
).toEqual(
|
|
{
|
|
devDependencies: { ava: '^0.0.4' },
|
|
engines: { node: '>=0.10.0' },
|
|
name: 'is-positive',
|
|
version: '1.0.0',
|
|
}
|
|
)
|
|
}
|
|
})
|
|
|
|
// Covers https://github.com/pnpm/pnpm/issues/1293
|
|
test('fetchPackageToStore() fetch raw manifest of cached package', async () => {
|
|
const agent = await setupMockAgent()
|
|
const tarballContent = fs.readFileSync(IS_POSITIVE_TARBALL)
|
|
agent.get(registry)
|
|
.intercept({ path: '/is-positive/-/is-positive-1.0.0.tgz', method: 'GET' })
|
|
.reply(200, tarballContent, {
|
|
headers: { 'content-length': String(tarballContent.length) },
|
|
})
|
|
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
const packageRequester = createPackageRequester({
|
|
resolve,
|
|
fetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const pkgId = 'is-positive@1.0.0'
|
|
const resolution = {
|
|
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`,
|
|
}
|
|
const fetchResults = await Promise.all([
|
|
packageRequester.fetchPackageToStore({
|
|
fetchRawManifest: false,
|
|
force: false,
|
|
lockfileDir: temporaryDirectory(),
|
|
pkg: {
|
|
name: 'is-positive',
|
|
version: '1.0.0',
|
|
id: pkgId,
|
|
resolution,
|
|
},
|
|
}),
|
|
packageRequester.fetchPackageToStore({
|
|
fetchRawManifest: true,
|
|
force: false,
|
|
lockfileDir: temporaryDirectory(),
|
|
pkg: {
|
|
name: 'is-positive',
|
|
version: '1.0.0',
|
|
id: pkgId,
|
|
resolution,
|
|
},
|
|
}),
|
|
])
|
|
|
|
expect((await fetchResults[1].fetching()).bundledManifest).toBeTruthy()
|
|
await teardownMockAgent()
|
|
})
|
|
|
|
test('refetch package to store if it has been modified', async () => {
|
|
const storeDir = temporaryDirectory()
|
|
const lockfileDir = temporaryDirectory()
|
|
const localFetchers = createFetchersForStore(storeDir)
|
|
|
|
const pkgId = 'magic-hook@2.0.0'
|
|
const resolution = {
|
|
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/magic-hook/-/magic-hook-2.0.0.tgz`,
|
|
}
|
|
|
|
let indexJsFile!: string
|
|
{
|
|
const cafs = createCafsStore(storeDir)
|
|
const packageRequester = createPackageRequester({
|
|
resolve,
|
|
fetchers: localFetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const fetchResult = packageRequester.fetchPackageToStore({
|
|
fetchRawManifest: false,
|
|
force: false,
|
|
lockfileDir,
|
|
pkg: {
|
|
name: 'magic-hook',
|
|
version: '2.0.0',
|
|
id: pkgId,
|
|
resolution,
|
|
},
|
|
})
|
|
|
|
const { filesMap } = (await fetchResult.fetching()).files
|
|
indexJsFile = filesMap.get('index.js') as string
|
|
}
|
|
|
|
// We should restart the workers otherwise the locker cache will still try to read the file
|
|
// that will be removed from the store due to integrity change
|
|
await restartWorkerPool()
|
|
|
|
await delay(200)
|
|
// Adding some content to the file to change its integrity
|
|
fs.appendFileSync(indexJsFile, '// foobar')
|
|
|
|
const reporter = jest.fn()
|
|
streamParser.on('data', reporter)
|
|
|
|
{
|
|
const cafs = createCafsStore(storeDir)
|
|
const packageRequester = createPackageRequester({
|
|
resolve,
|
|
fetchers: localFetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const fetchResult = packageRequester.fetchPackageToStore({
|
|
fetchRawManifest: false,
|
|
force: false,
|
|
lockfileDir,
|
|
pkg: {
|
|
name: 'magic-hook',
|
|
version: '2.0.0',
|
|
id: pkgId,
|
|
resolution,
|
|
},
|
|
})
|
|
|
|
await fetchResult.fetching()
|
|
}
|
|
|
|
streamParser.removeListener('data', reporter)
|
|
|
|
expect(fs.readFileSync(indexJsFile, 'utf8')).not.toContain('// foobar')
|
|
|
|
expect(reporter).toHaveBeenCalledWith(expect.objectContaining({
|
|
level: 'warn',
|
|
message: `Refetching ${path.join(storeDir, depPathToFilename(pkgId, 120))} to store. It was either modified or had no integrity checksums`,
|
|
name: 'pnpm:package-requester',
|
|
prefix: lockfileDir,
|
|
}))
|
|
})
|
|
|
|
test('do not fetch an optional package that is not installable', async () => {
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
expect(typeof requestPackage).toBe('function')
|
|
|
|
const projectDir = temporaryDirectory()
|
|
const pkgResponse = await requestPackage({ alias: '@pnpm.e2e/not-compatible-with-any-os', optional: true, bareSpecifier: '*' }, {
|
|
downloadPriority: 0,
|
|
lockfileDir: projectDir,
|
|
preferredVersions: {},
|
|
projectDir,
|
|
})
|
|
|
|
expect(pkgResponse).toBeTruthy()
|
|
expect(pkgResponse.body).toBeTruthy()
|
|
|
|
expect(pkgResponse.body.isInstallable).toBe(false)
|
|
expect(pkgResponse.body.id).toBe('@pnpm.e2e/not-compatible-with-any-os@1.0.0')
|
|
|
|
expect(pkgResponse.fetching).toBeFalsy()
|
|
})
|
|
|
|
// Test case for https://github.com/pnpm/pnpm/issues/1866
|
|
test('fetch a git package without a package.json', async () => {
|
|
// a small Deno library with a 'denolib.json' instead of a 'package.json'
|
|
const repo = 'denolib/camelcase'
|
|
const commit = 'aeb6b15f9c9957c8fa56f9731e914c4d8a6d2f2b'
|
|
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
expect(typeof requestPackage).toBe('function')
|
|
const projectDir = temporaryDirectory()
|
|
|
|
{
|
|
const pkgResponse = await requestPackage({ alias: 'camelcase', bareSpecifier: `${repo}#${commit}` }, {
|
|
downloadPriority: 0,
|
|
lockfileDir: projectDir,
|
|
preferredVersions: {},
|
|
projectDir,
|
|
}) as PackageResponse & { body: { manifest: { name: string } } }
|
|
|
|
expect(pkgResponse.body).toBeTruthy()
|
|
expect(pkgResponse.body.manifest).toBeUndefined()
|
|
expect(pkgResponse.body.isInstallable).toBeFalsy()
|
|
expect(pkgResponse.body.id).toBe(`https://codeload.github.com/${repo}/tar.gz/${commit}`)
|
|
}
|
|
})
|
|
|
|
test('throw exception if the package data in the store differs from the expected data', async () => {
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
const localFetchers = createFetchersForStore(storeDir)
|
|
let pkgResponse!: PackageResponse
|
|
|
|
{
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers: localFetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const projectDir = temporaryDirectory()
|
|
pkgResponse = await requestPackage({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {
|
|
downloadPriority: 0,
|
|
lockfileDir: projectDir,
|
|
preferredVersions: {},
|
|
projectDir,
|
|
})
|
|
await pkgResponse.fetching!()
|
|
}
|
|
|
|
// Fail when the name of the package is different in the store
|
|
{
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers: localFetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
const { fetching } = requestPackage.fetchPackageToStore({
|
|
force: false,
|
|
lockfileDir: temporaryDirectory(),
|
|
pkg: {
|
|
name: 'is-negative',
|
|
version: '1.0.0',
|
|
id: pkgResponse.body.id,
|
|
resolution: pkgResponse.body.resolution,
|
|
},
|
|
})
|
|
await expect(fetching()).rejects.toThrow(/Package name or version mismatch found while reading/)
|
|
}
|
|
|
|
// Fail when the version of the package is different in the store
|
|
{
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers: localFetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
const { fetching } = requestPackage.fetchPackageToStore({
|
|
force: false,
|
|
lockfileDir: temporaryDirectory(),
|
|
pkg: {
|
|
name: 'is-negative',
|
|
version: '2.0.0',
|
|
id: pkgResponse.body.id,
|
|
resolution: pkgResponse.body.resolution,
|
|
},
|
|
})
|
|
await expect(fetching()).rejects.toThrow(/Package name or version mismatch found while reading/)
|
|
}
|
|
|
|
// Do not fail when the versions are the same but written in a different format (1.0.0 is the same as v1.0.0)
|
|
{
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers: localFetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
const { fetching } = requestPackage.fetchPackageToStore({
|
|
force: false,
|
|
lockfileDir: temporaryDirectory(),
|
|
pkg: {
|
|
name: 'is-positive',
|
|
version: 'v1.0.0',
|
|
id: pkgResponse.body.id,
|
|
resolution: pkgResponse.body.resolution,
|
|
},
|
|
})
|
|
await expect(fetching()).resolves.toStrictEqual(expect.anything())
|
|
}
|
|
|
|
{
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
const { fetching } = requestPackage.fetchPackageToStore({
|
|
force: false,
|
|
lockfileDir: temporaryDirectory(),
|
|
pkg: {
|
|
name: 'IS-positive',
|
|
version: 'v1.0.0',
|
|
id: pkgResponse.body.id,
|
|
resolution: pkgResponse.body.resolution,
|
|
},
|
|
})
|
|
await expect(fetching()).resolves.toStrictEqual(expect.anything())
|
|
}
|
|
})
|
|
|
|
test("don't throw an error if the package was updated, so the expectedPkg has a different version than the version in the store", async () => {
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
{
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const projectDir = temporaryDirectory()
|
|
const pkgResponse = await requestPackage({ alias: 'is-positive', bareSpecifier: '3.1.0' }, {
|
|
downloadPriority: 0,
|
|
lockfileDir: projectDir,
|
|
preferredVersions: {},
|
|
projectDir,
|
|
})
|
|
await pkgResponse.fetching!()
|
|
}
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
const projectDir = temporaryDirectory()
|
|
const pkgResponse = await requestPackage({ alias: 'is-positive', bareSpecifier: '3.1.0' }, {
|
|
downloadPriority: 0,
|
|
lockfileDir: temporaryDirectory(),
|
|
preferredVersions: {},
|
|
projectDir,
|
|
expectedPkg: {
|
|
name: 'is-positive',
|
|
version: '3.0.0',
|
|
},
|
|
})
|
|
await expect(pkgResponse.fetching!()).resolves.toStrictEqual(expect.anything())
|
|
})
|
|
|
|
test('the version in the bundled manifest should be normalized', async () => {
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const pkgResponse = await requestPackage({ alias: 'react-terminal', bareSpecifier: '1.2.1' }, {
|
|
downloadPriority: 0,
|
|
lockfileDir: temporaryDirectory(),
|
|
preferredVersions: {},
|
|
projectDir: temporaryDirectory(),
|
|
})
|
|
expect((await pkgResponse.fetching!()).bundledManifest?.version).toBe('1.2.1')
|
|
})
|
|
|
|
test('should skip store integrity check and resolve manifest if fetchRawManifest is true', async () => {
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
|
|
let pkgResponse!: PackageResponse
|
|
|
|
{
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: false,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const projectDir = temporaryDirectory()
|
|
|
|
pkgResponse = await requestPackage({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {
|
|
downloadPriority: 0,
|
|
lockfileDir: projectDir,
|
|
preferredVersions: {},
|
|
projectDir,
|
|
})
|
|
|
|
await pkgResponse.fetching!()
|
|
}
|
|
|
|
{
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: false,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const fetchResult = requestPackage.fetchPackageToStore({
|
|
force: false,
|
|
fetchRawManifest: true,
|
|
lockfileDir: temporaryDirectory(),
|
|
pkg: {
|
|
name: 'is-positive',
|
|
version: '1.0.0',
|
|
id: pkgResponse.body.id,
|
|
resolution: pkgResponse.body.resolution,
|
|
},
|
|
})
|
|
|
|
await fetchResult.fetching()
|
|
|
|
expect((await fetchResult.fetching!()).bundledManifest).toMatchObject({
|
|
name: 'is-positive',
|
|
version: '1.0.0',
|
|
devDependencies: {
|
|
ava: '^0.0.4',
|
|
},
|
|
})
|
|
}
|
|
})
|
|
|
|
test('HTTP tarball without integrity gets integrity computed during fetch', async () => {
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
const requestPackage = createPackageRequester({
|
|
resolve,
|
|
fetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const projectDir = temporaryDirectory()
|
|
// Request a package via HTTP tarball URL (simulated via the local registry)
|
|
const pkgResponse = await requestPackage(
|
|
{ alias: 'is-positive', bareSpecifier: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz` },
|
|
{
|
|
downloadPriority: 0,
|
|
lockfileDir: projectDir,
|
|
preferredVersions: {},
|
|
projectDir,
|
|
}
|
|
)
|
|
|
|
expect(pkgResponse.body).toBeTruthy()
|
|
// The resolution should now include an integrity hash computed during fetch
|
|
expect(pkgResponse.body.resolution).toHaveProperty('integrity')
|
|
expect((pkgResponse.body.resolution as { integrity?: string }).integrity).toMatch(/^sha512-/)
|
|
})
|
|
|
|
test('should pass optional flag to resolve function', async () => {
|
|
const storeDir = temporaryDirectory()
|
|
const cafs = createCafsStore(storeDir)
|
|
|
|
let capturedOptional: boolean | undefined
|
|
const mockResolve: typeof resolve = async (wantedDependency, _options) => {
|
|
capturedOptional = wantedDependency.optional
|
|
return resolve(wantedDependency, _options)
|
|
}
|
|
|
|
const requestPackage = createPackageRequester({
|
|
resolve: mockResolve,
|
|
fetchers,
|
|
cafs,
|
|
networkConcurrency: 1,
|
|
storeDir,
|
|
verifyStoreIntegrity: true,
|
|
virtualStoreDirMaxLength: 120,
|
|
})
|
|
|
|
const projectDir = temporaryDirectory()
|
|
|
|
await requestPackage(
|
|
{ alias: 'is-positive', bareSpecifier: '1.0.0', optional: true },
|
|
{
|
|
downloadPriority: 0,
|
|
lockfileDir: projectDir,
|
|
preferredVersions: {},
|
|
projectDir,
|
|
}
|
|
)
|
|
|
|
expect(capturedOptional).toBe(true)
|
|
|
|
await requestPackage(
|
|
{ alias: 'is-positive', bareSpecifier: '1.0.0', optional: false },
|
|
{
|
|
downloadPriority: 0,
|
|
lockfileDir: projectDir,
|
|
preferredVersions: {},
|
|
projectDir,
|
|
}
|
|
)
|
|
|
|
expect(capturedOptional).toBe(false)
|
|
|
|
await requestPackage(
|
|
{ alias: 'is-positive', bareSpecifier: '1.0.0' },
|
|
{
|
|
downloadPriority: 0,
|
|
lockfileDir: projectDir,
|
|
preferredVersions: {},
|
|
projectDir,
|
|
}
|
|
)
|
|
|
|
expect(capturedOptional).toBeUndefined()
|
|
})
|