@verdaccio/e2e-cli 2.10.4 → 3.1.0
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/build/adapters/index.d.ts +0 -1
- package/build/cjs/adapters/index.cjs +0 -2
- package/build/cjs/adapters/npm.cjs +10 -0
- package/build/cjs/adapters/npm.cjs.map +1 -1
- package/build/cjs/adapters/pnpm.cjs +11 -1
- package/build/cjs/adapters/pnpm.cjs.map +1 -1
- package/build/cjs/index.cjs +28 -8
- package/build/cjs/index.cjs.map +1 -1
- package/build/cjs/runner.cjs +1 -1
- package/build/cjs/runner.cjs.map +1 -1
- package/build/cjs/scenarios/index.cjs +16 -1
- package/build/cjs/scenarios/index.cjs.map +1 -1
- package/build/cjs/scenarios/install-multiple-deps.cjs +25 -23
- package/build/cjs/scenarios/install-multiple-deps.cjs.map +1 -1
- package/build/cjs/scenarios/metadata.cjs +121 -0
- package/build/cjs/scenarios/metadata.cjs.map +1 -0
- package/build/cjs/scenarios/minimum-release-age.cjs +6 -8
- package/build/cjs/scenarios/minimum-release-age.cjs.map +1 -1
- package/build/cjs/scenarios/search.cjs +136 -0
- package/build/cjs/scenarios/search.cjs.map +1 -0
- package/build/cjs/scenarios/tarballs.cjs +147 -0
- package/build/cjs/scenarios/tarballs.cjs.map +1 -0
- package/build/cjs/scenarios/uplink-failure.cjs +141 -0
- package/build/cjs/scenarios/uplink-failure.cjs.map +1 -0
- package/build/cjs/tests/audit.cjs +15 -2
- package/build/cjs/tests/audit.cjs.map +1 -1
- package/build/cjs/tests/ci.cjs +4 -6
- package/build/cjs/tests/ci.cjs.map +1 -1
- package/build/cjs/tests/index.cjs +7 -3
- package/build/cjs/tests/index.cjs.map +1 -1
- package/build/cjs/tests/info.cjs +10 -21
- package/build/cjs/tests/info.cjs.map +1 -1
- package/build/cjs/tests/install.cjs +4 -1
- package/build/cjs/tests/install.cjs.map +1 -1
- package/build/cjs/tests/ping.cjs +3 -7
- package/build/cjs/tests/ping.cjs.map +1 -1
- package/build/cjs/tests/publish.cjs +0 -1
- package/build/cjs/tests/publish.cjs.map +1 -1
- package/build/cjs/tests/search.cjs +9 -5
- package/build/cjs/tests/search.cjs.map +1 -1
- package/build/cjs/utils/e2e-config.cjs +59 -0
- package/build/cjs/utils/e2e-config.cjs.map +1 -0
- package/build/cjs/utils/http-client.cjs +111 -0
- package/build/cjs/utils/http-client.cjs.map +1 -0
- package/build/cjs/utils/mock-uplink.cjs +196 -0
- package/build/cjs/utils/mock-uplink.cjs.map +1 -0
- package/build/cjs/utils/process.cjs +11 -0
- package/build/cjs/utils/process.cjs.map +1 -1
- package/build/cjs/utils/project.cjs +1 -1
- package/build/cjs/utils/project.cjs.map +1 -1
- package/build/cjs/utils/publish.cjs +117 -0
- package/build/cjs/utils/publish.cjs.map +1 -0
- package/build/esm/adapters/index.js +1 -2
- package/build/esm/adapters/npm.js +10 -0
- package/build/esm/adapters/npm.js.map +1 -1
- package/build/esm/adapters/pnpm.js +11 -1
- package/build/esm/adapters/pnpm.js.map +1 -1
- package/build/esm/index.js +29 -8
- package/build/esm/index.js.map +1 -1
- package/build/esm/runner.js +1 -1
- package/build/esm/runner.js.map +1 -1
- package/build/esm/scenarios/index.js +13 -2
- package/build/esm/scenarios/index.js.map +1 -1
- package/build/esm/scenarios/install-multiple-deps.js +25 -22
- package/build/esm/scenarios/install-multiple-deps.js.map +1 -1
- package/build/esm/scenarios/metadata.js +119 -0
- package/build/esm/scenarios/metadata.js.map +1 -0
- package/build/esm/scenarios/minimum-release-age.js +6 -7
- package/build/esm/scenarios/minimum-release-age.js.map +1 -1
- package/build/esm/scenarios/search.js +134 -0
- package/build/esm/scenarios/search.js.map +1 -0
- package/build/esm/scenarios/tarballs.js +145 -0
- package/build/esm/scenarios/tarballs.js.map +1 -0
- package/build/esm/scenarios/uplink-failure.js +139 -0
- package/build/esm/scenarios/uplink-failure.js.map +1 -0
- package/build/esm/tests/audit.js +15 -2
- package/build/esm/tests/audit.js.map +1 -1
- package/build/esm/tests/ci.js +4 -6
- package/build/esm/tests/ci.js.map +1 -1
- package/build/esm/tests/index.js +4 -0
- package/build/esm/tests/index.js.map +1 -1
- package/build/esm/tests/info.js +10 -21
- package/build/esm/tests/info.js.map +1 -1
- package/build/esm/tests/install.js +4 -1
- package/build/esm/tests/install.js.map +1 -1
- package/build/esm/tests/ping.js +3 -7
- package/build/esm/tests/ping.js.map +1 -1
- package/build/esm/tests/publish.js +0 -1
- package/build/esm/tests/publish.js.map +1 -1
- package/build/esm/tests/search.js +9 -5
- package/build/esm/tests/search.js.map +1 -1
- package/build/esm/utils/e2e-config.js +58 -0
- package/build/esm/utils/e2e-config.js.map +1 -0
- package/build/esm/utils/http-client.js +105 -0
- package/build/esm/utils/http-client.js.map +1 -0
- package/build/esm/utils/mock-uplink.js +194 -0
- package/build/esm/utils/mock-uplink.js.map +1 -0
- package/build/esm/utils/process.js +11 -1
- package/build/esm/utils/process.js.map +1 -1
- package/build/esm/utils/project.js +1 -1
- package/build/esm/utils/project.js.map +1 -1
- package/build/esm/utils/publish.js +113 -0
- package/build/esm/utils/publish.js.map +1 -0
- package/build/index.d.ts +1 -1
- package/build/scenarios/index.d.ts +5 -1
- package/build/scenarios/metadata.d.ts +2 -0
- package/build/scenarios/search.d.ts +2 -0
- package/build/scenarios/tarballs.d.ts +2 -0
- package/build/scenarios/uplink-failure.d.ts +2 -0
- package/build/tests/index.d.ts +2 -2
- package/build/types.d.ts +12 -2
- package/build/utils/e2e-config.d.ts +21 -0
- package/build/utils/http-client.d.ts +49 -0
- package/build/utils/mock-uplink.d.ts +47 -0
- package/build/utils/process.d.ts +7 -0
- package/build/utils/publish.d.ts +17 -0
- package/package.json +1 -1
- package/build/adapters/yarn-classic.d.ts +0 -2
- package/build/cjs/adapters/yarn-classic.cjs +0 -98
- package/build/cjs/adapters/yarn-classic.cjs.map +0 -1
- package/build/esm/adapters/yarn-classic.js +0 -96
- package/build/esm/adapters/yarn-classic.js.map +0 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
let debug = require("debug");
|
|
3
|
+
debug = require_runtime.__toESM(debug, 1);
|
|
4
|
+
let crypto = require("crypto");
|
|
5
|
+
let http = require("http");
|
|
6
|
+
//#region src/utils/http-client.ts
|
|
7
|
+
var debug$1 = (0, debug.default)("verdaccio:e2e-cli:http-client");
|
|
8
|
+
var ABBREVIATED_ACCEPT = "application/vnd.npm.install-v1+json";
|
|
9
|
+
/**
|
|
10
|
+
* Registry URL for a package name. Scoped names are encoded with the same
|
|
11
|
+
* `%2f` form npm clients use: `@scope%2fname`.
|
|
12
|
+
*/
|
|
13
|
+
function packumentUrl(registryUrl, pkgName) {
|
|
14
|
+
const encoded = pkgName.startsWith("@") ? pkgName.replaceAll("/", "%2f") : pkgName;
|
|
15
|
+
return `${registryUrl.replace(/\/$/, "")}/${encoded}`;
|
|
16
|
+
}
|
|
17
|
+
async function fetchPackument(registryUrl, pkgName, options = {}) {
|
|
18
|
+
const url = packumentUrl(registryUrl, pkgName);
|
|
19
|
+
const headers = {};
|
|
20
|
+
if (options.abbreviated) headers["Accept"] = ABBREVIATED_ACCEPT;
|
|
21
|
+
if (options.etag) headers["If-None-Match"] = options.etag;
|
|
22
|
+
if (options.token) headers["Authorization"] = `Bearer ${options.token}`;
|
|
23
|
+
debug$1("GET %s %o", url, headers);
|
|
24
|
+
if (options.etag) return new Promise((resolve, reject) => {
|
|
25
|
+
(0, http.get)(url, { headers }, (response) => {
|
|
26
|
+
let text = "";
|
|
27
|
+
response.setEncoding("utf8");
|
|
28
|
+
response.on("data", (chunk) => text += chunk);
|
|
29
|
+
response.on("end", () => {
|
|
30
|
+
let body;
|
|
31
|
+
try {
|
|
32
|
+
body = JSON.parse(text);
|
|
33
|
+
} catch {
|
|
34
|
+
body = void 0;
|
|
35
|
+
}
|
|
36
|
+
const responseHeaders = new Headers();
|
|
37
|
+
for (const [name, value] of Object.entries(response.headers)) if (value !== void 0) responseHeaders.set(name, Array.isArray(value) ? value.join(", ") : value);
|
|
38
|
+
resolve({
|
|
39
|
+
status: response.statusCode || 0,
|
|
40
|
+
headers: responseHeaders,
|
|
41
|
+
body
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}).on("error", reject);
|
|
45
|
+
});
|
|
46
|
+
const response = await fetch(url, { headers });
|
|
47
|
+
let body;
|
|
48
|
+
if (response.status !== 304) {
|
|
49
|
+
const text = await response.text();
|
|
50
|
+
try {
|
|
51
|
+
body = JSON.parse(text);
|
|
52
|
+
} catch {
|
|
53
|
+
body = void 0;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
status: response.status,
|
|
58
|
+
headers: response.headers,
|
|
59
|
+
body
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Download a tarball fully, computing sha1/sha512 integrity on the fly.
|
|
64
|
+
* Rejects with the underlying error when aborted or the connection drops.
|
|
65
|
+
*/
|
|
66
|
+
async function downloadTarball(url, options = {}) {
|
|
67
|
+
const headers = {};
|
|
68
|
+
if (options.token) headers["Authorization"] = `Bearer ${options.token}`;
|
|
69
|
+
debug$1("GET (tarball) %s", url);
|
|
70
|
+
const response = await fetch(url, {
|
|
71
|
+
headers,
|
|
72
|
+
signal: options.signal
|
|
73
|
+
});
|
|
74
|
+
const sha1 = (0, crypto.createHash)("sha1");
|
|
75
|
+
const sha512 = (0, crypto.createHash)("sha512");
|
|
76
|
+
let bytes = 0;
|
|
77
|
+
let first = true;
|
|
78
|
+
if (response.body) for await (const chunk of response.body) {
|
|
79
|
+
const buf = Buffer.from(chunk);
|
|
80
|
+
bytes += buf.length;
|
|
81
|
+
sha1.update(buf);
|
|
82
|
+
sha512.update(buf);
|
|
83
|
+
if (first) {
|
|
84
|
+
first = false;
|
|
85
|
+
options.onFirstChunk?.();
|
|
86
|
+
}
|
|
87
|
+
options.onChunk?.(bytes);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
status: response.status,
|
|
91
|
+
headers: response.headers,
|
|
92
|
+
bytes,
|
|
93
|
+
sha1: sha1.digest("hex"),
|
|
94
|
+
integrity: `sha512-${sha512.digest("base64")}`
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/** Compute npm-style dist values (shasum/integrity) for a buffer. */
|
|
98
|
+
function computeDist(buffer) {
|
|
99
|
+
return {
|
|
100
|
+
shasum: (0, crypto.createHash)("sha1").update(buffer).digest("hex"),
|
|
101
|
+
integrity: `sha512-${(0, crypto.createHash)("sha512").update(buffer).digest("base64")}`
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
exports.ABBREVIATED_ACCEPT = ABBREVIATED_ACCEPT;
|
|
106
|
+
exports.computeDist = computeDist;
|
|
107
|
+
exports.downloadTarball = downloadTarball;
|
|
108
|
+
exports.fetchPackument = fetchPackument;
|
|
109
|
+
exports.packumentUrl = packumentUrl;
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=http-client.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-client.cjs","names":[],"sources":["../../../src/utils/http-client.ts"],"sourcesContent":["import { createHash } from 'crypto';\nimport buildDebug from 'debug';\nimport { get } from 'http';\n\nconst debug = buildDebug('verdaccio:e2e-cli:http-client');\n\nexport type PackumentResponse = {\n status: number;\n headers: Headers;\n /** Parsed JSON body, or undefined when the response is not JSON (e.g. 304) */\n body?: any;\n};\n\nexport type TarballDownload = {\n status: number;\n headers: Headers;\n /** Total bytes received */\n bytes: number;\n /** hex-encoded sha1 of the received bytes (matches dist.shasum) */\n sha1: string;\n /** `sha512-<base64>` of the received bytes (matches dist.integrity) */\n integrity: string;\n};\n\nexport type FetchPackumentOptions = {\n /** Request the abbreviated (install) metadata format */\n abbreviated?: boolean;\n /** Send If-None-Match with this value */\n etag?: string;\n /** Bearer token */\n token?: string;\n};\n\nexport const ABBREVIATED_ACCEPT = 'application/vnd.npm.install-v1+json';\n\n/**\n * Registry URL for a package name. Scoped names are encoded with the same\n * `%2f` form npm clients use: `@scope%2fname`.\n */\nexport function packumentUrl(registryUrl: string, pkgName: string): string {\n const encoded = pkgName.startsWith('@') ? pkgName.replaceAll('/', '%2f') : pkgName;\n return `${registryUrl.replace(/\\/$/, '')}/${encoded}`;\n}\n\nexport async function fetchPackument(\n registryUrl: string,\n pkgName: string,\n options: FetchPackumentOptions = {}\n): Promise<PackumentResponse> {\n const url = packumentUrl(registryUrl, pkgName);\n const headers: Record<string, string> = {};\n if (options.abbreviated) {\n headers['Accept'] = ABBREVIATED_ACCEPT;\n }\n if (options.etag) {\n headers['If-None-Match'] = options.etag;\n }\n if (options.token) {\n headers['Authorization'] = `Bearer ${options.token}`;\n }\n debug('GET %s %o', url, headers);\n\n // Conditional requests must bypass fetch(): undici appends\n // `cache-control: no-cache` whenever If-None-Match is present, which\n // (correctly, per HTTP semantics) disables the server's 304 revalidation.\n if (options.etag) {\n return new Promise((resolve, reject) => {\n get(url, { headers }, (response) => {\n let text = '';\n response.setEncoding('utf8');\n response.on('data', (chunk) => (text += chunk));\n response.on('end', () => {\n let body: any;\n try {\n body = JSON.parse(text);\n } catch {\n body = undefined;\n }\n const responseHeaders = new Headers();\n for (const [name, value] of Object.entries(response.headers)) {\n if (value !== undefined) {\n responseHeaders.set(name, Array.isArray(value) ? value.join(', ') : value);\n }\n }\n resolve({ status: response.statusCode || 0, headers: responseHeaders, body });\n });\n }).on('error', reject);\n });\n }\n\n const response = await fetch(url, { headers });\n let body: any;\n if (response.status !== 304) {\n const text = await response.text();\n try {\n body = JSON.parse(text);\n } catch {\n body = undefined;\n }\n }\n return { status: response.status, headers: response.headers, body };\n}\n\nexport type DownloadTarballOptions = {\n signal?: AbortSignal;\n token?: string;\n /** Called after the first chunk of the body arrives (useful to abort mid-stream) */\n onFirstChunk?: () => void;\n /** Called after every chunk with the total bytes received so far (useful to abort at a threshold) */\n onChunk?: (bytesReceived: number) => void;\n};\n\n/**\n * Download a tarball fully, computing sha1/sha512 integrity on the fly.\n * Rejects with the underlying error when aborted or the connection drops.\n */\nexport async function downloadTarball(\n url: string,\n options: DownloadTarballOptions = {}\n): Promise<TarballDownload> {\n const headers: Record<string, string> = {};\n if (options.token) {\n headers['Authorization'] = `Bearer ${options.token}`;\n }\n debug('GET (tarball) %s', url);\n const response = await fetch(url, { headers, signal: options.signal });\n\n const sha1 = createHash('sha1');\n const sha512 = createHash('sha512');\n let bytes = 0;\n let first = true;\n\n if (response.body) {\n for await (const chunk of response.body as any) {\n const buf = Buffer.from(chunk);\n bytes += buf.length;\n sha1.update(buf);\n sha512.update(buf);\n if (first) {\n first = false;\n options.onFirstChunk?.();\n }\n options.onChunk?.(bytes);\n }\n }\n\n return {\n status: response.status,\n headers: response.headers,\n bytes,\n sha1: sha1.digest('hex'),\n integrity: `sha512-${sha512.digest('base64')}`,\n };\n}\n\n/** Compute npm-style dist values (shasum/integrity) for a buffer. */\nexport function computeDist(buffer: Buffer): { shasum: string; integrity: string } {\n return {\n shasum: createHash('sha1').update(buffer).digest('hex'),\n integrity: `sha512-${createHash('sha512').update(buffer).digest('base64')}`,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_http_client = require("./http-client.cjs");
|
|
3
|
+
let debug = require("debug");
|
|
4
|
+
debug = require_runtime.__toESM(debug, 1);
|
|
5
|
+
let crypto = require("crypto");
|
|
6
|
+
let http = require("http");
|
|
7
|
+
//#region src/utils/mock-uplink.ts
|
|
8
|
+
var debug$1 = (0, debug.default)("verdaccio:e2e-cli:mock-uplink");
|
|
9
|
+
/**
|
|
10
|
+
* Minimal controllable npm uplink used by scenario:uplink-failure.
|
|
11
|
+
*
|
|
12
|
+
* The registry under test is configured (see --print-config) to proxy the
|
|
13
|
+
* `e2e-uplink-*` package pattern to this server, so the scenario can flip the
|
|
14
|
+
* uplink between healthy, dropping connections mid-tarball, slow, and down.
|
|
15
|
+
*/
|
|
16
|
+
var MockUplink = class {
|
|
17
|
+
port;
|
|
18
|
+
mode = "ok";
|
|
19
|
+
requests = [];
|
|
20
|
+
server;
|
|
21
|
+
packages = /* @__PURE__ */ new Map();
|
|
22
|
+
searchResults = [];
|
|
23
|
+
constructor(port) {
|
|
24
|
+
this.port = port;
|
|
25
|
+
}
|
|
26
|
+
/** Register a package served by this uplink; tarball is random (incompressible) bytes. */
|
|
27
|
+
addPackage(name, tarballBytes = 65536) {
|
|
28
|
+
const tarball = (0, crypto.randomBytes)(tarballBytes);
|
|
29
|
+
const { shasum, integrity } = require_http_client.computeDist(tarball);
|
|
30
|
+
const pkg = {
|
|
31
|
+
name,
|
|
32
|
+
version: "1.0.0",
|
|
33
|
+
tarball,
|
|
34
|
+
shasum,
|
|
35
|
+
integrity
|
|
36
|
+
};
|
|
37
|
+
this.packages.set(name, pkg);
|
|
38
|
+
return pkg;
|
|
39
|
+
}
|
|
40
|
+
/** Register a result served by the /-/v1/search endpoint. */
|
|
41
|
+
addSearchResult(name, version = "1.0.0") {
|
|
42
|
+
this.searchResults.push({
|
|
43
|
+
name,
|
|
44
|
+
version
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* npmjs-faithful /-/v1/search: filters by `text` substring, applies
|
|
49
|
+
* `from`/`size` itself (like registry.npmjs.org does) and reports the real
|
|
50
|
+
* `total` of matches. Mimicking the upstream offsetting is what exposes
|
|
51
|
+
* double pagination in the registry under test.
|
|
52
|
+
*/
|
|
53
|
+
searchResponse(url) {
|
|
54
|
+
const text = url.searchParams.get("text") ?? "";
|
|
55
|
+
const from = Math.max(0, parseInt(url.searchParams.get("from") ?? "0", 10) || 0);
|
|
56
|
+
const size = Math.max(0, parseInt(url.searchParams.get("size") ?? "20", 10) || 20);
|
|
57
|
+
const matches = this.searchResults.filter((r) => r.name.includes(text));
|
|
58
|
+
const page = matches.slice(from, from + size);
|
|
59
|
+
return JSON.stringify({
|
|
60
|
+
objects: page.map((r) => ({
|
|
61
|
+
package: {
|
|
62
|
+
name: r.name,
|
|
63
|
+
version: r.version,
|
|
64
|
+
description: "mock uplink search result",
|
|
65
|
+
date: "2020-01-01T00:00:00.000Z",
|
|
66
|
+
maintainers: [{
|
|
67
|
+
username: "mock-uplink",
|
|
68
|
+
email: "mock@example.org"
|
|
69
|
+
}],
|
|
70
|
+
links: { npm: `http://localhost:${this.port}/${r.name}` }
|
|
71
|
+
},
|
|
72
|
+
score: {
|
|
73
|
+
final: 1,
|
|
74
|
+
detail: {
|
|
75
|
+
quality: 1,
|
|
76
|
+
popularity: 1,
|
|
77
|
+
maintenance: 1
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
searchScore: 1,
|
|
81
|
+
flags: {}
|
|
82
|
+
})),
|
|
83
|
+
total: matches.length,
|
|
84
|
+
time: (/* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z")).toISOString()
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
packument(pkg) {
|
|
88
|
+
const tarballUrl = `http://localhost:${this.port}/${pkg.name}/-/${pkg.name}-${pkg.version}.tgz`;
|
|
89
|
+
return JSON.stringify({
|
|
90
|
+
name: pkg.name,
|
|
91
|
+
"dist-tags": { latest: pkg.version },
|
|
92
|
+
versions: { [pkg.version]: {
|
|
93
|
+
name: pkg.name,
|
|
94
|
+
version: pkg.version,
|
|
95
|
+
description: "mock uplink package",
|
|
96
|
+
dist: {
|
|
97
|
+
tarball: tarballUrl,
|
|
98
|
+
shasum: pkg.shasum,
|
|
99
|
+
integrity: pkg.integrity
|
|
100
|
+
}
|
|
101
|
+
} },
|
|
102
|
+
time: {
|
|
103
|
+
created: "2020-01-01T00:00:00.000Z",
|
|
104
|
+
modified: "2020-01-01T00:00:00.000Z",
|
|
105
|
+
[pkg.version]: "2020-01-01T00:00:00.000Z"
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
start() {
|
|
110
|
+
this.server = (0, http.createServer)((req, res) => {
|
|
111
|
+
const url = req.url || "";
|
|
112
|
+
this.requests.push(url);
|
|
113
|
+
debug$1("mock uplink %s %s (mode: %s)", req.method, url, this.mode);
|
|
114
|
+
const respond = () => {
|
|
115
|
+
if (this.mode === "error-500") {
|
|
116
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
117
|
+
res.end(JSON.stringify({ error: "mock uplink internal error" }));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (url.startsWith("/-/v1/search")) {
|
|
121
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
122
|
+
res.end(this.searchResponse(new URL(url, `http://localhost:${this.port}`)));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const tarballSep = url.indexOf("/-/");
|
|
126
|
+
if (tarballSep > 0 && url.endsWith(".tgz")) {
|
|
127
|
+
const pkg = this.packages.get(decodeURIComponent(url.slice(1, tarballSep)));
|
|
128
|
+
if (!pkg) {
|
|
129
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
130
|
+
res.end(JSON.stringify({ error: "not found" }));
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (this.mode === "drop-mid-stream") {
|
|
134
|
+
res.writeHead(200, {
|
|
135
|
+
"Content-Type": "application/octet-stream",
|
|
136
|
+
"Content-Length": String(pkg.tarball.length)
|
|
137
|
+
});
|
|
138
|
+
res.write(pkg.tarball.subarray(0, Math.floor(pkg.tarball.length / 2)));
|
|
139
|
+
setTimeout(() => res.socket?.destroy(), 50);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
res.writeHead(200, {
|
|
143
|
+
"Content-Type": "application/octet-stream",
|
|
144
|
+
"Content-Length": String(pkg.tarball.length)
|
|
145
|
+
});
|
|
146
|
+
res.end(pkg.tarball);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const pkg = this.packages.get(decodeURIComponent(url.slice(1)));
|
|
150
|
+
if (!pkg) {
|
|
151
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
152
|
+
res.end(JSON.stringify({ error: "not found" }));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
156
|
+
res.end(this.packument(pkg));
|
|
157
|
+
};
|
|
158
|
+
const safeRespond = () => {
|
|
159
|
+
try {
|
|
160
|
+
respond();
|
|
161
|
+
} catch (err) {
|
|
162
|
+
debug$1("mock uplink response failed (socket gone?): %s", err);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
if (this.mode === "slow") {
|
|
166
|
+
const timer = setTimeout(safeRespond, 1e4);
|
|
167
|
+
res.socket?.once("close", () => clearTimeout(timer));
|
|
168
|
+
} else safeRespond();
|
|
169
|
+
});
|
|
170
|
+
return new Promise((resolve, reject) => {
|
|
171
|
+
this.server.once("error", reject);
|
|
172
|
+
this.server.listen(this.port, () => {
|
|
173
|
+
debug$1("mock uplink listening on %d", this.port);
|
|
174
|
+
resolve();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
stop() {
|
|
179
|
+
return new Promise((resolve) => {
|
|
180
|
+
if (!this.server) {
|
|
181
|
+
resolve();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
this.server.closeAllConnections?.();
|
|
185
|
+
this.server.close(() => {
|
|
186
|
+
debug$1("mock uplink stopped");
|
|
187
|
+
this.server = void 0;
|
|
188
|
+
resolve();
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
//#endregion
|
|
194
|
+
exports.MockUplink = MockUplink;
|
|
195
|
+
|
|
196
|
+
//# sourceMappingURL=mock-uplink.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-uplink.cjs","names":[],"sources":["../../../src/utils/mock-uplink.ts"],"sourcesContent":["import { randomBytes } from 'crypto';\nimport buildDebug from 'debug';\nimport { Server, createServer } from 'http';\n\nimport { computeDist } from './http-client';\n\nconst debug = buildDebug('verdaccio:e2e-cli:mock-uplink');\n\nexport type UplinkMode =\n /** Serve packuments and tarballs normally */\n | 'ok'\n /** Send tarball headers + half the body, then destroy the socket */\n | 'drop-mid-stream'\n /** Delay every response long enough to trip the registry's uplink timeout */\n | 'slow'\n /** Respond 500 to everything */\n | 'error-500';\n\ntype MockPackage = {\n name: string;\n version: string;\n tarball: Buffer;\n shasum: string;\n integrity: string;\n};\n\n/**\n * Minimal controllable npm uplink used by scenario:uplink-failure.\n *\n * The registry under test is configured (see --print-config) to proxy the\n * `e2e-uplink-*` package pattern to this server, so the scenario can flip the\n * uplink between healthy, dropping connections mid-tarball, slow, and down.\n */\nexport class MockUplink {\n public mode: UplinkMode = 'ok';\n public requests: string[] = [];\n private server?: Server;\n private packages = new Map<string, MockPackage>();\n private searchResults: { name: string; version: string }[] = [];\n\n constructor(public readonly port: number) {}\n\n /** Register a package served by this uplink; tarball is random (incompressible) bytes. */\n addPackage(name: string, tarballBytes = 64 * 1024): MockPackage {\n const tarball = randomBytes(tarballBytes);\n const { shasum, integrity } = computeDist(tarball);\n const pkg: MockPackage = { name, version: '1.0.0', tarball, shasum, integrity };\n this.packages.set(name, pkg);\n return pkg;\n }\n\n /** Register a result served by the /-/v1/search endpoint. */\n addSearchResult(name: string, version = '1.0.0'): void {\n this.searchResults.push({ name, version });\n }\n\n /**\n * npmjs-faithful /-/v1/search: filters by `text` substring, applies\n * `from`/`size` itself (like registry.npmjs.org does) and reports the real\n * `total` of matches. Mimicking the upstream offsetting is what exposes\n * double pagination in the registry under test.\n */\n private searchResponse(url: URL): string {\n const text = url.searchParams.get('text') ?? '';\n const from = Math.max(0, parseInt(url.searchParams.get('from') ?? '0', 10) || 0);\n const size = Math.max(0, parseInt(url.searchParams.get('size') ?? '20', 10) || 20);\n const matches = this.searchResults.filter((r) => r.name.includes(text));\n const page = matches.slice(from, from + size);\n return JSON.stringify({\n objects: page.map((r) => ({\n package: {\n name: r.name,\n version: r.version,\n description: 'mock uplink search result',\n date: '2020-01-01T00:00:00.000Z',\n maintainers: [{ username: 'mock-uplink', email: 'mock@example.org' }],\n links: { npm: `http://localhost:${this.port}/${r.name}` },\n },\n score: { final: 1, detail: { quality: 1, popularity: 1, maintenance: 1 } },\n searchScore: 1,\n flags: {},\n })),\n total: matches.length,\n time: new Date('2020-01-01T00:00:00.000Z').toISOString(),\n });\n }\n\n private packument(pkg: MockPackage): string {\n const tarballUrl = `http://localhost:${this.port}/${pkg.name}/-/${pkg.name}-${pkg.version}.tgz`;\n return JSON.stringify({\n name: pkg.name,\n 'dist-tags': { latest: pkg.version },\n versions: {\n [pkg.version]: {\n name: pkg.name,\n version: pkg.version,\n description: 'mock uplink package',\n dist: { tarball: tarballUrl, shasum: pkg.shasum, integrity: pkg.integrity },\n },\n },\n time: {\n created: '2020-01-01T00:00:00.000Z',\n modified: '2020-01-01T00:00:00.000Z',\n [pkg.version]: '2020-01-01T00:00:00.000Z',\n },\n });\n }\n\n start(): Promise<void> {\n this.server = createServer((req, res) => {\n const url = req.url || '';\n this.requests.push(url);\n debug('mock uplink %s %s (mode: %s)', req.method, url, this.mode);\n\n const respond = () => {\n if (this.mode === 'error-500') {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'mock uplink internal error' }));\n return;\n }\n\n // Search route: /-/v1/search?text=...&from=...&size=...\n if (url.startsWith('/-/v1/search')) {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(this.searchResponse(new URL(url, `http://localhost:${this.port}`)));\n return;\n }\n\n // Tarball route: /<name>/-/<file>.tgz — parsed without a regex to\n // avoid backtracking on untrusted input (CodeQL js/polynomial-redos).\n const tarballSep = url.indexOf('/-/');\n if (tarballSep > 0 && url.endsWith('.tgz')) {\n const pkg = this.packages.get(decodeURIComponent(url.slice(1, tarballSep)));\n if (!pkg) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'not found' }));\n return;\n }\n if (this.mode === 'drop-mid-stream') {\n res.writeHead(200, {\n 'Content-Type': 'application/octet-stream',\n 'Content-Length': String(pkg.tarball.length),\n });\n res.write(pkg.tarball.subarray(0, Math.floor(pkg.tarball.length / 2)));\n // Kill the connection before the body completes.\n setTimeout(() => res.socket?.destroy(), 50);\n return;\n }\n res.writeHead(200, {\n 'Content-Type': 'application/octet-stream',\n 'Content-Length': String(pkg.tarball.length),\n });\n res.end(pkg.tarball);\n return;\n }\n\n // Packument route: /<name>\n const pkg = this.packages.get(decodeURIComponent(url.slice(1)));\n if (!pkg) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'not found' }));\n return;\n }\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(this.packument(pkg));\n };\n\n // The registry (or a stopped server) may have torn the socket down by the\n // time a delayed response fires — never let that crash the mock.\n const safeRespond = () => {\n try {\n respond();\n } catch (err) {\n debug('mock uplink response failed (socket gone?): %s', err);\n }\n };\n\n if (this.mode === 'slow') {\n // Longer than the uplink timeout configured for the e2e registry (3s).\n const timer = setTimeout(safeRespond, 10_000);\n res.socket?.once('close', () => clearTimeout(timer));\n } else {\n safeRespond();\n }\n });\n\n return new Promise((resolve, reject) => {\n this.server!.once('error', reject);\n this.server!.listen(this.port, () => {\n debug('mock uplink listening on %d', this.port);\n resolve();\n });\n });\n }\n\n stop(): Promise<void> {\n return new Promise((resolve) => {\n if (!this.server) {\n resolve();\n return;\n }\n // closeAllConnections drops keep-alive sockets so close() completes.\n this.server.closeAllConnections?.();\n this.server.close(() => {\n debug('mock uplink stopped');\n this.server = undefined;\n resolve();\n });\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -4,6 +4,7 @@ debug = require_runtime.__toESM(debug, 1);
|
|
|
4
4
|
let child_process = require("child_process");
|
|
5
5
|
let path = require("path");
|
|
6
6
|
let readline = require("readline");
|
|
7
|
+
let util = require("util");
|
|
7
8
|
//#region src/utils/process.ts
|
|
8
9
|
var debug$1 = (0, debug.default)("verdaccio:e2e-cli:process");
|
|
9
10
|
var debugRead = (0, debug.default)("verdaccio:e2e-cli:line");
|
|
@@ -22,6 +23,15 @@ var COLORS = {
|
|
|
22
23
|
function shortCmd(cmd, args) {
|
|
23
24
|
return `${(0, path.basename)(cmd)} ${args.join(" ")}`;
|
|
24
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Always-on scenario tracing, printed inline with the test output in the same
|
|
28
|
+
* style as the verbose `$ <command>` lines — so a red scenario explains what
|
|
29
|
+
* it actually saw (statuses, headers, bodies, timings) in any run, CI or
|
|
30
|
+
* local, without a reproduction. Accepts util.format tokens (%s %d %j %o).
|
|
31
|
+
*/
|
|
32
|
+
function trace(fmt, ...args) {
|
|
33
|
+
process.stdout.write(` ${COLORS.dim}» ${(0, util.format)(fmt, ...args)}${COLORS.reset}\n`);
|
|
34
|
+
}
|
|
25
35
|
async function exec(options, cmd, args) {
|
|
26
36
|
debug$1("start exec %o %o %o", options, cmd, args ? args.join(" ") : "");
|
|
27
37
|
let stdout = "";
|
|
@@ -81,5 +91,6 @@ async function exec(options, cmd, args) {
|
|
|
81
91
|
//#endregion
|
|
82
92
|
exports.exec = exec;
|
|
83
93
|
exports.setVerbose = setVerbose;
|
|
94
|
+
exports.trace = trace;
|
|
84
95
|
|
|
85
96
|
//# sourceMappingURL=process.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process.cjs","names":[],"sources":["../../../src/utils/process.ts"],"sourcesContent":["import { SpawnOptions, spawn } from 'child_process';\nimport buildDebug from 'debug';\nimport { basename } from 'path';\nimport { createInterface } from 'readline';\n\nimport { ExecOutput } from '../types';\n\nconst debug = buildDebug('verdaccio:e2e-cli:process');\nconst debugRead = buildDebug('verdaccio:e2e-cli:line');\n\nlet _verbose = false;\n\nexport function setVerbose(v: boolean) {\n _verbose = v;\n}\n\nconst COLORS = {\n reset: '\\x1b[0m',\n dim: '\\x1b[2m',\n cyan: '\\x1b[36m',\n green: '\\x1b[32m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n};\n\nfunction shortCmd(cmd: string, args: string[]): string {\n const bin = basename(cmd);\n return `${bin} ${args.join(' ')}`;\n}\n\nexport async function exec(\n options: SpawnOptions,\n cmd: string,\n args: string[]\n): Promise<ExecOutput> {\n debug('start exec %o %o %o', options, cmd, args ? args.join(' ') : '');\n let stdout = '';\n let stderr = '';\n\n if (_verbose) {\n const cwd = options.cwd\n ? ` ${COLORS.dim}(cwd: ${basename(String(options.cwd))})${COLORS.reset}`\n : '';\n process.stdout.write(` ${COLORS.cyan}$${COLORS.reset} ${shortCmd(cmd, args)}${cwd}\\n`);\n }\n\n const spawnOptions: SpawnOptions = {\n cwd: options.cwd,\n stdio: options.stdio || 'pipe',\n env: options.env || process.env,\n };\n\n if (process.platform.startsWith('win')) {\n args.unshift('/c', cmd);\n cmd = 'cmd.exe';\n spawnOptions.stdio = 'pipe';\n }\n\n const start = Date.now();\n const childProcess = spawn(cmd, args, spawnOptions);\n\n if (childProcess.stdout) {\n childProcess.stdout.on('data', (data) => {\n debugRead('data %o', data.toString());\n });\n const rl = createInterface({\n input: childProcess.stdout,\n output: process.stdout,\n terminal: false,\n });\n\n rl.on('line', function (line) {\n debugRead('line %o', line);\n stdout += line;\n });\n }\n\n if (childProcess.stderr) {\n childProcess.stderr.on('data', (data) => {\n stderr += data.toString();\n });\n }\n\n const err = new Error(`Running \"${cmd} ${args.join(' ')}\" returned error code `);\n return new Promise((resolve, reject) => {\n childProcess.on('exit', (code) => {\n const duration = Date.now() - start;\n debugRead('exit %o', code);\n\n if (_verbose) {\n const status = code\n ? `${COLORS.red}exit ${code}${COLORS.reset}`\n : `${COLORS.green}ok${COLORS.reset}`;\n process.stdout.write(\n ` ${COLORS.dim} -> ${status} ${COLORS.dim}(${duration}ms)${COLORS.reset}\\n`\n );\n }\n\n if (!code) {\n resolve({ stdout, stderr });\n } else {\n err.message += `${code}...\\n\\nSTDOUT:\\n${stdout}\\n\\nSTDERR:\\n${stderr}\\n`;\n reject(err);\n }\n });\n });\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"process.cjs","names":[],"sources":["../../../src/utils/process.ts"],"sourcesContent":["import { SpawnOptions, spawn } from 'child_process';\nimport buildDebug from 'debug';\nimport { basename } from 'path';\nimport { createInterface } from 'readline';\nimport { format } from 'util';\n\nimport { ExecOutput } from '../types';\n\nconst debug = buildDebug('verdaccio:e2e-cli:process');\nconst debugRead = buildDebug('verdaccio:e2e-cli:line');\n\nlet _verbose = false;\n\nexport function setVerbose(v: boolean) {\n _verbose = v;\n}\n\nconst COLORS = {\n reset: '\\x1b[0m',\n dim: '\\x1b[2m',\n cyan: '\\x1b[36m',\n green: '\\x1b[32m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n};\n\nfunction shortCmd(cmd: string, args: string[]): string {\n const bin = basename(cmd);\n return `${bin} ${args.join(' ')}`;\n}\n\n/**\n * Always-on scenario tracing, printed inline with the test output in the same\n * style as the verbose `$ <command>` lines — so a red scenario explains what\n * it actually saw (statuses, headers, bodies, timings) in any run, CI or\n * local, without a reproduction. Accepts util.format tokens (%s %d %j %o).\n */\nexport function trace(fmt: string, ...args: unknown[]): void {\n process.stdout.write(` ${COLORS.dim}» ${format(fmt, ...args)}${COLORS.reset}\\n`);\n}\n\nexport async function exec(\n options: SpawnOptions,\n cmd: string,\n args: string[]\n): Promise<ExecOutput> {\n debug('start exec %o %o %o', options, cmd, args ? args.join(' ') : '');\n let stdout = '';\n let stderr = '';\n\n if (_verbose) {\n const cwd = options.cwd\n ? ` ${COLORS.dim}(cwd: ${basename(String(options.cwd))})${COLORS.reset}`\n : '';\n process.stdout.write(` ${COLORS.cyan}$${COLORS.reset} ${shortCmd(cmd, args)}${cwd}\\n`);\n }\n\n const spawnOptions: SpawnOptions = {\n cwd: options.cwd,\n stdio: options.stdio || 'pipe',\n env: options.env || process.env,\n };\n\n if (process.platform.startsWith('win')) {\n args.unshift('/c', cmd);\n cmd = 'cmd.exe';\n spawnOptions.stdio = 'pipe';\n }\n\n const start = Date.now();\n const childProcess = spawn(cmd, args, spawnOptions);\n\n if (childProcess.stdout) {\n childProcess.stdout.on('data', (data) => {\n debugRead('data %o', data.toString());\n });\n const rl = createInterface({\n input: childProcess.stdout,\n output: process.stdout,\n terminal: false,\n });\n\n rl.on('line', function (line) {\n debugRead('line %o', line);\n stdout += line;\n });\n }\n\n if (childProcess.stderr) {\n childProcess.stderr.on('data', (data) => {\n stderr += data.toString();\n });\n }\n\n const err = new Error(`Running \"${cmd} ${args.join(' ')}\" returned error code `);\n return new Promise((resolve, reject) => {\n childProcess.on('exit', (code) => {\n const duration = Date.now() - start;\n debugRead('exit %o', code);\n\n if (_verbose) {\n const status = code\n ? `${COLORS.red}exit ${code}${COLORS.reset}`\n : `${COLORS.green}ok${COLORS.reset}`;\n process.stdout.write(\n ` ${COLORS.dim} -> ${status} ${COLORS.dim}(${duration}ms)${COLORS.reset}\\n`\n );\n }\n\n if (!code) {\n resolve({ stdout, stderr });\n } else {\n err.message += `${code}...\\n\\nSTDOUT:\\n${stdout}\\n\\nSTDERR:\\n${stderr}\\n`;\n reject(err);\n }\n });\n });\n}\n"],"mappings":";;;;;;;;AAQA,IAAM,WAAA,GAAQ,MAAA,QAAA,CAAW,2BAA2B;AACpD,IAAM,aAAA,GAAY,MAAA,QAAA,CAAW,wBAAwB;AAErD,IAAI,WAAW;AAEf,SAAgB,WAAW,GAAY;CACrC,WAAW;AACb;AAEA,IAAM,SAAS;CACb,OAAO;CACP,KAAK;CACL,MAAM;CACN,OAAO;CACP,KAAK;CACL,QAAQ;AACV;AAEA,SAAS,SAAS,KAAa,MAAwB;CAErD,OAAO,IAAA,GADK,KAAA,SAAA,CAAS,GACX,EAAI,GAAG,KAAK,KAAK,GAAG;AAChC;;;;;;;AAQA,SAAgB,MAAM,KAAa,GAAG,MAAuB;CAC3D,QAAQ,OAAO,MAAM,SAAS,OAAO,IAAI,KAAA,GAAI,KAAA,OAAA,CAAO,KAAK,GAAG,IAAI,IAAI,OAAO,MAAM,GAAG;AACtF;AAEA,eAAsB,KACpB,SACA,KACA,MACqB;CACrB,QAAM,uBAAuB,SAAS,KAAK,OAAO,KAAK,KAAK,GAAG,IAAI,EAAE;CACrE,IAAI,SAAS;CACb,IAAI,SAAS;CAEb,IAAI,UAAU;EACZ,MAAM,MAAM,QAAQ,MAChB,IAAI,OAAO,IAAI,SAAA,GAAQ,KAAA,SAAA,CAAS,OAAO,QAAQ,GAAG,CAAC,EAAE,GAAG,OAAO,UAC/D;EACJ,QAAQ,OAAO,MAAM,SAAS,OAAO,KAAK,GAAG,OAAO,MAAM,GAAG,SAAS,KAAK,IAAI,IAAI,IAAI,GAAG;CAC5F;CAEA,MAAM,eAA6B;EACjC,KAAK,QAAQ;EACb,OAAO,QAAQ,SAAS;EACxB,KAAK,QAAQ,OAAO,QAAQ;CAC9B;CAEA,IAAI,QAAQ,SAAS,WAAW,KAAK,GAAG;EACtC,KAAK,QAAQ,MAAM,GAAG;EACtB,MAAM;EACN,aAAa,QAAQ;CACvB;CAEA,MAAM,QAAQ,KAAK,IAAI;CACvB,MAAM,gBAAA,GAAe,cAAA,MAAA,CAAM,KAAK,MAAM,YAAY;CAElD,IAAI,aAAa,QAAQ;EACvB,aAAa,OAAO,GAAG,SAAS,SAAS;GACvC,UAAU,WAAW,KAAK,SAAS,CAAC;EACtC,CAAC;EAOD,CAAA,GANW,SAAA,gBAAA,CAAgB;GACzB,OAAO,aAAa;GACpB,QAAQ,QAAQ;GAChB,UAAU;EACZ,CAEA,CAAA,CAAG,GAAG,QAAQ,SAAU,MAAM;GAC5B,UAAU,WAAW,IAAI;GACzB,UAAU;EACZ,CAAC;CACH;CAEA,IAAI,aAAa,QACf,aAAa,OAAO,GAAG,SAAS,SAAS;EACvC,UAAU,KAAK,SAAS;CAC1B,CAAC;CAGH,MAAM,sBAAM,IAAI,MAAM,YAAY,IAAI,GAAG,KAAK,KAAK,GAAG,EAAE,uBAAuB;CAC/E,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,aAAa,GAAG,SAAS,SAAS;GAChC,MAAM,WAAW,KAAK,IAAI,IAAI;GAC9B,UAAU,WAAW,IAAI;GAEzB,IAAI,UAAU;IACZ,MAAM,SAAS,OACX,GAAG,OAAO,IAAI,OAAO,OAAO,OAAO,UACnC,GAAG,OAAO,MAAM,IAAI,OAAO;IAC/B,QAAQ,OAAO,MACb,SAAS,OAAO,IAAI,OAAO,OAAO,GAAG,OAAO,IAAI,GAAG,SAAS,KAAK,OAAO,MAAM,GAChF;GACF;GAEA,IAAI,CAAC,MACH,QAAQ;IAAE;IAAQ;GAAO,CAAC;QACrB;IACL,IAAI,WAAW,GAAG,KAAK,kBAAkB,OAAO,eAAe,OAAO;IACtE,OAAO,GAAG;GACZ;EACF,CAAC;CACH,CAAC;AACH"}
|
|
@@ -39,7 +39,7 @@ function getREADME(packageName) {
|
|
|
39
39
|
}
|
|
40
40
|
async function prepareGenericEmptyProject(packageName, version, port, token, registryDomain, dependencies = {}, devDependencies = {}) {
|
|
41
41
|
debug$1("preparing generic project %o", packageName);
|
|
42
|
-
const getNPMrc = (port, token, registry) => `//localhost:${port}/:_authToken=${token}\nregistry=${registry}\nmin-release-age=0`;
|
|
42
|
+
const getNPMrc = (port, token, registry) => `//localhost:${port}/:_authToken=${token}\nregistry=${registry}\nmin-release-age=0\naudit=false\nfund=false`;
|
|
43
43
|
const tempFolder = await createTempFolder("temp-folder");
|
|
44
44
|
await (0, fs_promises.writeFile)((0, path.join)(tempFolder, "package.json"), getPackageJSON(packageName, version, dependencies, devDependencies));
|
|
45
45
|
await (0, fs_promises.writeFile)((0, path.join)(tempFolder, "README.md"), getREADME(packageName));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.cjs","names":[],"sources":["../../../src/utils/project.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { mkdirSync } from 'fs';\nimport { mkdtemp, writeFile } from 'fs/promises';\nimport { tmpdir } from 'os';\nimport { join } from 'path';\n\nconst debug = buildDebug('verdaccio:e2e-cli:project');\n\nexport async function createTempFolder(prefix: string): Promise<string> {\n const sanitized = prefix.replace(/[^a-zA-Z0-9-_]/g, '-');\n debug('creating temp folder %o', sanitized);\n return mkdtemp(join(tmpdir(), `verdaccio-e2e-${sanitized}-`));\n}\n\nexport function getPackageJSON(\n packageName: string,\n version = '1.0.0',\n dependencies: Record<string, string> = {},\n devDependencies: Record<string, string> = {}\n): string {\n debug('creating package.json %o', packageName);\n const json = {\n name: packageName,\n version,\n description: 'some cool project',\n main: 'index.js',\n scripts: {\n test: 'echo exit 1',\n },\n dependencies,\n devDependencies,\n keywords: ['foo', 'bar'],\n author: 'Verdaccio E2E <verdaccio@example.org>',\n license: 'MIT',\n };\n return JSON.stringify(json);\n}\n\nexport function getREADME(packageName: string): string {\n return `\n # My README ${packageName}\n\n some text\n\n ## subtitle\n\n more text\n `;\n}\n\nexport async function prepareGenericEmptyProject(\n packageName: string,\n version: string,\n port: number,\n token: string,\n registryDomain: string,\n dependencies: Record<string, string> = {},\n devDependencies: Record<string, string> = {}\n): Promise<{ tempFolder: string }> {\n debug('preparing generic project %o', packageName);\n // `min-release-age=0` keeps the harness hermetic: npm 12 added a release-age\n // cooldown that, if set in the developer's global ~/.npmrc, would reject the\n // just-published packages these tests install (ETARGET / \"no matching version\n // ... before <date>\"). Pinning it per-project overrides any global value.\n // It's an npm-only key — pnpm (minimum-release-age) and yarn ignore it.\n const getNPMrc = (port: number, token: string, registry: string) =>\n `//localhost:${port}/:_authToken=${token}\\nregistry=${registry}\\nmin-release-age=0`;\n const tempFolder = await createTempFolder('temp-folder');\n await writeFile(\n join(tempFolder, 'package.json'),\n getPackageJSON(packageName, version, dependencies, devDependencies)\n );\n await writeFile(join(tempFolder, 'README.md'), getREADME(packageName));\n await writeFile(join(tempFolder, '.npmrc'), getNPMrc(port, token, registryDomain));\n return { tempFolder };\n}\n"],"mappings":";;;;;;;AAMA,IAAM,WAAA,GAAQ,MAAA,QAAA,CAAW,2BAA2B;AAEpD,eAAsB,iBAAiB,QAAiC;CACtE,MAAM,YAAY,OAAO,QAAQ,mBAAmB,GAAG;CACvD,QAAM,2BAA2B,SAAS;CAC1C,QAAA,GAAO,YAAA,QAAA,EAAA,GAAQ,KAAA,KAAA,EAAA,GAAK,GAAA,OAAA,CAAO,GAAG,iBAAiB,UAAU,EAAE,CAAC;AAC9D;AAEA,SAAgB,eACd,aACA,UAAU,SACV,eAAuC,CAAC,GACxC,kBAA0C,CAAC,GACnC;CACR,QAAM,4BAA4B,WAAW;CAe7C,OAAO,KAAK,UAAU;EAbpB,MAAM;EACN;EACA,aAAa;EACb,MAAM;EACN,SAAS,EACP,MAAM,cACR;EACA;EACA;EACA,UAAU,CAAC,OAAO,KAAK;EACvB,QAAQ;EACR,SAAS;CAEW,CAAI;AAC5B;AAEA,SAAgB,UAAU,aAA6B;CACrD,OAAO;iBACQ,YAAY;;;;;;;;AAQ7B;AAEA,eAAsB,2BACpB,aACA,SACA,MACA,OACA,gBACA,eAAuC,CAAC,GACxC,kBAA0C,CAAC,GACV;CACjC,QAAM,gCAAgC,WAAW;
|
|
1
|
+
{"version":3,"file":"project.cjs","names":[],"sources":["../../../src/utils/project.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { mkdirSync } from 'fs';\nimport { mkdtemp, writeFile } from 'fs/promises';\nimport { tmpdir } from 'os';\nimport { join } from 'path';\n\nconst debug = buildDebug('verdaccio:e2e-cli:project');\n\nexport async function createTempFolder(prefix: string): Promise<string> {\n const sanitized = prefix.replace(/[^a-zA-Z0-9-_]/g, '-');\n debug('creating temp folder %o', sanitized);\n return mkdtemp(join(tmpdir(), `verdaccio-e2e-${sanitized}-`));\n}\n\nexport function getPackageJSON(\n packageName: string,\n version = '1.0.0',\n dependencies: Record<string, string> = {},\n devDependencies: Record<string, string> = {}\n): string {\n debug('creating package.json %o', packageName);\n const json = {\n name: packageName,\n version,\n description: 'some cool project',\n main: 'index.js',\n scripts: {\n test: 'echo exit 1',\n },\n dependencies,\n devDependencies,\n keywords: ['foo', 'bar'],\n author: 'Verdaccio E2E <verdaccio@example.org>',\n license: 'MIT',\n };\n return JSON.stringify(json);\n}\n\nexport function getREADME(packageName: string): string {\n return `\n # My README ${packageName}\n\n some text\n\n ## subtitle\n\n more text\n `;\n}\n\nexport async function prepareGenericEmptyProject(\n packageName: string,\n version: string,\n port: number,\n token: string,\n registryDomain: string,\n dependencies: Record<string, string> = {},\n devDependencies: Record<string, string> = {}\n): Promise<{ tempFolder: string }> {\n debug('preparing generic project %o', packageName);\n // `min-release-age=0` keeps the harness hermetic: npm 12 added a release-age\n // cooldown that, if set in the developer's global ~/.npmrc, would reject the\n // just-published packages these tests install (ETARGET / \"no matching version\n // ... before <date>\"). Pinning it per-project overrides any global value.\n // It's an npm-only key — pnpm (minimum-release-age) and yarn ignore it.\n // `audit=false`/`fund=false` stop npm's implicit audit/fund calls on every\n // install — the registry would proxy those upstream, and the suite must run\n // fully offline (the dedicated audit test still runs `npm audit` explicitly).\n const getNPMrc = (port: number, token: string, registry: string) =>\n `//localhost:${port}/:_authToken=${token}\\nregistry=${registry}\\nmin-release-age=0\\naudit=false\\nfund=false`;\n const tempFolder = await createTempFolder('temp-folder');\n await writeFile(\n join(tempFolder, 'package.json'),\n getPackageJSON(packageName, version, dependencies, devDependencies)\n );\n await writeFile(join(tempFolder, 'README.md'), getREADME(packageName));\n await writeFile(join(tempFolder, '.npmrc'), getNPMrc(port, token, registryDomain));\n return { tempFolder };\n}\n"],"mappings":";;;;;;;AAMA,IAAM,WAAA,GAAQ,MAAA,QAAA,CAAW,2BAA2B;AAEpD,eAAsB,iBAAiB,QAAiC;CACtE,MAAM,YAAY,OAAO,QAAQ,mBAAmB,GAAG;CACvD,QAAM,2BAA2B,SAAS;CAC1C,QAAA,GAAO,YAAA,QAAA,EAAA,GAAQ,KAAA,KAAA,EAAA,GAAK,GAAA,OAAA,CAAO,GAAG,iBAAiB,UAAU,EAAE,CAAC;AAC9D;AAEA,SAAgB,eACd,aACA,UAAU,SACV,eAAuC,CAAC,GACxC,kBAA0C,CAAC,GACnC;CACR,QAAM,4BAA4B,WAAW;CAe7C,OAAO,KAAK,UAAU;EAbpB,MAAM;EACN;EACA,aAAa;EACb,MAAM;EACN,SAAS,EACP,MAAM,cACR;EACA;EACA;EACA,UAAU,CAAC,OAAO,KAAK;EACvB,QAAQ;EACR,SAAS;CAEW,CAAI;AAC5B;AAEA,SAAgB,UAAU,aAA6B;CACrD,OAAO;iBACQ,YAAY;;;;;;;;AAQ7B;AAEA,eAAsB,2BACpB,aACA,SACA,MACA,OACA,gBACA,eAAuC,CAAC,GACxC,kBAA0C,CAAC,GACV;CACjC,QAAM,gCAAgC,WAAW;CASjD,MAAM,YAAY,MAAc,OAAe,aAC7C,eAAe,KAAK,eAAe,MAAM,aAAa,SAAS;CACjE,MAAM,aAAa,MAAM,iBAAiB,aAAa;CACvD,OAAA,GAAM,YAAA,UAAA,EAAA,GACJ,KAAA,KAAA,CAAK,YAAY,cAAc,GAC/B,eAAe,aAAa,SAAS,cAAc,eAAe,CACpE;CACA,OAAA,GAAM,YAAA,UAAA,EAAA,GAAU,KAAA,KAAA,CAAK,YAAY,WAAW,GAAG,UAAU,WAAW,CAAC;CACrE,OAAA,GAAM,YAAA,UAAA,EAAA,GAAU,KAAA,KAAA,CAAK,YAAY,QAAQ,GAAG,SAAS,MAAM,OAAO,cAAc,CAAC;CACjF,OAAO,EAAE,WAAW;AACtB"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_http_client = require("./http-client.cjs");
|
|
3
|
+
let debug = require("debug");
|
|
4
|
+
debug = require_runtime.__toESM(debug, 1);
|
|
5
|
+
let zlib = require("zlib");
|
|
6
|
+
//#region src/utils/publish.ts
|
|
7
|
+
var debug$1 = (0, debug.default)("verdaccio:e2e-cli:publish");
|
|
8
|
+
/** Build a single ustar tar entry (512-byte header + padded content). */
|
|
9
|
+
function tarEntry(name, content) {
|
|
10
|
+
const header = Buffer.alloc(512);
|
|
11
|
+
header.write(name, 0, 100, "utf8");
|
|
12
|
+
header.write("0000644\0", 100, 8);
|
|
13
|
+
header.write("0000000\0", 108, 8);
|
|
14
|
+
header.write("0000000\0", 116, 8);
|
|
15
|
+
header.write(content.length.toString(8).padStart(11, "0") + "\0", 124, 12);
|
|
16
|
+
header.write("00000000000\0", 136, 12);
|
|
17
|
+
header.write(" ", 148, 8);
|
|
18
|
+
header.write("0", 156, 1);
|
|
19
|
+
header.write("ustar\0", 257, 6);
|
|
20
|
+
header.write("00", 263, 2);
|
|
21
|
+
let checksum = 0;
|
|
22
|
+
for (const byte of header) checksum += byte;
|
|
23
|
+
header.write(checksum.toString(8).padStart(6, "0") + "\0 ", 148, 8);
|
|
24
|
+
const padding = Buffer.alloc((512 - content.length % 512) % 512);
|
|
25
|
+
return Buffer.concat([
|
|
26
|
+
header,
|
|
27
|
+
content,
|
|
28
|
+
padding
|
|
29
|
+
]);
|
|
30
|
+
}
|
|
31
|
+
/** Build a minimal valid npm package tarball (package/package.json inside). */
|
|
32
|
+
function buildPackageTarball(pkgName, version, dependencies = {}) {
|
|
33
|
+
const manifest = {
|
|
34
|
+
name: pkgName,
|
|
35
|
+
version,
|
|
36
|
+
description: "e2e local package",
|
|
37
|
+
main: "index.js",
|
|
38
|
+
dependencies,
|
|
39
|
+
license: "MIT"
|
|
40
|
+
};
|
|
41
|
+
const tar = Buffer.concat([
|
|
42
|
+
tarEntry("package/package.json", Buffer.from(JSON.stringify(manifest, null, 2))),
|
|
43
|
+
tarEntry("package/index.js", Buffer.from("module.exports = {};\n")),
|
|
44
|
+
Buffer.alloc(1024)
|
|
45
|
+
]);
|
|
46
|
+
return (0, zlib.gzipSync)(tar, { level: 9 });
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Publish straight through the registry HTTP API (same PUT an npm client
|
|
50
|
+
* sends). Used for adapters that cannot publish themselves (eg. deno) so
|
|
51
|
+
* every fixture still comes from the registry under test — never from an
|
|
52
|
+
* external registry.
|
|
53
|
+
*/
|
|
54
|
+
async function publishViaHttp(ctx, pkgName, version = "1.0.0", dependencies = {}) {
|
|
55
|
+
const tarball = buildPackageTarball(pkgName, version, dependencies);
|
|
56
|
+
const { shasum, integrity } = require_http_client.computeDist(tarball);
|
|
57
|
+
const filename = `${pkgName.replace(/^@[^/]+\//, "")}-${version}.tgz`;
|
|
58
|
+
const encodedName = pkgName.startsWith("@") ? pkgName.replaceAll("/", "%2f") : pkgName;
|
|
59
|
+
const tarballUrl = `${ctx.registryUrl}/${encodedName}/-/${filename}`;
|
|
60
|
+
const body = {
|
|
61
|
+
_id: pkgName,
|
|
62
|
+
name: pkgName,
|
|
63
|
+
description: "e2e local package",
|
|
64
|
+
"dist-tags": { latest: version },
|
|
65
|
+
versions: { [version]: {
|
|
66
|
+
_id: `${pkgName}@${version}`,
|
|
67
|
+
name: pkgName,
|
|
68
|
+
version,
|
|
69
|
+
description: "e2e local package",
|
|
70
|
+
main: "index.js",
|
|
71
|
+
dependencies,
|
|
72
|
+
license: "MIT",
|
|
73
|
+
dist: {
|
|
74
|
+
tarball: tarballUrl,
|
|
75
|
+
shasum,
|
|
76
|
+
integrity
|
|
77
|
+
}
|
|
78
|
+
} },
|
|
79
|
+
_attachments: { [filename]: {
|
|
80
|
+
content_type: "application/octet-stream",
|
|
81
|
+
data: tarball.toString("base64"),
|
|
82
|
+
length: tarball.length
|
|
83
|
+
} }
|
|
84
|
+
};
|
|
85
|
+
const response = await fetch(`${ctx.registryUrl}/${encodedName}`, {
|
|
86
|
+
method: "PUT",
|
|
87
|
+
headers: {
|
|
88
|
+
"Content-Type": "application/json",
|
|
89
|
+
Authorization: `Bearer ${ctx.token}`
|
|
90
|
+
},
|
|
91
|
+
body: JSON.stringify(body)
|
|
92
|
+
});
|
|
93
|
+
if (response.status !== 201 && response.status !== 200) {
|
|
94
|
+
const text = await response.text();
|
|
95
|
+
throw new Error(`HTTP publish of ${pkgName}@${version} failed: ${response.status} ${text}`);
|
|
96
|
+
}
|
|
97
|
+
debug$1("published %s@%s via HTTP", pkgName, version);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Publish a package to the registry under test so later steps (install, ci,
|
|
101
|
+
* audit, info, search…) can consume it without ever touching an external
|
|
102
|
+
* registry — the whole suite must be runnable offline. Uses the package
|
|
103
|
+
* manager when it can publish, the raw HTTP API otherwise (eg. deno).
|
|
104
|
+
*/
|
|
105
|
+
async function publishLocalPackage(ctx, pkgName, version = "1.0.0", dependencies = {}) {
|
|
106
|
+
if (!ctx.adapter.supports.has("publish")) return publishViaHttp(ctx, pkgName, version, dependencies);
|
|
107
|
+
const { tempFolder } = await ctx.adapter.prepareProject(pkgName, version, ctx.registryUrl, ctx.port, ctx.token, dependencies);
|
|
108
|
+
if (ctx.adapter.type === "yarn-modern") await ctx.adapter.exec({ cwd: tempFolder }, "install");
|
|
109
|
+
await ctx.adapter.exec({ cwd: tempFolder }, "publish", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
110
|
+
debug$1("published local package %s@%s", pkgName, version);
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
exports.buildPackageTarball = buildPackageTarball;
|
|
114
|
+
exports.publishLocalPackage = publishLocalPackage;
|
|
115
|
+
exports.publishViaHttp = publishViaHttp;
|
|
116
|
+
|
|
117
|
+
//# sourceMappingURL=publish.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish.cjs","names":[],"sources":["../../../src/utils/publish.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { gzipSync } from 'zlib';\n\nimport { TestContext } from '../types';\nimport { computeDist } from './http-client';\n\nconst debug = buildDebug('verdaccio:e2e-cli:publish');\n\n/** Build a single ustar tar entry (512-byte header + padded content). */\nfunction tarEntry(name: string, content: Buffer): Buffer {\n const header = Buffer.alloc(512);\n header.write(name, 0, 100, 'utf8');\n header.write('0000644\\0', 100, 8); // mode\n header.write('0000000\\0', 108, 8); // uid\n header.write('0000000\\0', 116, 8); // gid\n header.write(content.length.toString(8).padStart(11, '0') + '\\0', 124, 12); // size\n header.write('00000000000\\0', 136, 12); // mtime (epoch, keeps output deterministic)\n header.write(' ', 148, 8); // chksum placeholder (spaces while summing)\n header.write('0', 156, 1); // typeflag: regular file\n header.write('ustar\\0', 257, 6); // magic\n header.write('00', 263, 2); // version\n let checksum = 0;\n for (const byte of header) {\n checksum += byte;\n }\n header.write(checksum.toString(8).padStart(6, '0') + '\\0 ', 148, 8);\n const padding = Buffer.alloc((512 - (content.length % 512)) % 512);\n return Buffer.concat([header, content, padding]);\n}\n\n/** Build a minimal valid npm package tarball (package/package.json inside). */\nexport function buildPackageTarball(\n pkgName: string,\n version: string,\n dependencies: Record<string, string> = {}\n): Buffer {\n const manifest = {\n name: pkgName,\n version,\n description: 'e2e local package',\n main: 'index.js',\n dependencies,\n license: 'MIT',\n };\n const tar = Buffer.concat([\n tarEntry('package/package.json', Buffer.from(JSON.stringify(manifest, null, 2))),\n tarEntry('package/index.js', Buffer.from('module.exports = {};\\n')),\n Buffer.alloc(1024), // tar end-of-archive marker\n ]);\n return gzipSync(tar, { level: 9 });\n}\n\n/**\n * Publish straight through the registry HTTP API (same PUT an npm client\n * sends). Used for adapters that cannot publish themselves (eg. deno) so\n * every fixture still comes from the registry under test — never from an\n * external registry.\n */\nexport async function publishViaHttp(\n ctx: TestContext,\n pkgName: string,\n version = '1.0.0',\n dependencies: Record<string, string> = {}\n): Promise<void> {\n const tarball = buildPackageTarball(pkgName, version, dependencies);\n const { shasum, integrity } = computeDist(tarball);\n const filename = `${pkgName.replace(/^@[^/]+\\//, '')}-${version}.tgz`;\n const encodedName = pkgName.startsWith('@') ? pkgName.replaceAll('/', '%2f') : pkgName;\n const tarballUrl = `${ctx.registryUrl}/${encodedName}/-/${filename}`;\n const body = {\n _id: pkgName,\n name: pkgName,\n description: 'e2e local package',\n 'dist-tags': { latest: version },\n versions: {\n [version]: {\n _id: `${pkgName}@${version}`,\n name: pkgName,\n version,\n description: 'e2e local package',\n main: 'index.js',\n dependencies,\n license: 'MIT',\n dist: { tarball: tarballUrl, shasum, integrity },\n },\n },\n _attachments: {\n [filename]: {\n content_type: 'application/octet-stream',\n data: tarball.toString('base64'),\n length: tarball.length,\n },\n },\n };\n const response = await fetch(`${ctx.registryUrl}/${encodedName}`, {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${ctx.token}`,\n },\n body: JSON.stringify(body),\n });\n if (response.status !== 201 && response.status !== 200) {\n const text = await response.text();\n throw new Error(`HTTP publish of ${pkgName}@${version} failed: ${response.status} ${text}`);\n }\n debug('published %s@%s via HTTP', pkgName, version);\n}\n\n/**\n * Publish a package to the registry under test so later steps (install, ci,\n * audit, info, search…) can consume it without ever touching an external\n * registry — the whole suite must be runnable offline. Uses the package\n * manager when it can publish, the raw HTTP API otherwise (eg. deno).\n */\nexport async function publishLocalPackage(\n ctx: TestContext,\n pkgName: string,\n version = '1.0.0',\n dependencies: Record<string, string> = {}\n): Promise<void> {\n if (!ctx.adapter.supports.has('publish')) {\n return publishViaHttp(ctx, pkgName, version, dependencies);\n }\n const { tempFolder } = await ctx.adapter.prepareProject(\n pkgName,\n version,\n ctx.registryUrl,\n ctx.port,\n ctx.token,\n dependencies\n );\n // yarn modern requires an install first to generate the lockfile\n if (ctx.adapter.type === 'yarn-modern') {\n await ctx.adapter.exec({ cwd: tempFolder }, 'install');\n }\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'publish',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n debug('published local package %s@%s', pkgName, version);\n}\n"],"mappings":"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"}
|
|
@@ -2,6 +2,5 @@ import { createBunAdapter } from "./bun.js";
|
|
|
2
2
|
import { createDenoAdapter } from "./deno.js";
|
|
3
3
|
import { createNpmAdapter } from "./npm.js";
|
|
4
4
|
import { createPnpmAdapter } from "./pnpm.js";
|
|
5
|
-
import { createYarnClassicAdapter } from "./yarn-classic.js";
|
|
6
5
|
import { createYarnModernAdapter } from "./yarn-modern.js";
|
|
7
|
-
export { createBunAdapter, createDenoAdapter, createNpmAdapter, createPnpmAdapter,
|
|
6
|
+
export { createBunAdapter, createDenoAdapter, createNpmAdapter, createPnpmAdapter, createYarnModernAdapter };
|
|
@@ -15,6 +15,11 @@ var NPM_SUPPORTED_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
15
15
|
"ping",
|
|
16
16
|
"search"
|
|
17
17
|
]);
|
|
18
|
+
var NPM_SUPPORTED_MAJORS = [
|
|
19
|
+
10,
|
|
20
|
+
11,
|
|
21
|
+
12
|
|
22
|
+
];
|
|
18
23
|
function detectVersion(bin) {
|
|
19
24
|
try {
|
|
20
25
|
return execSync(`${bin} --version`, {
|
|
@@ -25,6 +30,10 @@ function detectVersion(bin) {
|
|
|
25
30
|
return "unknown";
|
|
26
31
|
}
|
|
27
32
|
}
|
|
33
|
+
function assertSupportedVersion(resolved) {
|
|
34
|
+
const major = parseInt(resolved.split(".")[0], 10);
|
|
35
|
+
if (!Number.isNaN(major) && !NPM_SUPPORTED_MAJORS.includes(major)) throw new Error(`npm@${resolved} is not supported. Supported majors: ${NPM_SUPPORTED_MAJORS.join(", ")}`);
|
|
36
|
+
}
|
|
28
37
|
function installNpm(version) {
|
|
29
38
|
const pkg = `npm@${version}`;
|
|
30
39
|
debug("installing %s into temp dir", pkg);
|
|
@@ -47,6 +56,7 @@ function resolveNpmBin(binPath, version) {
|
|
|
47
56
|
function createNpmAdapter(binPath, version) {
|
|
48
57
|
const bin = resolveNpmBin(binPath, version);
|
|
49
58
|
const resolved = detectVersion(bin);
|
|
59
|
+
assertSupportedVersion(resolved);
|
|
50
60
|
debug("creating npm adapter with bin: %s (%s)", bin, resolved);
|
|
51
61
|
return {
|
|
52
62
|
name: `npm@${resolved}`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"npm.js","names":[],"sources":["../../../src/adapters/npm.ts"],"sourcesContent":["import { SpawnOptions, execSync } from 'child_process';\nimport buildDebug from 'debug';\n\nimport { ExecOutput, PackageManagerAdapter } from '../types';\nimport { exec } from '../utils/process';\nimport { prepareGenericEmptyProject } from '../utils/project';\n\nconst debug = buildDebug('verdaccio:e2e-cli:npm');\n\nconst NPM_SUPPORTED_COMMANDS = new Set([\n 'publish',\n 'unpublish',\n 'install',\n 'info',\n 'audit',\n 'deprecate',\n 'dist-tag',\n 'ping',\n 'search',\n]);\n\nfunction detectVersion(bin: string): string {\n try {\n return execSync(`${bin} --version`, { encoding: 'utf8', timeout: 5000 }).trim();\n } catch {\n return 'unknown';\n }\n}\n\nfunction installNpm(version: string): string {\n const pkg = `npm@${version}`;\n debug('installing %s into temp dir', pkg);\n const tmpDir = execSync('mktemp -d', { encoding: 'utf8' }).trim();\n execSync(`npm install --prefix \"${tmpDir}\" ${pkg} --loglevel=error`, {\n encoding: 'utf8',\n timeout: 30000,\n });\n const bin = `${tmpDir}/node_modules/.bin/npm`;\n const installed = detectVersion(bin);\n debug('installed npm %s at %s', installed, bin);\n console.log(` Auto-installed npm ${installed}`);\n return bin;\n}\n\nfunction resolveNpmBin(binPath?: string, version?: string): string {\n if (binPath) return binPath;\n if (version) return installNpm(version);\n return 'npm';\n}\n\nexport function createNpmAdapter(binPath?: string, version?: string): PackageManagerAdapter {\n const bin = resolveNpmBin(binPath, version);\n const resolved = detectVersion(bin);\n debug('creating npm adapter with bin: %s (%s)', bin, resolved);\n\n const adapter: PackageManagerAdapter = {\n name: `npm@${resolved}`,\n type: 'npm',\n bin,\n supports: NPM_SUPPORTED_COMMANDS,\n\n registryArg(url: string): string[] {\n return ['--registry', url];\n },\n\n prefixArg(folder: string): string[] {\n return ['--prefix', folder];\n },\n\n exec(options: SpawnOptions, ...args: string[]): Promise<ExecOutput> {\n return exec(options, bin, args);\n },\n\n async prepareProject(\n packageName: string,\n version: string,\n registryUrl: string,\n port: number,\n token: string,\n dependencies: Record<string, string> = {},\n devDependencies: Record<string, string> = {}\n ): Promise<{ tempFolder: string }> {\n return prepareGenericEmptyProject(\n packageName,\n version,\n port,\n token,\n registryUrl,\n dependencies,\n devDependencies\n );\n },\n };\n\n return adapter;\n}\n"],"mappings":";;;;;AAOA,IAAM,QAAQ,WAAW,uBAAuB;AAEhD,IAAM,yCAAyB,IAAI,IAAI;CACrC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;
|
|
1
|
+
{"version":3,"file":"npm.js","names":[],"sources":["../../../src/adapters/npm.ts"],"sourcesContent":["import { SpawnOptions, execSync } from 'child_process';\nimport buildDebug from 'debug';\n\nimport { ExecOutput, PackageManagerAdapter } from '../types';\nimport { exec } from '../utils/process';\nimport { prepareGenericEmptyProject } from '../utils/project';\n\nconst debug = buildDebug('verdaccio:e2e-cli:npm');\n\nconst NPM_SUPPORTED_COMMANDS = new Set([\n 'publish',\n 'unpublish',\n 'install',\n 'info',\n 'audit',\n 'deprecate',\n 'dist-tag',\n 'ping',\n 'search',\n]);\n\n// Only currently maintained npm majors are supported by the suite.\nconst NPM_SUPPORTED_MAJORS = [10, 11, 12];\n\nfunction detectVersion(bin: string): string {\n try {\n return execSync(`${bin} --version`, { encoding: 'utf8', timeout: 5000 }).trim();\n } catch {\n return 'unknown';\n }\n}\n\nfunction assertSupportedVersion(resolved: string): void {\n const major = parseInt(resolved.split('.')[0], 10);\n if (!Number.isNaN(major) && !NPM_SUPPORTED_MAJORS.includes(major)) {\n throw new Error(\n `npm@${resolved} is not supported. Supported majors: ${NPM_SUPPORTED_MAJORS.join(', ')}`\n );\n }\n}\n\nfunction installNpm(version: string): string {\n const pkg = `npm@${version}`;\n debug('installing %s into temp dir', pkg);\n const tmpDir = execSync('mktemp -d', { encoding: 'utf8' }).trim();\n execSync(`npm install --prefix \"${tmpDir}\" ${pkg} --loglevel=error`, {\n encoding: 'utf8',\n timeout: 30000,\n });\n const bin = `${tmpDir}/node_modules/.bin/npm`;\n const installed = detectVersion(bin);\n debug('installed npm %s at %s', installed, bin);\n console.log(` Auto-installed npm ${installed}`);\n return bin;\n}\n\nfunction resolveNpmBin(binPath?: string, version?: string): string {\n if (binPath) return binPath;\n if (version) return installNpm(version);\n return 'npm';\n}\n\nexport function createNpmAdapter(binPath?: string, version?: string): PackageManagerAdapter {\n const bin = resolveNpmBin(binPath, version);\n const resolved = detectVersion(bin);\n assertSupportedVersion(resolved);\n debug('creating npm adapter with bin: %s (%s)', bin, resolved);\n\n const adapter: PackageManagerAdapter = {\n name: `npm@${resolved}`,\n type: 'npm',\n bin,\n supports: NPM_SUPPORTED_COMMANDS,\n\n registryArg(url: string): string[] {\n return ['--registry', url];\n },\n\n prefixArg(folder: string): string[] {\n return ['--prefix', folder];\n },\n\n exec(options: SpawnOptions, ...args: string[]): Promise<ExecOutput> {\n return exec(options, bin, args);\n },\n\n async prepareProject(\n packageName: string,\n version: string,\n registryUrl: string,\n port: number,\n token: string,\n dependencies: Record<string, string> = {},\n devDependencies: Record<string, string> = {}\n ): Promise<{ tempFolder: string }> {\n return prepareGenericEmptyProject(\n packageName,\n version,\n port,\n token,\n registryUrl,\n dependencies,\n devDependencies\n );\n },\n };\n\n return adapter;\n}\n"],"mappings":";;;;;AAOA,IAAM,QAAQ,WAAW,uBAAuB;AAEhD,IAAM,yCAAyB,IAAI,IAAI;CACrC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAGD,IAAM,uBAAuB;CAAC;CAAI;CAAI;AAAE;AAExC,SAAS,cAAc,KAAqB;CAC1C,IAAI;EACF,OAAO,SAAS,GAAG,IAAI,aAAa;GAAE,UAAU;GAAQ,SAAS;EAAK,CAAC,CAAC,CAAC,KAAK;CAChF,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,uBAAuB,UAAwB;CACtD,MAAM,QAAQ,SAAS,SAAS,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE;CACjD,IAAI,CAAC,OAAO,MAAM,KAAK,KAAK,CAAC,qBAAqB,SAAS,KAAK,GAC9D,MAAM,IAAI,MACR,OAAO,SAAS,uCAAuC,qBAAqB,KAAK,IAAI,GACvF;AAEJ;AAEA,SAAS,WAAW,SAAyB;CAC3C,MAAM,MAAM,OAAO;CACnB,MAAM,+BAA+B,GAAG;CACxC,MAAM,SAAS,SAAS,aAAa,EAAE,UAAU,OAAO,CAAC,CAAC,CAAC,KAAK;CAChE,SAAS,yBAAyB,OAAO,IAAI,IAAI,oBAAoB;EACnE,UAAU;EACV,SAAS;CACX,CAAC;CACD,MAAM,MAAM,GAAG,OAAO;CACtB,MAAM,YAAY,cAAc,GAAG;CACnC,MAAM,0BAA0B,WAAW,GAAG;CAC9C,QAAQ,IAAI,wBAAwB,WAAW;CAC/C,OAAO;AACT;AAEA,SAAS,cAAc,SAAkB,SAA0B;CACjE,IAAI,SAAS,OAAO;CACpB,IAAI,SAAS,OAAO,WAAW,OAAO;CACtC,OAAO;AACT;AAEA,SAAgB,iBAAiB,SAAkB,SAAyC;CAC1F,MAAM,MAAM,cAAc,SAAS,OAAO;CAC1C,MAAM,WAAW,cAAc,GAAG;CAClC,uBAAuB,QAAQ;CAC/B,MAAM,0CAA0C,KAAK,QAAQ;CAyC7D,OAAO;EAtCL,MAAM,OAAO;EACb,MAAM;EACN;EACA,UAAU;EAEV,YAAY,KAAuB;GACjC,OAAO,CAAC,cAAc,GAAG;EAC3B;EAEA,UAAU,QAA0B;GAClC,OAAO,CAAC,YAAY,MAAM;EAC5B;EAEA,KAAK,SAAuB,GAAG,MAAqC;GAClE,OAAO,KAAK,SAAS,KAAK,IAAI;EAChC;EAEA,MAAM,eACJ,aACA,SACA,aACA,MACA,OACA,eAAuC,CAAC,GACxC,kBAA0C,CAAC,GACV;GACjC,OAAO,2BACL,aACA,SACA,MACA,OACA,aACA,cACA,eACF;EACF;CAGK;AACT"}
|