@pnpm/fetching.tarball-fetcher 1102.0.4 → 1102.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/package.json +8 -8
- package/lib/errorTypes/BadTarballError.d.ts +0 -11
- package/lib/errorTypes/BadTarballError.js +0 -14
- package/lib/errorTypes/index.d.ts +0 -1
- package/lib/errorTypes/index.js +0 -2
- package/lib/gitHostedTarballFetcher.d.ts +0 -8
- package/lib/gitHostedTarballFetcher.js +0 -100
- package/lib/index.d.ts +0 -23
- package/lib/localTarballFetcher.d.ts +0 -3
- package/lib/localTarballFetcher.js +0 -29
- package/lib/remoteTarballFetcher.d.ts +0 -37
- package/lib/remoteTarballFetcher.js +0 -184
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/fetching.tarball-fetcher",
|
|
3
|
-
"version": "1102.0.
|
|
3
|
+
"version": "1102.0.5",
|
|
4
4
|
"description": "Fetcher for packages hosted as tarballs",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -29,13 +29,13 @@
|
|
|
29
29
|
"!*.map"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@pnpm/core-loggers": "1100.2.
|
|
32
|
+
"@pnpm/core-loggers": "1100.2.3",
|
|
33
33
|
"@pnpm/error": "1100.0.1",
|
|
34
|
-
"@pnpm/exec.prepare-package": "1100.0.
|
|
34
|
+
"@pnpm/exec.prepare-package": "1100.0.23",
|
|
35
35
|
"@pnpm/fetching.fetcher-base": "1100.2.2",
|
|
36
36
|
"@pnpm/fetching.types": "1100.0.2",
|
|
37
37
|
"@pnpm/fs.graceful-fs": "1100.1.0",
|
|
38
|
-
"@pnpm/fs.packlist": "1100.0.
|
|
38
|
+
"@pnpm/fs.packlist": "1100.0.3",
|
|
39
39
|
"@pnpm/store.index": "1100.2.1",
|
|
40
40
|
"@pnpm/types": "1101.4.0",
|
|
41
41
|
"@zkochan/retry": "^0.2.0",
|
|
@@ -45,15 +45,15 @@
|
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@pnpm/logger": "^1100.0.0",
|
|
48
|
-
"@pnpm/worker": "^1100.2.
|
|
48
|
+
"@pnpm/worker": "^1100.2.5"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@jest/globals": "30.4.1",
|
|
52
|
-
"@pnpm/fetching.tarball-fetcher": "1102.0.
|
|
52
|
+
"@pnpm/fetching.tarball-fetcher": "1102.0.5",
|
|
53
53
|
"@pnpm/logger": "1100.0.0",
|
|
54
|
-
"@pnpm/network.fetch": "1100.1.
|
|
54
|
+
"@pnpm/network.fetch": "1100.1.6",
|
|
55
55
|
"@pnpm/store.cafs-types": "1100.0.1",
|
|
56
|
-
"@pnpm/store.create-cafs-store": "1100.0.
|
|
56
|
+
"@pnpm/store.create-cafs-store": "1100.0.19",
|
|
57
57
|
"@pnpm/test-fixtures": "1100.0.0",
|
|
58
58
|
"@pnpm/types": "1101.4.0",
|
|
59
59
|
"@pnpm/util.lex-comparator": "^4.0.1",
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { PnpmError } from '@pnpm/error';
|
|
2
|
-
export declare class BadTarballError extends PnpmError {
|
|
3
|
-
expectedSize: number;
|
|
4
|
-
receivedSize: number;
|
|
5
|
-
constructor(opts: {
|
|
6
|
-
attempts?: number;
|
|
7
|
-
expectedSize: number;
|
|
8
|
-
receivedSize: number;
|
|
9
|
-
tarballUrl: string;
|
|
10
|
-
});
|
|
11
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { PnpmError } from '@pnpm/error';
|
|
2
|
-
export class BadTarballError extends PnpmError {
|
|
3
|
-
expectedSize;
|
|
4
|
-
receivedSize;
|
|
5
|
-
constructor(opts) {
|
|
6
|
-
const message = `Actual size (${opts.receivedSize}) of tarball (${opts.tarballUrl}) did not match the one specified in 'Content-Length' header (${opts.expectedSize})`;
|
|
7
|
-
super('BAD_TARBALL_SIZE', message, {
|
|
8
|
-
attempts: opts?.attempts,
|
|
9
|
-
});
|
|
10
|
-
this.expectedSize = opts.expectedSize;
|
|
11
|
-
this.receivedSize = opts.receivedSize;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
//# sourceMappingURL=BadTarballError.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { BadTarballError } from './BadTarballError.js';
|
package/lib/errorTypes/index.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { FetchFunction } from '@pnpm/fetching.fetcher-base';
|
|
2
|
-
import type { StoreIndex } from '@pnpm/store.index';
|
|
3
|
-
export interface CreateGitHostedTarballFetcher {
|
|
4
|
-
ignoreScripts?: boolean;
|
|
5
|
-
storeIndex: StoreIndex;
|
|
6
|
-
unsafePerm?: boolean;
|
|
7
|
-
}
|
|
8
|
-
export declare function createGitHostedTarballFetcher(fetchRemoteTarball: FetchFunction, fetcherOpts: CreateGitHostedTarballFetcher): FetchFunction;
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert';
|
|
2
|
-
import util from 'node:util';
|
|
3
|
-
import { preparePackage } from '@pnpm/exec.prepare-package';
|
|
4
|
-
import { packlist } from '@pnpm/fs.packlist';
|
|
5
|
-
import { globalWarn } from '@pnpm/logger';
|
|
6
|
-
import { addFilesFromDir } from '@pnpm/worker';
|
|
7
|
-
export function createGitHostedTarballFetcher(fetchRemoteTarball, fetcherOpts) {
|
|
8
|
-
const fetch = async (cafs, resolution, opts) => {
|
|
9
|
-
const rawFilesIndexFile = `${opts.filesIndexFile}\traw`;
|
|
10
|
-
const { filesMap, manifest, requiresBuild, integrity } = await fetchRemoteTarball(cafs, resolution, {
|
|
11
|
-
...opts,
|
|
12
|
-
filesIndexFile: rawFilesIndexFile,
|
|
13
|
-
});
|
|
14
|
-
// Flush any queued store index writes so that the raw files index entry
|
|
15
|
-
// written during tarball extraction is visible to subsequent reads.
|
|
16
|
-
fetcherOpts.storeIndex.flush();
|
|
17
|
-
try {
|
|
18
|
-
const prepareResult = await prepareGitHostedPkg(filesMap, cafs, rawFilesIndexFile, opts.filesIndexFile, fetcherOpts, opts, resolution);
|
|
19
|
-
if (prepareResult.ignoredBuild) {
|
|
20
|
-
globalWarn(`The git-hosted package fetched from "${resolution.tarball}" has to be built but the build scripts were ignored.`);
|
|
21
|
-
}
|
|
22
|
-
return {
|
|
23
|
-
filesMap: prepareResult.filesMap,
|
|
24
|
-
manifest: prepareResult.manifest ?? manifest,
|
|
25
|
-
requiresBuild,
|
|
26
|
-
// Propagate the raw tarball integrity so the lockfile pins it and
|
|
27
|
-
// future installs detect a tampered tarball from the git host.
|
|
28
|
-
integrity,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
catch (err) {
|
|
32
|
-
assert(util.types.isNativeError(err));
|
|
33
|
-
err.message = `Failed to prepare git-hosted package fetched from "${resolution.tarball}": ${err.message}`;
|
|
34
|
-
throw err;
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
return fetch;
|
|
38
|
-
}
|
|
39
|
-
async function prepareGitHostedPkg(filesMap, cafs, rawFilesIndexFile, filesIndexFile, opts, fetcherOpts, resolution) {
|
|
40
|
-
const tempLocation = await cafs.tempDir();
|
|
41
|
-
cafs.importPackage(tempLocation, {
|
|
42
|
-
filesResponse: {
|
|
43
|
-
filesMap,
|
|
44
|
-
resolvedFrom: 'remote',
|
|
45
|
-
requiresBuild: false,
|
|
46
|
-
},
|
|
47
|
-
force: true,
|
|
48
|
-
});
|
|
49
|
-
const { shouldBeBuilt, pkgDir } = await preparePackage({
|
|
50
|
-
...opts,
|
|
51
|
-
allowBuild: fetcherOpts.allowBuild,
|
|
52
|
-
pkgResolutionId: createGitHostedTarballPkgResolutionId(resolution),
|
|
53
|
-
}, tempLocation, resolution.path ?? '');
|
|
54
|
-
const files = await packlist(pkgDir);
|
|
55
|
-
const { storeIndex } = opts;
|
|
56
|
-
if (!resolution.path && files.length === filesMap.size) {
|
|
57
|
-
if (!shouldBeBuilt) {
|
|
58
|
-
const data = storeIndex.get(rawFilesIndexFile);
|
|
59
|
-
if (data) {
|
|
60
|
-
storeIndex.set(filesIndexFile, data);
|
|
61
|
-
storeIndex.delete(rawFilesIndexFile);
|
|
62
|
-
}
|
|
63
|
-
return {
|
|
64
|
-
filesMap,
|
|
65
|
-
ignoredBuild: false,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
if (opts.ignoreScripts) {
|
|
69
|
-
storeIndex.delete(rawFilesIndexFile);
|
|
70
|
-
return {
|
|
71
|
-
filesMap,
|
|
72
|
-
ignoredBuild: true,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
storeIndex.delete(rawFilesIndexFile);
|
|
77
|
-
// Important! We cannot remove the temp location at this stage.
|
|
78
|
-
// Even though we have the index of the package,
|
|
79
|
-
// the linking of files to the store is in progress.
|
|
80
|
-
return {
|
|
81
|
-
...await addFilesFromDir({
|
|
82
|
-
storeDir: cafs.storeDir,
|
|
83
|
-
storeIndex: opts.storeIndex,
|
|
84
|
-
dir: pkgDir,
|
|
85
|
-
files,
|
|
86
|
-
filesIndexFile,
|
|
87
|
-
pkg: fetcherOpts.pkg,
|
|
88
|
-
readManifest: fetcherOpts.readManifest,
|
|
89
|
-
}),
|
|
90
|
-
ignoredBuild: Boolean(opts.ignoreScripts),
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
function createGitHostedTarballPkgResolutionId(resolution) {
|
|
94
|
-
let pkgResolutionId = resolution.tarball;
|
|
95
|
-
if (resolution.path) {
|
|
96
|
-
pkgResolutionId += `#path:${resolution.path}`;
|
|
97
|
-
}
|
|
98
|
-
return pkgResolutionId;
|
|
99
|
-
}
|
|
100
|
-
//# sourceMappingURL=gitHostedTarballFetcher.js.map
|
package/lib/index.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { FetchFunction } from '@pnpm/fetching.fetcher-base';
|
|
2
|
-
import type { FetchFromRegistry, GetAuthHeader, RetryTimeoutOptions } from '@pnpm/fetching.types';
|
|
3
|
-
import type { StoreIndex } from '@pnpm/store.index';
|
|
4
|
-
import { TarballIntegrityError } from '@pnpm/worker';
|
|
5
|
-
import { type CreateDownloaderOptions } from './remoteTarballFetcher.js';
|
|
6
|
-
export { BadTarballError } from './errorTypes/index.js';
|
|
7
|
-
export { TarballIntegrityError };
|
|
8
|
-
export { createGitHostedTarballFetcher } from './gitHostedTarballFetcher.js';
|
|
9
|
-
export { createLocalTarballFetcher } from './localTarballFetcher.js';
|
|
10
|
-
export { createDownloader, type CreateDownloaderOptions, type DownloadFunction } from './remoteTarballFetcher.js';
|
|
11
|
-
export interface TarballFetchers {
|
|
12
|
-
localTarball: FetchFunction;
|
|
13
|
-
remoteTarball: FetchFunction;
|
|
14
|
-
gitHostedTarball: FetchFunction;
|
|
15
|
-
}
|
|
16
|
-
export declare function createTarballFetcher(fetchFromRegistry: FetchFromRegistry, getAuthHeader: GetAuthHeader, opts: {
|
|
17
|
-
unsafePerm?: boolean;
|
|
18
|
-
ignoreScripts?: boolean;
|
|
19
|
-
storeIndex: StoreIndex;
|
|
20
|
-
timeout?: number;
|
|
21
|
-
retry?: RetryTimeoutOptions;
|
|
22
|
-
offline?: boolean;
|
|
23
|
-
} & Pick<CreateDownloaderOptions, 'fetchMinSpeedKiBps'>): TarballFetchers;
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import gfs from '@pnpm/fs.graceful-fs';
|
|
3
|
-
import { addFilesFromTarball } from '@pnpm/worker';
|
|
4
|
-
const isAbsolutePath = /^\/|^[A-Z]:/i;
|
|
5
|
-
export function createLocalTarballFetcher(storeIndex) {
|
|
6
|
-
const fetch = (cafs, resolution, opts) => {
|
|
7
|
-
const tarball = resolvePath(opts.lockfileDir, resolution.tarball.slice(5));
|
|
8
|
-
const buffer = gfs.readFileSync(tarball);
|
|
9
|
-
return addFilesFromTarball({
|
|
10
|
-
storeDir: cafs.storeDir,
|
|
11
|
-
storeIndex,
|
|
12
|
-
buffer,
|
|
13
|
-
filesIndexFile: opts.filesIndexFile,
|
|
14
|
-
integrity: resolution.integrity,
|
|
15
|
-
readManifest: opts.readManifest,
|
|
16
|
-
url: tarball,
|
|
17
|
-
pkg: opts.pkg,
|
|
18
|
-
appendManifest: opts.appendManifest,
|
|
19
|
-
ignoreFilePattern: opts.ignoreFilePattern,
|
|
20
|
-
});
|
|
21
|
-
};
|
|
22
|
-
return fetch;
|
|
23
|
-
}
|
|
24
|
-
function resolvePath(where, spec) {
|
|
25
|
-
if (isAbsolutePath.test(spec))
|
|
26
|
-
return spec;
|
|
27
|
-
return path.resolve(where, spec);
|
|
28
|
-
}
|
|
29
|
-
//# sourceMappingURL=localTarballFetcher.js.map
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import type { IncomingMessage } from 'node:http';
|
|
2
|
-
import type { FetchOptions, FetchResult } from '@pnpm/fetching.fetcher-base';
|
|
3
|
-
import type { FetchFromRegistry, GetAuthHeader } from '@pnpm/fetching.types';
|
|
4
|
-
import type { Cafs } from '@pnpm/store.cafs-types';
|
|
5
|
-
import type { StoreIndex } from '@pnpm/store.index';
|
|
6
|
-
export interface HttpResponse {
|
|
7
|
-
body: string;
|
|
8
|
-
}
|
|
9
|
-
export type DownloadOptions = {
|
|
10
|
-
getAuthHeaderByURI: GetAuthHeader;
|
|
11
|
-
cafs: Cafs;
|
|
12
|
-
registry?: string;
|
|
13
|
-
onStart?: (totalSize: number | null, attempt: number) => void;
|
|
14
|
-
onProgress?: (downloaded: number) => void;
|
|
15
|
-
integrity?: string;
|
|
16
|
-
storeIndex: StoreIndex;
|
|
17
|
-
pkg?: FetchOptions['pkg'];
|
|
18
|
-
} & Pick<FetchOptions, 'appendManifest' | 'readManifest' | 'filesIndexFile' | 'ignoreFilePattern'>;
|
|
19
|
-
export type DownloadFunction = (url: string, opts: DownloadOptions) => Promise<FetchResult>;
|
|
20
|
-
export interface NpmRegistryClient {
|
|
21
|
-
get: (url: string, getOpts: object, cb: (err: Error, data: object, raw: object, res: HttpResponse) => void) => void;
|
|
22
|
-
fetch: (url: string, opts: {
|
|
23
|
-
auth?: object;
|
|
24
|
-
}, cb: (err: Error, res: IncomingMessage) => void) => void;
|
|
25
|
-
}
|
|
26
|
-
export interface CreateDownloaderOptions {
|
|
27
|
-
retry?: {
|
|
28
|
-
retries?: number;
|
|
29
|
-
factor?: number;
|
|
30
|
-
minTimeout?: number;
|
|
31
|
-
maxTimeout?: number;
|
|
32
|
-
randomize?: boolean;
|
|
33
|
-
};
|
|
34
|
-
timeout?: number;
|
|
35
|
-
fetchMinSpeedKiBps?: number;
|
|
36
|
-
}
|
|
37
|
-
export declare function createDownloader(fetchFromRegistry: FetchFromRegistry, gotOpts: CreateDownloaderOptions): DownloadFunction;
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import util from 'node:util';
|
|
2
|
-
import { requestRetryLogger } from '@pnpm/core-loggers';
|
|
3
|
-
import { FetchError } from '@pnpm/error';
|
|
4
|
-
import { globalWarn } from '@pnpm/logger';
|
|
5
|
-
import { addFilesFromTarball } from '@pnpm/worker';
|
|
6
|
-
import * as retry from '@zkochan/retry';
|
|
7
|
-
import throttle from 'lodash.throttle';
|
|
8
|
-
import { BadTarballError } from './errorTypes/index.js';
|
|
9
|
-
const BIG_TARBALL_SIZE = 1024 * 1024 * 5; // 5 MB
|
|
10
|
-
export function createDownloader(fetchFromRegistry, gotOpts) {
|
|
11
|
-
const retryOpts = {
|
|
12
|
-
factor: 10,
|
|
13
|
-
maxTimeout: 6e4, // 1 minute
|
|
14
|
-
minTimeout: 1e4, // 10 seconds
|
|
15
|
-
retries: 2,
|
|
16
|
-
...gotOpts.retry,
|
|
17
|
-
};
|
|
18
|
-
const fetchMinSpeedKiBps = gotOpts.fetchMinSpeedKiBps ?? 50; // 50 KiB/s
|
|
19
|
-
return async function download(url, opts) {
|
|
20
|
-
const authHeaderValue = opts.getAuthHeaderByURI(url, { pkgName: opts.pkg?.name });
|
|
21
|
-
const op = retry.operation(retryOpts);
|
|
22
|
-
return new Promise((resolve, reject) => {
|
|
23
|
-
op.attempt(async (attempt) => {
|
|
24
|
-
try {
|
|
25
|
-
resolve(await fetch(attempt));
|
|
26
|
-
}
|
|
27
|
-
catch (error) { // eslint-disable-line
|
|
28
|
-
if (error.response?.status === 401 ||
|
|
29
|
-
error.response?.status === 403 ||
|
|
30
|
-
error.response?.status === 404 ||
|
|
31
|
-
error.code === 'ERR_PNPM_PREPARE_PKG_FAILURE') {
|
|
32
|
-
reject(error);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
const timeout = op.retry(error);
|
|
36
|
-
if (timeout === false) {
|
|
37
|
-
reject(op.mainError());
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
// Extract error properties into a plain object because Error properties
|
|
41
|
-
// are non-enumerable and don't serialize well through the logging system
|
|
42
|
-
const errorInfo = {
|
|
43
|
-
name: error.name,
|
|
44
|
-
message: error.message,
|
|
45
|
-
code: error.code,
|
|
46
|
-
errno: error.errno,
|
|
47
|
-
// For HTTP errors from our ResponseError class
|
|
48
|
-
status: error.status,
|
|
49
|
-
statusCode: error.statusCode,
|
|
50
|
-
// undici wraps the actual network error in a cause property
|
|
51
|
-
cause: error.cause ? {
|
|
52
|
-
code: error.cause.code,
|
|
53
|
-
errno: error.cause.errno,
|
|
54
|
-
} : undefined,
|
|
55
|
-
};
|
|
56
|
-
requestRetryLogger.debug({
|
|
57
|
-
attempt,
|
|
58
|
-
error: errorInfo,
|
|
59
|
-
maxRetries: retryOpts.retries,
|
|
60
|
-
method: 'GET',
|
|
61
|
-
timeout,
|
|
62
|
-
url,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
async function fetch(currentAttempt) {
|
|
68
|
-
let data;
|
|
69
|
-
try {
|
|
70
|
-
const res = await fetchFromRegistry(url, {
|
|
71
|
-
authHeaderValue,
|
|
72
|
-
// Tarballs are already compressed; ask the server not to apply an additional
|
|
73
|
-
// Content-Encoding so Content-Length matches the body we receive and we don't
|
|
74
|
-
// waste CPU on round-trip re-compression. See https://github.com/pnpm/pnpm/issues/11506
|
|
75
|
-
headers: { 'accept-encoding': 'identity' },
|
|
76
|
-
// The fetch library can retry requests on bad HTTP responses.
|
|
77
|
-
// However, it is not enough to retry on bad HTTP responses only.
|
|
78
|
-
// Requests should also be retried when the tarball's integrity check fails.
|
|
79
|
-
// Hence, we tell fetch to not retry,
|
|
80
|
-
// and we perform the retries from this function instead.
|
|
81
|
-
retry: { retries: 0 },
|
|
82
|
-
timeout: gotOpts.timeout,
|
|
83
|
-
});
|
|
84
|
-
if (res.status !== 200) {
|
|
85
|
-
throw new FetchError({ url, authHeaderValue }, res);
|
|
86
|
-
}
|
|
87
|
-
// When Content-Encoding is present, Content-Length refers to the encoded form
|
|
88
|
-
// of the data, not the decoded bytes that the fetch implementation yields.
|
|
89
|
-
// See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Encoding
|
|
90
|
-
const isEncoded = isContentEncoded(res.headers.get('content-encoding'));
|
|
91
|
-
const contentLength = !isEncoded && res.headers.has('content-length') && res.headers.get('content-length');
|
|
92
|
-
const parsedLength = typeof contentLength === 'string' ? parseInt(contentLength, 10) : NaN;
|
|
93
|
-
const size = Number.isFinite(parsedLength) && parsedLength >= 0 ? parsedLength : null;
|
|
94
|
-
if (opts.onStart != null) {
|
|
95
|
-
opts.onStart(size, currentAttempt);
|
|
96
|
-
}
|
|
97
|
-
// In order to reduce the amount of logs, we only report the download progress of big tarballs
|
|
98
|
-
const onProgress = (size != null && size >= BIG_TARBALL_SIZE && opts.onProgress)
|
|
99
|
-
? throttle(opts.onProgress, 500)
|
|
100
|
-
: undefined;
|
|
101
|
-
const startTime = Date.now();
|
|
102
|
-
let downloaded = 0;
|
|
103
|
-
if (size !== null) {
|
|
104
|
-
// Known size: pre-allocate and copy directly (avoids intermediate array + second copy pass)
|
|
105
|
-
data = Buffer.from(new SharedArrayBuffer(size));
|
|
106
|
-
for await (const chunk of res.body) {
|
|
107
|
-
const c = chunk;
|
|
108
|
-
const nextDownloaded = downloaded + c.byteLength;
|
|
109
|
-
if (nextDownloaded > size) {
|
|
110
|
-
throw new BadTarballError({
|
|
111
|
-
expectedSize: size,
|
|
112
|
-
receivedSize: nextDownloaded,
|
|
113
|
-
tarballUrl: url,
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
data.set(c, downloaded);
|
|
117
|
-
downloaded = nextDownloaded;
|
|
118
|
-
onProgress?.(downloaded);
|
|
119
|
-
}
|
|
120
|
-
if (size !== downloaded) {
|
|
121
|
-
throw new BadTarballError({
|
|
122
|
-
expectedSize: size,
|
|
123
|
-
receivedSize: downloaded,
|
|
124
|
-
tarballUrl: url,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
const chunks = [];
|
|
130
|
-
for await (const chunk of res.body) {
|
|
131
|
-
const c = chunk;
|
|
132
|
-
chunks.push(c);
|
|
133
|
-
downloaded += c.byteLength;
|
|
134
|
-
onProgress?.(downloaded);
|
|
135
|
-
}
|
|
136
|
-
data = Buffer.from(new SharedArrayBuffer(downloaded));
|
|
137
|
-
let offset = 0;
|
|
138
|
-
for (const chunk of chunks) {
|
|
139
|
-
data.set(chunk, offset);
|
|
140
|
-
offset += chunk.byteLength;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
const elapsedSec = (Date.now() - startTime) / 1000;
|
|
144
|
-
const avgKiBps = Math.floor((downloaded / elapsedSec) / 1024);
|
|
145
|
-
if (downloaded > 0 && elapsedSec > 1 && avgKiBps < fetchMinSpeedKiBps) {
|
|
146
|
-
const sizeKb = Math.floor(downloaded / 1024);
|
|
147
|
-
globalWarn(`Tarball download average speed ${avgKiBps} KiB/s (size ${sizeKb} KiB) is below ${fetchMinSpeedKiBps} KiB/s: ${url} (GET)`);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
catch (err) {
|
|
151
|
-
const error = util.types.isNativeError(err) ? err : new Error(String(err), { cause: err });
|
|
152
|
-
Object.assign(error, {
|
|
153
|
-
attempts: currentAttempt,
|
|
154
|
-
resource: url,
|
|
155
|
-
});
|
|
156
|
-
throw error;
|
|
157
|
-
}
|
|
158
|
-
return addFilesFromTarball({
|
|
159
|
-
buffer: data,
|
|
160
|
-
storeDir: opts.cafs.storeDir,
|
|
161
|
-
storeIndex: opts.storeIndex,
|
|
162
|
-
readManifest: opts.readManifest,
|
|
163
|
-
integrity: opts.integrity,
|
|
164
|
-
filesIndexFile: opts.filesIndexFile,
|
|
165
|
-
url,
|
|
166
|
-
pkg: opts.pkg,
|
|
167
|
-
appendManifest: opts.appendManifest,
|
|
168
|
-
ignoreFilePattern: opts.ignoreFilePattern,
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
// Per RFC 9110 §8.4, Content-Encoding is a comma-separated list of codings.
|
|
174
|
-
// The response is encoded unless every coding is `identity` (case-insensitive,
|
|
175
|
-
// surrounding whitespace ignored).
|
|
176
|
-
function isContentEncoded(header) {
|
|
177
|
-
if (header == null)
|
|
178
|
-
return false;
|
|
179
|
-
return header
|
|
180
|
-
.split(',')
|
|
181
|
-
.map(coding => coding.trim().toLowerCase())
|
|
182
|
-
.some(coding => coding !== '' && coding !== 'identity');
|
|
183
|
-
}
|
|
184
|
-
//# sourceMappingURL=remoteTarballFetcher.js.map
|