@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,26 @@
|
|
|
1
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
let assert = require("assert");
|
|
3
|
+
assert = require_runtime.__toESM(assert, 1);
|
|
4
|
+
//#region src/tests/ping.ts
|
|
5
|
+
async function testPing(ctx) {
|
|
6
|
+
const type = ctx.adapter.type;
|
|
7
|
+
const { tempFolder } = await ctx.adapter.prepareProject(`verdaccio-ping-${ctx.runId}`, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
8
|
+
const cwd = tempFolder;
|
|
9
|
+
if (type === "yarn-modern" && ctx.adapter.importPlugin) await ctx.adapter.importPlugin(tempFolder, "npm-ping");
|
|
10
|
+
const jsonFlag = type === "yarn-modern" ? [] : ["--json"];
|
|
11
|
+
const resp = await ctx.adapter.exec({ cwd }, "ping", ...jsonFlag, ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
12
|
+
if (type === "yarn-modern") assert.default.ok(true, "ping succeeded");
|
|
13
|
+
else {
|
|
14
|
+
const parsedBody = JSON.parse(resp.stdout);
|
|
15
|
+
assert.default.strictEqual(parsedBody.registry, ctx.registryUrl + "/", `Expected registry "${ctx.registryUrl}/" but got "${parsedBody.registry}"`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
var pingTest = {
|
|
19
|
+
name: "ping",
|
|
20
|
+
requires: ["ping"],
|
|
21
|
+
run: testPing
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
24
|
+
exports.pingTest = pingTest;
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=ping.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ping.cjs","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,OAAA,QAAO,GAAG,MAAM,gBAAgB;MAC3B;EACL,MAAM,aAAa,KAAK,MAAM,KAAK,MAAM;EACzC,OAAA,QAAO,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,36 @@
|
|
|
1
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
let assert = require("assert");
|
|
3
|
+
assert = require_runtime.__toESM(assert, 1);
|
|
4
|
+
//#region src/tests/publish.ts
|
|
5
|
+
async function testPublish(ctx) {
|
|
6
|
+
const packages = [
|
|
7
|
+
`verdaccio-memory-${ctx.runId}`,
|
|
8
|
+
`verdaccio-${ctx.runId}`,
|
|
9
|
+
`@verdaccio/foo-${ctx.runId}`,
|
|
10
|
+
`@verdaccio/some-foo-${ctx.runId}`
|
|
11
|
+
];
|
|
12
|
+
const type = ctx.adapter.type;
|
|
13
|
+
for (const pkgName of packages) {
|
|
14
|
+
const { tempFolder } = await ctx.adapter.prepareProject(pkgName, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
15
|
+
if (type === "yarn-modern") await ctx.adapter.exec({ cwd: tempFolder }, "install");
|
|
16
|
+
const resp = await ctx.adapter.exec({ cwd: tempFolder }, "publish", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
17
|
+
if (type === "yarn-modern") assert.default.ok(resp.stdout.match(/Package archive published/), `Expected "Package archive published" for ${pkgName} but got "${resp.stdout}"`);
|
|
18
|
+
else if (type === "bun") assert.default.ok(true, `Publish succeeded for ${pkgName}`);
|
|
19
|
+
else try {
|
|
20
|
+
const parsedBody = JSON.parse(resp.stdout);
|
|
21
|
+
assert.default.strictEqual(parsedBody.name, pkgName, `Expected package name "${pkgName}" but got "${parsedBody.name}"`);
|
|
22
|
+
assert.default.ok(parsedBody.files, `Expected files to be defined for ${pkgName}`);
|
|
23
|
+
} catch {
|
|
24
|
+
assert.default.ok(true, `Publish succeeded for ${pkgName}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
var publishTest = {
|
|
29
|
+
name: "publish",
|
|
30
|
+
requires: ["publish"],
|
|
31
|
+
run: testPublish
|
|
32
|
+
};
|
|
33
|
+
//#endregion
|
|
34
|
+
exports.publishTest = publishTest;
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=publish.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish.cjs","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,OAAA,QAAO,GACL,KAAK,OAAO,MAAM,2BAA2B,GAC7C,4CAA4C,QAAQ,YAAY,KAAK,OAAO,EAC9E;OACK,IAAI,SAAS,OAElB,OAAA,QAAO,GAAG,MAAM,yBAAyB,SAAS;OAGlD,IAAI;GACF,MAAM,aAAa,KAAK,MAAM,KAAK,MAAM;GACzC,OAAA,QAAO,YACL,WAAW,MACX,SACA,0BAA0B,QAAQ,aAAa,WAAW,KAAK,EACjE;GACA,OAAA,QAAO,GAAG,WAAW,OAAO,oCAAoC,SAAS;EAC3E,QAAQ;GAEN,OAAA,QAAO,GAAG,MAAM,yBAAyB,SAAS;EACpD;CAEJ;AACF;AAEA,IAAa,cAA8B;CACzC,MAAM;CACN,UAAU,CAAC,SAAS;CACpB,KAAK;AACP"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_publish = require("../utils/publish.cjs");
|
|
3
|
+
let assert = require("assert");
|
|
4
|
+
assert = require_runtime.__toESM(assert, 1);
|
|
5
|
+
//#region src/tests/search.ts
|
|
6
|
+
async function testSearch(ctx) {
|
|
7
|
+
const pkgName = `verdaccio-search-${ctx.runId}`;
|
|
8
|
+
await require_publish.publishLocalPackage(ctx, pkgName, "1.0.0");
|
|
9
|
+
const { tempFolder } = await ctx.adapter.prepareProject(`search-client-${ctx.runId}`, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
10
|
+
const resp = await ctx.adapter.exec({ cwd: tempFolder }, "search", pkgName, "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
11
|
+
const pkgFind = JSON.parse(resp.stdout).find((item) => item.name === pkgName);
|
|
12
|
+
assert.default.ok(pkgFind, `Expected to find ${pkgName} in search results`);
|
|
13
|
+
assert.default.strictEqual(pkgFind.name, pkgName);
|
|
14
|
+
}
|
|
15
|
+
var searchTest = {
|
|
16
|
+
name: "search",
|
|
17
|
+
requires: ["search", "publish"],
|
|
18
|
+
run: testSearch
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
exports.searchTest = searchTest;
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=search.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.cjs","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,gBAAA,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,OAAA,QAAO,GAAG,SAAS,oBAAoB,QAAQ,mBAAmB;CAClE,OAAA,QAAO,YAAY,QAAQ,MAAM,OAAO;AAC1C;AAEA,IAAa,aAA6B;CACxC,MAAM;CACN,UAAU,CAAC,UAAU,SAAS;CAC9B,KAAK;AACP"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
let assert = require("assert");
|
|
3
|
+
assert = require_runtime.__toESM(assert, 1);
|
|
4
|
+
//#region src/tests/unpublish.ts
|
|
5
|
+
async function publishPkg(ctx, tempFolder) {
|
|
6
|
+
await ctx.adapter.exec({ cwd: tempFolder }, "publish", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
7
|
+
}
|
|
8
|
+
async function bumpVersion(ctx, tempFolder, bump) {
|
|
9
|
+
await ctx.adapter.exec({ cwd: tempFolder }, "version", bump, "--no--git-tag-version", "--loglevel=info", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
10
|
+
}
|
|
11
|
+
async function testUnpublish(ctx) {
|
|
12
|
+
const id = ctx.runId;
|
|
13
|
+
const pkgName = `@verdaccio/unpub1-${id}`;
|
|
14
|
+
const { tempFolder } = await ctx.adapter.prepareProject(pkgName, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
15
|
+
await publishPkg(ctx, tempFolder);
|
|
16
|
+
await bumpVersion(ctx, tempFolder, "minor");
|
|
17
|
+
await publishPkg(ctx, tempFolder);
|
|
18
|
+
await bumpVersion(ctx, tempFolder, "minor");
|
|
19
|
+
await publishPkg(ctx, tempFolder);
|
|
20
|
+
await bumpVersion(ctx, tempFolder, "major");
|
|
21
|
+
await publishPkg(ctx, tempFolder);
|
|
22
|
+
const resp = await ctx.adapter.exec({ cwd: tempFolder }, "unpublish", pkgName, "--force", "--loglevel=info", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
23
|
+
assert.default.ok(resp.stdout.includes(pkgName), `Expected unpublish output to contain "${pkgName}" but got "${resp.stdout}"`);
|
|
24
|
+
const pkgName2 = `@verdaccio/unpub2-${id}`;
|
|
25
|
+
const { tempFolder: tf2 } = await ctx.adapter.prepareProject(pkgName2, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
|
|
26
|
+
await publishPkg(ctx, tf2);
|
|
27
|
+
await bumpVersion(ctx, tf2, "minor");
|
|
28
|
+
await publishPkg(ctx, tf2);
|
|
29
|
+
const resp2 = await ctx.adapter.exec({ cwd: tf2 }, "unpublish", `${pkgName2}@1.0.0`, "--force", "--loglevel=info", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
|
|
30
|
+
assert.default.ok(resp2.stdout.includes(pkgName2), `Expected unpublish output to contain "${pkgName2}" but got "${resp2.stdout}"`);
|
|
31
|
+
}
|
|
32
|
+
var unpublishTest = {
|
|
33
|
+
name: "unpublish",
|
|
34
|
+
requires: ["unpublish"],
|
|
35
|
+
run: testUnpublish
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
exports.unpublishTest = unpublishTest;
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=unpublish.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unpublish.cjs","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,OAAA,QAAO,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,OAAA,QAAO,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,59 @@
|
|
|
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
|
+
exports.DEFAULT_UPLINK_PORT = DEFAULT_UPLINK_PORT;
|
|
57
|
+
exports.buildE2EConfig = buildE2EConfig;
|
|
58
|
+
|
|
59
|
+
//# sourceMappingURL=e2e-config.cjs.map
|
|
@@ -0,0 +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-*` 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,111 @@
|
|
|
1
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
let debug = require("debug");
|
|
3
|
+
debug = require_runtime.__toESM(debug, 1);
|
|
4
|
+
let crypto = require("crypto");
|
|
5
|
+
let http = require("http");
|
|
6
|
+
//#region src/utils/http-client.ts
|
|
7
|
+
var debug$1 = (0, debug.default)("verdaccio:e2e-cli:http-client");
|
|
8
|
+
var ABBREVIATED_ACCEPT = "application/vnd.npm.install-v1+json";
|
|
9
|
+
/**
|
|
10
|
+
* Registry URL for a package name. Scoped names are encoded with the same
|
|
11
|
+
* `%2f` form npm clients use: `@scope%2fname`.
|
|
12
|
+
*/
|
|
13
|
+
function packumentUrl(registryUrl, pkgName) {
|
|
14
|
+
const encoded = pkgName.startsWith("@") ? pkgName.replaceAll("/", "%2f") : pkgName;
|
|
15
|
+
return `${registryUrl.replace(/\/$/, "")}/${encoded}`;
|
|
16
|
+
}
|
|
17
|
+
async function fetchPackument(registryUrl, pkgName, options = {}) {
|
|
18
|
+
const url = packumentUrl(registryUrl, pkgName);
|
|
19
|
+
const headers = {};
|
|
20
|
+
if (options.abbreviated) headers["Accept"] = ABBREVIATED_ACCEPT;
|
|
21
|
+
if (options.etag) headers["If-None-Match"] = options.etag;
|
|
22
|
+
if (options.token) headers["Authorization"] = `Bearer ${options.token}`;
|
|
23
|
+
debug$1("GET %s %o", url, headers);
|
|
24
|
+
if (options.etag) return new Promise((resolve, reject) => {
|
|
25
|
+
(0, http.get)(url, { headers }, (response) => {
|
|
26
|
+
let text = "";
|
|
27
|
+
response.setEncoding("utf8");
|
|
28
|
+
response.on("data", (chunk) => text += chunk);
|
|
29
|
+
response.on("end", () => {
|
|
30
|
+
let body;
|
|
31
|
+
try {
|
|
32
|
+
body = JSON.parse(text);
|
|
33
|
+
} catch {
|
|
34
|
+
body = void 0;
|
|
35
|
+
}
|
|
36
|
+
const responseHeaders = new Headers();
|
|
37
|
+
for (const [name, value] of Object.entries(response.headers)) if (value !== void 0) responseHeaders.set(name, Array.isArray(value) ? value.join(", ") : value);
|
|
38
|
+
resolve({
|
|
39
|
+
status: response.statusCode || 0,
|
|
40
|
+
headers: responseHeaders,
|
|
41
|
+
body
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}).on("error", reject);
|
|
45
|
+
});
|
|
46
|
+
const response = await fetch(url, { headers });
|
|
47
|
+
let body;
|
|
48
|
+
if (response.status !== 304) {
|
|
49
|
+
const text = await response.text();
|
|
50
|
+
try {
|
|
51
|
+
body = JSON.parse(text);
|
|
52
|
+
} catch {
|
|
53
|
+
body = void 0;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
status: response.status,
|
|
58
|
+
headers: response.headers,
|
|
59
|
+
body
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Download a tarball fully, computing sha1/sha512 integrity on the fly.
|
|
64
|
+
* Rejects with the underlying error when aborted or the connection drops.
|
|
65
|
+
*/
|
|
66
|
+
async function downloadTarball(url, options = {}) {
|
|
67
|
+
const headers = {};
|
|
68
|
+
if (options.token) headers["Authorization"] = `Bearer ${options.token}`;
|
|
69
|
+
debug$1("GET (tarball) %s", url);
|
|
70
|
+
const response = await fetch(url, {
|
|
71
|
+
headers,
|
|
72
|
+
signal: options.signal
|
|
73
|
+
});
|
|
74
|
+
const sha1 = (0, crypto.createHash)("sha1");
|
|
75
|
+
const sha512 = (0, crypto.createHash)("sha512");
|
|
76
|
+
let bytes = 0;
|
|
77
|
+
let first = true;
|
|
78
|
+
if (response.body) for await (const chunk of response.body) {
|
|
79
|
+
const buf = Buffer.from(chunk);
|
|
80
|
+
bytes += buf.length;
|
|
81
|
+
sha1.update(buf);
|
|
82
|
+
sha512.update(buf);
|
|
83
|
+
if (first) {
|
|
84
|
+
first = false;
|
|
85
|
+
options.onFirstChunk?.();
|
|
86
|
+
}
|
|
87
|
+
options.onChunk?.(bytes);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
status: response.status,
|
|
91
|
+
headers: response.headers,
|
|
92
|
+
bytes,
|
|
93
|
+
sha1: sha1.digest("hex"),
|
|
94
|
+
integrity: `sha512-${sha512.digest("base64")}`
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/** Compute npm-style dist values (shasum/integrity) for a buffer. */
|
|
98
|
+
function computeDist(buffer) {
|
|
99
|
+
return {
|
|
100
|
+
shasum: (0, crypto.createHash)("sha1").update(buffer).digest("hex"),
|
|
101
|
+
integrity: `sha512-${(0, crypto.createHash)("sha512").update(buffer).digest("base64")}`
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
exports.ABBREVIATED_ACCEPT = ABBREVIATED_ACCEPT;
|
|
106
|
+
exports.computeDist = computeDist;
|
|
107
|
+
exports.downloadTarball = downloadTarball;
|
|
108
|
+
exports.fetchPackument = fetchPackument;
|
|
109
|
+
exports.packumentUrl = packumentUrl;
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=http-client.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-client.cjs","names":[],"sources":["../../../src/utils/http-client.ts"],"sourcesContent":["import { createHash } from 'crypto';\nimport buildDebug from 'debug';\nimport { get } from 'http';\n\nconst debug = buildDebug('verdaccio:e2e-cli:http-client');\n\nexport type PackumentResponse = {\n status: number;\n headers: Headers;\n /** Parsed JSON body, or undefined when the response is not JSON (e.g. 304) */\n body?: any;\n};\n\nexport type TarballDownload = {\n status: number;\n headers: Headers;\n /** Total bytes received */\n bytes: number;\n /** hex-encoded sha1 of the received bytes (matches dist.shasum) */\n sha1: string;\n /** `sha512-<base64>` of the received bytes (matches dist.integrity) */\n integrity: string;\n};\n\nexport type FetchPackumentOptions = {\n /** Request the abbreviated (install) metadata format */\n abbreviated?: boolean;\n /** Send If-None-Match with this value */\n etag?: string;\n /** Bearer token */\n token?: string;\n};\n\nexport const ABBREVIATED_ACCEPT = 'application/vnd.npm.install-v1+json';\n\n/**\n * Registry URL for a package name. Scoped names are encoded with the same\n * `%2f` form npm clients use: `@scope%2fname`.\n */\nexport function packumentUrl(registryUrl: string, pkgName: string): string {\n const encoded = pkgName.startsWith('@') ? pkgName.replaceAll('/', '%2f') : pkgName;\n return `${registryUrl.replace(/\\/$/, '')}/${encoded}`;\n}\n\nexport async function fetchPackument(\n registryUrl: string,\n pkgName: string,\n options: FetchPackumentOptions = {}\n): Promise<PackumentResponse> {\n const url = packumentUrl(registryUrl, pkgName);\n const headers: Record<string, string> = {};\n if (options.abbreviated) {\n headers['Accept'] = ABBREVIATED_ACCEPT;\n }\n if (options.etag) {\n headers['If-None-Match'] = options.etag;\n }\n if (options.token) {\n headers['Authorization'] = `Bearer ${options.token}`;\n }\n debug('GET %s %o', url, headers);\n\n // Conditional requests must bypass fetch(): undici appends\n // `cache-control: no-cache` whenever If-None-Match is present, which\n // (correctly, per HTTP semantics) disables the server's 304 revalidation.\n if (options.etag) {\n return new Promise((resolve, reject) => {\n get(url, { headers }, (response) => {\n let text = '';\n response.setEncoding('utf8');\n response.on('data', (chunk) => (text += chunk));\n response.on('end', () => {\n let body: any;\n try {\n body = JSON.parse(text);\n } catch {\n body = undefined;\n }\n const responseHeaders = new Headers();\n for (const [name, value] of Object.entries(response.headers)) {\n if (value !== undefined) {\n responseHeaders.set(name, Array.isArray(value) ? value.join(', ') : value);\n }\n }\n resolve({ status: response.statusCode || 0, headers: responseHeaders, body });\n });\n }).on('error', reject);\n });\n }\n\n const response = await fetch(url, { headers });\n let body: any;\n if (response.status !== 304) {\n const text = await response.text();\n try {\n body = JSON.parse(text);\n } catch {\n body = undefined;\n }\n }\n return { status: response.status, headers: response.headers, body };\n}\n\nexport type DownloadTarballOptions = {\n signal?: AbortSignal;\n token?: string;\n /** Called after the first chunk of the body arrives (useful to abort mid-stream) */\n onFirstChunk?: () => void;\n /** Called after every chunk with the total bytes received so far (useful to abort at a threshold) */\n onChunk?: (bytesReceived: number) => void;\n};\n\n/**\n * Download a tarball fully, computing sha1/sha512 integrity on the fly.\n * Rejects with the underlying error when aborted or the connection drops.\n */\nexport async function downloadTarball(\n url: string,\n options: DownloadTarballOptions = {}\n): Promise<TarballDownload> {\n const headers: Record<string, string> = {};\n if (options.token) {\n headers['Authorization'] = `Bearer ${options.token}`;\n }\n debug('GET (tarball) %s', url);\n const response = await fetch(url, { headers, signal: options.signal });\n\n const sha1 = createHash('sha1');\n const sha512 = createHash('sha512');\n let bytes = 0;\n let first = true;\n\n if (response.body) {\n for await (const chunk of response.body as any) {\n const buf = Buffer.from(chunk);\n bytes += buf.length;\n sha1.update(buf);\n sha512.update(buf);\n if (first) {\n first = false;\n options.onFirstChunk?.();\n }\n options.onChunk?.(bytes);\n }\n }\n\n return {\n status: response.status,\n headers: response.headers,\n bytes,\n sha1: sha1.digest('hex'),\n integrity: `sha512-${sha512.digest('base64')}`,\n };\n}\n\n/** Compute npm-style dist values (shasum/integrity) for a buffer. */\nexport function computeDist(buffer: Buffer): { shasum: string; integrity: string } {\n return {\n shasum: createHash('sha1').update(buffer).digest('hex'),\n integrity: `sha512-${createHash('sha512').update(buffer).digest('base64')}`,\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
exports.normalizeInfo = normalizeInfo;
|
|
23
|
+
exports.parseInfoJson = parseInfoJson;
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=info.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"info.cjs","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"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_http_client = require("./http-client.cjs");
|
|
3
|
+
let debug = require("debug");
|
|
4
|
+
debug = require_runtime.__toESM(debug, 1);
|
|
5
|
+
let crypto = require("crypto");
|
|
6
|
+
let http = require("http");
|
|
7
|
+
//#region src/utils/mock-uplink.ts
|
|
8
|
+
var debug$1 = (0, debug.default)("verdaccio:e2e-cli:mock-uplink");
|
|
9
|
+
/**
|
|
10
|
+
* Minimal controllable npm uplink used by scenario:uplink-failure.
|
|
11
|
+
*
|
|
12
|
+
* The registry under test is configured (see --print-config) to proxy the
|
|
13
|
+
* `e2e-uplink-*` package pattern to this server, so the scenario can flip the
|
|
14
|
+
* uplink between healthy, dropping connections mid-tarball, slow, and down.
|
|
15
|
+
*/
|
|
16
|
+
var MockUplink = class {
|
|
17
|
+
port;
|
|
18
|
+
mode = "ok";
|
|
19
|
+
requests = [];
|
|
20
|
+
server;
|
|
21
|
+
packages = /* @__PURE__ */ new Map();
|
|
22
|
+
searchResults = [];
|
|
23
|
+
constructor(port) {
|
|
24
|
+
this.port = port;
|
|
25
|
+
}
|
|
26
|
+
/** Register a package served by this uplink; tarball is random (incompressible) bytes. */
|
|
27
|
+
addPackage(name, tarballBytes = 65536) {
|
|
28
|
+
const tarball = (0, crypto.randomBytes)(tarballBytes);
|
|
29
|
+
const { shasum, integrity } = require_http_client.computeDist(tarball);
|
|
30
|
+
const pkg = {
|
|
31
|
+
name,
|
|
32
|
+
version: "1.0.0",
|
|
33
|
+
tarball,
|
|
34
|
+
shasum,
|
|
35
|
+
integrity
|
|
36
|
+
};
|
|
37
|
+
this.packages.set(name, pkg);
|
|
38
|
+
return pkg;
|
|
39
|
+
}
|
|
40
|
+
/** Register a result served by the /-/v1/search endpoint. */
|
|
41
|
+
addSearchResult(name, version = "1.0.0") {
|
|
42
|
+
this.searchResults.push({
|
|
43
|
+
name,
|
|
44
|
+
version
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* npmjs-faithful /-/v1/search: filters by `text` substring, applies
|
|
49
|
+
* `from`/`size` itself (like registry.npmjs.org does) and reports the real
|
|
50
|
+
* `total` of matches. Mimicking the upstream offsetting is what exposes
|
|
51
|
+
* double pagination in the registry under test.
|
|
52
|
+
*/
|
|
53
|
+
searchResponse(url) {
|
|
54
|
+
const text = url.searchParams.get("text") ?? "";
|
|
55
|
+
const from = Math.max(0, parseInt(url.searchParams.get("from") ?? "0", 10) || 0);
|
|
56
|
+
const size = Math.max(0, parseInt(url.searchParams.get("size") ?? "20", 10) || 20);
|
|
57
|
+
const matches = this.searchResults.filter((r) => r.name.includes(text));
|
|
58
|
+
const page = matches.slice(from, from + size);
|
|
59
|
+
return JSON.stringify({
|
|
60
|
+
objects: page.map((r) => ({
|
|
61
|
+
package: {
|
|
62
|
+
name: r.name,
|
|
63
|
+
version: r.version,
|
|
64
|
+
description: "mock uplink search result",
|
|
65
|
+
date: "2020-01-01T00:00:00.000Z",
|
|
66
|
+
maintainers: [{
|
|
67
|
+
username: "mock-uplink",
|
|
68
|
+
email: "mock@example.org"
|
|
69
|
+
}],
|
|
70
|
+
links: { npm: `http://localhost:${this.port}/${r.name}` }
|
|
71
|
+
},
|
|
72
|
+
score: {
|
|
73
|
+
final: 1,
|
|
74
|
+
detail: {
|
|
75
|
+
quality: 1,
|
|
76
|
+
popularity: 1,
|
|
77
|
+
maintenance: 1
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
searchScore: 1,
|
|
81
|
+
flags: {}
|
|
82
|
+
})),
|
|
83
|
+
total: matches.length,
|
|
84
|
+
time: (/* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z")).toISOString()
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
packument(pkg) {
|
|
88
|
+
const tarballUrl = `http://localhost:${this.port}/${pkg.name}/-/${pkg.name}-${pkg.version}.tgz`;
|
|
89
|
+
return JSON.stringify({
|
|
90
|
+
name: pkg.name,
|
|
91
|
+
"dist-tags": { latest: pkg.version },
|
|
92
|
+
versions: { [pkg.version]: {
|
|
93
|
+
name: pkg.name,
|
|
94
|
+
version: pkg.version,
|
|
95
|
+
description: "mock uplink package",
|
|
96
|
+
dist: {
|
|
97
|
+
tarball: tarballUrl,
|
|
98
|
+
shasum: pkg.shasum,
|
|
99
|
+
integrity: pkg.integrity
|
|
100
|
+
}
|
|
101
|
+
} },
|
|
102
|
+
time: {
|
|
103
|
+
created: "2020-01-01T00:00:00.000Z",
|
|
104
|
+
modified: "2020-01-01T00:00:00.000Z",
|
|
105
|
+
[pkg.version]: "2020-01-01T00:00:00.000Z"
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
start() {
|
|
110
|
+
this.server = (0, http.createServer)((req, res) => {
|
|
111
|
+
const url = req.url || "";
|
|
112
|
+
this.requests.push(url);
|
|
113
|
+
debug$1("mock uplink %s %s (mode: %s)", req.method, url, this.mode);
|
|
114
|
+
const respond = () => {
|
|
115
|
+
if (this.mode === "error-500") {
|
|
116
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
117
|
+
res.end(JSON.stringify({ error: "mock uplink internal error" }));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (url.startsWith("/-/v1/search")) {
|
|
121
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
122
|
+
res.end(this.searchResponse(new URL(url, `http://localhost:${this.port}`)));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const tarballSep = url.indexOf("/-/");
|
|
126
|
+
if (tarballSep > 0 && url.endsWith(".tgz")) {
|
|
127
|
+
const pkg = this.packages.get(decodeURIComponent(url.slice(1, tarballSep)));
|
|
128
|
+
if (!pkg) {
|
|
129
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
130
|
+
res.end(JSON.stringify({ error: "not found" }));
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (this.mode === "drop-mid-stream") {
|
|
134
|
+
res.writeHead(200, {
|
|
135
|
+
"Content-Type": "application/octet-stream",
|
|
136
|
+
"Content-Length": String(pkg.tarball.length)
|
|
137
|
+
});
|
|
138
|
+
res.write(pkg.tarball.subarray(0, Math.floor(pkg.tarball.length / 2)));
|
|
139
|
+
setTimeout(() => res.socket?.destroy(), 50);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
res.writeHead(200, {
|
|
143
|
+
"Content-Type": "application/octet-stream",
|
|
144
|
+
"Content-Length": String(pkg.tarball.length)
|
|
145
|
+
});
|
|
146
|
+
res.end(pkg.tarball);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const pkg = this.packages.get(decodeURIComponent(url.slice(1)));
|
|
150
|
+
if (!pkg) {
|
|
151
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
152
|
+
res.end(JSON.stringify({ error: "not found" }));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
156
|
+
res.end(this.packument(pkg));
|
|
157
|
+
};
|
|
158
|
+
const safeRespond = () => {
|
|
159
|
+
try {
|
|
160
|
+
respond();
|
|
161
|
+
} catch (err) {
|
|
162
|
+
debug$1("mock uplink response failed (socket gone?): %s", err);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
if (this.mode === "slow") {
|
|
166
|
+
const timer = setTimeout(safeRespond, 1e4);
|
|
167
|
+
res.socket?.once("close", () => clearTimeout(timer));
|
|
168
|
+
} else safeRespond();
|
|
169
|
+
});
|
|
170
|
+
return new Promise((resolve, reject) => {
|
|
171
|
+
this.server.once("error", reject);
|
|
172
|
+
this.server.listen(this.port, () => {
|
|
173
|
+
debug$1("mock uplink listening on %d", this.port);
|
|
174
|
+
resolve();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
stop() {
|
|
179
|
+
return new Promise((resolve) => {
|
|
180
|
+
if (!this.server) {
|
|
181
|
+
resolve();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
this.server.closeAllConnections?.();
|
|
185
|
+
this.server.close(() => {
|
|
186
|
+
debug$1("mock uplink stopped");
|
|
187
|
+
this.server = void 0;
|
|
188
|
+
resolve();
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
//#endregion
|
|
194
|
+
exports.MockUplink = MockUplink;
|
|
195
|
+
|
|
196
|
+
//# sourceMappingURL=mock-uplink.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-uplink.cjs","names":[],"sources":["../../../src/utils/mock-uplink.ts"],"sourcesContent":["import { randomBytes } from 'crypto';\nimport buildDebug from 'debug';\nimport { Server, createServer } from 'http';\n\nimport { computeDist } from './http-client';\n\nconst debug = buildDebug('verdaccio:e2e-cli:mock-uplink');\n\nexport type UplinkMode =\n /** Serve packuments and tarballs normally */\n | 'ok'\n /** Send tarball headers + half the body, then destroy the socket */\n | 'drop-mid-stream'\n /** Delay every response long enough to trip the registry's uplink timeout */\n | 'slow'\n /** Respond 500 to everything */\n | 'error-500';\n\ntype MockPackage = {\n name: string;\n version: string;\n tarball: Buffer;\n shasum: string;\n integrity: string;\n};\n\n/**\n * Minimal controllable npm uplink used by scenario:uplink-failure.\n *\n * The registry under test is configured (see --print-config) to proxy the\n * `e2e-uplink-*` package pattern to this server, so the scenario can flip the\n * uplink between healthy, dropping connections mid-tarball, slow, and down.\n */\nexport class MockUplink {\n public mode: UplinkMode = 'ok';\n public requests: string[] = [];\n private server?: Server;\n private packages = new Map<string, MockPackage>();\n private searchResults: { name: string; version: string }[] = [];\n\n constructor(public readonly port: number) {}\n\n /** Register a package served by this uplink; tarball is random (incompressible) bytes. */\n addPackage(name: string, tarballBytes = 64 * 1024): MockPackage {\n const tarball = randomBytes(tarballBytes);\n const { shasum, integrity } = computeDist(tarball);\n const pkg: MockPackage = { name, version: '1.0.0', tarball, shasum, integrity };\n this.packages.set(name, pkg);\n return pkg;\n }\n\n /** Register a result served by the /-/v1/search endpoint. */\n addSearchResult(name: string, version = '1.0.0'): void {\n this.searchResults.push({ name, version });\n }\n\n /**\n * npmjs-faithful /-/v1/search: filters by `text` substring, applies\n * `from`/`size` itself (like registry.npmjs.org does) and reports the real\n * `total` of matches. Mimicking the upstream offsetting is what exposes\n * double pagination in the registry under test.\n */\n private searchResponse(url: URL): string {\n const text = url.searchParams.get('text') ?? '';\n const from = Math.max(0, parseInt(url.searchParams.get('from') ?? '0', 10) || 0);\n const size = Math.max(0, parseInt(url.searchParams.get('size') ?? '20', 10) || 20);\n const matches = this.searchResults.filter((r) => r.name.includes(text));\n const page = matches.slice(from, from + size);\n return JSON.stringify({\n objects: page.map((r) => ({\n package: {\n name: r.name,\n version: r.version,\n description: 'mock uplink search result',\n date: '2020-01-01T00:00:00.000Z',\n maintainers: [{ username: 'mock-uplink', email: 'mock@example.org' }],\n links: { npm: `http://localhost:${this.port}/${r.name}` },\n },\n score: { final: 1, detail: { quality: 1, popularity: 1, maintenance: 1 } },\n searchScore: 1,\n flags: {},\n })),\n total: matches.length,\n time: new Date('2020-01-01T00:00:00.000Z').toISOString(),\n });\n }\n\n private packument(pkg: MockPackage): string {\n const tarballUrl = `http://localhost:${this.port}/${pkg.name}/-/${pkg.name}-${pkg.version}.tgz`;\n return JSON.stringify({\n name: pkg.name,\n 'dist-tags': { latest: pkg.version },\n versions: {\n [pkg.version]: {\n name: pkg.name,\n version: pkg.version,\n description: 'mock uplink package',\n dist: { tarball: tarballUrl, shasum: pkg.shasum, integrity: pkg.integrity },\n },\n },\n time: {\n created: '2020-01-01T00:00:00.000Z',\n modified: '2020-01-01T00:00:00.000Z',\n [pkg.version]: '2020-01-01T00:00:00.000Z',\n },\n });\n }\n\n start(): Promise<void> {\n this.server = createServer((req, res) => {\n const url = req.url || '';\n this.requests.push(url);\n debug('mock uplink %s %s (mode: %s)', req.method, url, this.mode);\n\n const respond = () => {\n if (this.mode === 'error-500') {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'mock uplink internal error' }));\n return;\n }\n\n // Search route: /-/v1/search?text=...&from=...&size=...\n if (url.startsWith('/-/v1/search')) {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(this.searchResponse(new URL(url, `http://localhost:${this.port}`)));\n return;\n }\n\n // Tarball route: /<name>/-/<file>.tgz — parsed without a regex to\n // avoid backtracking on untrusted input (CodeQL js/polynomial-redos).\n const tarballSep = url.indexOf('/-/');\n if (tarballSep > 0 && url.endsWith('.tgz')) {\n const pkg = this.packages.get(decodeURIComponent(url.slice(1, tarballSep)));\n if (!pkg) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'not found' }));\n return;\n }\n if (this.mode === 'drop-mid-stream') {\n res.writeHead(200, {\n 'Content-Type': 'application/octet-stream',\n 'Content-Length': String(pkg.tarball.length),\n });\n res.write(pkg.tarball.subarray(0, Math.floor(pkg.tarball.length / 2)));\n // Kill the connection before the body completes.\n setTimeout(() => res.socket?.destroy(), 50);\n return;\n }\n res.writeHead(200, {\n 'Content-Type': 'application/octet-stream',\n 'Content-Length': String(pkg.tarball.length),\n });\n res.end(pkg.tarball);\n return;\n }\n\n // Packument route: /<name>\n const pkg = this.packages.get(decodeURIComponent(url.slice(1)));\n if (!pkg) {\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'not found' }));\n return;\n }\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(this.packument(pkg));\n };\n\n // The registry (or a stopped server) may have torn the socket down by the\n // time a delayed response fires — never let that crash the mock.\n const safeRespond = () => {\n try {\n respond();\n } catch (err) {\n debug('mock uplink response failed (socket gone?): %s', err);\n }\n };\n\n if (this.mode === 'slow') {\n // Longer than the uplink timeout configured for the e2e registry (3s).\n const timer = setTimeout(safeRespond, 10_000);\n res.socket?.once('close', () => clearTimeout(timer));\n } else {\n safeRespond();\n }\n });\n\n return new Promise((resolve, reject) => {\n this.server!.once('error', reject);\n this.server!.listen(this.port, () => {\n debug('mock uplink listening on %d', this.port);\n resolve();\n });\n });\n }\n\n stop(): Promise<void> {\n return new Promise((resolve) => {\n if (!this.server) {\n resolve();\n return;\n }\n // closeAllConnections drops keep-alive sockets so close() completes.\n this.server.closeAllConnections?.();\n this.server.close(() => {\n debug('mock uplink stopped');\n this.server = undefined;\n resolve();\n });\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/utils/pending-checks.ts
|
|
2
|
+
/**
|
|
3
|
+
* Pending contract checks pin registry behavior that is correct per the
|
|
4
|
+
* npmjs contract but still red against some published Verdaccio versions.
|
|
5
|
+
* They are off by default; E2E_PENDING_CONTRACT_CHECKS enables them:
|
|
6
|
+
*
|
|
7
|
+
* E2E_PENDING_CONTRACT_CHECKS=true → all scenarios
|
|
8
|
+
* E2E_PENDING_CONTRACT_CHECKS=tarballs → one scenario
|
|
9
|
+
* E2E_PENDING_CONTRACT_CHECKS=tarballs,search → a list
|
|
10
|
+
*
|
|
11
|
+
* Flip the default once the registry-side fixes are published everywhere.
|
|
12
|
+
*/
|
|
13
|
+
function pendingContractChecksEnabled(scenario) {
|
|
14
|
+
const value = (process.env.E2E_PENDING_CONTRACT_CHECKS || "").toLowerCase();
|
|
15
|
+
if (value === "true" || value === "1" || value === "all") return true;
|
|
16
|
+
return value.split(",").map((entry) => entry.trim()).includes(scenario);
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
exports.pendingContractChecksEnabled = pendingContractChecksEnabled;
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=pending-checks.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-checks.cjs","names":[],"sources":["../../../src/utils/pending-checks.ts"],"sourcesContent":["/**\n * Pending contract checks pin registry behavior that is correct per the\n * npmjs contract but still red against some published Verdaccio versions.\n * They are off by default; E2E_PENDING_CONTRACT_CHECKS enables them:\n *\n * E2E_PENDING_CONTRACT_CHECKS=true → all scenarios\n * E2E_PENDING_CONTRACT_CHECKS=tarballs → one scenario\n * E2E_PENDING_CONTRACT_CHECKS=tarballs,search → a list\n *\n * Flip the default once the registry-side fixes are published everywhere.\n */\nexport function pendingContractChecksEnabled(scenario: string): boolean {\n const value = (process.env.E2E_PENDING_CONTRACT_CHECKS || '').toLowerCase();\n if (value === 'true' || value === '1' || value === 'all') {\n return true;\n }\n return value\n .split(',')\n .map((entry) => entry.trim())\n .includes(scenario);\n}\n"],"mappings":";;;;;;;;;;;;AAWA,SAAgB,6BAA6B,UAA2B;CACtE,MAAM,SAAS,QAAQ,IAAI,+BAA+B,GAAA,CAAI,YAAY;CAC1E,IAAI,UAAU,UAAU,UAAU,OAAO,UAAU,OACjD,OAAO;CAET,OAAO,MACJ,MAAM,GAAG,CAAC,CACV,KAAK,UAAU,MAAM,KAAK,CAAC,CAAC,CAC5B,SAAS,QAAQ;AACtB"}
|