@verdaccio/e2e-cli 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/bin/e2e-cli.js +6 -26
  2. package/build/adapters/adapter.d.ts +1 -0
  3. package/build/adapters/bun.d.ts +2 -0
  4. package/build/adapters/deno.d.ts +2 -0
  5. package/build/adapters/index.d.ts +5 -0
  6. package/build/adapters/npm.d.ts +2 -0
  7. package/build/adapters/pnpm.d.ts +2 -0
  8. package/build/adapters/yarn-modern.d.ts +2 -0
  9. package/build/cjs/_virtual/_rolldown/runtime.cjs +23 -0
  10. package/build/cjs/adapters/bun.cjs +64 -0
  11. package/build/cjs/adapters/bun.cjs.map +1 -0
  12. package/build/cjs/adapters/deno.cjs +61 -0
  13. package/build/cjs/adapters/deno.cjs.map +1 -0
  14. package/build/cjs/adapters/index.cjs +10 -0
  15. package/build/cjs/adapters/npm.cjs +85 -0
  16. package/build/cjs/adapters/npm.cjs.map +1 -0
  17. package/build/cjs/adapters/pnpm.cjs +114 -0
  18. package/build/cjs/adapters/pnpm.cjs.map +1 -0
  19. package/build/cjs/adapters/yarn-modern.cjs +207 -0
  20. package/build/cjs/adapters/yarn-modern.cjs.map +1 -0
  21. package/build/cjs/index.cjs +186 -11
  22. package/build/cjs/index.cjs.map +1 -0
  23. package/build/cjs/reporter.cjs +113 -0
  24. package/build/cjs/reporter.cjs.map +1 -0
  25. package/build/cjs/runner.cjs +141 -0
  26. package/build/cjs/runner.cjs.map +1 -0
  27. package/build/cjs/scenarios/index.cjs +25 -0
  28. package/build/cjs/scenarios/index.cjs.map +1 -0
  29. package/build/cjs/scenarios/install-multiple-deps.cjs +119 -0
  30. package/build/cjs/scenarios/install-multiple-deps.cjs.map +1 -0
  31. package/build/cjs/scenarios/metadata.cjs +121 -0
  32. package/build/cjs/scenarios/metadata.cjs.map +1 -0
  33. package/build/cjs/scenarios/minimum-release-age.cjs +111 -0
  34. package/build/cjs/scenarios/minimum-release-age.cjs.map +1 -0
  35. package/build/cjs/scenarios/search.cjs +136 -0
  36. package/build/cjs/scenarios/search.cjs.map +1 -0
  37. package/build/cjs/scenarios/tarballs.cjs +147 -0
  38. package/build/cjs/scenarios/tarballs.cjs.map +1 -0
  39. package/build/cjs/scenarios/uplink-failure.cjs +141 -0
  40. package/build/cjs/scenarios/uplink-failure.cjs.map +1 -0
  41. package/build/cjs/tests/audit.cjs +60 -0
  42. package/build/cjs/tests/audit.cjs.map +1 -0
  43. package/build/cjs/tests/ci.cjs +83 -0
  44. package/build/cjs/tests/ci.cjs.map +1 -0
  45. package/build/cjs/tests/deprecate.cjs +108 -0
  46. package/build/cjs/tests/deprecate.cjs.map +1 -0
  47. package/build/cjs/tests/dist-tags.cjs +43 -0
  48. package/build/cjs/tests/dist-tags.cjs.map +1 -0
  49. package/build/cjs/tests/index.cjs +49 -0
  50. package/build/cjs/tests/index.cjs.map +1 -0
  51. package/build/cjs/tests/info.cjs +31 -0
  52. package/build/cjs/tests/info.cjs.map +1 -0
  53. package/build/cjs/tests/install.cjs +30 -0
  54. package/build/cjs/tests/install.cjs.map +1 -0
  55. package/build/cjs/tests/login.cjs +126 -0
  56. package/build/cjs/tests/login.cjs.map +1 -0
  57. package/build/cjs/tests/ping.cjs +26 -0
  58. package/build/cjs/tests/ping.cjs.map +1 -0
  59. package/build/cjs/tests/publish.cjs +36 -0
  60. package/build/cjs/tests/publish.cjs.map +1 -0
  61. package/build/cjs/tests/search.cjs +23 -0
  62. package/build/cjs/tests/search.cjs.map +1 -0
  63. package/build/cjs/tests/unpublish.cjs +40 -0
  64. package/build/cjs/tests/unpublish.cjs.map +1 -0
  65. package/build/cjs/utils/e2e-config.cjs +59 -0
  66. package/build/cjs/utils/e2e-config.cjs.map +1 -0
  67. package/build/cjs/utils/http-client.cjs +111 -0
  68. package/build/cjs/utils/http-client.cjs.map +1 -0
  69. package/build/cjs/utils/info.cjs +25 -0
  70. package/build/cjs/utils/info.cjs.map +1 -0
  71. package/build/cjs/utils/mock-uplink.cjs +196 -0
  72. package/build/cjs/utils/mock-uplink.cjs.map +1 -0
  73. package/build/cjs/utils/process.cjs +96 -0
  74. package/build/cjs/utils/process.cjs.map +1 -0
  75. package/build/cjs/utils/project.cjs +55 -0
  76. package/build/cjs/utils/project.cjs.map +1 -0
  77. package/build/cjs/utils/publish.cjs +117 -0
  78. package/build/cjs/utils/publish.cjs.map +1 -0
  79. package/build/cjs/utils/registry-client.cjs +56 -0
  80. package/build/cjs/utils/registry-client.cjs.map +1 -0
  81. package/build/esm/adapters/bun.js +62 -0
  82. package/build/esm/adapters/bun.js.map +1 -0
  83. package/build/esm/adapters/deno.js +59 -0
  84. package/build/esm/adapters/deno.js.map +1 -0
  85. package/build/esm/adapters/index.js +6 -0
  86. package/build/esm/adapters/npm.js +83 -0
  87. package/build/esm/adapters/npm.js.map +1 -0
  88. package/build/esm/adapters/pnpm.js +112 -0
  89. package/build/esm/adapters/pnpm.js.map +1 -0
  90. package/build/esm/adapters/yarn-modern.js +204 -0
  91. package/build/esm/adapters/yarn-modern.js.map +1 -0
  92. package/build/esm/index.js +174 -10
  93. package/build/esm/index.js.map +1 -0
  94. package/build/esm/reporter.js +108 -0
  95. package/build/esm/reporter.js.map +1 -0
  96. package/build/esm/runner.js +138 -0
  97. package/build/esm/runner.js.map +1 -0
  98. package/build/esm/scenarios/index.js +19 -0
  99. package/build/esm/scenarios/index.js.map +1 -0
  100. package/build/esm/scenarios/install-multiple-deps.js +117 -0
  101. package/build/esm/scenarios/install-multiple-deps.js.map +1 -0
  102. package/build/esm/scenarios/metadata.js +119 -0
  103. package/build/esm/scenarios/metadata.js.map +1 -0
  104. package/build/esm/scenarios/minimum-release-age.js +108 -0
  105. package/build/esm/scenarios/minimum-release-age.js.map +1 -0
  106. package/build/esm/scenarios/search.js +134 -0
  107. package/build/esm/scenarios/search.js.map +1 -0
  108. package/build/esm/scenarios/tarballs.js +145 -0
  109. package/build/esm/scenarios/tarballs.js.map +1 -0
  110. package/build/esm/scenarios/uplink-failure.js +139 -0
  111. package/build/esm/scenarios/uplink-failure.js.map +1 -0
  112. package/build/esm/tests/audit.js +57 -0
  113. package/build/esm/tests/audit.js.map +1 -0
  114. package/build/esm/tests/ci.js +80 -0
  115. package/build/esm/tests/ci.js.map +1 -0
  116. package/build/esm/tests/deprecate.js +105 -0
  117. package/build/esm/tests/deprecate.js.map +1 -0
  118. package/build/esm/tests/dist-tags.js +41 -0
  119. package/build/esm/tests/dist-tags.js.map +1 -0
  120. package/build/esm/tests/index.js +37 -0
  121. package/build/esm/tests/index.js.map +1 -0
  122. package/build/esm/tests/info.js +29 -0
  123. package/build/esm/tests/info.js.map +1 -0
  124. package/build/esm/tests/install.js +28 -0
  125. package/build/esm/tests/install.js.map +1 -0
  126. package/build/esm/tests/login.js +122 -0
  127. package/build/esm/tests/login.js.map +1 -0
  128. package/build/esm/tests/ping.js +24 -0
  129. package/build/esm/tests/ping.js.map +1 -0
  130. package/build/esm/tests/publish.js +34 -0
  131. package/build/esm/tests/publish.js.map +1 -0
  132. package/build/esm/tests/search.js +21 -0
  133. package/build/esm/tests/search.js.map +1 -0
  134. package/build/esm/tests/unpublish.js +38 -0
  135. package/build/esm/tests/unpublish.js.map +1 -0
  136. package/build/esm/utils/e2e-config.js +58 -0
  137. package/build/esm/utils/e2e-config.js.map +1 -0
  138. package/build/esm/utils/http-client.js +105 -0
  139. package/build/esm/utils/http-client.js.map +1 -0
  140. package/build/esm/utils/info.js +24 -0
  141. package/build/esm/utils/info.js.map +1 -0
  142. package/build/esm/utils/mock-uplink.js +194 -0
  143. package/build/esm/utils/mock-uplink.js.map +1 -0
  144. package/build/esm/utils/process.js +92 -0
  145. package/build/esm/utils/process.js.map +1 -0
  146. package/build/esm/utils/project.js +50 -0
  147. package/build/esm/utils/project.js.map +1 -0
  148. package/build/esm/utils/publish.js +113 -0
  149. package/build/esm/utils/publish.js.map +1 -0
  150. package/build/esm/utils/registry-client.js +53 -0
  151. package/build/esm/utils/registry-client.js.map +1 -0
  152. package/build/index.d.ts +6 -15
  153. package/build/reporter.d.ts +7 -0
  154. package/build/runner.d.ts +13 -0
  155. package/build/scenarios/index.d.ts +9 -0
  156. package/build/scenarios/install-multiple-deps.d.ts +2 -0
  157. package/build/scenarios/metadata.d.ts +2 -0
  158. package/build/scenarios/minimum-release-age.d.ts +11 -0
  159. package/build/scenarios/search.d.ts +2 -0
  160. package/build/scenarios/tarballs.d.ts +2 -0
  161. package/build/scenarios/uplink-failure.d.ts +2 -0
  162. package/build/tests/audit.d.ts +2 -0
  163. package/build/tests/ci.d.ts +2 -0
  164. package/build/tests/deprecate.d.ts +2 -0
  165. package/build/tests/dist-tags.d.ts +2 -0
  166. package/build/tests/index.d.ts +15 -0
  167. package/build/tests/info.d.ts +2 -0
  168. package/build/tests/install.d.ts +2 -0
  169. package/build/tests/login.d.ts +2 -0
  170. package/build/tests/ping.d.ts +2 -0
  171. package/build/tests/publish.d.ts +2 -0
  172. package/build/tests/search.d.ts +2 -0
  173. package/build/tests/unpublish.d.ts +2 -0
  174. package/build/types.d.ts +91 -0
  175. package/build/utils/e2e-config.d.ts +21 -0
  176. package/build/utils/http-client.d.ts +49 -0
  177. package/build/utils/index.d.ts +4 -0
  178. package/build/utils/info.d.ts +14 -0
  179. package/build/utils/mock-uplink.d.ts +47 -0
  180. package/build/utils/process.d.ts +11 -0
  181. package/build/utils/project.d.ts +6 -0
  182. package/build/utils/publish.d.ts +17 -0
  183. package/build/utils/registry-client.d.ts +15 -0
  184. package/package.json +16 -14
  185. package/Cargo.lock +0 -1591
  186. package/Cargo.toml +0 -19
  187. package/build/bin/verdaccio-e2e +0 -0
  188. package/scripts/build-js-package.mjs +0 -25
  189. package/src/e2e_tests/audit.rs +0 -51
  190. package/src/e2e_tests/ci.rs +0 -32
  191. package/src/e2e_tests/deprecate.rs +0 -64
  192. package/src/e2e_tests/dist_tags.rs +0 -56
  193. package/src/e2e_tests/info.rs +0 -43
  194. package/src/e2e_tests/install.rs +0 -27
  195. package/src/e2e_tests/login.rs +0 -64
  196. package/src/e2e_tests/mod.rs +0 -104
  197. package/src/e2e_tests/ping.rs +0 -33
  198. package/src/e2e_tests/publish.rs +0 -68
  199. package/src/e2e_tests/search.rs +0 -14
  200. package/src/e2e_tests/unpublish.rs +0 -37
  201. package/src/main.rs +0 -1411
  202. package/src/scenarios/install_multiple_deps.rs +0 -99
  203. package/src/scenarios/minimum_release_age.rs +0 -45
  204. package/src/scenarios/mod.rs +0 -11
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","names":[],"sources":["../../../src/utils/project.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { mkdirSync } from 'fs';\nimport { mkdtemp, writeFile } from 'fs/promises';\nimport { tmpdir } from 'os';\nimport { join } from 'path';\n\nconst debug = buildDebug('verdaccio:e2e-cli:project');\n\nexport async function createTempFolder(prefix: string): Promise<string> {\n const sanitized = prefix.replace(/[^a-zA-Z0-9-_]/g, '-');\n debug('creating temp folder %o', sanitized);\n return mkdtemp(join(tmpdir(), `verdaccio-e2e-${sanitized}-`));\n}\n\nexport function getPackageJSON(\n packageName: string,\n version = '1.0.0',\n dependencies: Record<string, string> = {},\n devDependencies: Record<string, string> = {}\n): string {\n debug('creating package.json %o', packageName);\n const json = {\n name: packageName,\n version,\n description: 'some cool project',\n main: 'index.js',\n scripts: {\n test: 'echo exit 1',\n },\n dependencies,\n devDependencies,\n keywords: ['foo', 'bar'],\n author: 'Verdaccio E2E <verdaccio@example.org>',\n license: 'MIT',\n };\n return JSON.stringify(json);\n}\n\nexport function getREADME(packageName: string): string {\n return `\n # My README ${packageName}\n\n some text\n\n ## subtitle\n\n more text\n `;\n}\n\nexport async function prepareGenericEmptyProject(\n packageName: string,\n version: string,\n port: number,\n token: string,\n registryDomain: string,\n dependencies: Record<string, string> = {},\n devDependencies: Record<string, string> = {}\n): Promise<{ tempFolder: string }> {\n debug('preparing generic project %o', packageName);\n // `min-release-age=0` keeps the harness hermetic: npm 12 added a release-age\n // cooldown that, if set in the developer's global ~/.npmrc, would reject the\n // just-published packages these tests install (ETARGET / \"no matching version\n // ... before <date>\"). Pinning it per-project overrides any global value.\n // It's an npm-only key — pnpm (minimum-release-age) and yarn ignore it.\n // `audit=false`/`fund=false` stop npm's implicit audit/fund calls on every\n // install — the registry would proxy those upstream, and the suite must run\n // fully offline (the dedicated audit test still runs `npm audit` explicitly).\n const getNPMrc = (port: number, token: string, registry: string) =>\n `//localhost:${port}/:_authToken=${token}\\nregistry=${registry}\\nmin-release-age=0\\naudit=false\\nfund=false`;\n const tempFolder = await createTempFolder('temp-folder');\n await writeFile(\n join(tempFolder, 'package.json'),\n getPackageJSON(packageName, version, dependencies, devDependencies)\n );\n await writeFile(join(tempFolder, 'README.md'), getREADME(packageName));\n await writeFile(join(tempFolder, '.npmrc'), getNPMrc(port, token, registryDomain));\n return { tempFolder };\n}\n"],"mappings":";;;;;AAMA,IAAM,QAAQ,WAAW,2BAA2B;AAEpD,eAAsB,iBAAiB,QAAiC;CACtE,MAAM,YAAY,OAAO,QAAQ,mBAAmB,GAAG;CACvD,MAAM,2BAA2B,SAAS;CAC1C,OAAO,QAAQ,KAAK,OAAO,GAAG,iBAAiB,UAAU,EAAE,CAAC;AAC9D;AAEA,SAAgB,eACd,aACA,UAAU,SACV,eAAuC,CAAC,GACxC,kBAA0C,CAAC,GACnC;CACR,MAAM,4BAA4B,WAAW;CAe7C,OAAO,KAAK,UAAU;EAbpB,MAAM;EACN;EACA,aAAa;EACb,MAAM;EACN,SAAS,EACP,MAAM,cACR;EACA;EACA;EACA,UAAU,CAAC,OAAO,KAAK;EACvB,QAAQ;EACR,SAAS;CAEW,CAAI;AAC5B;AAEA,SAAgB,UAAU,aAA6B;CACrD,OAAO;iBACQ,YAAY;;;;;;;;AAQ7B;AAEA,eAAsB,2BACpB,aACA,SACA,MACA,OACA,gBACA,eAAuC,CAAC,GACxC,kBAA0C,CAAC,GACV;CACjC,MAAM,gCAAgC,WAAW;CASjD,MAAM,YAAY,MAAc,OAAe,aAC7C,eAAe,KAAK,eAAe,MAAM,aAAa,SAAS;CACjE,MAAM,aAAa,MAAM,iBAAiB,aAAa;CACvD,MAAM,UACJ,KAAK,YAAY,cAAc,GAC/B,eAAe,aAAa,SAAS,cAAc,eAAe,CACpE;CACA,MAAM,UAAU,KAAK,YAAY,WAAW,GAAG,UAAU,WAAW,CAAC;CACrE,MAAM,UAAU,KAAK,YAAY,QAAQ,GAAG,SAAS,MAAM,OAAO,cAAc,CAAC;CACjF,OAAO,EAAE,WAAW;AACtB"}
@@ -0,0 +1,113 @@
1
+ import { computeDist } from "./http-client.js";
2
+ import buildDebug from "debug";
3
+ import { gzipSync } from "zlib";
4
+ //#region src/utils/publish.ts
5
+ var debug = buildDebug("verdaccio:e2e-cli:publish");
6
+ /** Build a single ustar tar entry (512-byte header + padded content). */
7
+ function tarEntry(name, content) {
8
+ const header = Buffer.alloc(512);
9
+ header.write(name, 0, 100, "utf8");
10
+ header.write("0000644\0", 100, 8);
11
+ header.write("0000000\0", 108, 8);
12
+ header.write("0000000\0", 116, 8);
13
+ header.write(content.length.toString(8).padStart(11, "0") + "\0", 124, 12);
14
+ header.write("00000000000\0", 136, 12);
15
+ header.write(" ", 148, 8);
16
+ header.write("0", 156, 1);
17
+ header.write("ustar\0", 257, 6);
18
+ header.write("00", 263, 2);
19
+ let checksum = 0;
20
+ for (const byte of header) checksum += byte;
21
+ header.write(checksum.toString(8).padStart(6, "0") + "\0 ", 148, 8);
22
+ const padding = Buffer.alloc((512 - content.length % 512) % 512);
23
+ return Buffer.concat([
24
+ header,
25
+ content,
26
+ padding
27
+ ]);
28
+ }
29
+ /** Build a minimal valid npm package tarball (package/package.json inside). */
30
+ function buildPackageTarball(pkgName, version, dependencies = {}) {
31
+ const manifest = {
32
+ name: pkgName,
33
+ version,
34
+ description: "e2e local package",
35
+ main: "index.js",
36
+ dependencies,
37
+ license: "MIT"
38
+ };
39
+ const tar = Buffer.concat([
40
+ tarEntry("package/package.json", Buffer.from(JSON.stringify(manifest, null, 2))),
41
+ tarEntry("package/index.js", Buffer.from("module.exports = {};\n")),
42
+ Buffer.alloc(1024)
43
+ ]);
44
+ return gzipSync(tar, { level: 9 });
45
+ }
46
+ /**
47
+ * Publish straight through the registry HTTP API (same PUT an npm client
48
+ * sends). Used for adapters that cannot publish themselves (eg. deno) so
49
+ * every fixture still comes from the registry under test — never from an
50
+ * external registry.
51
+ */
52
+ async function publishViaHttp(ctx, pkgName, version = "1.0.0", dependencies = {}) {
53
+ const tarball = buildPackageTarball(pkgName, version, dependencies);
54
+ const { shasum, integrity } = computeDist(tarball);
55
+ const filename = `${pkgName.replace(/^@[^/]+\//, "")}-${version}.tgz`;
56
+ const encodedName = pkgName.startsWith("@") ? pkgName.replaceAll("/", "%2f") : pkgName;
57
+ const tarballUrl = `${ctx.registryUrl}/${encodedName}/-/${filename}`;
58
+ const body = {
59
+ _id: pkgName,
60
+ name: pkgName,
61
+ description: "e2e local package",
62
+ "dist-tags": { latest: version },
63
+ versions: { [version]: {
64
+ _id: `${pkgName}@${version}`,
65
+ name: pkgName,
66
+ version,
67
+ description: "e2e local package",
68
+ main: "index.js",
69
+ dependencies,
70
+ license: "MIT",
71
+ dist: {
72
+ tarball: tarballUrl,
73
+ shasum,
74
+ integrity
75
+ }
76
+ } },
77
+ _attachments: { [filename]: {
78
+ content_type: "application/octet-stream",
79
+ data: tarball.toString("base64"),
80
+ length: tarball.length
81
+ } }
82
+ };
83
+ const response = await fetch(`${ctx.registryUrl}/${encodedName}`, {
84
+ method: "PUT",
85
+ headers: {
86
+ "Content-Type": "application/json",
87
+ Authorization: `Bearer ${ctx.token}`
88
+ },
89
+ body: JSON.stringify(body)
90
+ });
91
+ if (response.status !== 201 && response.status !== 200) {
92
+ const text = await response.text();
93
+ throw new Error(`HTTP publish of ${pkgName}@${version} failed: ${response.status} ${text}`);
94
+ }
95
+ debug("published %s@%s via HTTP", pkgName, version);
96
+ }
97
+ /**
98
+ * Publish a package to the registry under test so later steps (install, ci,
99
+ * audit, info, search…) can consume it without ever touching an external
100
+ * registry — the whole suite must be runnable offline. Uses the package
101
+ * manager when it can publish, the raw HTTP API otherwise (eg. deno).
102
+ */
103
+ async function publishLocalPackage(ctx, pkgName, version = "1.0.0", dependencies = {}) {
104
+ if (!ctx.adapter.supports.has("publish")) return publishViaHttp(ctx, pkgName, version, dependencies);
105
+ const { tempFolder } = await ctx.adapter.prepareProject(pkgName, version, ctx.registryUrl, ctx.port, ctx.token, dependencies);
106
+ if (ctx.adapter.type === "yarn-modern") await ctx.adapter.exec({ cwd: tempFolder }, "install");
107
+ await ctx.adapter.exec({ cwd: tempFolder }, "publish", ...ctx.adapter.registryArg(ctx.registryUrl));
108
+ debug("published local package %s@%s", pkgName, version);
109
+ }
110
+ //#endregion
111
+ export { buildPackageTarball, publishLocalPackage, publishViaHttp };
112
+
113
+ //# sourceMappingURL=publish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish.js","names":[],"sources":["../../../src/utils/publish.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { gzipSync } from 'zlib';\n\nimport { TestContext } from '../types';\nimport { computeDist } from './http-client';\n\nconst debug = buildDebug('verdaccio:e2e-cli:publish');\n\n/** Build a single ustar tar entry (512-byte header + padded content). */\nfunction tarEntry(name: string, content: Buffer): Buffer {\n const header = Buffer.alloc(512);\n header.write(name, 0, 100, 'utf8');\n header.write('0000644\\0', 100, 8); // mode\n header.write('0000000\\0', 108, 8); // uid\n header.write('0000000\\0', 116, 8); // gid\n header.write(content.length.toString(8).padStart(11, '0') + '\\0', 124, 12); // size\n header.write('00000000000\\0', 136, 12); // mtime (epoch, keeps output deterministic)\n header.write(' ', 148, 8); // chksum placeholder (spaces while summing)\n header.write('0', 156, 1); // typeflag: regular file\n header.write('ustar\\0', 257, 6); // magic\n header.write('00', 263, 2); // version\n let checksum = 0;\n for (const byte of header) {\n checksum += byte;\n }\n header.write(checksum.toString(8).padStart(6, '0') + '\\0 ', 148, 8);\n const padding = Buffer.alloc((512 - (content.length % 512)) % 512);\n return Buffer.concat([header, content, padding]);\n}\n\n/** Build a minimal valid npm package tarball (package/package.json inside). */\nexport function buildPackageTarball(\n pkgName: string,\n version: string,\n dependencies: Record<string, string> = {}\n): Buffer {\n const manifest = {\n name: pkgName,\n version,\n description: 'e2e local package',\n main: 'index.js',\n dependencies,\n license: 'MIT',\n };\n const tar = Buffer.concat([\n tarEntry('package/package.json', Buffer.from(JSON.stringify(manifest, null, 2))),\n tarEntry('package/index.js', Buffer.from('module.exports = {};\\n')),\n Buffer.alloc(1024), // tar end-of-archive marker\n ]);\n return gzipSync(tar, { level: 9 });\n}\n\n/**\n * Publish straight through the registry HTTP API (same PUT an npm client\n * sends). Used for adapters that cannot publish themselves (eg. deno) so\n * every fixture still comes from the registry under test — never from an\n * external registry.\n */\nexport async function publishViaHttp(\n ctx: TestContext,\n pkgName: string,\n version = '1.0.0',\n dependencies: Record<string, string> = {}\n): Promise<void> {\n const tarball = buildPackageTarball(pkgName, version, dependencies);\n const { shasum, integrity } = computeDist(tarball);\n const filename = `${pkgName.replace(/^@[^/]+\\//, '')}-${version}.tgz`;\n const encodedName = pkgName.startsWith('@') ? pkgName.replaceAll('/', '%2f') : pkgName;\n const tarballUrl = `${ctx.registryUrl}/${encodedName}/-/${filename}`;\n const body = {\n _id: pkgName,\n name: pkgName,\n description: 'e2e local package',\n 'dist-tags': { latest: version },\n versions: {\n [version]: {\n _id: `${pkgName}@${version}`,\n name: pkgName,\n version,\n description: 'e2e local package',\n main: 'index.js',\n dependencies,\n license: 'MIT',\n dist: { tarball: tarballUrl, shasum, integrity },\n },\n },\n _attachments: {\n [filename]: {\n content_type: 'application/octet-stream',\n data: tarball.toString('base64'),\n length: tarball.length,\n },\n },\n };\n const response = await fetch(`${ctx.registryUrl}/${encodedName}`, {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${ctx.token}`,\n },\n body: JSON.stringify(body),\n });\n if (response.status !== 201 && response.status !== 200) {\n const text = await response.text();\n throw new Error(`HTTP publish of ${pkgName}@${version} failed: ${response.status} ${text}`);\n }\n debug('published %s@%s via HTTP', pkgName, version);\n}\n\n/**\n * Publish a package to the registry under test so later steps (install, ci,\n * audit, info, search…) can consume it without ever touching an external\n * registry — the whole suite must be runnable offline. Uses the package\n * manager when it can publish, the raw HTTP API otherwise (eg. deno).\n */\nexport async function publishLocalPackage(\n ctx: TestContext,\n pkgName: string,\n version = '1.0.0',\n dependencies: Record<string, string> = {}\n): Promise<void> {\n if (!ctx.adapter.supports.has('publish')) {\n return publishViaHttp(ctx, pkgName, version, dependencies);\n }\n const { tempFolder } = await ctx.adapter.prepareProject(\n pkgName,\n version,\n ctx.registryUrl,\n ctx.port,\n ctx.token,\n dependencies\n );\n // yarn modern requires an install first to generate the lockfile\n if (ctx.adapter.type === 'yarn-modern') {\n await ctx.adapter.exec({ cwd: tempFolder }, 'install');\n }\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'publish',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n debug('published local package %s@%s', pkgName, version);\n}\n"],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import buildDebug from "debug";
2
+ import { ping } from "@verdaccio/registry-cli";
3
+ //#region src/utils/registry-client.ts
4
+ var debug = buildDebug("verdaccio:e2e-cli:registry-client");
5
+ /**
6
+ * Creates a user on the registry and returns the auth token.
7
+ * Uses a unique username per run to avoid 409 conflicts.
8
+ * Works against any running Verdaccio instance with htpasswd auth.
9
+ */
10
+ async function createUser(registryUrl, user = `e2e-user-${Date.now()}`, password = "e2e-test-password") {
11
+ debug("creating user %s on %s", user, registryUrl);
12
+ const url = `${registryUrl}/-/user/org.couchdb.user:${encodeURIComponent(user)}`;
13
+ const response = await fetch(url, {
14
+ method: "PUT",
15
+ headers: { "Content-Type": "application/json" },
16
+ body: JSON.stringify({
17
+ name: user,
18
+ password,
19
+ _id: `org.couchdb.user:${user}`,
20
+ type: "user",
21
+ roles: [],
22
+ date: (/* @__PURE__ */ new Date()).toISOString()
23
+ })
24
+ });
25
+ const body = await response.json();
26
+ if (response.status === 201 || response.status === 200 || response.status === 409) {
27
+ const token = body.token;
28
+ if (token) {
29
+ debug("user %s authenticated, token obtained", user);
30
+ return {
31
+ token,
32
+ user
33
+ };
34
+ }
35
+ }
36
+ throw new Error(`Failed to create/login user "${user}" on ${registryUrl}: ${response.status} ${JSON.stringify(body)}`);
37
+ }
38
+ /**
39
+ * Ping the registry to check it's alive.
40
+ * Uses @verdaccio/registry-cli ping.
41
+ */
42
+ async function pingRegistry(registryUrl) {
43
+ debug("pinging %s", registryUrl);
44
+ try {
45
+ return (await ping(registryUrl)).ok;
46
+ } catch {
47
+ return false;
48
+ }
49
+ }
50
+ //#endregion
51
+ export { createUser, pingRegistry };
52
+
53
+ //# sourceMappingURL=registry-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-client.js","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,QAAQ,WAAW,mCAAmC;;;;;;AAY5D,eAAsB,WACpB,aACA,OAAO,YAAY,KAAK,IAAI,KAC5B,WAAW,qBACY;CACvB,MAAM,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,MAAM,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,MAAM,cAAc,WAAW;CAC/B,IAAI;EAEF,QAAO,MADc,KAAK,WAAW,EAAA,CACvB;CAChB,QAAQ;EACN,OAAO;CACT;AACF"}
package/build/index.d.ts CHANGED
@@ -1,15 +1,6 @@
1
- export type ExecOutput = { stdout: string; stderr: string };
2
- export type PackageManagerAdapter = unknown;
3
- export type TestDefinition = unknown;
4
- export type TestContext = unknown;
5
- export type CliOptions = unknown;
6
- export const allTests: TestDefinition[];
7
- export const allScenarios: TestDefinition[];
8
- export function runAll(...args: unknown[]): never;
9
- export function runSuite(...args: unknown[]): never;
10
- export function createNpmAdapter(...args: unknown[]): never;
11
- export function createPnpmAdapter(...args: unknown[]): never;
12
- export function createYarnClassicAdapter(...args: unknown[]): never;
13
- export function createYarnModernAdapter(...args: unknown[]): never;
14
- export function createBunAdapter(...args: unknown[]): never;
15
- export function createDenoAdapter(...args: unknown[]): never;
1
+ export declare function main(argv?: string[]): Promise<void>;
2
+ export { allTests } from './tests';
3
+ export { allScenarios } from './scenarios';
4
+ export { createBunAdapter, createDenoAdapter, createNpmAdapter, createPnpmAdapter, createYarnModernAdapter, } from './adapters';
5
+ export { runAll, runSuite } from './runner';
6
+ export type { PackageManagerAdapter, TestDefinition, TestContext, CliOptions } from './types';
@@ -0,0 +1,7 @@
1
+ import { SuiteResult, TestResult } from './types';
2
+ export declare function reportTestStart(adapterName: string, testName: string): void;
3
+ export declare function reportTestResult(result: TestResult, hasSubTests?: boolean): void;
4
+ export declare function reportSuiteStart(adapterName: string): void;
5
+ export declare function reportSubTestResult(label: string, passed: boolean, duration?: number): void;
6
+ export declare function reportSkipped(testName: string): void;
7
+ export declare function reportSummary(results: SuiteResult[]): void;
@@ -0,0 +1,13 @@
1
+ import { PackageManagerAdapter, SuiteResult, TestDefinition } from './types';
2
+ export declare function runSuite(adapter: PackageManagerAdapter, tests: TestDefinition[], registryUrl: string, token: string, options: {
3
+ timeout: number;
4
+ testFilter?: string[];
5
+ }): Promise<SuiteResult>;
6
+ export declare function runAll(adapters: PackageManagerAdapter[], tests: TestDefinition[], registryUrl: string, token: string, options: {
7
+ timeout: number;
8
+ concurrency: number;
9
+ testFilter?: string[];
10
+ }): Promise<{
11
+ results: SuiteResult[];
12
+ exitCode: number;
13
+ }>;
@@ -0,0 +1,9 @@
1
+ import { TestDefinition } from '../types';
2
+ import { installMultipleDepsScenario } from './install-multiple-deps';
3
+ import { metadataScenario } from './metadata';
4
+ import { minimumReleaseAgeScenario } from './minimum-release-age';
5
+ import { searchScenario } from './search';
6
+ import { tarballsScenario } from './tarballs';
7
+ import { uplinkFailureScenario } from './uplink-failure';
8
+ export declare const allScenarios: TestDefinition[];
9
+ export { installMultipleDepsScenario, minimumReleaseAgeScenario, searchScenario, tarballsScenario, metadataScenario, uplinkFailureScenario, };
@@ -0,0 +1,2 @@
1
+ import { TestDefinition } from '../types';
2
+ export declare const installMultipleDepsScenario: TestDefinition;
@@ -0,0 +1,2 @@
1
+ import { TestDefinition } from '../types';
2
+ export declare const metadataScenario: TestDefinition;
@@ -0,0 +1,11 @@
1
+ import { PackageManagerAdapter, TestDefinition } from '../types';
2
+ /**
3
+ * True only for pnpm versions that actually enforce minimumReleaseAge.
4
+ *
5
+ * The cooldown is honored in pnpm 11.1.0+, but it is silently ignored in
6
+ * 11.0.0 / 11.0.1 (and the 11.0.0 release candidates) — those versions install
7
+ * brand-new packages regardless of the configured cooldown. We scope this
8
+ * scenario to pnpm 11.1.0 and newer so it doesn't false-fail on that regression.
9
+ */
10
+ export declare function isPnpm11Plus(adapter: PackageManagerAdapter): boolean;
11
+ export declare const minimumReleaseAgeScenario: TestDefinition;
@@ -0,0 +1,2 @@
1
+ import { TestDefinition } from '../types';
2
+ export declare const searchScenario: TestDefinition;
@@ -0,0 +1,2 @@
1
+ import { TestDefinition } from '../types';
2
+ export declare const tarballsScenario: TestDefinition;
@@ -0,0 +1,2 @@
1
+ import { TestDefinition } from '../types';
2
+ export declare const uplinkFailureScenario: TestDefinition;
@@ -0,0 +1,2 @@
1
+ import { TestDefinition } from '../types';
2
+ export declare const auditTest: TestDefinition;
@@ -0,0 +1,2 @@
1
+ import { TestDefinition } from '../types';
2
+ export declare const ciTest: TestDefinition;
@@ -0,0 +1,2 @@
1
+ import { TestDefinition } from '../types';
2
+ export declare const deprecateTest: TestDefinition;
@@ -0,0 +1,2 @@
1
+ import { TestDefinition } from '../types';
2
+ export declare const distTagsTest: TestDefinition;
@@ -0,0 +1,15 @@
1
+ import { allScenarios, installMultipleDepsScenario, metadataScenario, minimumReleaseAgeScenario, searchScenario, tarballsScenario, uplinkFailureScenario } from '../scenarios';
2
+ import { TestDefinition } from '../types';
3
+ import { auditTest } from './audit';
4
+ import { ciTest } from './ci';
5
+ import { deprecateTest } from './deprecate';
6
+ import { distTagsTest } from './dist-tags';
7
+ import { infoTest } from './info';
8
+ import { installTest } from './install';
9
+ import { loginTest } from './login';
10
+ import { pingTest } from './ping';
11
+ import { publishTest } from './publish';
12
+ import { searchTest } from './search';
13
+ import { unpublishTest } from './unpublish';
14
+ export declare const allTests: TestDefinition[];
15
+ export { publishTest, installTest, ciTest, auditTest, infoTest, deprecateTest, distTagsTest, loginTest, pingTest, searchTest, unpublishTest, installMultipleDepsScenario, minimumReleaseAgeScenario, tarballsScenario, metadataScenario, searchScenario, uplinkFailureScenario, allScenarios, };
@@ -0,0 +1,2 @@
1
+ import { TestDefinition } from '../types';
2
+ export declare const infoTest: TestDefinition;
@@ -0,0 +1,2 @@
1
+ import { TestDefinition } from '../types';
2
+ export declare const installTest: TestDefinition;
@@ -0,0 +1,2 @@
1
+ import { TestDefinition } from '../types';
2
+ export declare const loginTest: TestDefinition;
@@ -0,0 +1,2 @@
1
+ import { TestDefinition } from '../types';
2
+ export declare const pingTest: TestDefinition;
@@ -0,0 +1,2 @@
1
+ import { TestDefinition } from '../types';
2
+ export declare const publishTest: TestDefinition;
@@ -0,0 +1,2 @@
1
+ import { TestDefinition } from '../types';
2
+ export declare const searchTest: TestDefinition;
@@ -0,0 +1,2 @@
1
+ import { TestDefinition } from '../types';
2
+ export declare const unpublishTest: TestDefinition;
@@ -0,0 +1,91 @@
1
+ import { SpawnOptions } from 'child_process';
2
+ export type ExecOutput = {
3
+ stdout: string;
4
+ stderr: string;
5
+ };
6
+ export type ExecFn = (options: SpawnOptions, ...args: string[]) => Promise<ExecOutput>;
7
+ export type TestContext = {
8
+ registryUrl: string;
9
+ token: string;
10
+ port: number;
11
+ exec: ExecFn;
12
+ adapter: PackageManagerAdapter;
13
+ /** Unique suffix for package names to avoid conflicts across runs */
14
+ runId: string;
15
+ /** Report a sub-test step. Runs the callback and reports pass/fail. */
16
+ subTest: (label: string, fn: () => Promise<void>) => Promise<void>;
17
+ };
18
+ export type SubTestResult = {
19
+ label: string;
20
+ passed: boolean;
21
+ duration: number;
22
+ error?: string;
23
+ };
24
+ export type TestResult = {
25
+ name: string;
26
+ passed: boolean;
27
+ duration: number;
28
+ error?: string;
29
+ subTests?: SubTestResult[];
30
+ };
31
+ export type SuiteResult = {
32
+ adapter: string;
33
+ tests: TestResult[];
34
+ passed: number;
35
+ failed: number;
36
+ skipped: number;
37
+ duration: number;
38
+ };
39
+ export type TestDefinition = {
40
+ name: string;
41
+ run: (ctx: TestContext) => Promise<void>;
42
+ /** Commands required for this test (e.g. 'deprecate', 'search'). Skipped if adapter doesn't support them. */
43
+ requires?: string[];
44
+ /**
45
+ * Minimum timeout (ms) for this test. The effective timeout is the larger of
46
+ * this and the CLI-level --timeout. Use for long scenarios (large tarballs,
47
+ * uplink failure simulation) that outgrow the default per-test budget.
48
+ */
49
+ timeout?: number;
50
+ /**
51
+ * Predicate to gate a test to specific adapters/versions. Returns false to skip.
52
+ * Use for tests that only make sense on a particular package manager or version
53
+ * (e.g. pnpm-only settings). Evaluated after the `requires` check.
54
+ */
55
+ appliesTo?: (adapter: PackageManagerAdapter) => boolean;
56
+ };
57
+ export interface PackageManagerAdapter {
58
+ /** Display name, e.g. "npm@10" */
59
+ name: string;
60
+ /** Package manager type: npm, pnpm, yarn-modern, bun, deno */
61
+ type: 'npm' | 'pnpm' | 'yarn-modern' | 'bun' | 'deno';
62
+ /** Resolved path to the binary */
63
+ bin: string;
64
+ /** Commands this PM supports */
65
+ supports: Set<string>;
66
+ /** Build --registry args */
67
+ registryArg(url: string): string[];
68
+ /** Build --prefix / --cwd args */
69
+ prefixArg(folder: string): string[];
70
+ /** Execute a command with this PM */
71
+ exec(options: SpawnOptions, ...args: string[]): Promise<ExecOutput>;
72
+ /** Prepare a temp project for testing. Returns path to temp folder. */
73
+ prepareProject(packageName: string, version: string, registryUrl: string, port: number, token: string, dependencies?: Record<string, string>, devDependencies?: Record<string, string>): Promise<{
74
+ tempFolder: string;
75
+ }>;
76
+ /** Import a Verdaccio yarn plugin into a project (yarn-modern only) */
77
+ importPlugin?(cwd: string, pluginName: string): Promise<void>;
78
+ }
79
+ export type CliOptions = {
80
+ registry: string;
81
+ pm?: string[];
82
+ test?: string[];
83
+ concurrency: number;
84
+ timeout: number;
85
+ token?: string;
86
+ verbose: boolean;
87
+ /** Port of the mock uplink used by scenario:uplink-failure (also E2E_UPLINK_PORT) */
88
+ uplinkPort?: number;
89
+ /** Print the recommended registry config for the full battery and exit */
90
+ printConfig?: boolean;
91
+ };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Recommended Verdaccio config for running the full e2e-cli battery,
3
+ * printed by `verdaccio-e2e --print-config`. It is what the consuming repos'
4
+ * CI jobs and scripts/run-e2e.sh feed to `verdaccio --config`:
5
+ *
6
+ * - max_body_size raised so scenario:tarballs can publish ~30 MB packages
7
+ * (Verdaccio's default is 10mb)
8
+ * - an `e2emock` uplink + `e2e-uplink-*` package pattern wired to the mock
9
+ * uplink server that scenario:uplink-failure starts on E2E_UPLINK_PORT
10
+ * (short timeout so the "slow uplink" case completes quickly; high
11
+ * max_fails so simulated failures don't trip the circuit breaker for
12
+ * later sub-tests)
13
+ * - NO npmjs uplink: the whole suite publishes every package it consumes,
14
+ * so the registry under test never talks to the outside world and the
15
+ * battery runs fully offline (and deterministically in CI)
16
+ *
17
+ * Relative paths (storage/htpasswd) resolve next to the config file, so write
18
+ * it into a disposable directory.
19
+ */
20
+ export declare const DEFAULT_UPLINK_PORT = 4874;
21
+ export declare function buildE2EConfig(uplinkPort?: number): string;
@@ -0,0 +1,49 @@
1
+ export type PackumentResponse = {
2
+ status: number;
3
+ headers: Headers;
4
+ /** Parsed JSON body, or undefined when the response is not JSON (e.g. 304) */
5
+ body?: any;
6
+ };
7
+ export type TarballDownload = {
8
+ status: number;
9
+ headers: Headers;
10
+ /** Total bytes received */
11
+ bytes: number;
12
+ /** hex-encoded sha1 of the received bytes (matches dist.shasum) */
13
+ sha1: string;
14
+ /** `sha512-<base64>` of the received bytes (matches dist.integrity) */
15
+ integrity: string;
16
+ };
17
+ export type FetchPackumentOptions = {
18
+ /** Request the abbreviated (install) metadata format */
19
+ abbreviated?: boolean;
20
+ /** Send If-None-Match with this value */
21
+ etag?: string;
22
+ /** Bearer token */
23
+ token?: string;
24
+ };
25
+ export declare const ABBREVIATED_ACCEPT = "application/vnd.npm.install-v1+json";
26
+ /**
27
+ * Registry URL for a package name. Scoped names are encoded with the same
28
+ * `%2f` form npm clients use: `@scope%2fname`.
29
+ */
30
+ export declare function packumentUrl(registryUrl: string, pkgName: string): string;
31
+ export declare function fetchPackument(registryUrl: string, pkgName: string, options?: FetchPackumentOptions): Promise<PackumentResponse>;
32
+ export type DownloadTarballOptions = {
33
+ signal?: AbortSignal;
34
+ token?: string;
35
+ /** Called after the first chunk of the body arrives (useful to abort mid-stream) */
36
+ onFirstChunk?: () => void;
37
+ /** Called after every chunk with the total bytes received so far (useful to abort at a threshold) */
38
+ onChunk?: (bytesReceived: number) => void;
39
+ };
40
+ /**
41
+ * Download a tarball fully, computing sha1/sha512 integrity on the fly.
42
+ * Rejects with the underlying error when aborted or the connection drops.
43
+ */
44
+ export declare function downloadTarball(url: string, options?: DownloadTarballOptions): Promise<TarballDownload>;
45
+ /** Compute npm-style dist values (shasum/integrity) for a buffer. */
46
+ export declare function computeDist(buffer: Buffer): {
47
+ shasum: string;
48
+ integrity: string;
49
+ };
@@ -0,0 +1,4 @@
1
+ export { exec, setVerbose } from './process';
2
+ export { createTempFolder, getPackageJSON, getREADME, prepareGenericEmptyProject } from './project';
3
+ export { createUser, pingRegistry } from './registry-client';
4
+ export { normalizeInfo, parseInfoJson } from './info';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Normalize the parsed output of `npm/pnpm info <pkg> --json`.
3
+ *
4
+ * npm 12+ wraps the result in a single-element JSON array
5
+ * (e.g. `[{ "name": "verdaccio", ... }]`) for both full-packument and
6
+ * version-specific queries, whereas older npm versions return a bare object.
7
+ * When multiple results match a range, the array holds one entry per match;
8
+ * the last entry is the highest (latest) version, which is what the tests want.
9
+ *
10
+ * Returns the bare object as-is for backward compatibility.
11
+ */
12
+ export declare function normalizeInfo(parsed: any): any;
13
+ /** Parse `info --json` stdout and normalize to a single info object. */
14
+ export declare function parseInfoJson(stdout: string): any;
@@ -0,0 +1,47 @@
1
+ export type UplinkMode =
2
+ /** Serve packuments and tarballs normally */
3
+ 'ok'
4
+ /** Send tarball headers + half the body, then destroy the socket */
5
+ | 'drop-mid-stream'
6
+ /** Delay every response long enough to trip the registry's uplink timeout */
7
+ | 'slow'
8
+ /** Respond 500 to everything */
9
+ | 'error-500';
10
+ type MockPackage = {
11
+ name: string;
12
+ version: string;
13
+ tarball: Buffer;
14
+ shasum: string;
15
+ integrity: string;
16
+ };
17
+ /**
18
+ * Minimal controllable npm uplink used by scenario:uplink-failure.
19
+ *
20
+ * The registry under test is configured (see --print-config) to proxy the
21
+ * `e2e-uplink-*` package pattern to this server, so the scenario can flip the
22
+ * uplink between healthy, dropping connections mid-tarball, slow, and down.
23
+ */
24
+ export declare class MockUplink {
25
+ readonly port: number;
26
+ mode: UplinkMode;
27
+ requests: string[];
28
+ private server?;
29
+ private packages;
30
+ private searchResults;
31
+ constructor(port: number);
32
+ /** Register a package served by this uplink; tarball is random (incompressible) bytes. */
33
+ addPackage(name: string, tarballBytes?: number): MockPackage;
34
+ /** Register a result served by the /-/v1/search endpoint. */
35
+ addSearchResult(name: string, version?: string): void;
36
+ /**
37
+ * npmjs-faithful /-/v1/search: filters by `text` substring, applies
38
+ * `from`/`size` itself (like registry.npmjs.org does) and reports the real
39
+ * `total` of matches. Mimicking the upstream offsetting is what exposes
40
+ * double pagination in the registry under test.
41
+ */
42
+ private searchResponse;
43
+ private packument;
44
+ start(): Promise<void>;
45
+ stop(): Promise<void>;
46
+ }
47
+ export {};
@@ -0,0 +1,11 @@
1
+ import { SpawnOptions } from 'child_process';
2
+ import { ExecOutput } from '../types';
3
+ export declare function setVerbose(v: boolean): void;
4
+ /**
5
+ * Always-on scenario tracing, printed inline with the test output in the same
6
+ * style as the verbose `$ <command>` lines — so a red scenario explains what
7
+ * it actually saw (statuses, headers, bodies, timings) in any run, CI or
8
+ * local, without a reproduction. Accepts util.format tokens (%s %d %j %o).
9
+ */
10
+ export declare function trace(fmt: string, ...args: unknown[]): void;
11
+ export declare function exec(options: SpawnOptions, cmd: string, args: string[]): Promise<ExecOutput>;
@@ -0,0 +1,6 @@
1
+ export declare function createTempFolder(prefix: string): Promise<string>;
2
+ export declare function getPackageJSON(packageName: string, version?: string, dependencies?: Record<string, string>, devDependencies?: Record<string, string>): string;
3
+ export declare function getREADME(packageName: string): string;
4
+ export declare function prepareGenericEmptyProject(packageName: string, version: string, port: number, token: string, registryDomain: string, dependencies?: Record<string, string>, devDependencies?: Record<string, string>): Promise<{
5
+ tempFolder: string;
6
+ }>;
@@ -0,0 +1,17 @@
1
+ import { TestContext } from '../types';
2
+ /** Build a minimal valid npm package tarball (package/package.json inside). */
3
+ export declare function buildPackageTarball(pkgName: string, version: string, dependencies?: Record<string, string>): Buffer;
4
+ /**
5
+ * Publish straight through the registry HTTP API (same PUT an npm client
6
+ * sends). Used for adapters that cannot publish themselves (eg. deno) so
7
+ * every fixture still comes from the registry under test — never from an
8
+ * external registry.
9
+ */
10
+ export declare function publishViaHttp(ctx: TestContext, pkgName: string, version?: string, dependencies?: Record<string, string>): Promise<void>;
11
+ /**
12
+ * Publish a package to the registry under test so later steps (install, ci,
13
+ * audit, info, search…) can consume it without ever touching an external
14
+ * registry — the whole suite must be runnable offline. Uses the package
15
+ * manager when it can publish, the raw HTTP API otherwise (eg. deno).
16
+ */
17
+ export declare function publishLocalPackage(ctx: TestContext, pkgName: string, version?: string, dependencies?: Record<string, string>): Promise<void>;