@verdaccio/e2e-cli 3.3.0 → 4.0.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 +2 -0
- package/build/adapters/nub.d.ts +2 -0
- package/build/adapters/upm.d.ts +2 -0
- package/build/cjs/adapters/index.cjs +4 -0
- package/build/cjs/adapters/nub.cjs +67 -0
- package/build/cjs/adapters/nub.cjs.map +1 -0
- package/build/cjs/adapters/pnpm.cjs +3 -22
- package/build/cjs/adapters/pnpm.cjs.map +1 -1
- package/build/cjs/adapters/upm.cjs +65 -0
- package/build/cjs/adapters/upm.cjs.map +1 -0
- package/build/cjs/index.cjs +10 -3
- package/build/cjs/index.cjs.map +1 -1
- package/build/cjs/scenarios/index.cjs +4 -1
- package/build/cjs/scenarios/index.cjs.map +1 -1
- package/build/cjs/scenarios/minimum-release-age.cjs.map +1 -1
- package/build/cjs/scenarios/reserved-names.cjs +88 -0
- package/build/cjs/scenarios/reserved-names.cjs.map +1 -0
- package/build/cjs/tests/dist-tags.cjs +12 -0
- package/build/cjs/tests/dist-tags.cjs.map +1 -1
- package/build/cjs/tests/index.cjs +4 -0
- package/build/cjs/tests/index.cjs.map +1 -1
- package/build/cjs/tests/login.cjs +24 -0
- package/build/cjs/tests/login.cjs.map +1 -1
- package/build/cjs/tests/package-names.cjs +47 -0
- package/build/cjs/tests/package-names.cjs.map +1 -0
- package/build/cjs/utils/e2e-config.cjs +9 -2
- package/build/cjs/utils/e2e-config.cjs.map +1 -1
- package/build/cjs/utils/mock-uplink.cjs +4 -3
- package/build/cjs/utils/mock-uplink.cjs.map +1 -1
- package/build/cjs/utils/registry-client.cjs +59 -0
- package/build/cjs/utils/registry-client.cjs.map +1 -1
- package/build/esm/adapters/index.js +3 -1
- package/build/esm/adapters/nub.js +65 -0
- package/build/esm/adapters/nub.js.map +1 -0
- package/build/esm/adapters/pnpm.js +3 -22
- package/build/esm/adapters/pnpm.js.map +1 -1
- package/build/esm/adapters/upm.js +63 -0
- package/build/esm/adapters/upm.js.map +1 -0
- package/build/esm/index.js +9 -4
- package/build/esm/index.js.map +1 -1
- package/build/esm/scenarios/index.js +4 -2
- package/build/esm/scenarios/index.js.map +1 -1
- package/build/esm/scenarios/minimum-release-age.js.map +1 -1
- package/build/esm/scenarios/reserved-names.js +86 -0
- package/build/esm/scenarios/reserved-names.js.map +1 -0
- package/build/esm/tests/dist-tags.js +12 -0
- package/build/esm/tests/dist-tags.js.map +1 -1
- package/build/esm/tests/index.js +4 -1
- package/build/esm/tests/index.js.map +1 -1
- package/build/esm/tests/login.js +24 -0
- package/build/esm/tests/login.js.map +1 -1
- package/build/esm/tests/package-names.js +45 -0
- package/build/esm/tests/package-names.js.map +1 -0
- package/build/esm/utils/e2e-config.js +9 -2
- package/build/esm/utils/e2e-config.js.map +1 -1
- package/build/esm/utils/mock-uplink.js +4 -3
- package/build/esm/utils/mock-uplink.js.map +1 -1
- package/build/esm/utils/registry-client.js +57 -1
- package/build/esm/utils/registry-client.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/scenarios/index.d.ts +2 -1
- package/build/scenarios/reserved-names.d.ts +2 -0
- package/build/tests/index.d.ts +3 -2
- package/build/tests/package-names.d.ts +2 -0
- package/build/types.d.ts +2 -2
- package/build/utils/e2e-config.d.ts +3 -2
- package/build/utils/mock-uplink.d.ts +2 -2
- package/build/utils/registry-client.d.ts +11 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dist-tags.cjs","names":[],"sources":["../../../src/tests/dist-tags.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function publishPkg(ctx: TestContext, tempFolder: string, extraArgs: string[] = []) {\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'publish',\n ...extraArgs,\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n}\n\nasync function bumpVersion(ctx: TestContext, tempFolder: string) {\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'version',\n 'minor',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n}\n\nasync function testDistTags(ctx: TestContext): Promise<void> {\n const id = ctx.runId;\n\n // Test 1: list dist-tags\n const pkgName1 = `@foo/dt1-${id}`;\n const { tempFolder: tf1 } = await ctx.adapter.prepareProject(\n pkgName1,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n await publishPkg(ctx, tf1);\n await bumpVersion(ctx, tf1);\n await publishPkg(ctx, tf1, ['--tag', 'beta']);\n const resp1 = await ctx.adapter.exec(\n { cwd: tf1 },\n 'dist-tag',\n 'ls',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.strictEqual(resp1.stdout, 'beta: 1.1.0latest: 1.0.0');\n\n // Test 2: remove tag\n const pkgName2 = `@verdaccio/dt2-${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);\n await publishPkg(ctx, tf2, ['--tag', 'beta']);\n const resp2 = await ctx.adapter.exec(\n { cwd: tf2 },\n 'dist-tag',\n 'rm',\n `${pkgName2}@1.1.0`,\n 'beta',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.strictEqual(resp2.stdout, `-beta: ${pkgName2}@1.1.0`);\n\n // Test 3: add tag\n const pkgName3 = `@verdaccio/dt3-${id}`;\n const { tempFolder: tf3 } = await ctx.adapter.prepareProject(\n pkgName3,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n await publishPkg(ctx, tf3);\n await bumpVersion(ctx, tf3);\n await publishPkg(ctx, tf3);\n const resp3 = await ctx.adapter.exec(\n { cwd: tf3 },\n 'dist-tag',\n 'add',\n `${pkgName3}@1.1.0`,\n 'alfa',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.strictEqual(resp3.stdout, `+alfa: ${pkgName3}@1.1.0`);\n}\n\nexport const distTagsTest: TestDefinition = {\n name: 'dist-tags',\n requires: ['dist-tag'],\n run: testDistTags,\n};\n"],"mappings":";;;;AAIA,eAAe,WAAW,KAAkB,YAAoB,YAAsB,CAAC,GAAG;CACxF,MAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,WAAW,GAClB,WACA,GAAG,WACH,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;AACF;AAEA,eAAe,YAAY,KAAkB,YAAoB;CAC/D,MAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,WAAW,GAClB,WACA,SACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;AACF;AAEA,eAAe,aAAa,KAAiC;CAC3D,MAAM,KAAK,IAAI;CAGf,MAAM,WAAW,YAAY;CAC7B,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,GAAG;CAC1B,MAAM,WAAW,KAAK,KAAK,CAAC,SAAS,MAAM,CAAC;CAC5C,MAAM,QAAQ,MAAM,IAAI,QAAQ,KAC9B,EAAE,KAAK,IAAI,GACX,YACA,MACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CACA,OAAA,QAAO,YAAY,MAAM,QAAQ,0BAA0B;CAG3D,MAAM,WAAW,kBAAkB;CACnC,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,GAAG;CAC1B,MAAM,WAAW,KAAK,KAAK,CAAC,SAAS,MAAM,CAAC;CAC5C,MAAM,QAAQ,MAAM,IAAI,QAAQ,KAC9B,EAAE,KAAK,IAAI,GACX,YACA,MACA,GAAG,SAAS,SACZ,QACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CACA,OAAA,QAAO,YAAY,MAAM,QAAQ,UAAU,SAAS,OAAO;CAG3D,MAAM,WAAW,kBAAkB;CACnC,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,GAAG;CAC1B,MAAM,WAAW,KAAK,GAAG;CACzB,MAAM,QAAQ,MAAM,IAAI,QAAQ,KAC9B,EAAE,KAAK,IAAI,GACX,YACA,OACA,GAAG,SAAS,SACZ,QACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CACA,OAAA,QAAO,YAAY,MAAM,QAAQ,UAAU,SAAS,OAAO;
|
|
1
|
+
{"version":3,"file":"dist-tags.cjs","names":[],"sources":["../../../src/tests/dist-tags.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function publishPkg(ctx: TestContext, tempFolder: string, extraArgs: string[] = []) {\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'publish',\n ...extraArgs,\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n}\n\nasync function bumpVersion(ctx: TestContext, tempFolder: string) {\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'version',\n 'minor',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n}\n\nasync function testDistTags(ctx: TestContext): Promise<void> {\n const id = ctx.runId;\n\n // Test 1: list dist-tags\n const pkgName1 = `@foo/dt1-${id}`;\n const { tempFolder: tf1 } = await ctx.adapter.prepareProject(\n pkgName1,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n await publishPkg(ctx, tf1);\n await bumpVersion(ctx, tf1);\n await publishPkg(ctx, tf1, ['--tag', 'beta']);\n const resp1 = await ctx.adapter.exec(\n { cwd: tf1 },\n 'dist-tag',\n 'ls',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.strictEqual(resp1.stdout, 'beta: 1.1.0latest: 1.0.0');\n\n // Test 2: remove tag\n const pkgName2 = `@verdaccio/dt2-${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);\n await publishPkg(ctx, tf2, ['--tag', 'beta']);\n const resp2 = await ctx.adapter.exec(\n { cwd: tf2 },\n 'dist-tag',\n 'rm',\n `${pkgName2}@1.1.0`,\n 'beta',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.strictEqual(resp2.stdout, `-beta: ${pkgName2}@1.1.0`);\n\n // Test 3: add tag\n const pkgName3 = `@verdaccio/dt3-${id}`;\n const { tempFolder: tf3 } = await ctx.adapter.prepareProject(\n pkgName3,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n await publishPkg(ctx, tf3);\n await bumpVersion(ctx, tf3);\n await publishPkg(ctx, tf3);\n const resp3 = await ctx.adapter.exec(\n { cwd: tf3 },\n 'dist-tag',\n 'add',\n `${pkgName3}@1.1.0`,\n 'alfa',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.strictEqual(resp3.stdout, `+alfa: ${pkgName3}@1.1.0`);\n\n // Test 4: tags named like Windows device names are plain metadata keys\n const pkgName4 = `@verdaccio/dt4-${id}`;\n const { tempFolder: tf4 } = await ctx.adapter.prepareProject(\n pkgName4,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n await publishPkg(ctx, tf4);\n for (const tag of ['aux', 'con']) {\n const added = await ctx.adapter.exec(\n { cwd: tf4 },\n 'dist-tag',\n 'add',\n `${pkgName4}@1.0.0`,\n tag,\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.strictEqual(added.stdout, `+${tag}: ${pkgName4}@1.0.0`);\n }\n const listed = await ctx.adapter.exec(\n { cwd: tf4 },\n 'dist-tag',\n 'ls',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.ok(listed.stdout.includes('aux: 1.0.0'), `Expected aux tag in: ${listed.stdout}`);\n assert.ok(listed.stdout.includes('con: 1.0.0'), `Expected con tag in: ${listed.stdout}`);\n const removed = await ctx.adapter.exec(\n { cwd: tf4 },\n 'dist-tag',\n 'rm',\n pkgName4,\n 'aux',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.strictEqual(removed.stdout, `-aux: ${pkgName4}@1.0.0`);\n}\n\nexport const distTagsTest: TestDefinition = {\n name: 'dist-tags',\n requires: ['dist-tag'],\n run: testDistTags,\n};\n"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require("../scenarios/install-multiple-deps.cjs");
|
|
2
2
|
require("../scenarios/metadata.cjs");
|
|
3
3
|
require("../scenarios/minimum-release-age.cjs");
|
|
4
|
+
require("../scenarios/reserved-names.cjs");
|
|
4
5
|
require("../scenarios/search.cjs");
|
|
5
6
|
require("../scenarios/tarballs.cjs");
|
|
6
7
|
require("../scenarios/uplink-failure.cjs");
|
|
@@ -12,6 +13,7 @@ const require_dist_tags = require("./dist-tags.cjs");
|
|
|
12
13
|
const require_info = require("./info.cjs");
|
|
13
14
|
const require_install = require("./install.cjs");
|
|
14
15
|
const require_login = require("./login.cjs");
|
|
16
|
+
const require_package_names = require("./package-names.cjs");
|
|
15
17
|
const require_ping = require("./ping.cjs");
|
|
16
18
|
const require_publish = require("./publish.cjs");
|
|
17
19
|
const require_search$1 = require("./search.cjs");
|
|
@@ -25,6 +27,7 @@ var allTests = [
|
|
|
25
27
|
require_info.infoTest,
|
|
26
28
|
require_deprecate.deprecateTest,
|
|
27
29
|
require_dist_tags.distTagsTest,
|
|
30
|
+
require_package_names.packageNamesTest,
|
|
28
31
|
require_login.loginTest,
|
|
29
32
|
require_ping.pingTest,
|
|
30
33
|
require_search$1.searchTest,
|
|
@@ -41,6 +44,7 @@ exports.distTagsTest = require_dist_tags.distTagsTest;
|
|
|
41
44
|
exports.infoTest = require_info.infoTest;
|
|
42
45
|
exports.installTest = require_install.installTest;
|
|
43
46
|
exports.loginTest = require_login.loginTest;
|
|
47
|
+
exports.packageNamesTest = require_package_names.packageNamesTest;
|
|
44
48
|
exports.pingTest = require_ping.pingTest;
|
|
45
49
|
exports.publishTest = require_publish.publishTest;
|
|
46
50
|
exports.searchTest = require_search$1.searchTest;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/tests/index.ts"],"sourcesContent":["import {\n allScenarios,\n installMultipleDepsScenario,\n metadataScenario,\n minimumReleaseAgeScenario,\n searchScenario,\n tarballsScenario,\n uplinkFailureScenario,\n} from '../scenarios';\nimport { TestDefinition } from '../types';\nimport { auditTest } from './audit';\nimport { ciTest } from './ci';\nimport { deprecateTest } from './deprecate';\nimport { distTagsTest } from './dist-tags';\nimport { infoTest } from './info';\nimport { installTest } from './install';\nimport { loginTest } from './login';\nimport { pingTest } from './ping';\nimport { publishTest } from './publish';\nimport { searchTest } from './search';\nimport { unpublishTest } from './unpublish';\n\nexport const allTests: TestDefinition[] = [\n publishTest,\n installTest,\n ciTest,\n auditTest,\n infoTest,\n deprecateTest,\n distTagsTest,\n loginTest,\n pingTest,\n searchTest,\n unpublishTest,\n // Scenarios (complex, multi-step tests)\n ...allScenarios,\n];\n\nexport {\n publishTest,\n installTest,\n ciTest,\n auditTest,\n infoTest,\n deprecateTest,\n distTagsTest,\n loginTest,\n pingTest,\n searchTest,\n unpublishTest,\n // Scenarios\n installMultipleDepsScenario,\n minimumReleaseAgeScenario,\n tarballsScenario,\n metadataScenario,\n searchScenario,\n uplinkFailureScenario,\n allScenarios,\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/tests/index.ts"],"sourcesContent":["import {\n allScenarios,\n installMultipleDepsScenario,\n metadataScenario,\n minimumReleaseAgeScenario,\n reservedNamesScenario,\n searchScenario,\n tarballsScenario,\n uplinkFailureScenario,\n} from '../scenarios';\nimport { TestDefinition } from '../types';\nimport { auditTest } from './audit';\nimport { ciTest } from './ci';\nimport { deprecateTest } from './deprecate';\nimport { distTagsTest } from './dist-tags';\nimport { infoTest } from './info';\nimport { installTest } from './install';\nimport { loginTest } from './login';\nimport { packageNamesTest } from './package-names';\nimport { pingTest } from './ping';\nimport { publishTest } from './publish';\nimport { searchTest } from './search';\nimport { unpublishTest } from './unpublish';\n\nexport const allTests: TestDefinition[] = [\n publishTest,\n installTest,\n ciTest,\n auditTest,\n infoTest,\n deprecateTest,\n distTagsTest,\n packageNamesTest,\n loginTest,\n pingTest,\n searchTest,\n unpublishTest,\n // Scenarios (complex, multi-step tests)\n ...allScenarios,\n];\n\nexport {\n publishTest,\n installTest,\n ciTest,\n auditTest,\n infoTest,\n deprecateTest,\n distTagsTest,\n packageNamesTest,\n loginTest,\n pingTest,\n searchTest,\n unpublishTest,\n // Scenarios\n installMultipleDepsScenario,\n minimumReleaseAgeScenario,\n tarballsScenario,\n metadataScenario,\n searchScenario,\n uplinkFailureScenario,\n reservedNamesScenario,\n allScenarios,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwBA,IAAa,WAA6B;CACxC,gBAAA;CACA,gBAAA;CACA,WAAA;CACA,cAAA;CACA,aAAA;CACA,kBAAA;CACA,kBAAA;CACA,sBAAA;CACA,cAAA;CACA,aAAA;CACA,iBAAA;CACA,kBAAA;CAEA,GAAG,cAAA;AACL"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
2
|
const require_project = require("../utils/project.cjs");
|
|
3
|
+
const require_registry_client = require("../utils/registry-client.cjs");
|
|
3
4
|
let debug = require("debug");
|
|
4
5
|
debug = require_runtime.__toESM(debug, 1);
|
|
5
6
|
let path = require("path");
|
|
@@ -114,6 +115,29 @@ async function testLogin(ctx) {
|
|
|
114
115
|
const who2 = await yarnWhoami(ctx, tf);
|
|
115
116
|
assert.default.ok(who2.includes(user2), `Expected "${user2}" after switch but got "${who2}"`);
|
|
116
117
|
});
|
|
118
|
+
for (const client of [
|
|
119
|
+
"npm",
|
|
120
|
+
"yarn",
|
|
121
|
+
"pnpm"
|
|
122
|
+
]) {
|
|
123
|
+
await ctx.subTest(`${client} logs an existing user in with the Basic retry`, async () => {
|
|
124
|
+
const user = `login-retry-${client}-${id}`;
|
|
125
|
+
const password = "retry-password-123";
|
|
126
|
+
await require_registry_client.createUser(ctx.registryUrl, user, password);
|
|
127
|
+
const token = await require_registry_client.legacyLogin(ctx.registryUrl, user, password, client);
|
|
128
|
+
assert.default.strictEqual(await require_registry_client.whoami(ctx.registryUrl, token), user);
|
|
129
|
+
});
|
|
130
|
+
await ctx.subTest(`${client} registers a new user`, async () => {
|
|
131
|
+
const user = `login-new-${client}-${id}`;
|
|
132
|
+
const token = await require_registry_client.legacyLogin(ctx.registryUrl, user, "retry-password-123", client);
|
|
133
|
+
assert.default.strictEqual(await require_registry_client.whoami(ctx.registryUrl, token), user);
|
|
134
|
+
});
|
|
135
|
+
await ctx.subTest(`${client} cannot log an existing user in with a wrong password`, async () => {
|
|
136
|
+
const user = `login-wrong-${client}-${id}`;
|
|
137
|
+
await require_registry_client.createUser(ctx.registryUrl, user, "retry-password-123");
|
|
138
|
+
await assert.default.rejects(require_registry_client.legacyLogin(ctx.registryUrl, user, "wrong-password", client), (error) => error instanceof require_registry_client.LegacyLoginError && error.status === 401);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
117
141
|
}
|
|
118
142
|
var loginTest = {
|
|
119
143
|
name: "login",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.cjs","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":"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"}
|
|
1
|
+
{"version":3,"file":"login.cjs","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';\nimport { LegacyLoginError, createUser, legacyLogin, whoami } from '../utils/registry-client';\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 // replayed over HTTP: what `npm login`, native `yarn npm login` and `pnpm login` send\n for (const client of ['npm', 'yarn', 'pnpm'] as const) {\n await ctx.subTest(`${client} logs an existing user in with the Basic retry`, async () => {\n const user = `login-retry-${client}-${id}`;\n const password = 'retry-password-123';\n await createUser(ctx.registryUrl, user, password);\n\n const token = await legacyLogin(ctx.registryUrl, user, password, client);\n assert.strictEqual(await whoami(ctx.registryUrl, token), user);\n });\n\n await ctx.subTest(`${client} registers a new user`, async () => {\n const user = `login-new-${client}-${id}`;\n\n const token = await legacyLogin(ctx.registryUrl, user, 'retry-password-123', client);\n assert.strictEqual(await whoami(ctx.registryUrl, token), user);\n });\n\n await ctx.subTest(\n `${client} cannot log an existing user in with a wrong password`,\n async () => {\n const user = `login-wrong-${client}-${id}`;\n await createUser(ctx.registryUrl, user, 'retry-password-123');\n\n await assert.rejects(\n legacyLogin(ctx.registryUrl, user, 'wrong-password', client),\n (error: unknown) => error instanceof LegacyLoginError && error.status === 401\n );\n }\n );\n }\n}\n\nexport const loginTest: TestDefinition = {\n name: 'login',\n requires: ['login'],\n run: testLogin,\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_http_client = require("../utils/http-client.cjs");
|
|
3
|
+
const require_pending_checks = require("../utils/pending-checks.cjs");
|
|
4
|
+
const require_publish = require("../utils/publish.cjs");
|
|
5
|
+
let assert = require("assert");
|
|
6
|
+
assert = require_runtime.__toESM(assert, 1);
|
|
7
|
+
//#region src/tests/package-names.ts
|
|
8
|
+
async function testPackageNames(ctx) {
|
|
9
|
+
const id = ctx.runId;
|
|
10
|
+
const installable = [
|
|
11
|
+
`JSONStream-${id}`,
|
|
12
|
+
`@e2e-names/_x-${id}`,
|
|
13
|
+
`@con/x-${id}`
|
|
14
|
+
];
|
|
15
|
+
await ctx.subTest("unusual valid names are published and served", async () => {
|
|
16
|
+
for (const name of [...installable, `-x-${id}`]) {
|
|
17
|
+
await require_publish.publishViaHttp(ctx, name);
|
|
18
|
+
const { status, body } = await require_http_client.fetchPackument(ctx.registryUrl, name);
|
|
19
|
+
assert.default.strictEqual(status, 200, `Expected 200 for ${name}, got ${status}`);
|
|
20
|
+
assert.default.strictEqual(body.name, name, `Packument name mismatch for ${name}`);
|
|
21
|
+
const download = await require_http_client.downloadTarball(body.versions["1.0.0"].dist.tarball);
|
|
22
|
+
assert.default.strictEqual(download.status, 200, `Tarball download failed for ${name}`);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
if (ctx.adapter.supports.has("install")) await ctx.subTest("unusual valid names install with the client", async () => {
|
|
26
|
+
const { tempFolder } = await ctx.adapter.prepareProject(`e2e-names-consumer-${id}`, "1.0.0", ctx.registryUrl, ctx.port, ctx.token, Object.fromEntries(installable.map((name) => [name, "1.0.0"])));
|
|
27
|
+
await ctx.adapter.exec({ cwd: tempFolder }, "install", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
28
|
+
});
|
|
29
|
+
if (require_pending_checks.pendingContractChecksEnabled("package-names")) await ctx.subTest("names npm does not accept are rejected", async () => {
|
|
30
|
+
for (const name of [
|
|
31
|
+
`_x-${id}`,
|
|
32
|
+
`@x-${id}`,
|
|
33
|
+
`x-${id}@y`
|
|
34
|
+
]) {
|
|
35
|
+
const response = await fetch(`${ctx.registryUrl}/${encodeURIComponent(name)}`);
|
|
36
|
+
assert.default.strictEqual(response.status, 400, `Expected 400 for ${name}, got ${response.status}`);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
var packageNamesTest = {
|
|
41
|
+
name: "package-names",
|
|
42
|
+
run: testPackageNames
|
|
43
|
+
};
|
|
44
|
+
//#endregion
|
|
45
|
+
exports.packageNamesTest = packageNamesTest;
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=package-names.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-names.cjs","names":[],"sources":["../../../src/tests/package-names.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\nimport { downloadTarball, fetchPackument } from '../utils/http-client';\nimport { pendingContractChecksEnabled } from '../utils/pending-checks';\nimport { publishViaHttp } from '../utils/publish';\n\nasync function testPackageNames(ctx: TestContext): Promise<void> {\n const id = ctx.runId;\n // Valid for existing packages, though some clients refuse to publish them (capitals).\n const installable = [`JSONStream-${id}`, `@e2e-names/_x-${id}`, `@con/x-${id}`];\n\n await ctx.subTest('unusual valid names are published and served', async () => {\n // A leading hyphen is valid for existing packages but npm clients no longer parse it.\n for (const name of [...installable, `-x-${id}`]) {\n await publishViaHttp(ctx, name);\n const { status, body } = await fetchPackument(ctx.registryUrl, name);\n assert.strictEqual(status, 200, `Expected 200 for ${name}, got ${status}`);\n assert.strictEqual(body.name, name, `Packument name mismatch for ${name}`);\n const download = await downloadTarball(body.versions['1.0.0'].dist.tarball);\n assert.strictEqual(download.status, 200, `Tarball download failed for ${name}`);\n }\n });\n\n if (ctx.adapter.supports.has('install')) {\n await ctx.subTest('unusual valid names install with the client', async () => {\n const { tempFolder } = await ctx.adapter.prepareProject(\n `e2e-names-consumer-${id}`,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token,\n Object.fromEntries(installable.map((name) => [name, '1.0.0']))\n );\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'install',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n });\n }\n\n if (pendingContractChecksEnabled('package-names')) {\n await ctx.subTest('names npm does not accept are rejected', async () => {\n for (const name of [`_x-${id}`, `@x-${id}`, `x-${id}@y`]) {\n const response = await fetch(`${ctx.registryUrl}/${encodeURIComponent(name)}`);\n assert.strictEqual(\n response.status,\n 400,\n `Expected 400 for ${name}, got ${response.status}`\n );\n }\n });\n }\n}\n\nexport const packageNamesTest: TestDefinition = {\n name: 'package-names',\n run: testPackageNames,\n};\n"],"mappings":";;;;;;;AAOA,eAAe,iBAAiB,KAAiC;CAC/D,MAAM,KAAK,IAAI;CAEf,MAAM,cAAc;EAAC,cAAc;EAAM,iBAAiB;EAAM,UAAU;CAAI;CAE9E,MAAM,IAAI,QAAQ,gDAAgD,YAAY;EAE5E,KAAK,MAAM,QAAQ,CAAC,GAAG,aAAa,MAAM,IAAI,GAAG;GAC/C,MAAM,gBAAA,eAAe,KAAK,IAAI;GAC9B,MAAM,EAAE,QAAQ,SAAS,MAAM,oBAAA,eAAe,IAAI,aAAa,IAAI;GACnE,OAAA,QAAO,YAAY,QAAQ,KAAK,oBAAoB,KAAK,QAAQ,QAAQ;GACzE,OAAA,QAAO,YAAY,KAAK,MAAM,MAAM,+BAA+B,MAAM;GACzE,MAAM,WAAW,MAAM,oBAAA,gBAAgB,KAAK,SAAS,QAAQ,CAAC,KAAK,OAAO;GAC1E,OAAA,QAAO,YAAY,SAAS,QAAQ,KAAK,+BAA+B,MAAM;EAChF;CACF,CAAC;CAED,IAAI,IAAI,QAAQ,SAAS,IAAI,SAAS,GACpC,MAAM,IAAI,QAAQ,+CAA+C,YAAY;EAC3E,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,sBAAsB,MACtB,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,OACJ,OAAO,YAAY,YAAY,KAAK,SAAS,CAAC,MAAM,OAAO,CAAC,CAAC,CAC/D;EACA,MAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,WAAW,GAClB,WACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CACF,CAAC;CAGH,IAAI,uBAAA,6BAA6B,eAAe,GAC9C,MAAM,IAAI,QAAQ,0CAA0C,YAAY;EACtE,KAAK,MAAM,QAAQ;GAAC,MAAM;GAAM,MAAM;GAAM,KAAK,GAAG;EAAG,GAAG;GACxD,MAAM,WAAW,MAAM,MAAM,GAAG,IAAI,YAAY,GAAG,mBAAmB,IAAI,GAAG;GAC7E,OAAA,QAAO,YACL,SAAS,QACT,KACA,oBAAoB,KAAK,QAAQ,SAAS,QAC5C;EACF;CACF,CAAC;AAEL;AAEA,IAAa,mBAAmC;CAC9C,MAAM;CACN,KAAK;AACP"}
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
*
|
|
7
7
|
* - max_body_size raised so scenario:tarballs can publish ~30 MB packages
|
|
8
8
|
* (Verdaccio's default is 10mb)
|
|
9
|
-
* - an `e2emock` uplink + `e2e-uplink
|
|
10
|
-
* uplink server that scenario:uplink-failure
|
|
9
|
+
* - an `e2emock` uplink + `e2e-uplink-*`, `nul.*` and `@e2e-uplink/*` package
|
|
10
|
+
* patterns wired to the mock uplink server that scenario:uplink-failure and
|
|
11
|
+
* scenario:reserved-names start on E2E_UPLINK_PORT
|
|
11
12
|
* (short timeout so the "slow uplink" case completes quickly; high
|
|
12
13
|
* max_fails so simulated failures don't trip the circuit breaker for
|
|
13
14
|
* later sub-tests)
|
|
@@ -41,6 +42,12 @@ packages:
|
|
|
41
42
|
'e2e-uplink-*':
|
|
42
43
|
access: $all
|
|
43
44
|
proxy: e2emock
|
|
45
|
+
'nul.*':
|
|
46
|
+
access: $all
|
|
47
|
+
proxy: e2emock
|
|
48
|
+
'@e2e-uplink/*':
|
|
49
|
+
access: $all
|
|
50
|
+
proxy: e2emock
|
|
44
51
|
'@*/*':
|
|
45
52
|
access: $all
|
|
46
53
|
publish: $authenticated
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"e2e-config.cjs","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
|
|
1
|
+
{"version":3,"file":"e2e-config.cjs","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-*`, `nul.*` and `@e2e-uplink/*` package\n * patterns wired to the mock uplink server that scenario:uplink-failure and\n * scenario:reserved-names start 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 'nul.*':\n access: $all\n proxy: e2emock\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\n/**\n * Recommended pnpr config for the same battery, printed by\n * `verdaccio-e2e --print-config --server pnpr`. pnpr's config is\n * verdaccio-shaped but routes through a `registries:` block instead of a\n * global `packages:` map (which pnpr rejects loudly):\n *\n * - one hosted registry claiming every name (`**`), publish open to\n * authenticated users (pnpr's default) and unpublish loosened to match\n * the verdaccio battery config\n * - no `e2emock` uplink: pnpr registry namespaces only accept exact\n * names, scope wildcards or `**`, so the unscoped dynamic\n * `e2e-uplink-*` names can't be routed to the mock uplink. Run pnpr\n * without --uplink-port and scenario:uplink-failure (plus the uplink\n * search sub-tests) skip, exactly like docker mode.\n * - no max_body_size knob: pnpr's publish body limit is a fixed 100 MB,\n * already above what scenario:tarballs needs\n */\nexport function buildPnprE2EConfig(): string {\n return `# Generated by @verdaccio/e2e-cli --print-config --server pnpr\nstorage: ./storage\nauth:\n htpasswd:\n file: ./htpasswd\n max_users: 1000\nregistries:\n local:\n type: hosted\n access: $all\n packages:\n '**': { unpublish: $authenticated }\ndefaultRegistry: local\nlog: { type: stdout, format: pretty, level: warn }\n`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAoBA,IAAa,sBAAsB;AAEnC,SAAgB,eAAe,aAAqB,qBAA6B;CAC/E,OAAO;;;;;;;;;;;;4BAYmB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;AAyBvC;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,qBAA6B;CAC3C,OAAO;;;;;;;;;;;;;;;AAeT"}
|
|
@@ -23,9 +23,9 @@ var MockUplink = class {
|
|
|
23
23
|
constructor(port) {
|
|
24
24
|
this.port = port;
|
|
25
25
|
}
|
|
26
|
-
/** Register a package served by this uplink; tarball is random (incompressible) bytes. */
|
|
26
|
+
/** Register a package served by this uplink; tarball is random (incompressible) bytes unless one is given. */
|
|
27
27
|
addPackage(name, tarballBytes = 65536) {
|
|
28
|
-
const tarball = (0, crypto.randomBytes)(tarballBytes);
|
|
28
|
+
const tarball = Buffer.isBuffer(tarballBytes) ? tarballBytes : (0, crypto.randomBytes)(tarballBytes);
|
|
29
29
|
const { shasum, integrity } = require_http_client.computeDist(tarball);
|
|
30
30
|
const pkg = {
|
|
31
31
|
name,
|
|
@@ -85,7 +85,8 @@ var MockUplink = class {
|
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
87
|
packument(pkg) {
|
|
88
|
-
const
|
|
88
|
+
const filename = `${pkg.name.split("/").pop()}-${pkg.version}.tgz`;
|
|
89
|
+
const tarballUrl = `http://localhost:${this.port}/${pkg.name}/-/${filename}`;
|
|
89
90
|
return JSON.stringify({
|
|
90
91
|
name: pkg.name,
|
|
91
92
|
"dist-tags": { latest: pkg.version },
|
|
@@ -1 +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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"}
|
|
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 unless one is given. */\n addPackage(name: string, tarballBytes: number | Buffer = 64 * 1024): MockPackage {\n const tarball = Buffer.isBuffer(tarballBytes) ? tarballBytes : 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 filename = `${pkg.name.split('/').pop()}-${pkg.version}.tgz`;\n const tarballUrl = `http://localhost:${this.port}/${pkg.name}/-/${filename}`;\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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"}
|
|
@@ -49,8 +49,67 @@ async function pingRegistry(registryUrl) {
|
|
|
49
49
|
return false;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
+
var LegacyLoginError = class extends Error {
|
|
53
|
+
status;
|
|
54
|
+
constructor(message, status) {
|
|
55
|
+
super(message);
|
|
56
|
+
this.status = status;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Replays the legacy login the way each client sends it. npm and Yarn retry a
|
|
61
|
+
* 409 with Basic (Yarn on the GET too); pnpm sends Basic on a single PUT. A
|
|
62
|
+
* registry that authenticates the first PUT is accepted too.
|
|
63
|
+
*/
|
|
64
|
+
async function legacyLogin(registryUrl, user, password, client) {
|
|
65
|
+
const userUrl = `${registryUrl}/-/user/org.couchdb.user:${encodeURIComponent(user)}`;
|
|
66
|
+
const basic = `Basic ${Buffer.from(`${user}:${password}`).toString("base64")}`;
|
|
67
|
+
const jsonHeaders = { "Content-Type": "application/json" };
|
|
68
|
+
const body = {
|
|
69
|
+
_id: `org.couchdb.user:${user}`,
|
|
70
|
+
name: user,
|
|
71
|
+
password,
|
|
72
|
+
type: "user",
|
|
73
|
+
roles: [],
|
|
74
|
+
date: (/* @__PURE__ */ new Date()).toISOString()
|
|
75
|
+
};
|
|
76
|
+
const put = (url, headers) => fetch(url, {
|
|
77
|
+
method: "PUT",
|
|
78
|
+
headers,
|
|
79
|
+
body: JSON.stringify(body)
|
|
80
|
+
});
|
|
81
|
+
const first = await put(userUrl, client === "pnpm" ? {
|
|
82
|
+
...jsonHeaders,
|
|
83
|
+
Authorization: basic,
|
|
84
|
+
"npm-auth-type": "web"
|
|
85
|
+
} : jsonHeaders);
|
|
86
|
+
const firstBody = await first.json();
|
|
87
|
+
if (first.ok && firstBody.token) {
|
|
88
|
+
debug$1("%s: first PUT authenticated %s", client, user);
|
|
89
|
+
return firstBody.token;
|
|
90
|
+
}
|
|
91
|
+
if (first.status !== 409) throw new LegacyLoginError(`first PUT answered ${first.status}`, first.status);
|
|
92
|
+
const current = await fetch(`${userUrl}?write=true`, { headers: client === "yarn" ? { Authorization: basic } : {} });
|
|
93
|
+
if (!current.ok) throw new LegacyLoginError(`GET answered ${current.status}`, current.status);
|
|
94
|
+
const currentBody = await current.json();
|
|
95
|
+
for (const [key, value] of Object.entries(currentBody)) if (!body[key] || key === "roles") body[key] = value;
|
|
96
|
+
const second = await put(`${userUrl}/-rev/${body._rev}`, {
|
|
97
|
+
...jsonHeaders,
|
|
98
|
+
Authorization: basic
|
|
99
|
+
});
|
|
100
|
+
const secondBody = await second.json();
|
|
101
|
+
if (!second.ok || !secondBody.token) throw new LegacyLoginError(`PUT with revision answered ${second.status}`, second.status);
|
|
102
|
+
return secondBody.token;
|
|
103
|
+
}
|
|
104
|
+
async function whoami(registryUrl, token) {
|
|
105
|
+
const response = await fetch(`${registryUrl}/-/whoami`, { headers: { Authorization: `Bearer ${token}` } });
|
|
106
|
+
return response.ok ? (await response.json()).username : void 0;
|
|
107
|
+
}
|
|
52
108
|
//#endregion
|
|
109
|
+
exports.LegacyLoginError = LegacyLoginError;
|
|
53
110
|
exports.createUser = createUser;
|
|
111
|
+
exports.legacyLogin = legacyLogin;
|
|
54
112
|
exports.pingRegistry = pingRegistry;
|
|
113
|
+
exports.whoami = whoami;
|
|
55
114
|
|
|
56
115
|
//# sourceMappingURL=registry-client.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry-client.cjs","names":[],"sources":["../../../src/utils/registry-client.ts"],"sourcesContent":["import buildDebug from 'debug';\n\nimport { ping } from '@verdaccio/registry-cli';\n\nconst debug = buildDebug('verdaccio:e2e-cli:registry-client');\n\nexport type RegistryAuth = {\n token: string;\n user: string;\n};\n\n/**\n * Creates a user on the registry and returns the auth token.\n * Uses a unique username per run to avoid 409 conflicts.\n * Works against any running Verdaccio instance with htpasswd auth.\n */\nexport async function createUser(\n registryUrl: string,\n user = `e2e-user-${Date.now()}`,\n password = 'e2e-test-password'\n): Promise<RegistryAuth> {\n debug('creating user %s on %s', user, registryUrl);\n const url = `${registryUrl}/-/user/org.couchdb.user:${encodeURIComponent(user)}`;\n const response = await fetch(url, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n name: user,\n password,\n _id: `org.couchdb.user:${user}`,\n type: 'user',\n roles: [],\n date: new Date().toISOString(),\n }),\n });\n\n const body = (await response.json()) as any;\n\n if (response.status === 201 || response.status === 200 || response.status === 409) {\n const token = body.token;\n if (token) {\n debug('user %s authenticated, token obtained', user);\n return { token, user };\n }\n }\n\n throw new Error(\n `Failed to create/login user \"${user}\" on ${registryUrl}: ${response.status} ${JSON.stringify(body)}`\n );\n}\n\n/**\n * Ping the registry to check it's alive.\n * Uses @verdaccio/registry-cli ping.\n */\nexport async function pingRegistry(registryUrl: string): Promise<boolean> {\n debug('pinging %s', registryUrl);\n try {\n const result = await ping(registryUrl);\n return result.ok;\n } catch {\n return false;\n }\n}\n"],"mappings":";;;;;AAIA,IAAM,WAAA,GAAQ,MAAA,QAAA,CAAW,mCAAmC;;;;;;AAY5D,eAAsB,WACpB,aACA,OAAO,YAAY,KAAK,IAAI,KAC5B,WAAW,qBACY;CACvB,QAAM,0BAA0B,MAAM,WAAW;CACjD,MAAM,MAAM,GAAG,YAAY,2BAA2B,mBAAmB,IAAI;CAC7E,MAAM,WAAW,MAAM,MAAM,KAAK;EAChC,QAAQ;EACR,SAAS,EAAE,gBAAgB,mBAAmB;EAC9C,MAAM,KAAK,UAAU;GACnB,MAAM;GACN;GACA,KAAK,oBAAoB;GACzB,MAAM;GACN,OAAO,CAAC;GACR,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;EAC/B,CAAC;CACH,CAAC;CAED,MAAM,OAAQ,MAAM,SAAS,KAAK;CAElC,IAAI,SAAS,WAAW,OAAO,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;EACjF,MAAM,QAAQ,KAAK;EACnB,IAAI,OAAO;GACT,QAAM,yCAAyC,IAAI;GACnD,OAAO;IAAE;IAAO;GAAK;EACvB;CACF;CAEA,MAAM,IAAI,MACR,gCAAgC,KAAK,OAAO,YAAY,IAAI,SAAS,OAAO,GAAG,KAAK,UAAU,IAAI,GACpG;AACF;;;;;AAMA,eAAsB,aAAa,aAAuC;CACxE,QAAM,cAAc,WAAW;CAC/B,IAAI;EAEF,QAAO,OAAA,GADc,wBAAA,KAAA,CAAK,WAAW,EAAA,CACvB;CAChB,QAAQ;EACN,OAAO;CACT;AACF"}
|
|
1
|
+
{"version":3,"file":"registry-client.cjs","names":[],"sources":["../../../src/utils/registry-client.ts"],"sourcesContent":["import buildDebug from 'debug';\n\nimport { ping } from '@verdaccio/registry-cli';\n\nconst debug = buildDebug('verdaccio:e2e-cli:registry-client');\n\nexport type RegistryAuth = {\n token: string;\n user: string;\n};\n\n/**\n * Creates a user on the registry and returns the auth token.\n * Uses a unique username per run to avoid 409 conflicts.\n * Works against any running Verdaccio instance with htpasswd auth.\n */\nexport async function createUser(\n registryUrl: string,\n user = `e2e-user-${Date.now()}`,\n password = 'e2e-test-password'\n): Promise<RegistryAuth> {\n debug('creating user %s on %s', user, registryUrl);\n const url = `${registryUrl}/-/user/org.couchdb.user:${encodeURIComponent(user)}`;\n const response = await fetch(url, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n name: user,\n password,\n _id: `org.couchdb.user:${user}`,\n type: 'user',\n roles: [],\n date: new Date().toISOString(),\n }),\n });\n\n const body = (await response.json()) as any;\n\n if (response.status === 201 || response.status === 200 || response.status === 409) {\n const token = body.token;\n if (token) {\n debug('user %s authenticated, token obtained', user);\n return { token, user };\n }\n }\n\n throw new Error(\n `Failed to create/login user \"${user}\" on ${registryUrl}: ${response.status} ${JSON.stringify(body)}`\n );\n}\n\n/**\n * Ping the registry to check it's alive.\n * Uses @verdaccio/registry-cli ping.\n */\nexport async function pingRegistry(registryUrl: string): Promise<boolean> {\n debug('pinging %s', registryUrl);\n try {\n const result = await ping(registryUrl);\n return result.ok;\n } catch {\n return false;\n }\n}\n\nexport class LegacyLoginError extends Error {\n constructor(\n message: string,\n readonly status: number\n ) {\n super(message);\n }\n}\n\n/**\n * Replays the legacy login the way each client sends it. npm and Yarn retry a\n * 409 with Basic (Yarn on the GET too); pnpm sends Basic on a single PUT. A\n * registry that authenticates the first PUT is accepted too.\n */\nexport async function legacyLogin(\n registryUrl: string,\n user: string,\n password: string,\n client: 'npm' | 'yarn' | 'pnpm'\n): Promise<string> {\n const userUrl = `${registryUrl}/-/user/org.couchdb.user:${encodeURIComponent(user)}`;\n const basic = `Basic ${Buffer.from(`${user}:${password}`).toString('base64')}`;\n const jsonHeaders = { 'Content-Type': 'application/json' };\n const body: Record<string, unknown> = {\n _id: `org.couchdb.user:${user}`,\n name: user,\n password,\n type: 'user',\n roles: [],\n date: new Date().toISOString(),\n };\n\n const put = (url: string, headers: Record<string, string>) =>\n fetch(url, { method: 'PUT', headers, body: JSON.stringify(body) });\n\n const first = await put(\n userUrl,\n client === 'pnpm'\n ? { ...jsonHeaders, Authorization: basic, 'npm-auth-type': 'web' }\n : jsonHeaders\n );\n const firstBody = (await first.json()) as any;\n if (first.ok && firstBody.token) {\n debug('%s: first PUT authenticated %s', client, user);\n return firstBody.token;\n }\n if (first.status !== 409) {\n throw new LegacyLoginError(`first PUT answered ${first.status}`, first.status);\n }\n\n const current = await fetch(`${userUrl}?write=true`, {\n headers: client === 'yarn' ? { Authorization: basic } : {},\n });\n if (!current.ok) {\n throw new LegacyLoginError(`GET answered ${current.status}`, current.status);\n }\n const currentBody = (await current.json()) as Record<string, unknown>;\n for (const [key, value] of Object.entries(currentBody)) {\n if (!body[key] || key === 'roles') {\n body[key] = value;\n }\n }\n\n const second = await put(`${userUrl}/-rev/${body._rev}`, {\n ...jsonHeaders,\n Authorization: basic,\n });\n const secondBody = (await second.json()) as any;\n if (!second.ok || !secondBody.token) {\n throw new LegacyLoginError(`PUT with revision answered ${second.status}`, second.status);\n }\n return secondBody.token;\n}\n\nexport async function whoami(registryUrl: string, token: string): Promise<string | undefined> {\n const response = await fetch(`${registryUrl}/-/whoami`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n return response.ok ? ((await response.json()) as any).username : undefined;\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createBunAdapter } from "./bun.js";
|
|
2
2
|
import { createDenoAdapter } from "./deno.js";
|
|
3
3
|
import { createNpmAdapter } from "./npm.js";
|
|
4
|
+
import { createNubAdapter } from "./nub.js";
|
|
4
5
|
import { createPnpmAdapter } from "./pnpm.js";
|
|
6
|
+
import { createUpmAdapter } from "./upm.js";
|
|
5
7
|
import { createYarnModernAdapter } from "./yarn-modern.js";
|
|
6
|
-
export { createBunAdapter, createDenoAdapter, createNpmAdapter, createPnpmAdapter, createYarnModernAdapter };
|
|
8
|
+
export { createBunAdapter, createDenoAdapter, createNpmAdapter, createNubAdapter, createPnpmAdapter, createUpmAdapter, createYarnModernAdapter };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { exec } from "../utils/process.js";
|
|
2
|
+
import { prepareGenericEmptyProject } from "../utils/project.js";
|
|
3
|
+
import buildDebug from "debug";
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
//#region src/adapters/nub.ts
|
|
6
|
+
var debug = buildDebug("verdaccio:e2e-cli:nub");
|
|
7
|
+
var NUB_SUPPORTED_COMMANDS = /* @__PURE__ */ new Set(["install", "ci"]);
|
|
8
|
+
function detectVersion(bin) {
|
|
9
|
+
try {
|
|
10
|
+
return execSync(`${bin} --version`, {
|
|
11
|
+
encoding: "utf8",
|
|
12
|
+
timeout: 5e3
|
|
13
|
+
}).trim();
|
|
14
|
+
} catch {
|
|
15
|
+
return "unknown";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function installNub(version) {
|
|
19
|
+
const pkg = `@nubjs/nub@${version}`;
|
|
20
|
+
debug("installing %s into temp dir", pkg);
|
|
21
|
+
const tmpDir = execSync("mktemp -d", { encoding: "utf8" }).trim();
|
|
22
|
+
execSync(`npm install --prefix "${tmpDir}" ${pkg} --loglevel=error`, {
|
|
23
|
+
encoding: "utf8",
|
|
24
|
+
timeout: 3e4
|
|
25
|
+
});
|
|
26
|
+
const bin = `${tmpDir}/node_modules/.bin/nub`;
|
|
27
|
+
const installed = detectVersion(bin);
|
|
28
|
+
debug("installed nub %s at %s", installed, bin);
|
|
29
|
+
console.log(` Auto-installed nub ${installed}`);
|
|
30
|
+
return bin;
|
|
31
|
+
}
|
|
32
|
+
function resolveNubBin(binPath, version) {
|
|
33
|
+
if (binPath) return binPath;
|
|
34
|
+
if (version) return installNub(version);
|
|
35
|
+
return "nub";
|
|
36
|
+
}
|
|
37
|
+
function createNubAdapter(binPath, version) {
|
|
38
|
+
const bin = resolveNubBin(binPath, version);
|
|
39
|
+
const resolved = detectVersion(bin);
|
|
40
|
+
debug("creating nub adapter with bin: %s (%s)", bin, resolved);
|
|
41
|
+
return {
|
|
42
|
+
name: `nub@${resolved}`,
|
|
43
|
+
type: "nub",
|
|
44
|
+
bin,
|
|
45
|
+
supports: NUB_SUPPORTED_COMMANDS,
|
|
46
|
+
registryArg(_url) {
|
|
47
|
+
return [];
|
|
48
|
+
},
|
|
49
|
+
prefixArg(folder) {
|
|
50
|
+
return ["--dir", folder];
|
|
51
|
+
},
|
|
52
|
+
exec(options, ...args) {
|
|
53
|
+
const cmd = args[0];
|
|
54
|
+
if (cmd === "install" || cmd === "ci") args = [...args, "--minimum-release-age=0"];
|
|
55
|
+
return exec(options, bin, args);
|
|
56
|
+
},
|
|
57
|
+
async prepareProject(packageName, version, registryUrl, port, token, dependencies = {}, devDependencies = {}) {
|
|
58
|
+
return prepareGenericEmptyProject(packageName, version, port, token, registryUrl, dependencies, devDependencies);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { createNubAdapter };
|
|
64
|
+
|
|
65
|
+
//# sourceMappingURL=nub.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nub.js","names":[],"sources":["../../../src/adapters/nub.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:nub');\n\n// nub (https://nubjs.com, package @nubjs/nub) is an all-in-one Node.js toolkit\n// whose package-manager side only covers the install path (install, ci, add,\n// remove, update, dedupe) — it has no publish/info/audit/etc. So the suite\n// exercises just install and ci.\nconst NUB_SUPPORTED_COMMANDS = new Set(['install', 'ci']);\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 installNub(version: string): string {\n const pkg = `@nubjs/nub@${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/nub`;\n const installed = detectVersion(bin);\n debug('installed nub %s at %s', installed, bin);\n console.log(` Auto-installed nub ${installed}`);\n return bin;\n}\n\nfunction resolveNubBin(binPath?: string, version?: string): string {\n if (binPath) return binPath;\n if (version) return installNub(version);\n return 'nub';\n}\n\nexport function createNubAdapter(binPath?: string, version?: string): PackageManagerAdapter {\n const bin = resolveNubBin(binPath, version);\n const resolved = detectVersion(bin);\n debug('creating nub adapter with bin: %s (%s)', bin, resolved);\n\n const adapter: PackageManagerAdapter = {\n name: `nub@${resolved}`,\n type: 'nub',\n bin,\n supports: NUB_SUPPORTED_COMMANDS,\n\n registryArg(_url: string): string[] {\n // nub reads registry and _authToken from the .npmrc cascade written by\n // prepareProject, so no CLI flag is needed.\n return [];\n },\n\n prefixArg(folder: string): string[] {\n return ['--dir', folder];\n },\n\n exec(options: SpawnOptions, ...args: string[]): Promise<ExecOutput> {\n // nub enforces a strict 24h `minimumReleaseAge` by default and reads it\n // under the pnpm-style key, not npm's `min-release-age` that the harness\n // .npmrc sets. Disable it explicitly on install/ci so the just-published\n // fixtures (age ~0) resolve.\n const cmd = args[0];\n if (cmd === 'install' || cmd === 'ci') {\n args = [...args, '--minimum-release-age=0'];\n }\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;AAMhD,IAAM,yCAAyB,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC;AAExD,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,WAAW,SAAyB;CAC3C,MAAM,MAAM,cAAc;CAC1B,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,MAAM,0CAA0C,KAAK,QAAQ;CAmD7D,OAAO;EAhDL,MAAM,OAAO;EACb,MAAM;EACN;EACA,UAAU;EAEV,YAAY,MAAwB;GAGlC,OAAO,CAAC;EACV;EAEA,UAAU,QAA0B;GAClC,OAAO,CAAC,SAAS,MAAM;EACzB;EAEA,KAAK,SAAuB,GAAG,MAAqC;GAKlE,MAAM,MAAM,KAAK;GACjB,IAAI,QAAQ,aAAa,QAAQ,MAC/B,OAAO,CAAC,GAAG,MAAM,yBAAyB;GAE5C,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"}
|
|
@@ -6,18 +6,7 @@ import { join } from "path";
|
|
|
6
6
|
import { writeFile } from "fs/promises";
|
|
7
7
|
//#region src/adapters/pnpm.ts
|
|
8
8
|
var debug = buildDebug("verdaccio:e2e-cli:pnpm");
|
|
9
|
-
var
|
|
10
|
-
"publish",
|
|
11
|
-
"unpublish",
|
|
12
|
-
"install",
|
|
13
|
-
"info",
|
|
14
|
-
"audit",
|
|
15
|
-
"deprecate",
|
|
16
|
-
"dist-tag",
|
|
17
|
-
"ping",
|
|
18
|
-
"search"
|
|
19
|
-
]);
|
|
20
|
-
var PNPM_V11_COMMANDS = /* @__PURE__ */ new Set([
|
|
9
|
+
var PNPM_COMMANDS = /* @__PURE__ */ new Set([
|
|
21
10
|
"publish",
|
|
22
11
|
"unpublish",
|
|
23
12
|
"install",
|
|
@@ -25,15 +14,7 @@ var PNPM_V11_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
25
14
|
"audit",
|
|
26
15
|
"deprecate"
|
|
27
16
|
]);
|
|
28
|
-
|
|
29
|
-
const major = parseInt(version.split(".")[0], 10);
|
|
30
|
-
if (major >= 11) {
|
|
31
|
-
debug("pnpm v%d detected, using v11 command set", major);
|
|
32
|
-
return PNPM_V11_COMMANDS;
|
|
33
|
-
}
|
|
34
|
-
return PNPM_V10_COMMANDS;
|
|
35
|
-
}
|
|
36
|
-
var PNPM_MIN_SUPPORTED_MAJOR = 10;
|
|
17
|
+
var PNPM_MIN_SUPPORTED_MAJOR = 11;
|
|
37
18
|
function detectVersion(bin) {
|
|
38
19
|
try {
|
|
39
20
|
return execSync(`${bin} --version`, {
|
|
@@ -76,7 +57,7 @@ function createPnpmAdapter(binPath, version) {
|
|
|
76
57
|
name: `pnpm@${resolved}`,
|
|
77
58
|
type: "pnpm",
|
|
78
59
|
bin,
|
|
79
|
-
supports:
|
|
60
|
+
supports: PNPM_COMMANDS,
|
|
80
61
|
registryArg(url) {
|
|
81
62
|
return ["--registry", url];
|
|
82
63
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pnpm.js","names":[],"sources":["../../../src/adapters/pnpm.ts"],"sourcesContent":["import { SpawnOptions, execSync } from 'child_process';\nimport buildDebug from 'debug';\nimport { writeFile } from 'fs/promises';\nimport { join } from 'path';\n\nimport { ExecOutput, PackageManagerAdapter } from '../types';\nimport { exec } from '../utils/process';\nimport { prepareGenericEmptyProject } from '../utils/project';\n\nconst debug = buildDebug('verdaccio:e2e-cli:pnpm');\n\
|
|
1
|
+
{"version":3,"file":"pnpm.js","names":[],"sources":["../../../src/adapters/pnpm.ts"],"sourcesContent":["import { SpawnOptions, execSync } from 'child_process';\nimport buildDebug from 'debug';\nimport { writeFile } from 'fs/promises';\nimport { join } from 'path';\n\nimport { ExecOutput, PackageManagerAdapter } from '../types';\nimport { exec } from '../utils/process';\nimport { prepareGenericEmptyProject } from '../utils/project';\n\nconst debug = buildDebug('verdaccio:e2e-cli:pnpm');\n\n// pnpm removed ping and search; the dist-tag output changed (no --json) and needs adapted assertions\nconst PNPM_COMMANDS = new Set(['publish', 'unpublish', 'install', 'info', 'audit', 'deprecate']);\n\n// pnpm 8, 9 and 10 are no longer supported by the suite.\nconst PNPM_MIN_SUPPORTED_MAJOR = 11;\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) && major < PNPM_MIN_SUPPORTED_MAJOR) {\n throw new Error(\n `pnpm@${resolved} is not supported. Minimum supported major: ${PNPM_MIN_SUPPORTED_MAJOR}`\n );\n }\n}\n\nfunction installPnpm(version: string): string {\n const pkg = `pnpm@${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/pnpm`;\n const installed = detectVersion(bin);\n debug('installed pnpm %s at %s', installed, bin);\n console.log(` Auto-installed pnpm ${installed}`);\n return bin;\n}\n\nfunction resolvePnpmBin(binPath?: string, version?: string): string {\n if (binPath) return binPath;\n if (version) return installPnpm(version);\n return 'pnpm';\n}\n\nexport function createPnpmAdapter(binPath?: string, version?: string): PackageManagerAdapter {\n const bin = resolvePnpmBin(binPath, version);\n const resolved = detectVersion(bin);\n assertSupportedVersion(resolved);\n debug('creating pnpm adapter with bin: %s (%s)', bin, resolved);\n\n const adapter: PackageManagerAdapter = {\n name: `pnpm@${resolved}`,\n type: 'pnpm',\n bin,\n supports: PNPM_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 const major = parseInt(resolved.split('.')[0], 10);\n if (major >= 11) {\n // pnpm v11: native deprecate/unpublish don't support --json\n const cmd = args[0];\n if (cmd === 'deprecate' || cmd === 'unpublish' || cmd === 'publish' || cmd === 'dist-tag') {\n args = args.filter((a) => a !== '--json' && !a.startsWith('--loglevel'));\n }\n // pnpm v11: un-deprecate uses `undeprecate` command instead of `deprecate pkg \"\"`\n if (cmd === 'deprecate') {\n // Check if message arg is empty string (un-deprecate)\n // args: ['deprecate', 'pkg@version', '', ...flags]\n const msgIdx = 2; // message is the third arg\n if (args.length > msgIdx && args[msgIdx] === '') {\n args = ['undeprecate', args[1], ...args.slice(3)];\n }\n }\n // pnpm v11: native version command doesn't accept --registry, --loglevel, or --no--git-tag-version\n if (cmd === 'version') {\n args = args.filter(\n (a) =>\n !a.startsWith('--registry') &&\n !a.startsWith('--loglevel') &&\n !a.startsWith('--no--git-tag-version')\n );\n }\n }\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 const project = await prepareGenericEmptyProject(\n packageName,\n version,\n port,\n token,\n registryUrl,\n dependencies,\n devDependencies\n );\n // pnpm 11.24+ enforces a minimumReleaseAge cooldown by default, which\n // rejects the just-published packages these tests install\n // (ERR_PNPM_NO_MATURE_MATCHING_VERSION). Disable it per project via\n // pnpm-workspace.yaml — the file also marks the temp folder as its own\n // workspace root, isolating it from any parent workspace. Tests that\n // exercise the cooldown itself overwrite this file with their own config.\n await writeFile(join(project.tempFolder, 'pnpm-workspace.yaml'), 'minimumReleaseAge: 0\\n');\n return project;\n },\n };\n\n return adapter;\n}\n"],"mappings":";;;;;;;AASA,IAAM,QAAQ,WAAW,wBAAwB;AAGjD,IAAM,gCAAgB,IAAI,IAAI;CAAC;CAAW;CAAa;CAAW;CAAQ;CAAS;AAAW,CAAC;AAG/F,IAAM,2BAA2B;AAEjC,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,QAAQ,0BAClC,MAAM,IAAI,MACR,QAAQ,SAAS,8CAA8C,0BACjE;AAEJ;AAEA,SAAS,YAAY,SAAyB;CAC5C,MAAM,MAAM,QAAQ;CACpB,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,2BAA2B,WAAW,GAAG;CAC/C,QAAQ,IAAI,yBAAyB,WAAW;CAChD,OAAO;AACT;AAEA,SAAS,eAAe,SAAkB,SAA0B;CAClE,IAAI,SAAS,OAAO;CACpB,IAAI,SAAS,OAAO,YAAY,OAAO;CACvC,OAAO;AACT;AAEA,SAAgB,kBAAkB,SAAkB,SAAyC;CAC3F,MAAM,MAAM,eAAe,SAAS,OAAO;CAC3C,MAAM,WAAW,cAAc,GAAG;CAClC,uBAAuB,QAAQ;CAC/B,MAAM,2CAA2C,KAAK,QAAQ;CA2E9D,OAAO;EAxEL,MAAM,QAAQ;EACd,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;GAElE,IADc,SAAS,SAAS,MAAM,GAAG,CAAC,CAAC,IAAI,EAC3C,KAAS,IAAI;IAEf,MAAM,MAAM,KAAK;IACjB,IAAI,QAAQ,eAAe,QAAQ,eAAe,QAAQ,aAAa,QAAQ,YAC7E,OAAO,KAAK,QAAQ,MAAM,MAAM,YAAY,CAAC,EAAE,WAAW,YAAY,CAAC;IAGzE,IAAI,QAAQ,aAAa;KAGvB,MAAM,SAAS;KACf,IAAI,KAAK,SAAS,UAAU,KAAK,YAAY,IAC3C,OAAO;MAAC;MAAe,KAAK;MAAI,GAAG,KAAK,MAAM,CAAC;KAAC;IAEpD;IAEA,IAAI,QAAQ,WACV,OAAO,KAAK,QACT,MACC,CAAC,EAAE,WAAW,YAAY,KAC1B,CAAC,EAAE,WAAW,YAAY,KAC1B,CAAC,EAAE,WAAW,uBAAuB,CACzC;GAEJ;GACA,OAAO,KAAK,SAAS,KAAK,IAAI;EAChC;EAEA,MAAM,eACJ,aACA,SACA,aACA,MACA,OACA,eAAuC,CAAC,GACxC,kBAA0C,CAAC,GACV;GACjC,MAAM,UAAU,MAAM,2BACpB,aACA,SACA,MACA,OACA,aACA,cACA,eACF;GAOA,MAAM,UAAU,KAAK,QAAQ,YAAY,qBAAqB,GAAG,wBAAwB;GACzF,OAAO;EACT;CAGK;AACT"}
|