@verdaccio/e2e-cli 3.0.0 → 3.2.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/bin/e2e-cli.js +6 -26
- package/build/adapters/adapter.d.ts +1 -0
- package/build/adapters/bun.d.ts +2 -0
- package/build/adapters/deno.d.ts +2 -0
- package/build/adapters/index.d.ts +5 -0
- package/build/adapters/npm.d.ts +2 -0
- package/build/adapters/pnpm.d.ts +2 -0
- package/build/adapters/yarn-modern.d.ts +2 -0
- package/build/cjs/_virtual/_rolldown/runtime.cjs +23 -0
- package/build/cjs/adapters/bun.cjs +64 -0
- package/build/cjs/adapters/bun.cjs.map +1 -0
- package/build/cjs/adapters/deno.cjs +61 -0
- package/build/cjs/adapters/deno.cjs.map +1 -0
- package/build/cjs/adapters/index.cjs +10 -0
- package/build/cjs/adapters/npm.cjs +85 -0
- package/build/cjs/adapters/npm.cjs.map +1 -0
- package/build/cjs/adapters/pnpm.cjs +114 -0
- package/build/cjs/adapters/pnpm.cjs.map +1 -0
- package/build/cjs/adapters/yarn-modern.cjs +207 -0
- package/build/cjs/adapters/yarn-modern.cjs.map +1 -0
- package/build/cjs/index.cjs +186 -11
- package/build/cjs/index.cjs.map +1 -0
- package/build/cjs/reporter.cjs +113 -0
- package/build/cjs/reporter.cjs.map +1 -0
- package/build/cjs/runner.cjs +141 -0
- package/build/cjs/runner.cjs.map +1 -0
- package/build/cjs/scenarios/index.cjs +25 -0
- package/build/cjs/scenarios/index.cjs.map +1 -0
- package/build/cjs/scenarios/install-multiple-deps.cjs +119 -0
- package/build/cjs/scenarios/install-multiple-deps.cjs.map +1 -0
- 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 +111 -0
- package/build/cjs/scenarios/minimum-release-age.cjs.map +1 -0
- package/build/cjs/scenarios/search.cjs +200 -0
- package/build/cjs/scenarios/search.cjs.map +1 -0
- package/build/cjs/scenarios/tarballs.cjs +170 -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 +60 -0
- package/build/cjs/tests/audit.cjs.map +1 -0
- package/build/cjs/tests/ci.cjs +83 -0
- package/build/cjs/tests/ci.cjs.map +1 -0
- package/build/cjs/tests/deprecate.cjs +108 -0
- package/build/cjs/tests/deprecate.cjs.map +1 -0
- package/build/cjs/tests/dist-tags.cjs +43 -0
- package/build/cjs/tests/dist-tags.cjs.map +1 -0
- package/build/cjs/tests/index.cjs +49 -0
- package/build/cjs/tests/index.cjs.map +1 -0
- package/build/cjs/tests/info.cjs +31 -0
- package/build/cjs/tests/info.cjs.map +1 -0
- package/build/cjs/tests/install.cjs +30 -0
- package/build/cjs/tests/install.cjs.map +1 -0
- package/build/cjs/tests/login.cjs +126 -0
- package/build/cjs/tests/login.cjs.map +1 -0
- package/build/cjs/tests/ping.cjs +26 -0
- package/build/cjs/tests/ping.cjs.map +1 -0
- package/build/cjs/tests/publish.cjs +36 -0
- package/build/cjs/tests/publish.cjs.map +1 -0
- package/build/cjs/tests/search.cjs +23 -0
- package/build/cjs/tests/search.cjs.map +1 -0
- package/build/cjs/tests/unpublish.cjs +40 -0
- package/build/cjs/tests/unpublish.cjs.map +1 -0
- 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/info.cjs +25 -0
- package/build/cjs/utils/info.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/pending-checks.cjs +21 -0
- package/build/cjs/utils/pending-checks.cjs.map +1 -0
- package/build/cjs/utils/process.cjs +96 -0
- package/build/cjs/utils/process.cjs.map +1 -0
- package/build/cjs/utils/project.cjs +55 -0
- package/build/cjs/utils/project.cjs.map +1 -0
- package/build/cjs/utils/publish.cjs +117 -0
- package/build/cjs/utils/publish.cjs.map +1 -0
- package/build/cjs/utils/registry-client.cjs +56 -0
- package/build/cjs/utils/registry-client.cjs.map +1 -0
- package/build/esm/adapters/bun.js +62 -0
- package/build/esm/adapters/bun.js.map +1 -0
- package/build/esm/adapters/deno.js +59 -0
- package/build/esm/adapters/deno.js.map +1 -0
- package/build/esm/adapters/index.js +6 -0
- package/build/esm/adapters/npm.js +83 -0
- package/build/esm/adapters/npm.js.map +1 -0
- package/build/esm/adapters/pnpm.js +112 -0
- package/build/esm/adapters/pnpm.js.map +1 -0
- package/build/esm/adapters/yarn-modern.js +204 -0
- package/build/esm/adapters/yarn-modern.js.map +1 -0
- package/build/esm/index.js +174 -10
- package/build/esm/index.js.map +1 -0
- package/build/esm/reporter.js +108 -0
- package/build/esm/reporter.js.map +1 -0
- package/build/esm/runner.js +138 -0
- package/build/esm/runner.js.map +1 -0
- package/build/esm/scenarios/index.js +19 -0
- package/build/esm/scenarios/index.js.map +1 -0
- package/build/esm/scenarios/install-multiple-deps.js +117 -0
- package/build/esm/scenarios/install-multiple-deps.js.map +1 -0
- 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 +108 -0
- package/build/esm/scenarios/minimum-release-age.js.map +1 -0
- package/build/esm/scenarios/search.js +198 -0
- package/build/esm/scenarios/search.js.map +1 -0
- package/build/esm/scenarios/tarballs.js +168 -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 +57 -0
- package/build/esm/tests/audit.js.map +1 -0
- package/build/esm/tests/ci.js +80 -0
- package/build/esm/tests/ci.js.map +1 -0
- package/build/esm/tests/deprecate.js +105 -0
- package/build/esm/tests/deprecate.js.map +1 -0
- package/build/esm/tests/dist-tags.js +41 -0
- package/build/esm/tests/dist-tags.js.map +1 -0
- package/build/esm/tests/index.js +37 -0
- package/build/esm/tests/index.js.map +1 -0
- package/build/esm/tests/info.js +29 -0
- package/build/esm/tests/info.js.map +1 -0
- package/build/esm/tests/install.js +28 -0
- package/build/esm/tests/install.js.map +1 -0
- package/build/esm/tests/login.js +122 -0
- package/build/esm/tests/login.js.map +1 -0
- package/build/esm/tests/ping.js +24 -0
- package/build/esm/tests/ping.js.map +1 -0
- package/build/esm/tests/publish.js +34 -0
- package/build/esm/tests/publish.js.map +1 -0
- package/build/esm/tests/search.js +21 -0
- package/build/esm/tests/search.js.map +1 -0
- package/build/esm/tests/unpublish.js +38 -0
- package/build/esm/tests/unpublish.js.map +1 -0
- 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/info.js +24 -0
- package/build/esm/utils/info.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/pending-checks.js +21 -0
- package/build/esm/utils/pending-checks.js.map +1 -0
- package/build/esm/utils/process.js +92 -0
- package/build/esm/utils/process.js.map +1 -0
- package/build/esm/utils/project.js +50 -0
- package/build/esm/utils/project.js.map +1 -0
- package/build/esm/utils/publish.js +113 -0
- package/build/esm/utils/publish.js.map +1 -0
- package/build/esm/utils/registry-client.js +53 -0
- package/build/esm/utils/registry-client.js.map +1 -0
- package/build/index.d.ts +6 -15
- package/build/reporter.d.ts +7 -0
- package/build/runner.d.ts +13 -0
- package/build/scenarios/index.d.ts +9 -0
- package/build/scenarios/install-multiple-deps.d.ts +2 -0
- package/build/scenarios/metadata.d.ts +2 -0
- package/build/scenarios/minimum-release-age.d.ts +11 -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/audit.d.ts +2 -0
- package/build/tests/ci.d.ts +2 -0
- package/build/tests/deprecate.d.ts +2 -0
- package/build/tests/dist-tags.d.ts +2 -0
- package/build/tests/index.d.ts +15 -0
- package/build/tests/info.d.ts +2 -0
- package/build/tests/install.d.ts +2 -0
- package/build/tests/login.d.ts +2 -0
- package/build/tests/ping.d.ts +2 -0
- package/build/tests/publish.d.ts +2 -0
- package/build/tests/search.d.ts +2 -0
- package/build/tests/unpublish.d.ts +2 -0
- package/build/types.d.ts +91 -0
- package/build/utils/e2e-config.d.ts +21 -0
- package/build/utils/http-client.d.ts +49 -0
- package/build/utils/index.d.ts +4 -0
- package/build/utils/info.d.ts +14 -0
- package/build/utils/mock-uplink.d.ts +47 -0
- package/build/utils/pending-checks.d.ts +12 -0
- package/build/utils/process.d.ts +11 -0
- package/build/utils/project.d.ts +6 -0
- package/build/utils/publish.d.ts +17 -0
- package/build/utils/registry-client.d.ts +15 -0
- package/package.json +16 -14
- package/Cargo.lock +0 -1591
- package/Cargo.toml +0 -19
- package/build/bin/verdaccio-e2e +0 -0
- package/scripts/build-js-package.mjs +0 -25
- package/src/e2e_tests/audit.rs +0 -51
- package/src/e2e_tests/ci.rs +0 -32
- package/src/e2e_tests/deprecate.rs +0 -64
- package/src/e2e_tests/dist_tags.rs +0 -56
- package/src/e2e_tests/info.rs +0 -43
- package/src/e2e_tests/install.rs +0 -27
- package/src/e2e_tests/login.rs +0 -64
- package/src/e2e_tests/mod.rs +0 -104
- package/src/e2e_tests/ping.rs +0 -33
- package/src/e2e_tests/publish.rs +0 -68
- package/src/e2e_tests/search.rs +0 -14
- package/src/e2e_tests/unpublish.rs +0 -37
- package/src/main.rs +0 -1411
- package/src/scenarios/install_multiple_deps.rs +0 -99
- package/src/scenarios/minimum_release_age.rs +0 -45
- package/src/scenarios/mod.rs +0 -11
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { createTempFolder, getPackageJSON, getREADME } from "../utils/project.js";
|
|
2
|
+
import buildDebug from "debug";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { writeFile } from "fs/promises";
|
|
5
|
+
import * as YAML from "js-yaml";
|
|
6
|
+
import assert from "assert";
|
|
7
|
+
//#region src/tests/login.ts
|
|
8
|
+
var debug = buildDebug("verdaccio:e2e-cli:test:login");
|
|
9
|
+
/**
|
|
10
|
+
* Creates a project with registry config but WITHOUT auth token,
|
|
11
|
+
* so whoami will use the token saved by login to ~/.yarnrc.yml.
|
|
12
|
+
*/
|
|
13
|
+
async function prepareLoginProject(ctx, name) {
|
|
14
|
+
debug("preparing project for %s (no auth token)", name);
|
|
15
|
+
const tempFolder = await createTempFolder(name);
|
|
16
|
+
const yamlContent = YAML.dump({
|
|
17
|
+
npmRegistryServer: ctx.registryUrl,
|
|
18
|
+
enableImmutableInstalls: false,
|
|
19
|
+
unsafeHttpWhitelist: ["localhost"]
|
|
20
|
+
});
|
|
21
|
+
await writeFile(join(tempFolder, ".yarnrc.yml"), yamlContent);
|
|
22
|
+
await writeFile(join(tempFolder, "package.json"), JSON.stringify({
|
|
23
|
+
name,
|
|
24
|
+
version: "1.0.0"
|
|
25
|
+
}));
|
|
26
|
+
if (ctx.adapter.importPlugin) {
|
|
27
|
+
debug("importing npm-login plugin into %s", tempFolder);
|
|
28
|
+
await ctx.adapter.importPlugin(tempFolder, "npm-login");
|
|
29
|
+
}
|
|
30
|
+
return tempFolder;
|
|
31
|
+
}
|
|
32
|
+
async function yarnLogin(ctx, cwd, user, password, email) {
|
|
33
|
+
debug("yarn npm login as %s", user);
|
|
34
|
+
return ctx.adapter.exec({ cwd }, "login", "--auth-type=legacy", `--user=${user}`, `--password=${password}`, `--email=${email}`);
|
|
35
|
+
}
|
|
36
|
+
async function yarnWhoami(ctx, cwd) {
|
|
37
|
+
const resp = await ctx.adapter.exec({ cwd }, "whoami");
|
|
38
|
+
debug("yarn npm whoami: %s", resp.stdout);
|
|
39
|
+
return resp.stdout;
|
|
40
|
+
}
|
|
41
|
+
async function testLogin(ctx) {
|
|
42
|
+
const type = ctx.adapter.type;
|
|
43
|
+
const id = ctx.runId;
|
|
44
|
+
if (type !== "yarn-modern") {
|
|
45
|
+
debug("login test only supported for yarn-modern, skipping %s", type);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const user1 = `login-a-${id}`;
|
|
49
|
+
const password1 = "test-password-123";
|
|
50
|
+
const email1 = `${user1}@test.example.com`;
|
|
51
|
+
await ctx.subTest("create new user + whoami", async () => {
|
|
52
|
+
const tf = await prepareLoginProject(ctx, `verdaccio-login1-${id}`);
|
|
53
|
+
const loginResp = await yarnLogin(ctx, tf, user1, password1, email1);
|
|
54
|
+
assert.ok(loginResp.stdout.includes("Logged in") || loginResp.stdout.includes("token saved"), `Expected login success message but got "${loginResp.stdout}"`);
|
|
55
|
+
const who = await yarnWhoami(ctx, tf);
|
|
56
|
+
assert.ok(who.includes(user1), `Expected whoami "${user1}" but got "${who}"`);
|
|
57
|
+
});
|
|
58
|
+
await ctx.subTest("legacy re-login is rejected without basic auth", async () => {
|
|
59
|
+
const tf = await prepareLoginProject(ctx, `verdaccio-login2-${id}`);
|
|
60
|
+
let loginFailed = false;
|
|
61
|
+
try {
|
|
62
|
+
await yarnLogin(ctx, tf, user1, password1, email1);
|
|
63
|
+
} catch {
|
|
64
|
+
loginFailed = true;
|
|
65
|
+
}
|
|
66
|
+
assert.ok(loginFailed, "Legacy re-login for an existing user should fail without Basic auth");
|
|
67
|
+
});
|
|
68
|
+
await ctx.subTest("wrong password fails", async () => {
|
|
69
|
+
const tf = await prepareLoginProject(ctx, `verdaccio-login3-${id}`);
|
|
70
|
+
let loginFailed = false;
|
|
71
|
+
try {
|
|
72
|
+
await yarnLogin(ctx, tf, user1, "wrong-password", email1);
|
|
73
|
+
} catch {
|
|
74
|
+
loginFailed = true;
|
|
75
|
+
}
|
|
76
|
+
assert.ok(loginFailed, "Login with wrong password should have failed");
|
|
77
|
+
});
|
|
78
|
+
await ctx.subTest("login then publish", async () => {
|
|
79
|
+
const pkgName = `@verdaccio/login-pub-${id}`;
|
|
80
|
+
const publishUser = `login-pub-${id}`;
|
|
81
|
+
const publishPassword = "publish-password-123";
|
|
82
|
+
const publishEmail = `${publishUser}@test.example.com`;
|
|
83
|
+
const tf = await createTempFolder(`login-pub-${id}`);
|
|
84
|
+
const yamlContent = YAML.dump({
|
|
85
|
+
npmRegistryServer: ctx.registryUrl,
|
|
86
|
+
enableImmutableInstalls: false,
|
|
87
|
+
unsafeHttpWhitelist: ["localhost"]
|
|
88
|
+
});
|
|
89
|
+
await writeFile(join(tf, ".yarnrc.yml"), yamlContent);
|
|
90
|
+
await writeFile(join(tf, "package.json"), getPackageJSON(pkgName, "1.0.0"));
|
|
91
|
+
await writeFile(join(tf, "README.md"), getREADME(pkgName));
|
|
92
|
+
if (ctx.adapter.importPlugin) await ctx.adapter.importPlugin(tf, "npm-login");
|
|
93
|
+
await yarnLogin(ctx, tf, publishUser, publishPassword, publishEmail);
|
|
94
|
+
await ctx.adapter.exec({ cwd: tf }, "install");
|
|
95
|
+
const pubResp = await ctx.adapter.exec({ cwd: tf }, "publish");
|
|
96
|
+
assert.ok(pubResp.stdout.includes("Package archive published"), `Expected publish success but got "${pubResp.stdout}"`);
|
|
97
|
+
});
|
|
98
|
+
await ctx.subTest("switch users", async () => {
|
|
99
|
+
const switchUser1 = `login-switch-a-${id}`;
|
|
100
|
+
const switchPassword1 = "switch-password-123";
|
|
101
|
+
const switchEmail1 = `${switchUser1}@test.example.com`;
|
|
102
|
+
const user2 = `login-b-${id}`;
|
|
103
|
+
const password2 = "other-password-456";
|
|
104
|
+
const email2 = `${user2}@test.example.com`;
|
|
105
|
+
const tf = await prepareLoginProject(ctx, `verdaccio-login5-${id}`);
|
|
106
|
+
await yarnLogin(ctx, tf, switchUser1, switchPassword1, switchEmail1);
|
|
107
|
+
const who1 = await yarnWhoami(ctx, tf);
|
|
108
|
+
assert.ok(who1.includes(switchUser1), `Expected "${switchUser1}" but got "${who1}"`);
|
|
109
|
+
await yarnLogin(ctx, tf, user2, password2, email2);
|
|
110
|
+
const who2 = await yarnWhoami(ctx, tf);
|
|
111
|
+
assert.ok(who2.includes(user2), `Expected "${user2}" after switch but got "${who2}"`);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
var loginTest = {
|
|
115
|
+
name: "login",
|
|
116
|
+
requires: ["login"],
|
|
117
|
+
run: testLogin
|
|
118
|
+
};
|
|
119
|
+
//#endregion
|
|
120
|
+
export { loginTest };
|
|
121
|
+
|
|
122
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","names":[],"sources":["../../../src/tests/login.ts"],"sourcesContent":["import assert from 'assert';\nimport buildDebug from 'debug';\nimport { writeFile } from 'fs/promises';\nimport * as YAML from 'js-yaml';\nimport { join } from 'path';\n\nimport { TestContext, TestDefinition } from '../types';\nimport { createTempFolder, getPackageJSON, getREADME } from '../utils/project';\n\nconst debug = buildDebug('verdaccio:e2e-cli:test:login');\n\n/**\n * Creates a project with registry config but WITHOUT auth token,\n * so whoami will use the token saved by login to ~/.yarnrc.yml.\n */\nasync function prepareLoginProject(ctx: TestContext, name: string): Promise<string> {\n debug('preparing project for %s (no auth token)', name);\n const tempFolder = await createTempFolder(name);\n const yamlContent = YAML.dump({\n npmRegistryServer: ctx.registryUrl,\n enableImmutableInstalls: false,\n unsafeHttpWhitelist: ['localhost'],\n });\n await writeFile(join(tempFolder, '.yarnrc.yml'), yamlContent);\n await writeFile(join(tempFolder, 'package.json'), JSON.stringify({ name, version: '1.0.0' }));\n if (ctx.adapter.importPlugin) {\n debug('importing npm-login plugin into %s', tempFolder);\n await ctx.adapter.importPlugin(tempFolder, 'npm-login');\n }\n return tempFolder;\n}\n\nasync function yarnLogin(\n ctx: TestContext,\n cwd: string,\n user: string,\n password: string,\n email: string\n) {\n debug('yarn npm login as %s', user);\n return ctx.adapter.exec(\n { cwd },\n 'login',\n '--auth-type=legacy',\n `--user=${user}`,\n `--password=${password}`,\n `--email=${email}`\n );\n}\n\nasync function yarnWhoami(ctx: TestContext, cwd: string): Promise<string> {\n const resp = await ctx.adapter.exec({ cwd }, 'whoami');\n debug('yarn npm whoami: %s', resp.stdout);\n return resp.stdout;\n}\n\nasync function testLogin(ctx: TestContext): Promise<void> {\n const type = ctx.adapter.type;\n const id = ctx.runId;\n\n if (type !== 'yarn-modern') {\n debug('login test only supported for yarn-modern, skipping %s', type);\n return;\n }\n\n const user1 = `login-a-${id}`;\n const password1 = 'test-password-123';\n const email1 = `${user1}@test.example.com`;\n\n await ctx.subTest('create new user + whoami', async () => {\n const tf = await prepareLoginProject(ctx, `verdaccio-login1-${id}`);\n const loginResp = await yarnLogin(ctx, tf, user1, password1, email1);\n assert.ok(\n loginResp.stdout.includes('Logged in') || loginResp.stdout.includes('token saved'),\n `Expected login success message but got \"${loginResp.stdout}\"`\n );\n const who = await yarnWhoami(ctx, tf);\n assert.ok(who.includes(user1), `Expected whoami \"${user1}\" but got \"${who}\"`);\n });\n\n await ctx.subTest('legacy re-login is rejected without basic auth', async () => {\n const tf = await prepareLoginProject(ctx, `verdaccio-login2-${id}`);\n let loginFailed = false;\n try {\n await yarnLogin(ctx, tf, user1, password1, email1);\n } catch {\n loginFailed = true;\n }\n assert.ok(loginFailed, 'Legacy re-login for an existing user should fail without Basic auth');\n });\n\n await ctx.subTest('wrong password fails', async () => {\n const tf = await prepareLoginProject(ctx, `verdaccio-login3-${id}`);\n let loginFailed = false;\n try {\n await yarnLogin(ctx, tf, user1, 'wrong-password', email1);\n } catch {\n loginFailed = true;\n }\n assert.ok(loginFailed, 'Login with wrong password should have failed');\n });\n\n await ctx.subTest('login then publish', async () => {\n const pkgName = `@verdaccio/login-pub-${id}`;\n const publishUser = `login-pub-${id}`;\n const publishPassword = 'publish-password-123';\n const publishEmail = `${publishUser}@test.example.com`;\n const tf = await createTempFolder(`login-pub-${id}`);\n const yamlContent = YAML.dump({\n npmRegistryServer: ctx.registryUrl,\n enableImmutableInstalls: false,\n unsafeHttpWhitelist: ['localhost'],\n });\n await writeFile(join(tf, '.yarnrc.yml'), yamlContent);\n await writeFile(join(tf, 'package.json'), getPackageJSON(pkgName, '1.0.0'));\n await writeFile(join(tf, 'README.md'), getREADME(pkgName));\n if (ctx.adapter.importPlugin) {\n await ctx.adapter.importPlugin(tf, 'npm-login');\n }\n await yarnLogin(ctx, tf, publishUser, publishPassword, publishEmail);\n await ctx.adapter.exec({ cwd: tf }, 'install');\n const pubResp = await ctx.adapter.exec({ cwd: tf }, 'publish');\n assert.ok(\n pubResp.stdout.includes('Package archive published'),\n `Expected publish success but got \"${pubResp.stdout}\"`\n );\n });\n\n await ctx.subTest('switch users', async () => {\n const switchUser1 = `login-switch-a-${id}`;\n const switchPassword1 = 'switch-password-123';\n const switchEmail1 = `${switchUser1}@test.example.com`;\n const user2 = `login-b-${id}`;\n const password2 = 'other-password-456';\n const email2 = `${user2}@test.example.com`;\n const tf = await prepareLoginProject(ctx, `verdaccio-login5-${id}`);\n\n await yarnLogin(ctx, tf, switchUser1, switchPassword1, switchEmail1);\n const who1 = await yarnWhoami(ctx, tf);\n assert.ok(who1.includes(switchUser1), `Expected \"${switchUser1}\" but got \"${who1}\"`);\n\n await yarnLogin(ctx, tf, user2, password2, email2);\n const who2 = await yarnWhoami(ctx, tf);\n assert.ok(who2.includes(user2), `Expected \"${user2}\" after switch but got \"${who2}\"`);\n });\n}\n\nexport const loginTest: TestDefinition = {\n name: 'login',\n requires: ['login'],\n run: testLogin,\n};\n"],"mappings":";;;;;;;AASA,IAAM,QAAQ,WAAW,8BAA8B;;;;;AAMvD,eAAe,oBAAoB,KAAkB,MAA+B;CAClF,MAAM,4CAA4C,IAAI;CACtD,MAAM,aAAa,MAAM,iBAAiB,IAAI;CAC9C,MAAM,cAAc,KAAK,KAAK;EAC5B,mBAAmB,IAAI;EACvB,yBAAyB;EACzB,qBAAqB,CAAC,WAAW;CACnC,CAAC;CACD,MAAM,UAAU,KAAK,YAAY,aAAa,GAAG,WAAW;CAC5D,MAAM,UAAU,KAAK,YAAY,cAAc,GAAG,KAAK,UAAU;EAAE;EAAM,SAAS;CAAQ,CAAC,CAAC;CAC5F,IAAI,IAAI,QAAQ,cAAc;EAC5B,MAAM,sCAAsC,UAAU;EACtD,MAAM,IAAI,QAAQ,aAAa,YAAY,WAAW;CACxD;CACA,OAAO;AACT;AAEA,eAAe,UACb,KACA,KACA,MACA,UACA,OACA;CACA,MAAM,wBAAwB,IAAI;CAClC,OAAO,IAAI,QAAQ,KACjB,EAAE,IAAI,GACN,SACA,sBACA,UAAU,QACV,cAAc,YACd,WAAW,OACb;AACF;AAEA,eAAe,WAAW,KAAkB,KAA8B;CACxE,MAAM,OAAO,MAAM,IAAI,QAAQ,KAAK,EAAE,IAAI,GAAG,QAAQ;CACrD,MAAM,uBAAuB,KAAK,MAAM;CACxC,OAAO,KAAK;AACd;AAEA,eAAe,UAAU,KAAiC;CACxD,MAAM,OAAO,IAAI,QAAQ;CACzB,MAAM,KAAK,IAAI;CAEf,IAAI,SAAS,eAAe;EAC1B,MAAM,0DAA0D,IAAI;EACpE;CACF;CAEA,MAAM,QAAQ,WAAW;CACzB,MAAM,YAAY;CAClB,MAAM,SAAS,GAAG,MAAM;CAExB,MAAM,IAAI,QAAQ,4BAA4B,YAAY;EACxD,MAAM,KAAK,MAAM,oBAAoB,KAAK,oBAAoB,IAAI;EAClE,MAAM,YAAY,MAAM,UAAU,KAAK,IAAI,OAAO,WAAW,MAAM;EACnE,OAAO,GACL,UAAU,OAAO,SAAS,WAAW,KAAK,UAAU,OAAO,SAAS,aAAa,GACjF,2CAA2C,UAAU,OAAO,EAC9D;EACA,MAAM,MAAM,MAAM,WAAW,KAAK,EAAE;EACpC,OAAO,GAAG,IAAI,SAAS,KAAK,GAAG,oBAAoB,MAAM,aAAa,IAAI,EAAE;CAC9E,CAAC;CAED,MAAM,IAAI,QAAQ,kDAAkD,YAAY;EAC9E,MAAM,KAAK,MAAM,oBAAoB,KAAK,oBAAoB,IAAI;EAClE,IAAI,cAAc;EAClB,IAAI;GACF,MAAM,UAAU,KAAK,IAAI,OAAO,WAAW,MAAM;EACnD,QAAQ;GACN,cAAc;EAChB;EACA,OAAO,GAAG,aAAa,qEAAqE;CAC9F,CAAC;CAED,MAAM,IAAI,QAAQ,wBAAwB,YAAY;EACpD,MAAM,KAAK,MAAM,oBAAoB,KAAK,oBAAoB,IAAI;EAClE,IAAI,cAAc;EAClB,IAAI;GACF,MAAM,UAAU,KAAK,IAAI,OAAO,kBAAkB,MAAM;EAC1D,QAAQ;GACN,cAAc;EAChB;EACA,OAAO,GAAG,aAAa,8CAA8C;CACvE,CAAC;CAED,MAAM,IAAI,QAAQ,sBAAsB,YAAY;EAClD,MAAM,UAAU,wBAAwB;EACxC,MAAM,cAAc,aAAa;EACjC,MAAM,kBAAkB;EACxB,MAAM,eAAe,GAAG,YAAY;EACpC,MAAM,KAAK,MAAM,iBAAiB,aAAa,IAAI;EACnD,MAAM,cAAc,KAAK,KAAK;GAC5B,mBAAmB,IAAI;GACvB,yBAAyB;GACzB,qBAAqB,CAAC,WAAW;EACnC,CAAC;EACD,MAAM,UAAU,KAAK,IAAI,aAAa,GAAG,WAAW;EACpD,MAAM,UAAU,KAAK,IAAI,cAAc,GAAG,eAAe,SAAS,OAAO,CAAC;EAC1E,MAAM,UAAU,KAAK,IAAI,WAAW,GAAG,UAAU,OAAO,CAAC;EACzD,IAAI,IAAI,QAAQ,cACd,MAAM,IAAI,QAAQ,aAAa,IAAI,WAAW;EAEhD,MAAM,UAAU,KAAK,IAAI,aAAa,iBAAiB,YAAY;EACnE,MAAM,IAAI,QAAQ,KAAK,EAAE,KAAK,GAAG,GAAG,SAAS;EAC7C,MAAM,UAAU,MAAM,IAAI,QAAQ,KAAK,EAAE,KAAK,GAAG,GAAG,SAAS;EAC7D,OAAO,GACL,QAAQ,OAAO,SAAS,2BAA2B,GACnD,qCAAqC,QAAQ,OAAO,EACtD;CACF,CAAC;CAED,MAAM,IAAI,QAAQ,gBAAgB,YAAY;EAC5C,MAAM,cAAc,kBAAkB;EACtC,MAAM,kBAAkB;EACxB,MAAM,eAAe,GAAG,YAAY;EACpC,MAAM,QAAQ,WAAW;EACzB,MAAM,YAAY;EAClB,MAAM,SAAS,GAAG,MAAM;EACxB,MAAM,KAAK,MAAM,oBAAoB,KAAK,oBAAoB,IAAI;EAElE,MAAM,UAAU,KAAK,IAAI,aAAa,iBAAiB,YAAY;EACnE,MAAM,OAAO,MAAM,WAAW,KAAK,EAAE;EACrC,OAAO,GAAG,KAAK,SAAS,WAAW,GAAG,aAAa,YAAY,aAAa,KAAK,EAAE;EAEnF,MAAM,UAAU,KAAK,IAAI,OAAO,WAAW,MAAM;EACjD,MAAM,OAAO,MAAM,WAAW,KAAK,EAAE;EACrC,OAAO,GAAG,KAAK,SAAS,KAAK,GAAG,aAAa,MAAM,0BAA0B,KAAK,EAAE;CACtF,CAAC;AACH;AAEA,IAAa,YAA4B;CACvC,MAAM;CACN,UAAU,CAAC,OAAO;CAClB,KAAK;AACP"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
//#region src/tests/ping.ts
|
|
3
|
+
async function testPing(ctx) {
|
|
4
|
+
const type = ctx.adapter.type;
|
|
5
|
+
const { tempFolder } = await ctx.adapter.prepareProject(`verdaccio-ping-${ctx.runId}`, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
6
|
+
const cwd = tempFolder;
|
|
7
|
+
if (type === "yarn-modern" && ctx.adapter.importPlugin) await ctx.adapter.importPlugin(tempFolder, "npm-ping");
|
|
8
|
+
const jsonFlag = type === "yarn-modern" ? [] : ["--json"];
|
|
9
|
+
const resp = await ctx.adapter.exec({ cwd }, "ping", ...jsonFlag, ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
10
|
+
if (type === "yarn-modern") assert.ok(true, "ping succeeded");
|
|
11
|
+
else {
|
|
12
|
+
const parsedBody = JSON.parse(resp.stdout);
|
|
13
|
+
assert.strictEqual(parsedBody.registry, ctx.registryUrl + "/", `Expected registry "${ctx.registryUrl}/" but got "${parsedBody.registry}"`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
var pingTest = {
|
|
17
|
+
name: "ping",
|
|
18
|
+
requires: ["ping"],
|
|
19
|
+
run: testPing
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { pingTest };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=ping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ping.js","names":[],"sources":["../../../src/tests/ping.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function testPing(ctx: TestContext): Promise<void> {\n const type = ctx.adapter.type;\n\n // Always run from a prepared project: its .npmrc carries the fresh token and\n // shields the test from whatever the developer's global ~/.npmrc has for\n // this registry (a stale token there would turn into a spurious E401).\n const { tempFolder } = await ctx.adapter.prepareProject(\n `verdaccio-ping-${ctx.runId}`,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n const cwd = tempFolder;\n if (type === 'yarn-modern' && ctx.adapter.importPlugin) {\n await ctx.adapter.importPlugin(tempFolder, 'npm-ping');\n }\n\n const jsonFlag = type === 'yarn-modern' ? [] : ['--json'];\n const resp = await ctx.adapter.exec(\n { cwd },\n 'ping',\n ...jsonFlag,\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n if (type === 'yarn-modern') {\n // yarn npm ping succeeding (exit code 0) is sufficient proof the registry is reachable\n assert.ok(true, 'ping succeeded');\n } else {\n const parsedBody = JSON.parse(resp.stdout);\n assert.strictEqual(\n parsedBody.registry,\n ctx.registryUrl + '/',\n `Expected registry \"${ctx.registryUrl}/\" but got \"${parsedBody.registry}\"`\n );\n }\n}\n\nexport const pingTest: TestDefinition = {\n name: 'ping',\n requires: ['ping'],\n run: testPing,\n};\n"],"mappings":";;AAIA,eAAe,SAAS,KAAiC;CACvD,MAAM,OAAO,IAAI,QAAQ;CAKzB,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,kBAAkB,IAAI,SACtB,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,KACN;CACA,MAAM,MAAM;CACZ,IAAI,SAAS,iBAAiB,IAAI,QAAQ,cACxC,MAAM,IAAI,QAAQ,aAAa,YAAY,UAAU;CAGvD,MAAM,WAAW,SAAS,gBAAgB,CAAC,IAAI,CAAC,QAAQ;CACxD,MAAM,OAAO,MAAM,IAAI,QAAQ,KAC7B,EAAE,IAAI,GACN,QACA,GAAG,UACH,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CAEA,IAAI,SAAS,eAEX,OAAO,GAAG,MAAM,gBAAgB;MAC3B;EACL,MAAM,aAAa,KAAK,MAAM,KAAK,MAAM;EACzC,OAAO,YACL,WAAW,UACX,IAAI,cAAc,KAClB,sBAAsB,IAAI,YAAY,cAAc,WAAW,SAAS,EAC1E;CACF;AACF;AAEA,IAAa,WAA2B;CACtC,MAAM;CACN,UAAU,CAAC,MAAM;CACjB,KAAK;AACP"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
//#region src/tests/publish.ts
|
|
3
|
+
async function testPublish(ctx) {
|
|
4
|
+
const packages = [
|
|
5
|
+
`verdaccio-memory-${ctx.runId}`,
|
|
6
|
+
`verdaccio-${ctx.runId}`,
|
|
7
|
+
`@verdaccio/foo-${ctx.runId}`,
|
|
8
|
+
`@verdaccio/some-foo-${ctx.runId}`
|
|
9
|
+
];
|
|
10
|
+
const type = ctx.adapter.type;
|
|
11
|
+
for (const pkgName of packages) {
|
|
12
|
+
const { tempFolder } = await ctx.adapter.prepareProject(pkgName, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
13
|
+
if (type === "yarn-modern") await ctx.adapter.exec({ cwd: tempFolder }, "install");
|
|
14
|
+
const resp = await ctx.adapter.exec({ cwd: tempFolder }, "publish", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
15
|
+
if (type === "yarn-modern") assert.ok(resp.stdout.match(/Package archive published/), `Expected "Package archive published" for ${pkgName} but got "${resp.stdout}"`);
|
|
16
|
+
else if (type === "bun") assert.ok(true, `Publish succeeded for ${pkgName}`);
|
|
17
|
+
else try {
|
|
18
|
+
const parsedBody = JSON.parse(resp.stdout);
|
|
19
|
+
assert.strictEqual(parsedBody.name, pkgName, `Expected package name "${pkgName}" but got "${parsedBody.name}"`);
|
|
20
|
+
assert.ok(parsedBody.files, `Expected files to be defined for ${pkgName}`);
|
|
21
|
+
} catch {
|
|
22
|
+
assert.ok(true, `Publish succeeded for ${pkgName}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
var publishTest = {
|
|
27
|
+
name: "publish",
|
|
28
|
+
requires: ["publish"],
|
|
29
|
+
run: testPublish
|
|
30
|
+
};
|
|
31
|
+
//#endregion
|
|
32
|
+
export { publishTest };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=publish.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish.js","names":[],"sources":["../../../src/tests/publish.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function testPublish(ctx: TestContext): Promise<void> {\n const packages = [\n `verdaccio-memory-${ctx.runId}`,\n `verdaccio-${ctx.runId}`,\n `@verdaccio/foo-${ctx.runId}`,\n `@verdaccio/some-foo-${ctx.runId}`,\n ];\n\n const type = ctx.adapter.type;\n\n for (const pkgName of packages) {\n const { tempFolder } = await ctx.adapter.prepareProject(\n pkgName,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n\n // yarn modern requires install before publish to generate lockfile\n if (type === 'yarn-modern') {\n await ctx.adapter.exec({ cwd: tempFolder }, 'install');\n }\n\n const resp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'publish',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n\n if (type === 'yarn-modern') {\n assert.ok(\n resp.stdout.match(/Package archive published/),\n `Expected \"Package archive published\" for ${pkgName} but got \"${resp.stdout}\"`\n );\n } else if (type === 'bun') {\n // bun publish outputs text confirmation — exit code 0 is sufficient\n assert.ok(true, `Publish succeeded for ${pkgName}`);\n } else {\n // npm / pnpm\n try {\n const parsedBody = JSON.parse(resp.stdout);\n assert.strictEqual(\n parsedBody.name,\n pkgName,\n `Expected package name \"${pkgName}\" but got \"${parsedBody.name}\"`\n );\n assert.ok(parsedBody.files, `Expected files to be defined for ${pkgName}`);\n } catch {\n // pnpm v11+ native publish doesn't output JSON — exit code 0 is sufficient\n assert.ok(true, `Publish succeeded for ${pkgName}`);\n }\n }\n }\n}\n\nexport const publishTest: TestDefinition = {\n name: 'publish',\n requires: ['publish'],\n run: testPublish,\n};\n"],"mappings":";;AAIA,eAAe,YAAY,KAAiC;CAC1D,MAAM,WAAW;EACf,oBAAoB,IAAI;EACxB,aAAa,IAAI;EACjB,kBAAkB,IAAI;EACtB,uBAAuB,IAAI;CAC7B;CAEA,MAAM,OAAO,IAAI,QAAQ;CAEzB,KAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,SACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,KACN;EAGA,IAAI,SAAS,eACX,MAAM,IAAI,QAAQ,KAAK,EAAE,KAAK,WAAW,GAAG,SAAS;EAGvD,MAAM,OAAO,MAAM,IAAI,QAAQ,KAC7B,EAAE,KAAK,WAAW,GAClB,WACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;EAEA,IAAI,SAAS,eACX,OAAO,GACL,KAAK,OAAO,MAAM,2BAA2B,GAC7C,4CAA4C,QAAQ,YAAY,KAAK,OAAO,EAC9E;OACK,IAAI,SAAS,OAElB,OAAO,GAAG,MAAM,yBAAyB,SAAS;OAGlD,IAAI;GACF,MAAM,aAAa,KAAK,MAAM,KAAK,MAAM;GACzC,OAAO,YACL,WAAW,MACX,SACA,0BAA0B,QAAQ,aAAa,WAAW,KAAK,EACjE;GACA,OAAO,GAAG,WAAW,OAAO,oCAAoC,SAAS;EAC3E,QAAQ;GAEN,OAAO,GAAG,MAAM,yBAAyB,SAAS;EACpD;CAEJ;AACF;AAEA,IAAa,cAA8B;CACzC,MAAM;CACN,UAAU,CAAC,SAAS;CACpB,KAAK;AACP"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { publishLocalPackage } from "../utils/publish.js";
|
|
2
|
+
import assert from "assert";
|
|
3
|
+
//#region src/tests/search.ts
|
|
4
|
+
async function testSearch(ctx) {
|
|
5
|
+
const pkgName = `verdaccio-search-${ctx.runId}`;
|
|
6
|
+
await publishLocalPackage(ctx, pkgName, "1.0.0");
|
|
7
|
+
const { tempFolder } = await ctx.adapter.prepareProject(`search-client-${ctx.runId}`, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
8
|
+
const resp = await ctx.adapter.exec({ cwd: tempFolder }, "search", pkgName, "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
9
|
+
const pkgFind = JSON.parse(resp.stdout).find((item) => item.name === pkgName);
|
|
10
|
+
assert.ok(pkgFind, `Expected to find ${pkgName} in search results`);
|
|
11
|
+
assert.strictEqual(pkgFind.name, pkgName);
|
|
12
|
+
}
|
|
13
|
+
var searchTest = {
|
|
14
|
+
name: "search",
|
|
15
|
+
requires: ["search", "publish"],
|
|
16
|
+
run: testSearch
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
export { searchTest };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","names":[],"sources":["../../../src/tests/search.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\nimport { publishLocalPackage } from '../utils/publish';\n\nasync function testSearch(ctx: TestContext): Promise<void> {\n // Publish the package we search for — searching an npmjs package would need\n // an uplink and the suite must run offline.\n const pkgName = `verdaccio-search-${ctx.runId}`;\n await publishLocalPackage(ctx, pkgName, '1.0.0');\n\n // Run from a prepared project so its .npmrc (fresh token) shields the test\n // from stale credentials in the developer's global ~/.npmrc.\n const { tempFolder } = await ctx.adapter.prepareProject(\n `search-client-${ctx.runId}`,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n\n const resp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'search',\n pkgName,\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n const parsedBody = JSON.parse(resp.stdout);\n const pkgFind = parsedBody.find((item: any) => item.name === pkgName);\n assert.ok(pkgFind, `Expected to find ${pkgName} in search results`);\n assert.strictEqual(pkgFind.name, pkgName);\n}\n\nexport const searchTest: TestDefinition = {\n name: 'search',\n requires: ['search', 'publish'],\n run: testSearch,\n};\n"],"mappings":";;;AAKA,eAAe,WAAW,KAAiC;CAGzD,MAAM,UAAU,oBAAoB,IAAI;CACxC,MAAM,oBAAoB,KAAK,SAAS,OAAO;CAI/C,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,iBAAiB,IAAI,SACrB,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,KACN;CAEA,MAAM,OAAO,MAAM,IAAI,QAAQ,KAC7B,EAAE,KAAK,WAAW,GAClB,UACA,SACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CAEA,MAAM,UADa,KAAK,MAAM,KAAK,MACnB,CAAA,CAAW,MAAM,SAAc,KAAK,SAAS,OAAO;CACpE,OAAO,GAAG,SAAS,oBAAoB,QAAQ,mBAAmB;CAClE,OAAO,YAAY,QAAQ,MAAM,OAAO;AAC1C;AAEA,IAAa,aAA6B;CACxC,MAAM;CACN,UAAU,CAAC,UAAU,SAAS;CAC9B,KAAK;AACP"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
//#region src/tests/unpublish.ts
|
|
3
|
+
async function publishPkg(ctx, tempFolder) {
|
|
4
|
+
await ctx.adapter.exec({ cwd: tempFolder }, "publish", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
5
|
+
}
|
|
6
|
+
async function bumpVersion(ctx, tempFolder, bump) {
|
|
7
|
+
await ctx.adapter.exec({ cwd: tempFolder }, "version", bump, "--no--git-tag-version", "--loglevel=info", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
8
|
+
}
|
|
9
|
+
async function testUnpublish(ctx) {
|
|
10
|
+
const id = ctx.runId;
|
|
11
|
+
const pkgName = `@verdaccio/unpub1-${id}`;
|
|
12
|
+
const { tempFolder } = await ctx.adapter.prepareProject(pkgName, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
13
|
+
await publishPkg(ctx, tempFolder);
|
|
14
|
+
await bumpVersion(ctx, tempFolder, "minor");
|
|
15
|
+
await publishPkg(ctx, tempFolder);
|
|
16
|
+
await bumpVersion(ctx, tempFolder, "minor");
|
|
17
|
+
await publishPkg(ctx, tempFolder);
|
|
18
|
+
await bumpVersion(ctx, tempFolder, "major");
|
|
19
|
+
await publishPkg(ctx, tempFolder);
|
|
20
|
+
const resp = await ctx.adapter.exec({ cwd: tempFolder }, "unpublish", pkgName, "--force", "--loglevel=info", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
21
|
+
assert.ok(resp.stdout.includes(pkgName), `Expected unpublish output to contain "${pkgName}" but got "${resp.stdout}"`);
|
|
22
|
+
const pkgName2 = `@verdaccio/unpub2-${id}`;
|
|
23
|
+
const { tempFolder: tf2 } = await ctx.adapter.prepareProject(pkgName2, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
24
|
+
await publishPkg(ctx, tf2);
|
|
25
|
+
await bumpVersion(ctx, tf2, "minor");
|
|
26
|
+
await publishPkg(ctx, tf2);
|
|
27
|
+
const resp2 = await ctx.adapter.exec({ cwd: tf2 }, "unpublish", `${pkgName2}@1.0.0`, "--force", "--loglevel=info", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
28
|
+
assert.ok(resp2.stdout.includes(pkgName2), `Expected unpublish output to contain "${pkgName2}" but got "${resp2.stdout}"`);
|
|
29
|
+
}
|
|
30
|
+
var unpublishTest = {
|
|
31
|
+
name: "unpublish",
|
|
32
|
+
requires: ["unpublish"],
|
|
33
|
+
run: testUnpublish
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
export { unpublishTest };
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=unpublish.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unpublish.js","names":[],"sources":["../../../src/tests/unpublish.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function publishPkg(ctx: TestContext, tempFolder: string) {\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'publish',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n}\n\nasync function bumpVersion(ctx: TestContext, tempFolder: string, bump: string) {\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'version',\n bump,\n '--no--git-tag-version',\n '--loglevel=info',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n}\n\nasync function testUnpublish(ctx: TestContext): Promise<void> {\n const id = ctx.runId;\n\n // Test 1: unpublish a full package\n const pkgName = `@verdaccio/unpub1-${id}`;\n const { tempFolder } = await ctx.adapter.prepareProject(\n pkgName,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n await publishPkg(ctx, tempFolder);\n await bumpVersion(ctx, tempFolder, 'minor');\n await publishPkg(ctx, tempFolder);\n await bumpVersion(ctx, tempFolder, 'minor');\n await publishPkg(ctx, tempFolder);\n await bumpVersion(ctx, tempFolder, 'major');\n await publishPkg(ctx, tempFolder);\n\n const resp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'unpublish',\n pkgName,\n '--force',\n '--loglevel=info',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.ok(\n resp.stdout.includes(pkgName),\n `Expected unpublish output to contain \"${pkgName}\" but got \"${resp.stdout}\"`\n );\n\n // Test 2: unpublish a specific version\n const pkgName2 = `@verdaccio/unpub2-${id}`;\n const { tempFolder: tf2 } = await ctx.adapter.prepareProject(\n pkgName2,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n await publishPkg(ctx, tf2);\n await bumpVersion(ctx, tf2, 'minor');\n await publishPkg(ctx, tf2);\n\n const resp2 = await ctx.adapter.exec(\n { cwd: tf2 },\n 'unpublish',\n `${pkgName2}@1.0.0`,\n '--force',\n '--loglevel=info',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.ok(\n resp2.stdout.includes(pkgName2),\n `Expected unpublish output to contain \"${pkgName2}\" but got \"${resp2.stdout}\"`\n );\n}\n\nexport const unpublishTest: TestDefinition = {\n name: 'unpublish',\n requires: ['unpublish'],\n run: testUnpublish,\n};\n"],"mappings":";;AAIA,eAAe,WAAW,KAAkB,YAAoB;CAC9D,MAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,WAAW,GAClB,WACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;AACF;AAEA,eAAe,YAAY,KAAkB,YAAoB,MAAc;CAC7E,MAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,WAAW,GAClB,WACA,MACA,yBACA,mBACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;AACF;AAEA,eAAe,cAAc,KAAiC;CAC5D,MAAM,KAAK,IAAI;CAGf,MAAM,UAAU,qBAAqB;CACrC,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,SACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,KACN;CACA,MAAM,WAAW,KAAK,UAAU;CAChC,MAAM,YAAY,KAAK,YAAY,OAAO;CAC1C,MAAM,WAAW,KAAK,UAAU;CAChC,MAAM,YAAY,KAAK,YAAY,OAAO;CAC1C,MAAM,WAAW,KAAK,UAAU;CAChC,MAAM,YAAY,KAAK,YAAY,OAAO;CAC1C,MAAM,WAAW,KAAK,UAAU;CAEhC,MAAM,OAAO,MAAM,IAAI,QAAQ,KAC7B,EAAE,KAAK,WAAW,GAClB,aACA,SACA,WACA,mBACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CACA,OAAO,GACL,KAAK,OAAO,SAAS,OAAO,GAC5B,yCAAyC,QAAQ,aAAa,KAAK,OAAO,EAC5E;CAGA,MAAM,WAAW,qBAAqB;CACtC,MAAM,EAAE,YAAY,QAAQ,MAAM,IAAI,QAAQ,eAC5C,UACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,KACN;CACA,MAAM,WAAW,KAAK,GAAG;CACzB,MAAM,YAAY,KAAK,KAAK,OAAO;CACnC,MAAM,WAAW,KAAK,GAAG;CAEzB,MAAM,QAAQ,MAAM,IAAI,QAAQ,KAC9B,EAAE,KAAK,IAAI,GACX,aACA,GAAG,SAAS,SACZ,WACA,mBACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CACA,OAAO,GACL,MAAM,OAAO,SAAS,QAAQ,GAC9B,yCAAyC,SAAS,aAAa,MAAM,OAAO,EAC9E;AACF;AAEA,IAAa,gBAAgC;CAC3C,MAAM;CACN,UAAU,CAAC,WAAW;CACtB,KAAK;AACP"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
//#region src/utils/e2e-config.ts
|
|
2
|
+
/**
|
|
3
|
+
* Recommended Verdaccio config for running the full e2e-cli battery,
|
|
4
|
+
* printed by `verdaccio-e2e --print-config`. It is what the consuming repos'
|
|
5
|
+
* CI jobs and scripts/run-e2e.sh feed to `verdaccio --config`:
|
|
6
|
+
*
|
|
7
|
+
* - max_body_size raised so scenario:tarballs can publish ~30 MB packages
|
|
8
|
+
* (Verdaccio's default is 10mb)
|
|
9
|
+
* - an `e2emock` uplink + `e2e-uplink-*` package pattern wired to the mock
|
|
10
|
+
* uplink server that scenario:uplink-failure starts on E2E_UPLINK_PORT
|
|
11
|
+
* (short timeout so the "slow uplink" case completes quickly; high
|
|
12
|
+
* max_fails so simulated failures don't trip the circuit breaker for
|
|
13
|
+
* later sub-tests)
|
|
14
|
+
* - NO npmjs uplink: the whole suite publishes every package it consumes,
|
|
15
|
+
* so the registry under test never talks to the outside world and the
|
|
16
|
+
* battery runs fully offline (and deterministically in CI)
|
|
17
|
+
*
|
|
18
|
+
* Relative paths (storage/htpasswd) resolve next to the config file, so write
|
|
19
|
+
* it into a disposable directory.
|
|
20
|
+
*/
|
|
21
|
+
var DEFAULT_UPLINK_PORT = 4874;
|
|
22
|
+
function buildE2EConfig(uplinkPort = DEFAULT_UPLINK_PORT) {
|
|
23
|
+
return `# Generated by @verdaccio/e2e-cli --print-config
|
|
24
|
+
storage: ./storage
|
|
25
|
+
auth:
|
|
26
|
+
htpasswd:
|
|
27
|
+
file: ./htpasswd
|
|
28
|
+
max_users: 1000
|
|
29
|
+
web:
|
|
30
|
+
enable: true
|
|
31
|
+
title: Verdaccio E2E
|
|
32
|
+
max_body_size: 100mb
|
|
33
|
+
uplinks:
|
|
34
|
+
e2emock:
|
|
35
|
+
url: http://localhost:${uplinkPort}/
|
|
36
|
+
timeout: 3s
|
|
37
|
+
retry: 0
|
|
38
|
+
max_fails: 100
|
|
39
|
+
fail_timeout: 1s
|
|
40
|
+
packages:
|
|
41
|
+
'e2e-uplink-*':
|
|
42
|
+
access: $all
|
|
43
|
+
proxy: e2emock
|
|
44
|
+
'@*/*':
|
|
45
|
+
access: $all
|
|
46
|
+
publish: $authenticated
|
|
47
|
+
unpublish: $authenticated
|
|
48
|
+
'**':
|
|
49
|
+
access: $all
|
|
50
|
+
publish: $authenticated
|
|
51
|
+
unpublish: $authenticated
|
|
52
|
+
log: { type: stdout, format: pretty, level: warn }
|
|
53
|
+
`;
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { DEFAULT_UPLINK_PORT, buildE2EConfig };
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=e2e-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"e2e-config.js","names":[],"sources":["../../../src/utils/e2e-config.ts"],"sourcesContent":["/**\n * Recommended Verdaccio config for running the full e2e-cli battery,\n * printed by `verdaccio-e2e --print-config`. It is what the consuming repos'\n * CI jobs and scripts/run-e2e.sh feed to `verdaccio --config`:\n *\n * - max_body_size raised so scenario:tarballs can publish ~30 MB packages\n * (Verdaccio's default is 10mb)\n * - an `e2emock` uplink + `e2e-uplink-*` package pattern wired to the mock\n * uplink server that scenario:uplink-failure starts on E2E_UPLINK_PORT\n * (short timeout so the \"slow uplink\" case completes quickly; high\n * max_fails so simulated failures don't trip the circuit breaker for\n * later sub-tests)\n * - NO npmjs uplink: the whole suite publishes every package it consumes,\n * so the registry under test never talks to the outside world and the\n * battery runs fully offline (and deterministically in CI)\n *\n * Relative paths (storage/htpasswd) resolve next to the config file, so write\n * it into a disposable directory.\n */\nexport const DEFAULT_UPLINK_PORT = 4874;\n\nexport function buildE2EConfig(uplinkPort: number = DEFAULT_UPLINK_PORT): string {\n return `# Generated by @verdaccio/e2e-cli --print-config\nstorage: ./storage\nauth:\n htpasswd:\n file: ./htpasswd\n max_users: 1000\nweb:\n enable: true\n title: Verdaccio E2E\nmax_body_size: 100mb\nuplinks:\n e2emock:\n url: http://localhost:${uplinkPort}/\n timeout: 3s\n retry: 0\n max_fails: 100\n fail_timeout: 1s\npackages:\n 'e2e-uplink-*':\n access: $all\n proxy: e2emock\n '@*/*':\n access: $all\n publish: $authenticated\n unpublish: $authenticated\n '**':\n access: $all\n publish: $authenticated\n unpublish: $authenticated\nlog: { type: stdout, format: pretty, level: warn }\n`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAmBA,IAAa,sBAAsB;AAEnC,SAAgB,eAAe,aAAqB,qBAA6B;CAC/E,OAAO;;;;;;;;;;;;4BAYmB,WAAW;;;;;;;;;;;;;;;;;;;AAmBvC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import buildDebug from "debug";
|
|
2
|
+
import { createHash } from "crypto";
|
|
3
|
+
import { get } from "http";
|
|
4
|
+
//#region src/utils/http-client.ts
|
|
5
|
+
var debug = buildDebug("verdaccio:e2e-cli:http-client");
|
|
6
|
+
var ABBREVIATED_ACCEPT = "application/vnd.npm.install-v1+json";
|
|
7
|
+
/**
|
|
8
|
+
* Registry URL for a package name. Scoped names are encoded with the same
|
|
9
|
+
* `%2f` form npm clients use: `@scope%2fname`.
|
|
10
|
+
*/
|
|
11
|
+
function packumentUrl(registryUrl, pkgName) {
|
|
12
|
+
const encoded = pkgName.startsWith("@") ? pkgName.replaceAll("/", "%2f") : pkgName;
|
|
13
|
+
return `${registryUrl.replace(/\/$/, "")}/${encoded}`;
|
|
14
|
+
}
|
|
15
|
+
async function fetchPackument(registryUrl, pkgName, options = {}) {
|
|
16
|
+
const url = packumentUrl(registryUrl, pkgName);
|
|
17
|
+
const headers = {};
|
|
18
|
+
if (options.abbreviated) headers["Accept"] = ABBREVIATED_ACCEPT;
|
|
19
|
+
if (options.etag) headers["If-None-Match"] = options.etag;
|
|
20
|
+
if (options.token) headers["Authorization"] = `Bearer ${options.token}`;
|
|
21
|
+
debug("GET %s %o", url, headers);
|
|
22
|
+
if (options.etag) return new Promise((resolve, reject) => {
|
|
23
|
+
get(url, { headers }, (response) => {
|
|
24
|
+
let text = "";
|
|
25
|
+
response.setEncoding("utf8");
|
|
26
|
+
response.on("data", (chunk) => text += chunk);
|
|
27
|
+
response.on("end", () => {
|
|
28
|
+
let body;
|
|
29
|
+
try {
|
|
30
|
+
body = JSON.parse(text);
|
|
31
|
+
} catch {
|
|
32
|
+
body = void 0;
|
|
33
|
+
}
|
|
34
|
+
const responseHeaders = new Headers();
|
|
35
|
+
for (const [name, value] of Object.entries(response.headers)) if (value !== void 0) responseHeaders.set(name, Array.isArray(value) ? value.join(", ") : value);
|
|
36
|
+
resolve({
|
|
37
|
+
status: response.statusCode || 0,
|
|
38
|
+
headers: responseHeaders,
|
|
39
|
+
body
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}).on("error", reject);
|
|
43
|
+
});
|
|
44
|
+
const response = await fetch(url, { headers });
|
|
45
|
+
let body;
|
|
46
|
+
if (response.status !== 304) {
|
|
47
|
+
const text = await response.text();
|
|
48
|
+
try {
|
|
49
|
+
body = JSON.parse(text);
|
|
50
|
+
} catch {
|
|
51
|
+
body = void 0;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
status: response.status,
|
|
56
|
+
headers: response.headers,
|
|
57
|
+
body
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Download a tarball fully, computing sha1/sha512 integrity on the fly.
|
|
62
|
+
* Rejects with the underlying error when aborted or the connection drops.
|
|
63
|
+
*/
|
|
64
|
+
async function downloadTarball(url, options = {}) {
|
|
65
|
+
const headers = {};
|
|
66
|
+
if (options.token) headers["Authorization"] = `Bearer ${options.token}`;
|
|
67
|
+
debug("GET (tarball) %s", url);
|
|
68
|
+
const response = await fetch(url, {
|
|
69
|
+
headers,
|
|
70
|
+
signal: options.signal
|
|
71
|
+
});
|
|
72
|
+
const sha1 = createHash("sha1");
|
|
73
|
+
const sha512 = createHash("sha512");
|
|
74
|
+
let bytes = 0;
|
|
75
|
+
let first = true;
|
|
76
|
+
if (response.body) for await (const chunk of response.body) {
|
|
77
|
+
const buf = Buffer.from(chunk);
|
|
78
|
+
bytes += buf.length;
|
|
79
|
+
sha1.update(buf);
|
|
80
|
+
sha512.update(buf);
|
|
81
|
+
if (first) {
|
|
82
|
+
first = false;
|
|
83
|
+
options.onFirstChunk?.();
|
|
84
|
+
}
|
|
85
|
+
options.onChunk?.(bytes);
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
status: response.status,
|
|
89
|
+
headers: response.headers,
|
|
90
|
+
bytes,
|
|
91
|
+
sha1: sha1.digest("hex"),
|
|
92
|
+
integrity: `sha512-${sha512.digest("base64")}`
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/** Compute npm-style dist values (shasum/integrity) for a buffer. */
|
|
96
|
+
function computeDist(buffer) {
|
|
97
|
+
return {
|
|
98
|
+
shasum: createHash("sha1").update(buffer).digest("hex"),
|
|
99
|
+
integrity: `sha512-${createHash("sha512").update(buffer).digest("base64")}`
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
export { ABBREVIATED_ACCEPT, computeDist, downloadTarball, fetchPackument, packumentUrl };
|
|
104
|
+
|
|
105
|
+
//# sourceMappingURL=http-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-client.js","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":";;;;AAIA,IAAM,QAAQ,WAAW,+BAA+B;AA6BxD,IAAa,qBAAqB;;;;;AAMlC,SAAgB,aAAa,aAAqB,SAAyB;CACzE,MAAM,UAAU,QAAQ,WAAW,GAAG,IAAI,QAAQ,WAAW,KAAK,KAAK,IAAI;CAC3E,OAAO,GAAG,YAAY,QAAQ,OAAO,EAAE,EAAE,GAAG;AAC9C;AAEA,eAAsB,eACpB,aACA,SACA,UAAiC,CAAC,GACN;CAC5B,MAAM,MAAM,aAAa,aAAa,OAAO;CAC7C,MAAM,UAAkC,CAAC;CACzC,IAAI,QAAQ,aACV,QAAQ,YAAY;CAEtB,IAAI,QAAQ,MACV,QAAQ,mBAAmB,QAAQ;CAErC,IAAI,QAAQ,OACV,QAAQ,mBAAmB,UAAU,QAAQ;CAE/C,MAAM,aAAa,KAAK,OAAO;CAK/B,IAAI,QAAQ,MACV,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,IAAI,KAAK,EAAE,QAAQ,IAAI,aAAa;GAClC,IAAI,OAAO;GACX,SAAS,YAAY,MAAM;GAC3B,SAAS,GAAG,SAAS,UAAW,QAAQ,KAAM;GAC9C,SAAS,GAAG,aAAa;IACvB,IAAI;IACJ,IAAI;KACF,OAAO,KAAK,MAAM,IAAI;IACxB,QAAQ;KACN,OAAO,KAAA;IACT;IACA,MAAM,kBAAkB,IAAI,QAAQ;IACpC,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,SAAS,OAAO,GACzD,IAAI,UAAU,KAAA,GACZ,gBAAgB,IAAI,MAAM,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK;IAG7E,QAAQ;KAAE,QAAQ,SAAS,cAAc;KAAG,SAAS;KAAiB;IAAK,CAAC;GAC9E,CAAC;EACH,CAAC,CAAC,CAAC,GAAG,SAAS,MAAM;CACvB,CAAC;CAGH,MAAM,WAAW,MAAM,MAAM,KAAK,EAAE,QAAQ,CAAC;CAC7C,IAAI;CACJ,IAAI,SAAS,WAAW,KAAK;EAC3B,MAAM,OAAO,MAAM,SAAS,KAAK;EACjC,IAAI;GACF,OAAO,KAAK,MAAM,IAAI;EACxB,QAAQ;GACN,OAAO,KAAA;EACT;CACF;CACA,OAAO;EAAE,QAAQ,SAAS;EAAQ,SAAS,SAAS;EAAS;CAAK;AACpE;;;;;AAeA,eAAsB,gBACpB,KACA,UAAkC,CAAC,GACT;CAC1B,MAAM,UAAkC,CAAC;CACzC,IAAI,QAAQ,OACV,QAAQ,mBAAmB,UAAU,QAAQ;CAE/C,MAAM,oBAAoB,GAAG;CAC7B,MAAM,WAAW,MAAM,MAAM,KAAK;EAAE;EAAS,QAAQ,QAAQ;CAAO,CAAC;CAErE,MAAM,OAAO,WAAW,MAAM;CAC9B,MAAM,SAAS,WAAW,QAAQ;CAClC,IAAI,QAAQ;CACZ,IAAI,QAAQ;CAEZ,IAAI,SAAS,MACX,WAAW,MAAM,SAAS,SAAS,MAAa;EAC9C,MAAM,MAAM,OAAO,KAAK,KAAK;EAC7B,SAAS,IAAI;EACb,KAAK,OAAO,GAAG;EACf,OAAO,OAAO,GAAG;EACjB,IAAI,OAAO;GACT,QAAQ;GACR,QAAQ,eAAe;EACzB;EACA,QAAQ,UAAU,KAAK;CACzB;CAGF,OAAO;EACL,QAAQ,SAAS;EACjB,SAAS,SAAS;EAClB;EACA,MAAM,KAAK,OAAO,KAAK;EACvB,WAAW,UAAU,OAAO,OAAO,QAAQ;CAC7C;AACF;;AAGA,SAAgB,YAAY,QAAuD;CACjF,OAAO;EACL,QAAQ,WAAW,MAAM,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,OAAO,KAAK;EACtD,WAAW,UAAU,WAAW,QAAQ,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,OAAO,QAAQ;CAC1E;AACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/utils/info.ts
|
|
2
|
+
/**
|
|
3
|
+
* Normalize the parsed output of `npm/pnpm info <pkg> --json`.
|
|
4
|
+
*
|
|
5
|
+
* npm 12+ wraps the result in a single-element JSON array
|
|
6
|
+
* (e.g. `[{ "name": "verdaccio", ... }]`) for both full-packument and
|
|
7
|
+
* version-specific queries, whereas older npm versions return a bare object.
|
|
8
|
+
* When multiple results match a range, the array holds one entry per match;
|
|
9
|
+
* the last entry is the highest (latest) version, which is what the tests want.
|
|
10
|
+
*
|
|
11
|
+
* Returns the bare object as-is for backward compatibility.
|
|
12
|
+
*/
|
|
13
|
+
function normalizeInfo(parsed) {
|
|
14
|
+
if (Array.isArray(parsed)) return parsed[parsed.length - 1];
|
|
15
|
+
return parsed;
|
|
16
|
+
}
|
|
17
|
+
/** Parse `info --json` stdout and normalize to a single info object. */
|
|
18
|
+
function parseInfoJson(stdout) {
|
|
19
|
+
return normalizeInfo(JSON.parse(stdout));
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { normalizeInfo, parseInfoJson };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=info.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"info.js","names":[],"sources":["../../../src/utils/info.ts"],"sourcesContent":["/**\n * Normalize the parsed output of `npm/pnpm info <pkg> --json`.\n *\n * npm 12+ wraps the result in a single-element JSON array\n * (e.g. `[{ \"name\": \"verdaccio\", ... }]`) for both full-packument and\n * version-specific queries, whereas older npm versions return a bare object.\n * When multiple results match a range, the array holds one entry per match;\n * the last entry is the highest (latest) version, which is what the tests want.\n *\n * Returns the bare object as-is for backward compatibility.\n */\nexport function normalizeInfo(parsed: any): any {\n if (Array.isArray(parsed)) {\n return parsed[parsed.length - 1];\n }\n return parsed;\n}\n\n/** Parse `info --json` stdout and normalize to a single info object. */\nexport function parseInfoJson(stdout: string): any {\n return normalizeInfo(JSON.parse(stdout));\n}\n"],"mappings":";;;;;;;;;;;;AAWA,SAAgB,cAAc,QAAkB;CAC9C,IAAI,MAAM,QAAQ,MAAM,GACtB,OAAO,OAAO,OAAO,SAAS;CAEhC,OAAO;AACT;;AAGA,SAAgB,cAAc,QAAqB;CACjD,OAAO,cAAc,KAAK,MAAM,MAAM,CAAC;AACzC"}
|