@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":"search.js","names":[],"sources":["../../../src/tests/search.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\nimport { publishLocalPackage } from '../utils/publish';\n\nasync function testSearch(ctx: TestContext): Promise<void> {\n // Publish the package we search for — searching an npmjs package would need\n // an uplink and the suite must run offline.\n const pkgName = `verdaccio-search-${ctx.runId}`;\n await publishLocalPackage(ctx, pkgName, '1.0.0');\n\n // Run from a prepared project so its .npmrc (fresh token) shields the test\n // from stale credentials in the developer's global ~/.npmrc.\n const { tempFolder } = await ctx.adapter.prepareProject(\n `search-client-${ctx.runId}`,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n\n const resp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'search',\n pkgName,\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n const parsedBody = JSON.parse(resp.stdout);\n const pkgFind = parsedBody.find((item: any) => item.name === pkgName);\n assert.ok(pkgFind, `Expected to find ${pkgName} in search results`);\n assert.strictEqual(pkgFind.name, pkgName);\n}\n\nexport const searchTest: TestDefinition = {\n name: 'search',\n requires: ['search', 'publish'],\n run: testSearch,\n};\n"],"mappings":";;;AAKA,eAAe,WAAW,KAAiC;CAGzD,MAAM,UAAU,oBAAoB,IAAI;CACxC,MAAM,oBAAoB,KAAK,SAAS,OAAO;CAI/C,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,iBAAiB,IAAI,SACrB,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,KACN;CAEA,MAAM,OAAO,MAAM,IAAI,QAAQ,KAC7B,EAAE,KAAK,WAAW,GAClB,UACA,SACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CAEA,MAAM,UADa,KAAK,MAAM,KAAK,MACnB,CAAA,CAAW,MAAM,SAAc,KAAK,SAAS,OAAO;CACpE,OAAO,GAAG,SAAS,oBAAoB,QAAQ,mBAAmB;CAClE,OAAO,YAAY,QAAQ,MAAM,OAAO;AAC1C;AAEA,IAAa,aAA6B;CACxC,MAAM;CACN,UAAU,CAAC,UAAU,SAAS;CAC9B,KAAK;AACP"}
@@ -0,0 +1,38 @@
1
+ import assert from "assert";
2
+ //#region src/tests/unpublish.ts
3
+ async function publishPkg(ctx, tempFolder) {
4
+ await ctx.adapter.exec({ cwd: tempFolder }, "publish", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
5
+ }
6
+ async function bumpVersion(ctx, tempFolder, bump) {
7
+ await ctx.adapter.exec({ cwd: tempFolder }, "version", bump, "--no--git-tag-version", "--loglevel=info", ...ctx.adapter.registryArg(ctx.registryUrl));
8
+ }
9
+ async function testUnpublish(ctx) {
10
+ const id = ctx.runId;
11
+ const pkgName = `@verdaccio/unpub1-${id}`;
12
+ const { tempFolder } = await ctx.adapter.prepareProject(pkgName, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
13
+ await publishPkg(ctx, tempFolder);
14
+ await bumpVersion(ctx, tempFolder, "minor");
15
+ await publishPkg(ctx, tempFolder);
16
+ await bumpVersion(ctx, tempFolder, "minor");
17
+ await publishPkg(ctx, tempFolder);
18
+ await bumpVersion(ctx, tempFolder, "major");
19
+ await publishPkg(ctx, tempFolder);
20
+ const resp = await ctx.adapter.exec({ cwd: tempFolder }, "unpublish", pkgName, "--force", "--loglevel=info", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
21
+ assert.ok(resp.stdout.includes(pkgName), `Expected unpublish output to contain "${pkgName}" but got "${resp.stdout}"`);
22
+ const pkgName2 = `@verdaccio/unpub2-${id}`;
23
+ const { tempFolder: tf2 } = await ctx.adapter.prepareProject(pkgName2, "1.0.0", ctx.registryUrl, ctx.port, ctx.token);
24
+ await publishPkg(ctx, tf2);
25
+ await bumpVersion(ctx, tf2, "minor");
26
+ await publishPkg(ctx, tf2);
27
+ const resp2 = await ctx.adapter.exec({ cwd: tf2 }, "unpublish", `${pkgName2}@1.0.0`, "--force", "--loglevel=info", "--json", ...ctx.adapter.registryArg(ctx.registryUrl));
28
+ assert.ok(resp2.stdout.includes(pkgName2), `Expected unpublish output to contain "${pkgName2}" but got "${resp2.stdout}"`);
29
+ }
30
+ var unpublishTest = {
31
+ name: "unpublish",
32
+ requires: ["unpublish"],
33
+ run: testUnpublish
34
+ };
35
+ //#endregion
36
+ export { unpublishTest };
37
+
38
+ //# sourceMappingURL=unpublish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unpublish.js","names":[],"sources":["../../../src/tests/unpublish.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { TestContext, TestDefinition } from '../types';\n\nasync function publishPkg(ctx: TestContext, tempFolder: string) {\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'publish',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n}\n\nasync function bumpVersion(ctx: TestContext, tempFolder: string, bump: string) {\n await ctx.adapter.exec(\n { cwd: tempFolder },\n 'version',\n bump,\n '--no--git-tag-version',\n '--loglevel=info',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n}\n\nasync function testUnpublish(ctx: TestContext): Promise<void> {\n const id = ctx.runId;\n\n // Test 1: unpublish a full package\n const pkgName = `@verdaccio/unpub1-${id}`;\n const { tempFolder } = await ctx.adapter.prepareProject(\n pkgName,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n await publishPkg(ctx, tempFolder);\n await bumpVersion(ctx, tempFolder, 'minor');\n await publishPkg(ctx, tempFolder);\n await bumpVersion(ctx, tempFolder, 'minor');\n await publishPkg(ctx, tempFolder);\n await bumpVersion(ctx, tempFolder, 'major');\n await publishPkg(ctx, tempFolder);\n\n const resp = await ctx.adapter.exec(\n { cwd: tempFolder },\n 'unpublish',\n pkgName,\n '--force',\n '--loglevel=info',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.ok(\n resp.stdout.includes(pkgName),\n `Expected unpublish output to contain \"${pkgName}\" but got \"${resp.stdout}\"`\n );\n\n // Test 2: unpublish a specific version\n const pkgName2 = `@verdaccio/unpub2-${id}`;\n const { tempFolder: tf2 } = await ctx.adapter.prepareProject(\n pkgName2,\n '1.0.0',\n ctx.registryUrl,\n ctx.port,\n ctx.token\n );\n await publishPkg(ctx, tf2);\n await bumpVersion(ctx, tf2, 'minor');\n await publishPkg(ctx, tf2);\n\n const resp2 = await ctx.adapter.exec(\n { cwd: tf2 },\n 'unpublish',\n `${pkgName2}@1.0.0`,\n '--force',\n '--loglevel=info',\n '--json',\n ...ctx.adapter.registryArg(ctx.registryUrl)\n );\n assert.ok(\n resp2.stdout.includes(pkgName2),\n `Expected unpublish output to contain \"${pkgName2}\" but got \"${resp2.stdout}\"`\n );\n}\n\nexport const unpublishTest: TestDefinition = {\n name: 'unpublish',\n requires: ['unpublish'],\n run: testUnpublish,\n};\n"],"mappings":";;AAIA,eAAe,WAAW,KAAkB,YAAoB;CAC9D,MAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,WAAW,GAClB,WACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;AACF;AAEA,eAAe,YAAY,KAAkB,YAAoB,MAAc;CAC7E,MAAM,IAAI,QAAQ,KAChB,EAAE,KAAK,WAAW,GAClB,WACA,MACA,yBACA,mBACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;AACF;AAEA,eAAe,cAAc,KAAiC;CAC5D,MAAM,KAAK,IAAI;CAGf,MAAM,UAAU,qBAAqB;CACrC,MAAM,EAAE,eAAe,MAAM,IAAI,QAAQ,eACvC,SACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,KACN;CACA,MAAM,WAAW,KAAK,UAAU;CAChC,MAAM,YAAY,KAAK,YAAY,OAAO;CAC1C,MAAM,WAAW,KAAK,UAAU;CAChC,MAAM,YAAY,KAAK,YAAY,OAAO;CAC1C,MAAM,WAAW,KAAK,UAAU;CAChC,MAAM,YAAY,KAAK,YAAY,OAAO;CAC1C,MAAM,WAAW,KAAK,UAAU;CAEhC,MAAM,OAAO,MAAM,IAAI,QAAQ,KAC7B,EAAE,KAAK,WAAW,GAClB,aACA,SACA,WACA,mBACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CACA,OAAO,GACL,KAAK,OAAO,SAAS,OAAO,GAC5B,yCAAyC,QAAQ,aAAa,KAAK,OAAO,EAC5E;CAGA,MAAM,WAAW,qBAAqB;CACtC,MAAM,EAAE,YAAY,QAAQ,MAAM,IAAI,QAAQ,eAC5C,UACA,SACA,IAAI,aACJ,IAAI,MACJ,IAAI,KACN;CACA,MAAM,WAAW,KAAK,GAAG;CACzB,MAAM,YAAY,KAAK,KAAK,OAAO;CACnC,MAAM,WAAW,KAAK,GAAG;CAEzB,MAAM,QAAQ,MAAM,IAAI,QAAQ,KAC9B,EAAE,KAAK,IAAI,GACX,aACA,GAAG,SAAS,SACZ,WACA,mBACA,UACA,GAAG,IAAI,QAAQ,YAAY,IAAI,WAAW,CAC5C;CACA,OAAO,GACL,MAAM,OAAO,SAAS,QAAQ,GAC9B,yCAAyC,SAAS,aAAa,MAAM,OAAO,EAC9E;AACF;AAEA,IAAa,gBAAgC;CAC3C,MAAM;CACN,UAAU,CAAC,WAAW;CACtB,KAAK;AACP"}
@@ -0,0 +1,58 @@
1
+ //#region src/utils/e2e-config.ts
2
+ /**
3
+ * Recommended Verdaccio config for running the full e2e-cli battery,
4
+ * printed by `verdaccio-e2e --print-config`. It is what the consuming repos'
5
+ * CI jobs and scripts/run-e2e.sh feed to `verdaccio --config`:
6
+ *
7
+ * - max_body_size raised so scenario:tarballs can publish ~30 MB packages
8
+ * (Verdaccio's default is 10mb)
9
+ * - an `e2emock` uplink + `e2e-uplink-*` package pattern wired to the mock
10
+ * uplink server that scenario:uplink-failure starts on E2E_UPLINK_PORT
11
+ * (short timeout so the "slow uplink" case completes quickly; high
12
+ * max_fails so simulated failures don't trip the circuit breaker for
13
+ * later sub-tests)
14
+ * - NO npmjs uplink: the whole suite publishes every package it consumes,
15
+ * so the registry under test never talks to the outside world and the
16
+ * battery runs fully offline (and deterministically in CI)
17
+ *
18
+ * Relative paths (storage/htpasswd) resolve next to the config file, so write
19
+ * it into a disposable directory.
20
+ */
21
+ var DEFAULT_UPLINK_PORT = 4874;
22
+ function buildE2EConfig(uplinkPort = DEFAULT_UPLINK_PORT) {
23
+ return `# Generated by @verdaccio/e2e-cli --print-config
24
+ storage: ./storage
25
+ auth:
26
+ htpasswd:
27
+ file: ./htpasswd
28
+ max_users: 1000
29
+ web:
30
+ enable: true
31
+ title: Verdaccio E2E
32
+ max_body_size: 100mb
33
+ uplinks:
34
+ e2emock:
35
+ url: http://localhost:${uplinkPort}/
36
+ timeout: 3s
37
+ retry: 0
38
+ max_fails: 100
39
+ fail_timeout: 1s
40
+ packages:
41
+ 'e2e-uplink-*':
42
+ access: $all
43
+ proxy: e2emock
44
+ '@*/*':
45
+ access: $all
46
+ publish: $authenticated
47
+ unpublish: $authenticated
48
+ '**':
49
+ access: $all
50
+ publish: $authenticated
51
+ unpublish: $authenticated
52
+ log: { type: stdout, format: pretty, level: warn }
53
+ `;
54
+ }
55
+ //#endregion
56
+ export { DEFAULT_UPLINK_PORT, buildE2EConfig };
57
+
58
+ //# sourceMappingURL=e2e-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"e2e-config.js","names":[],"sources":["../../../src/utils/e2e-config.ts"],"sourcesContent":["/**\n * Recommended Verdaccio config for running the full e2e-cli battery,\n * printed by `verdaccio-e2e --print-config`. It is what the consuming repos'\n * CI jobs and scripts/run-e2e.sh feed to `verdaccio --config`:\n *\n * - max_body_size raised so scenario:tarballs can publish ~30 MB packages\n * (Verdaccio's default is 10mb)\n * - an `e2emock` uplink + `e2e-uplink-*` package pattern wired to the mock\n * uplink server that scenario:uplink-failure starts on E2E_UPLINK_PORT\n * (short timeout so the \"slow uplink\" case completes quickly; high\n * max_fails so simulated failures don't trip the circuit breaker for\n * later sub-tests)\n * - NO npmjs uplink: the whole suite publishes every package it consumes,\n * so the registry under test never talks to the outside world and the\n * battery runs fully offline (and deterministically in CI)\n *\n * Relative paths (storage/htpasswd) resolve next to the config file, so write\n * it into a disposable directory.\n */\nexport const DEFAULT_UPLINK_PORT = 4874;\n\nexport function buildE2EConfig(uplinkPort: number = DEFAULT_UPLINK_PORT): string {\n return `# Generated by @verdaccio/e2e-cli --print-config\nstorage: ./storage\nauth:\n htpasswd:\n file: ./htpasswd\n max_users: 1000\nweb:\n enable: true\n title: Verdaccio E2E\nmax_body_size: 100mb\nuplinks:\n e2emock:\n url: http://localhost:${uplinkPort}/\n timeout: 3s\n retry: 0\n max_fails: 100\n fail_timeout: 1s\npackages:\n 'e2e-uplink-*':\n access: $all\n proxy: e2emock\n '@*/*':\n access: $all\n publish: $authenticated\n unpublish: $authenticated\n '**':\n access: $all\n publish: $authenticated\n unpublish: $authenticated\nlog: { type: stdout, format: pretty, level: warn }\n`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAmBA,IAAa,sBAAsB;AAEnC,SAAgB,eAAe,aAAqB,qBAA6B;CAC/E,OAAO;;;;;;;;;;;;4BAYmB,WAAW;;;;;;;;;;;;;;;;;;;AAmBvC"}
@@ -0,0 +1,105 @@
1
+ import buildDebug from "debug";
2
+ import { createHash } from "crypto";
3
+ import { get } from "http";
4
+ //#region src/utils/http-client.ts
5
+ var debug = buildDebug("verdaccio:e2e-cli:http-client");
6
+ var ABBREVIATED_ACCEPT = "application/vnd.npm.install-v1+json";
7
+ /**
8
+ * Registry URL for a package name. Scoped names are encoded with the same
9
+ * `%2f` form npm clients use: `@scope%2fname`.
10
+ */
11
+ function packumentUrl(registryUrl, pkgName) {
12
+ const encoded = pkgName.startsWith("@") ? pkgName.replaceAll("/", "%2f") : pkgName;
13
+ return `${registryUrl.replace(/\/$/, "")}/${encoded}`;
14
+ }
15
+ async function fetchPackument(registryUrl, pkgName, options = {}) {
16
+ const url = packumentUrl(registryUrl, pkgName);
17
+ const headers = {};
18
+ if (options.abbreviated) headers["Accept"] = ABBREVIATED_ACCEPT;
19
+ if (options.etag) headers["If-None-Match"] = options.etag;
20
+ if (options.token) headers["Authorization"] = `Bearer ${options.token}`;
21
+ debug("GET %s %o", url, headers);
22
+ if (options.etag) return new Promise((resolve, reject) => {
23
+ get(url, { headers }, (response) => {
24
+ let text = "";
25
+ response.setEncoding("utf8");
26
+ response.on("data", (chunk) => text += chunk);
27
+ response.on("end", () => {
28
+ let body;
29
+ try {
30
+ body = JSON.parse(text);
31
+ } catch {
32
+ body = void 0;
33
+ }
34
+ const responseHeaders = new Headers();
35
+ for (const [name, value] of Object.entries(response.headers)) if (value !== void 0) responseHeaders.set(name, Array.isArray(value) ? value.join(", ") : value);
36
+ resolve({
37
+ status: response.statusCode || 0,
38
+ headers: responseHeaders,
39
+ body
40
+ });
41
+ });
42
+ }).on("error", reject);
43
+ });
44
+ const response = await fetch(url, { headers });
45
+ let body;
46
+ if (response.status !== 304) {
47
+ const text = await response.text();
48
+ try {
49
+ body = JSON.parse(text);
50
+ } catch {
51
+ body = void 0;
52
+ }
53
+ }
54
+ return {
55
+ status: response.status,
56
+ headers: response.headers,
57
+ body
58
+ };
59
+ }
60
+ /**
61
+ * Download a tarball fully, computing sha1/sha512 integrity on the fly.
62
+ * Rejects with the underlying error when aborted or the connection drops.
63
+ */
64
+ async function downloadTarball(url, options = {}) {
65
+ const headers = {};
66
+ if (options.token) headers["Authorization"] = `Bearer ${options.token}`;
67
+ debug("GET (tarball) %s", url);
68
+ const response = await fetch(url, {
69
+ headers,
70
+ signal: options.signal
71
+ });
72
+ const sha1 = createHash("sha1");
73
+ const sha512 = createHash("sha512");
74
+ let bytes = 0;
75
+ let first = true;
76
+ if (response.body) for await (const chunk of response.body) {
77
+ const buf = Buffer.from(chunk);
78
+ bytes += buf.length;
79
+ sha1.update(buf);
80
+ sha512.update(buf);
81
+ if (first) {
82
+ first = false;
83
+ options.onFirstChunk?.();
84
+ }
85
+ options.onChunk?.(bytes);
86
+ }
87
+ return {
88
+ status: response.status,
89
+ headers: response.headers,
90
+ bytes,
91
+ sha1: sha1.digest("hex"),
92
+ integrity: `sha512-${sha512.digest("base64")}`
93
+ };
94
+ }
95
+ /** Compute npm-style dist values (shasum/integrity) for a buffer. */
96
+ function computeDist(buffer) {
97
+ return {
98
+ shasum: createHash("sha1").update(buffer).digest("hex"),
99
+ integrity: `sha512-${createHash("sha512").update(buffer).digest("base64")}`
100
+ };
101
+ }
102
+ //#endregion
103
+ export { ABBREVIATED_ACCEPT, computeDist, downloadTarball, fetchPackument, packumentUrl };
104
+
105
+ //# sourceMappingURL=http-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-client.js","names":[],"sources":["../../../src/utils/http-client.ts"],"sourcesContent":["import { createHash } from 'crypto';\nimport buildDebug from 'debug';\nimport { get } from 'http';\n\nconst debug = buildDebug('verdaccio:e2e-cli:http-client');\n\nexport type PackumentResponse = {\n status: number;\n headers: Headers;\n /** Parsed JSON body, or undefined when the response is not JSON (e.g. 304) */\n body?: any;\n};\n\nexport type TarballDownload = {\n status: number;\n headers: Headers;\n /** Total bytes received */\n bytes: number;\n /** hex-encoded sha1 of the received bytes (matches dist.shasum) */\n sha1: string;\n /** `sha512-<base64>` of the received bytes (matches dist.integrity) */\n integrity: string;\n};\n\nexport type FetchPackumentOptions = {\n /** Request the abbreviated (install) metadata format */\n abbreviated?: boolean;\n /** Send If-None-Match with this value */\n etag?: string;\n /** Bearer token */\n token?: string;\n};\n\nexport const ABBREVIATED_ACCEPT = 'application/vnd.npm.install-v1+json';\n\n/**\n * Registry URL for a package name. Scoped names are encoded with the same\n * `%2f` form npm clients use: `@scope%2fname`.\n */\nexport function packumentUrl(registryUrl: string, pkgName: string): string {\n const encoded = pkgName.startsWith('@') ? pkgName.replaceAll('/', '%2f') : pkgName;\n return `${registryUrl.replace(/\\/$/, '')}/${encoded}`;\n}\n\nexport async function fetchPackument(\n registryUrl: string,\n pkgName: string,\n options: FetchPackumentOptions = {}\n): Promise<PackumentResponse> {\n const url = packumentUrl(registryUrl, pkgName);\n const headers: Record<string, string> = {};\n if (options.abbreviated) {\n headers['Accept'] = ABBREVIATED_ACCEPT;\n }\n if (options.etag) {\n headers['If-None-Match'] = options.etag;\n }\n if (options.token) {\n headers['Authorization'] = `Bearer ${options.token}`;\n }\n debug('GET %s %o', url, headers);\n\n // Conditional requests must bypass fetch(): undici appends\n // `cache-control: no-cache` whenever If-None-Match is present, which\n // (correctly, per HTTP semantics) disables the server's 304 revalidation.\n if (options.etag) {\n return new Promise((resolve, reject) => {\n get(url, { headers }, (response) => {\n let text = '';\n response.setEncoding('utf8');\n response.on('data', (chunk) => (text += chunk));\n response.on('end', () => {\n let body: any;\n try {\n body = JSON.parse(text);\n } catch {\n body = undefined;\n }\n const responseHeaders = new Headers();\n for (const [name, value] of Object.entries(response.headers)) {\n if (value !== undefined) {\n responseHeaders.set(name, Array.isArray(value) ? value.join(', ') : value);\n }\n }\n resolve({ status: response.statusCode || 0, headers: responseHeaders, body });\n });\n }).on('error', reject);\n });\n }\n\n const response = await fetch(url, { headers });\n let body: any;\n if (response.status !== 304) {\n const text = await response.text();\n try {\n body = JSON.parse(text);\n } catch {\n body = undefined;\n }\n }\n return { status: response.status, headers: response.headers, body };\n}\n\nexport type DownloadTarballOptions = {\n signal?: AbortSignal;\n token?: string;\n /** Called after the first chunk of the body arrives (useful to abort mid-stream) */\n onFirstChunk?: () => void;\n /** Called after every chunk with the total bytes received so far (useful to abort at a threshold) */\n onChunk?: (bytesReceived: number) => void;\n};\n\n/**\n * Download a tarball fully, computing sha1/sha512 integrity on the fly.\n * Rejects with the underlying error when aborted or the connection drops.\n */\nexport async function downloadTarball(\n url: string,\n options: DownloadTarballOptions = {}\n): Promise<TarballDownload> {\n const headers: Record<string, string> = {};\n if (options.token) {\n headers['Authorization'] = `Bearer ${options.token}`;\n }\n debug('GET (tarball) %s', url);\n const response = await fetch(url, { headers, signal: options.signal });\n\n const sha1 = createHash('sha1');\n const sha512 = createHash('sha512');\n let bytes = 0;\n let first = true;\n\n if (response.body) {\n for await (const chunk of response.body as any) {\n const buf = Buffer.from(chunk);\n bytes += buf.length;\n sha1.update(buf);\n sha512.update(buf);\n if (first) {\n first = false;\n options.onFirstChunk?.();\n }\n options.onChunk?.(bytes);\n }\n }\n\n return {\n status: response.status,\n headers: response.headers,\n bytes,\n sha1: sha1.digest('hex'),\n integrity: `sha512-${sha512.digest('base64')}`,\n };\n}\n\n/** Compute npm-style dist values (shasum/integrity) for a buffer. */\nexport function computeDist(buffer: Buffer): { shasum: string; integrity: string } {\n return {\n shasum: createHash('sha1').update(buffer).digest('hex'),\n integrity: `sha512-${createHash('sha512').update(buffer).digest('base64')}`,\n };\n}\n"],"mappings":";;;;AAIA,IAAM,QAAQ,WAAW,+BAA+B;AA6BxD,IAAa,qBAAqB;;;;;AAMlC,SAAgB,aAAa,aAAqB,SAAyB;CACzE,MAAM,UAAU,QAAQ,WAAW,GAAG,IAAI,QAAQ,WAAW,KAAK,KAAK,IAAI;CAC3E,OAAO,GAAG,YAAY,QAAQ,OAAO,EAAE,EAAE,GAAG;AAC9C;AAEA,eAAsB,eACpB,aACA,SACA,UAAiC,CAAC,GACN;CAC5B,MAAM,MAAM,aAAa,aAAa,OAAO;CAC7C,MAAM,UAAkC,CAAC;CACzC,IAAI,QAAQ,aACV,QAAQ,YAAY;CAEtB,IAAI,QAAQ,MACV,QAAQ,mBAAmB,QAAQ;CAErC,IAAI,QAAQ,OACV,QAAQ,mBAAmB,UAAU,QAAQ;CAE/C,MAAM,aAAa,KAAK,OAAO;CAK/B,IAAI,QAAQ,MACV,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,IAAI,KAAK,EAAE,QAAQ,IAAI,aAAa;GAClC,IAAI,OAAO;GACX,SAAS,YAAY,MAAM;GAC3B,SAAS,GAAG,SAAS,UAAW,QAAQ,KAAM;GAC9C,SAAS,GAAG,aAAa;IACvB,IAAI;IACJ,IAAI;KACF,OAAO,KAAK,MAAM,IAAI;IACxB,QAAQ;KACN,OAAO,KAAA;IACT;IACA,MAAM,kBAAkB,IAAI,QAAQ;IACpC,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,SAAS,OAAO,GACzD,IAAI,UAAU,KAAA,GACZ,gBAAgB,IAAI,MAAM,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK;IAG7E,QAAQ;KAAE,QAAQ,SAAS,cAAc;KAAG,SAAS;KAAiB;IAAK,CAAC;GAC9E,CAAC;EACH,CAAC,CAAC,CAAC,GAAG,SAAS,MAAM;CACvB,CAAC;CAGH,MAAM,WAAW,MAAM,MAAM,KAAK,EAAE,QAAQ,CAAC;CAC7C,IAAI;CACJ,IAAI,SAAS,WAAW,KAAK;EAC3B,MAAM,OAAO,MAAM,SAAS,KAAK;EACjC,IAAI;GACF,OAAO,KAAK,MAAM,IAAI;EACxB,QAAQ;GACN,OAAO,KAAA;EACT;CACF;CACA,OAAO;EAAE,QAAQ,SAAS;EAAQ,SAAS,SAAS;EAAS;CAAK;AACpE;;;;;AAeA,eAAsB,gBACpB,KACA,UAAkC,CAAC,GACT;CAC1B,MAAM,UAAkC,CAAC;CACzC,IAAI,QAAQ,OACV,QAAQ,mBAAmB,UAAU,QAAQ;CAE/C,MAAM,oBAAoB,GAAG;CAC7B,MAAM,WAAW,MAAM,MAAM,KAAK;EAAE;EAAS,QAAQ,QAAQ;CAAO,CAAC;CAErE,MAAM,OAAO,WAAW,MAAM;CAC9B,MAAM,SAAS,WAAW,QAAQ;CAClC,IAAI,QAAQ;CACZ,IAAI,QAAQ;CAEZ,IAAI,SAAS,MACX,WAAW,MAAM,SAAS,SAAS,MAAa;EAC9C,MAAM,MAAM,OAAO,KAAK,KAAK;EAC7B,SAAS,IAAI;EACb,KAAK,OAAO,GAAG;EACf,OAAO,OAAO,GAAG;EACjB,IAAI,OAAO;GACT,QAAQ;GACR,QAAQ,eAAe;EACzB;EACA,QAAQ,UAAU,KAAK;CACzB;CAGF,OAAO;EACL,QAAQ,SAAS;EACjB,SAAS,SAAS;EAClB;EACA,MAAM,KAAK,OAAO,KAAK;EACvB,WAAW,UAAU,OAAO,OAAO,QAAQ;CAC7C;AACF;;AAGA,SAAgB,YAAY,QAAuD;CACjF,OAAO;EACL,QAAQ,WAAW,MAAM,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,OAAO,KAAK;EACtD,WAAW,UAAU,WAAW,QAAQ,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,OAAO,QAAQ;CAC1E;AACF"}
@@ -0,0 +1,24 @@
1
+ //#region src/utils/info.ts
2
+ /**
3
+ * Normalize the parsed output of `npm/pnpm info <pkg> --json`.
4
+ *
5
+ * npm 12+ wraps the result in a single-element JSON array
6
+ * (e.g. `[{ "name": "verdaccio", ... }]`) for both full-packument and
7
+ * version-specific queries, whereas older npm versions return a bare object.
8
+ * When multiple results match a range, the array holds one entry per match;
9
+ * the last entry is the highest (latest) version, which is what the tests want.
10
+ *
11
+ * Returns the bare object as-is for backward compatibility.
12
+ */
13
+ function normalizeInfo(parsed) {
14
+ if (Array.isArray(parsed)) return parsed[parsed.length - 1];
15
+ return parsed;
16
+ }
17
+ /** Parse `info --json` stdout and normalize to a single info object. */
18
+ function parseInfoJson(stdout) {
19
+ return normalizeInfo(JSON.parse(stdout));
20
+ }
21
+ //#endregion
22
+ export { normalizeInfo, parseInfoJson };
23
+
24
+ //# sourceMappingURL=info.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"info.js","names":[],"sources":["../../../src/utils/info.ts"],"sourcesContent":["/**\n * Normalize the parsed output of `npm/pnpm info <pkg> --json`.\n *\n * npm 12+ wraps the result in a single-element JSON array\n * (e.g. `[{ \"name\": \"verdaccio\", ... }]`) for both full-packument and\n * version-specific queries, whereas older npm versions return a bare object.\n * When multiple results match a range, the array holds one entry per match;\n * the last entry is the highest (latest) version, which is what the tests want.\n *\n * Returns the bare object as-is for backward compatibility.\n */\nexport function normalizeInfo(parsed: any): any {\n if (Array.isArray(parsed)) {\n return parsed[parsed.length - 1];\n }\n return parsed;\n}\n\n/** Parse `info --json` stdout and normalize to a single info object. */\nexport function parseInfoJson(stdout: string): any {\n return normalizeInfo(JSON.parse(stdout));\n}\n"],"mappings":";;;;;;;;;;;;AAWA,SAAgB,cAAc,QAAkB;CAC9C,IAAI,MAAM,QAAQ,MAAM,GACtB,OAAO,OAAO,OAAO,SAAS;CAEhC,OAAO;AACT;;AAGA,SAAgB,cAAc,QAAqB;CACjD,OAAO,cAAc,KAAK,MAAM,MAAM,CAAC;AACzC"}
@@ -0,0 +1,194 @@
1
+ import { computeDist } from "./http-client.js";
2
+ import buildDebug from "debug";
3
+ import { randomBytes } from "crypto";
4
+ import { createServer } from "http";
5
+ //#region src/utils/mock-uplink.ts
6
+ var debug = buildDebug("verdaccio:e2e-cli:mock-uplink");
7
+ /**
8
+ * Minimal controllable npm uplink used by scenario:uplink-failure.
9
+ *
10
+ * The registry under test is configured (see --print-config) to proxy the
11
+ * `e2e-uplink-*` package pattern to this server, so the scenario can flip the
12
+ * uplink between healthy, dropping connections mid-tarball, slow, and down.
13
+ */
14
+ var MockUplink = class {
15
+ port;
16
+ mode = "ok";
17
+ requests = [];
18
+ server;
19
+ packages = /* @__PURE__ */ new Map();
20
+ searchResults = [];
21
+ constructor(port) {
22
+ this.port = port;
23
+ }
24
+ /** Register a package served by this uplink; tarball is random (incompressible) bytes. */
25
+ addPackage(name, tarballBytes = 65536) {
26
+ const tarball = randomBytes(tarballBytes);
27
+ const { shasum, integrity } = computeDist(tarball);
28
+ const pkg = {
29
+ name,
30
+ version: "1.0.0",
31
+ tarball,
32
+ shasum,
33
+ integrity
34
+ };
35
+ this.packages.set(name, pkg);
36
+ return pkg;
37
+ }
38
+ /** Register a result served by the /-/v1/search endpoint. */
39
+ addSearchResult(name, version = "1.0.0") {
40
+ this.searchResults.push({
41
+ name,
42
+ version
43
+ });
44
+ }
45
+ /**
46
+ * npmjs-faithful /-/v1/search: filters by `text` substring, applies
47
+ * `from`/`size` itself (like registry.npmjs.org does) and reports the real
48
+ * `total` of matches. Mimicking the upstream offsetting is what exposes
49
+ * double pagination in the registry under test.
50
+ */
51
+ searchResponse(url) {
52
+ const text = url.searchParams.get("text") ?? "";
53
+ const from = Math.max(0, parseInt(url.searchParams.get("from") ?? "0", 10) || 0);
54
+ const size = Math.max(0, parseInt(url.searchParams.get("size") ?? "20", 10) || 20);
55
+ const matches = this.searchResults.filter((r) => r.name.includes(text));
56
+ const page = matches.slice(from, from + size);
57
+ return JSON.stringify({
58
+ objects: page.map((r) => ({
59
+ package: {
60
+ name: r.name,
61
+ version: r.version,
62
+ description: "mock uplink search result",
63
+ date: "2020-01-01T00:00:00.000Z",
64
+ maintainers: [{
65
+ username: "mock-uplink",
66
+ email: "mock@example.org"
67
+ }],
68
+ links: { npm: `http://localhost:${this.port}/${r.name}` }
69
+ },
70
+ score: {
71
+ final: 1,
72
+ detail: {
73
+ quality: 1,
74
+ popularity: 1,
75
+ maintenance: 1
76
+ }
77
+ },
78
+ searchScore: 1,
79
+ flags: {}
80
+ })),
81
+ total: matches.length,
82
+ time: (/* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z")).toISOString()
83
+ });
84
+ }
85
+ packument(pkg) {
86
+ const tarballUrl = `http://localhost:${this.port}/${pkg.name}/-/${pkg.name}-${pkg.version}.tgz`;
87
+ return JSON.stringify({
88
+ name: pkg.name,
89
+ "dist-tags": { latest: pkg.version },
90
+ versions: { [pkg.version]: {
91
+ name: pkg.name,
92
+ version: pkg.version,
93
+ description: "mock uplink package",
94
+ dist: {
95
+ tarball: tarballUrl,
96
+ shasum: pkg.shasum,
97
+ integrity: pkg.integrity
98
+ }
99
+ } },
100
+ time: {
101
+ created: "2020-01-01T00:00:00.000Z",
102
+ modified: "2020-01-01T00:00:00.000Z",
103
+ [pkg.version]: "2020-01-01T00:00:00.000Z"
104
+ }
105
+ });
106
+ }
107
+ start() {
108
+ this.server = createServer((req, res) => {
109
+ const url = req.url || "";
110
+ this.requests.push(url);
111
+ debug("mock uplink %s %s (mode: %s)", req.method, url, this.mode);
112
+ const respond = () => {
113
+ if (this.mode === "error-500") {
114
+ res.writeHead(500, { "Content-Type": "application/json" });
115
+ res.end(JSON.stringify({ error: "mock uplink internal error" }));
116
+ return;
117
+ }
118
+ if (url.startsWith("/-/v1/search")) {
119
+ res.writeHead(200, { "Content-Type": "application/json" });
120
+ res.end(this.searchResponse(new URL(url, `http://localhost:${this.port}`)));
121
+ return;
122
+ }
123
+ const tarballSep = url.indexOf("/-/");
124
+ if (tarballSep > 0 && url.endsWith(".tgz")) {
125
+ const pkg = this.packages.get(decodeURIComponent(url.slice(1, tarballSep)));
126
+ if (!pkg) {
127
+ res.writeHead(404, { "Content-Type": "application/json" });
128
+ res.end(JSON.stringify({ error: "not found" }));
129
+ return;
130
+ }
131
+ if (this.mode === "drop-mid-stream") {
132
+ res.writeHead(200, {
133
+ "Content-Type": "application/octet-stream",
134
+ "Content-Length": String(pkg.tarball.length)
135
+ });
136
+ res.write(pkg.tarball.subarray(0, Math.floor(pkg.tarball.length / 2)));
137
+ setTimeout(() => res.socket?.destroy(), 50);
138
+ return;
139
+ }
140
+ res.writeHead(200, {
141
+ "Content-Type": "application/octet-stream",
142
+ "Content-Length": String(pkg.tarball.length)
143
+ });
144
+ res.end(pkg.tarball);
145
+ return;
146
+ }
147
+ const pkg = this.packages.get(decodeURIComponent(url.slice(1)));
148
+ if (!pkg) {
149
+ res.writeHead(404, { "Content-Type": "application/json" });
150
+ res.end(JSON.stringify({ error: "not found" }));
151
+ return;
152
+ }
153
+ res.writeHead(200, { "Content-Type": "application/json" });
154
+ res.end(this.packument(pkg));
155
+ };
156
+ const safeRespond = () => {
157
+ try {
158
+ respond();
159
+ } catch (err) {
160
+ debug("mock uplink response failed (socket gone?): %s", err);
161
+ }
162
+ };
163
+ if (this.mode === "slow") {
164
+ const timer = setTimeout(safeRespond, 1e4);
165
+ res.socket?.once("close", () => clearTimeout(timer));
166
+ } else safeRespond();
167
+ });
168
+ return new Promise((resolve, reject) => {
169
+ this.server.once("error", reject);
170
+ this.server.listen(this.port, () => {
171
+ debug("mock uplink listening on %d", this.port);
172
+ resolve();
173
+ });
174
+ });
175
+ }
176
+ stop() {
177
+ return new Promise((resolve) => {
178
+ if (!this.server) {
179
+ resolve();
180
+ return;
181
+ }
182
+ this.server.closeAllConnections?.();
183
+ this.server.close(() => {
184
+ debug("mock uplink stopped");
185
+ this.server = void 0;
186
+ resolve();
187
+ });
188
+ });
189
+ }
190
+ };
191
+ //#endregion
192
+ export { MockUplink };
193
+
194
+ //# sourceMappingURL=mock-uplink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock-uplink.js","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,92 @@
1
+ import buildDebug from "debug";
2
+ import { spawn } from "child_process";
3
+ import { basename } from "path";
4
+ import { createInterface } from "readline";
5
+ import { format } from "util";
6
+ //#region src/utils/process.ts
7
+ var debug = buildDebug("verdaccio:e2e-cli:process");
8
+ var debugRead = buildDebug("verdaccio:e2e-cli:line");
9
+ var _verbose = false;
10
+ function setVerbose(v) {
11
+ _verbose = v;
12
+ }
13
+ var COLORS = {
14
+ reset: "\x1B[0m",
15
+ dim: "\x1B[2m",
16
+ cyan: "\x1B[36m",
17
+ green: "\x1B[32m",
18
+ red: "\x1B[31m",
19
+ yellow: "\x1B[33m"
20
+ };
21
+ function shortCmd(cmd, args) {
22
+ return `${basename(cmd)} ${args.join(" ")}`;
23
+ }
24
+ /**
25
+ * Always-on scenario tracing, printed inline with the test output in the same
26
+ * style as the verbose `$ <command>` lines — so a red scenario explains what
27
+ * it actually saw (statuses, headers, bodies, timings) in any run, CI or
28
+ * local, without a reproduction. Accepts util.format tokens (%s %d %j %o).
29
+ */
30
+ function trace(fmt, ...args) {
31
+ process.stdout.write(` ${COLORS.dim}» ${format(fmt, ...args)}${COLORS.reset}\n`);
32
+ }
33
+ async function exec(options, cmd, args) {
34
+ debug("start exec %o %o %o", options, cmd, args ? args.join(" ") : "");
35
+ let stdout = "";
36
+ let stderr = "";
37
+ if (_verbose) {
38
+ const cwd = options.cwd ? ` ${COLORS.dim}(cwd: ${basename(String(options.cwd))})${COLORS.reset}` : "";
39
+ process.stdout.write(` ${COLORS.cyan}$${COLORS.reset} ${shortCmd(cmd, args)}${cwd}\n`);
40
+ }
41
+ const spawnOptions = {
42
+ cwd: options.cwd,
43
+ stdio: options.stdio || "pipe",
44
+ env: options.env || process.env
45
+ };
46
+ if (process.platform.startsWith("win")) {
47
+ args.unshift("/c", cmd);
48
+ cmd = "cmd.exe";
49
+ spawnOptions.stdio = "pipe";
50
+ }
51
+ const start = Date.now();
52
+ const childProcess = spawn(cmd, args, spawnOptions);
53
+ if (childProcess.stdout) {
54
+ childProcess.stdout.on("data", (data) => {
55
+ debugRead("data %o", data.toString());
56
+ });
57
+ createInterface({
58
+ input: childProcess.stdout,
59
+ output: process.stdout,
60
+ terminal: false
61
+ }).on("line", function(line) {
62
+ debugRead("line %o", line);
63
+ stdout += line;
64
+ });
65
+ }
66
+ if (childProcess.stderr) childProcess.stderr.on("data", (data) => {
67
+ stderr += data.toString();
68
+ });
69
+ const err = /* @__PURE__ */ new Error(`Running "${cmd} ${args.join(" ")}" returned error code `);
70
+ return new Promise((resolve, reject) => {
71
+ childProcess.on("exit", (code) => {
72
+ const duration = Date.now() - start;
73
+ debugRead("exit %o", code);
74
+ if (_verbose) {
75
+ const status = code ? `${COLORS.red}exit ${code}${COLORS.reset}` : `${COLORS.green}ok${COLORS.reset}`;
76
+ process.stdout.write(` ${COLORS.dim} -> ${status} ${COLORS.dim}(${duration}ms)${COLORS.reset}\n`);
77
+ }
78
+ if (!code) resolve({
79
+ stdout,
80
+ stderr
81
+ });
82
+ else {
83
+ err.message += `${code}...\n\nSTDOUT:\n${stdout}\n\nSTDERR:\n${stderr}\n`;
84
+ reject(err);
85
+ }
86
+ });
87
+ });
88
+ }
89
+ //#endregion
90
+ export { exec, setVerbose, trace };
91
+
92
+ //# sourceMappingURL=process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.js","names":[],"sources":["../../../src/utils/process.ts"],"sourcesContent":["import { SpawnOptions, spawn } from 'child_process';\nimport buildDebug from 'debug';\nimport { basename } from 'path';\nimport { createInterface } from 'readline';\nimport { format } from 'util';\n\nimport { ExecOutput } from '../types';\n\nconst debug = buildDebug('verdaccio:e2e-cli:process');\nconst debugRead = buildDebug('verdaccio:e2e-cli:line');\n\nlet _verbose = false;\n\nexport function setVerbose(v: boolean) {\n _verbose = v;\n}\n\nconst COLORS = {\n reset: '\\x1b[0m',\n dim: '\\x1b[2m',\n cyan: '\\x1b[36m',\n green: '\\x1b[32m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n};\n\nfunction shortCmd(cmd: string, args: string[]): string {\n const bin = basename(cmd);\n return `${bin} ${args.join(' ')}`;\n}\n\n/**\n * Always-on scenario tracing, printed inline with the test output in the same\n * style as the verbose `$ <command>` lines — so a red scenario explains what\n * it actually saw (statuses, headers, bodies, timings) in any run, CI or\n * local, without a reproduction. Accepts util.format tokens (%s %d %j %o).\n */\nexport function trace(fmt: string, ...args: unknown[]): void {\n process.stdout.write(` ${COLORS.dim}» ${format(fmt, ...args)}${COLORS.reset}\\n`);\n}\n\nexport async function exec(\n options: SpawnOptions,\n cmd: string,\n args: string[]\n): Promise<ExecOutput> {\n debug('start exec %o %o %o', options, cmd, args ? args.join(' ') : '');\n let stdout = '';\n let stderr = '';\n\n if (_verbose) {\n const cwd = options.cwd\n ? ` ${COLORS.dim}(cwd: ${basename(String(options.cwd))})${COLORS.reset}`\n : '';\n process.stdout.write(` ${COLORS.cyan}$${COLORS.reset} ${shortCmd(cmd, args)}${cwd}\\n`);\n }\n\n const spawnOptions: SpawnOptions = {\n cwd: options.cwd,\n stdio: options.stdio || 'pipe',\n env: options.env || process.env,\n };\n\n if (process.platform.startsWith('win')) {\n args.unshift('/c', cmd);\n cmd = 'cmd.exe';\n spawnOptions.stdio = 'pipe';\n }\n\n const start = Date.now();\n const childProcess = spawn(cmd, args, spawnOptions);\n\n if (childProcess.stdout) {\n childProcess.stdout.on('data', (data) => {\n debugRead('data %o', data.toString());\n });\n const rl = createInterface({\n input: childProcess.stdout,\n output: process.stdout,\n terminal: false,\n });\n\n rl.on('line', function (line) {\n debugRead('line %o', line);\n stdout += line;\n });\n }\n\n if (childProcess.stderr) {\n childProcess.stderr.on('data', (data) => {\n stderr += data.toString();\n });\n }\n\n const err = new Error(`Running \"${cmd} ${args.join(' ')}\" returned error code `);\n return new Promise((resolve, reject) => {\n childProcess.on('exit', (code) => {\n const duration = Date.now() - start;\n debugRead('exit %o', code);\n\n if (_verbose) {\n const status = code\n ? `${COLORS.red}exit ${code}${COLORS.reset}`\n : `${COLORS.green}ok${COLORS.reset}`;\n process.stdout.write(\n ` ${COLORS.dim} -> ${status} ${COLORS.dim}(${duration}ms)${COLORS.reset}\\n`\n );\n }\n\n if (!code) {\n resolve({ stdout, stderr });\n } else {\n err.message += `${code}...\\n\\nSTDOUT:\\n${stdout}\\n\\nSTDERR:\\n${stderr}\\n`;\n reject(err);\n }\n });\n });\n}\n"],"mappings":";;;;;;AAQA,IAAM,QAAQ,WAAW,2BAA2B;AACpD,IAAM,YAAY,WAAW,wBAAwB;AAErD,IAAI,WAAW;AAEf,SAAgB,WAAW,GAAY;CACrC,WAAW;AACb;AAEA,IAAM,SAAS;CACb,OAAO;CACP,KAAK;CACL,MAAM;CACN,OAAO;CACP,KAAK;CACL,QAAQ;AACV;AAEA,SAAS,SAAS,KAAa,MAAwB;CAErD,OAAO,GADK,SAAS,GACX,EAAI,GAAG,KAAK,KAAK,GAAG;AAChC;;;;;;;AAQA,SAAgB,MAAM,KAAa,GAAG,MAAuB;CAC3D,QAAQ,OAAO,MAAM,SAAS,OAAO,IAAI,IAAI,OAAO,KAAK,GAAG,IAAI,IAAI,OAAO,MAAM,GAAG;AACtF;AAEA,eAAsB,KACpB,SACA,KACA,MACqB;CACrB,MAAM,uBAAuB,SAAS,KAAK,OAAO,KAAK,KAAK,GAAG,IAAI,EAAE;CACrE,IAAI,SAAS;CACb,IAAI,SAAS;CAEb,IAAI,UAAU;EACZ,MAAM,MAAM,QAAQ,MAChB,IAAI,OAAO,IAAI,QAAQ,SAAS,OAAO,QAAQ,GAAG,CAAC,EAAE,GAAG,OAAO,UAC/D;EACJ,QAAQ,OAAO,MAAM,SAAS,OAAO,KAAK,GAAG,OAAO,MAAM,GAAG,SAAS,KAAK,IAAI,IAAI,IAAI,GAAG;CAC5F;CAEA,MAAM,eAA6B;EACjC,KAAK,QAAQ;EACb,OAAO,QAAQ,SAAS;EACxB,KAAK,QAAQ,OAAO,QAAQ;CAC9B;CAEA,IAAI,QAAQ,SAAS,WAAW,KAAK,GAAG;EACtC,KAAK,QAAQ,MAAM,GAAG;EACtB,MAAM;EACN,aAAa,QAAQ;CACvB;CAEA,MAAM,QAAQ,KAAK,IAAI;CACvB,MAAM,eAAe,MAAM,KAAK,MAAM,YAAY;CAElD,IAAI,aAAa,QAAQ;EACvB,aAAa,OAAO,GAAG,SAAS,SAAS;GACvC,UAAU,WAAW,KAAK,SAAS,CAAC;EACtC,CAAC;EAOD,gBAN2B;GACzB,OAAO,aAAa;GACpB,QAAQ,QAAQ;GAChB,UAAU;EACZ,CAEA,CAAA,CAAG,GAAG,QAAQ,SAAU,MAAM;GAC5B,UAAU,WAAW,IAAI;GACzB,UAAU;EACZ,CAAC;CACH;CAEA,IAAI,aAAa,QACf,aAAa,OAAO,GAAG,SAAS,SAAS;EACvC,UAAU,KAAK,SAAS;CAC1B,CAAC;CAGH,MAAM,sBAAM,IAAI,MAAM,YAAY,IAAI,GAAG,KAAK,KAAK,GAAG,EAAE,uBAAuB;CAC/E,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,aAAa,GAAG,SAAS,SAAS;GAChC,MAAM,WAAW,KAAK,IAAI,IAAI;GAC9B,UAAU,WAAW,IAAI;GAEzB,IAAI,UAAU;IACZ,MAAM,SAAS,OACX,GAAG,OAAO,IAAI,OAAO,OAAO,OAAO,UACnC,GAAG,OAAO,MAAM,IAAI,OAAO;IAC/B,QAAQ,OAAO,MACb,SAAS,OAAO,IAAI,OAAO,OAAO,GAAG,OAAO,IAAI,GAAG,SAAS,KAAK,OAAO,MAAM,GAChF;GACF;GAEA,IAAI,CAAC,MACH,QAAQ;IAAE;IAAQ;GAAO,CAAC;QACrB;IACL,IAAI,WAAW,GAAG,KAAK,kBAAkB,OAAO,eAAe,OAAO;IACtE,OAAO,GAAG;GACZ;EACF,CAAC;CACH,CAAC;AACH"}
@@ -0,0 +1,50 @@
1
+ import buildDebug from "debug";
2
+ import { join } from "path";
3
+ import { mkdtemp, writeFile } from "fs/promises";
4
+ import { tmpdir } from "os";
5
+ //#region src/utils/project.ts
6
+ var debug = buildDebug("verdaccio:e2e-cli:project");
7
+ async function createTempFolder(prefix) {
8
+ const sanitized = prefix.replace(/[^a-zA-Z0-9-_]/g, "-");
9
+ debug("creating temp folder %o", sanitized);
10
+ return mkdtemp(join(tmpdir(), `verdaccio-e2e-${sanitized}-`));
11
+ }
12
+ function getPackageJSON(packageName, version = "1.0.0", dependencies = {}, devDependencies = {}) {
13
+ debug("creating package.json %o", packageName);
14
+ return JSON.stringify({
15
+ name: packageName,
16
+ version,
17
+ description: "some cool project",
18
+ main: "index.js",
19
+ scripts: { test: "echo exit 1" },
20
+ dependencies,
21
+ devDependencies,
22
+ keywords: ["foo", "bar"],
23
+ author: "Verdaccio E2E <verdaccio@example.org>",
24
+ license: "MIT"
25
+ });
26
+ }
27
+ function getREADME(packageName) {
28
+ return `
29
+ # My README ${packageName}
30
+
31
+ some text
32
+
33
+ ## subtitle
34
+
35
+ more text
36
+ `;
37
+ }
38
+ async function prepareGenericEmptyProject(packageName, version, port, token, registryDomain, dependencies = {}, devDependencies = {}) {
39
+ debug("preparing generic project %o", packageName);
40
+ const getNPMrc = (port, token, registry) => `//localhost:${port}/:_authToken=${token}\nregistry=${registry}\nmin-release-age=0\naudit=false\nfund=false`;
41
+ const tempFolder = await createTempFolder("temp-folder");
42
+ await writeFile(join(tempFolder, "package.json"), getPackageJSON(packageName, version, dependencies, devDependencies));
43
+ await writeFile(join(tempFolder, "README.md"), getREADME(packageName));
44
+ await writeFile(join(tempFolder, ".npmrc"), getNPMrc(port, token, registryDomain));
45
+ return { tempFolder };
46
+ }
47
+ //#endregion
48
+ export { createTempFolder, getPackageJSON, getREADME, prepareGenericEmptyProject };
49
+
50
+ //# sourceMappingURL=project.js.map