pnpm 12.14.0 → 12.16.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 (57) hide show
  1. package/CHANGELOG.md +378 -0
  2. package/README.md +4 -4
  3. package/bin/pnpm.mjs +11 -233
  4. package/dist/node_modules/.pnpm-workspace-state-v1.json +97 -271
  5. package/dist/node_modules/confbox/LICENSE +27 -0
  6. package/dist/node_modules/confbox/dist/_chunks/_format.d.mts +22 -0
  7. package/dist/node_modules/confbox/dist/_chunks/_format.mjs +1 -0
  8. package/dist/node_modules/confbox/dist/_chunks/libs/detect-indent.mjs +1 -0
  9. package/dist/node_modules/confbox/dist/_chunks/libs/ini.mjs +3 -0
  10. package/dist/node_modules/confbox/dist/_chunks/libs/js-yaml.d.mts +33 -0
  11. package/dist/node_modules/confbox/dist/_chunks/libs/js-yaml.mjs +38 -0
  12. package/dist/node_modules/confbox/dist/_chunks/libs/json5.mjs +14 -0
  13. package/dist/node_modules/confbox/dist/_chunks/libs/smol-toml.mjs +11 -0
  14. package/dist/node_modules/confbox/dist/_chunks/libs/strip-json-comments.mjs +4 -0
  15. package/dist/node_modules/confbox/dist/_chunks/rolldown-runtime.mjs +1 -0
  16. package/dist/node_modules/confbox/dist/index.d.mts +7 -0
  17. package/dist/node_modules/confbox/dist/index.mjs +1 -0
  18. package/dist/node_modules/confbox/dist/ini.d.mts +59 -0
  19. package/dist/node_modules/confbox/dist/ini.mjs +1 -0
  20. package/dist/node_modules/confbox/dist/json.d.mts +25 -0
  21. package/dist/node_modules/confbox/dist/json.mjs +1 -0
  22. package/dist/node_modules/confbox/dist/json5.d.mts +55 -0
  23. package/dist/node_modules/confbox/dist/json5.mjs +1 -0
  24. package/dist/node_modules/confbox/dist/jsonc.d.mts +29 -0
  25. package/dist/node_modules/confbox/dist/jsonc.mjs +1 -0
  26. package/dist/node_modules/confbox/dist/toml.d.mts +20 -0
  27. package/dist/node_modules/confbox/dist/toml.mjs +1 -0
  28. package/dist/node_modules/confbox/dist/yaml.d.mts +101 -0
  29. package/dist/node_modules/confbox/dist/yaml.mjs +1 -0
  30. package/dist/node_modules/confbox/package.json +68 -0
  31. package/dist/node_modules/get-pnpm/lib/corepack.js +79 -0
  32. package/dist/node_modules/get-pnpm/lib/downloadExecutable.js +10 -4
  33. package/dist/node_modules/get-pnpm/lib/extractTarballMember.js +45 -35
  34. package/dist/node_modules/get-pnpm/lib/index.js +3 -219
  35. package/dist/node_modules/get-pnpm/lib/install.js +293 -0
  36. package/dist/node_modules/get-pnpm/lib/npmConfig.js +280 -0
  37. package/dist/node_modules/get-pnpm/lib/npmSigningKeys.js +2 -6
  38. package/dist/node_modules/get-pnpm/lib/platformPackageName.js +11 -7
  39. package/dist/node_modules/get-pnpm/lib/pnpmConfig.js +297 -0
  40. package/dist/node_modules/get-pnpm/lib/projectPin.js +201 -0
  41. package/dist/node_modules/get-pnpm/lib/registry.js +265 -41
  42. package/dist/node_modules/get-pnpm/lib/resolveVersion.js +19 -2
  43. package/dist/node_modules/get-pnpm/lib/sameFileContents.js +31 -27
  44. package/dist/node_modules/get-pnpm/lib/shadowingPnpm.js +174 -0
  45. package/dist/node_modules/get-pnpm/lib/undoHint.js +12 -0
  46. package/dist/node_modules/get-pnpm/package.json +20 -14
  47. package/dist/node_modules/undici/lib/dispatcher/agent.js +6 -2
  48. package/dist/node_modules/undici/lib/dispatcher/client-h1.js +0 -2
  49. package/dist/node_modules/undici/lib/dispatcher/client-h2.js +0 -4
  50. package/dist/node_modules/undici/lib/dispatcher/client.js +3 -2
  51. package/dist/node_modules/undici/lib/dispatcher/dispatcher-base.js +9 -0
  52. package/dist/node_modules/undici/lib/web/eventsource/eventsource-stream.js +91 -10
  53. package/dist/node_modules/undici/lib/web/eventsource/eventsource.js +4 -1
  54. package/dist/node_modules/undici/lib/web/fetch/index.js +7 -9
  55. package/dist/node_modules/undici/lib/web/websocket/receiver.js +14 -0
  56. package/dist/node_modules/undici/package.json +1 -1
  57. package/package.json +16 -16
package/bin/pnpm.mjs CHANGED
@@ -15,15 +15,13 @@
15
15
  // payload it ships node-gyp in. The placeholder's installs usually do carry
16
16
  // that package, and the binary is taken from there.
17
17
  //
18
- // The download itself is `get-pnpm`, the package behind https://get.pnpm.io,
18
+ // The download itself is `get-pnpm`, the installer in ../../get-pnpm,
19
19
  // which already knows how to verify one; it travels in that same `dist/`
20
- // payload. What is left here is what it does not know: where to download from,
21
- // what credentials to use, and whose signature to trust.
22
- import { Buffer } from 'node:buffer'
20
+ // payload, and reads the registry, credentials, and signing keys from pnpm's
21
+ // and Corepack's config.
23
22
  import { spawnSync } from 'node:child_process'
24
23
  import console from 'node:console'
25
24
  import fs from 'node:fs'
26
- import net from 'node:net'
27
25
  import os from 'node:os'
28
26
  import path from 'node:path'
29
27
  import process from 'node:process'
@@ -37,7 +35,6 @@ const DOWNLOADED_BINARY = path.join(
37
35
  process.platform === 'win32' ? 'pnpm-native.exe' : 'pnpm-native'
38
36
  )
39
37
  const GET_PNPM = new URL('../dist/node_modules/get-pnpm/lib/index.js', import.meta.url)
40
- const DEFAULT_REGISTRY = 'https://registry.npmjs.org'
41
38
 
42
39
  if ('webcontainer' in process.versions) {
43
40
  fail('The pnpm package runs a native binary, which WebContainers cannot execute. Install @pnpm/wasm instead.')
@@ -70,242 +67,23 @@ async function nativeBinary () {
70
67
  return DOWNLOADED_BINARY
71
68
  }
72
69
 
73
- if (process.env.COREPACK_ENABLE_NETWORK === '0') {
74
- fail('Network access is disabled by the environment, so the pnpm binary cannot be downloaded.')
75
- }
76
-
77
70
  const { version } = readWrapperManifest()
78
- console.error(`Downloading the pnpm ${version} binary for ${process.platform}-${process.arch}...`)
79
- const { downloadPnpmExecutable } = await import(GET_PNPM).catch((err) => {
71
+ const { corepackDownloadOptions, downloadPnpmExecutable } = await import(GET_PNPM).catch((err) => {
80
72
  fail(`This copy of the pnpm package is missing the downloader it needs: ${err.message}`)
81
73
  })
82
- const signature = signaturePolicy()
74
+ let options
83
75
  try {
84
- await downloadPnpmExecutable({
85
- version,
86
- destPath: DOWNLOADED_BINARY,
87
- ...registryAccess({ readProjectNpmrc: signature.verifySignature !== false }),
88
- ...signature,
89
- })
76
+ options = corepackDownloadOptions()
90
77
  } catch (err) {
91
- fail(`Could not download the pnpm ${version} binary: ${err.message}`)
92
- }
93
- return DOWNLOADED_BINARY
94
- }
95
-
96
- /**
97
- * The registry to download from, and the headers to send it. Corepack's
98
- * `COREPACK_NPM_REGISTRY` wins. Otherwise npm's sources decide, highest first:
99
- * the environment, the project `.npmrc`, the user `.npmrc`.
100
- *
101
- * The project `.npmrc` is read only with `readProjectNpmrc`, which the caller
102
- * passes while the download is checked against npm's signature. The signature
103
- * pins the bytes, so a repository picks only where they come from. Unchecked,
104
- * it would pick the binary itself, which is kept next to this wrapper and run
105
- * in every later project (GHSA-j2hc-m6cf-6jm8). `get-pnpm` keeps the headers on
106
- * the registry's origin, so a download host it names never receives them.
107
- */
108
- function registryAccess ({ readProjectNpmrc }) {
109
- const corepackRegistry = process.env.COREPACK_NPM_REGISTRY
110
- if (corepackRegistry) {
111
- return { registry: corepackRegistry, headers: corepackHeaders() }
78
+ fail(err.message)
112
79
  }
113
- const npmrc = readUserNpmrc()
114
- if (readProjectNpmrc) {
115
- Object.assign(npmrc, readNpmrc(path.join(projectDir(), '.npmrc'), projectEntry))
116
- }
117
- const registry = npmrc['@pnpm:registry'] || settingFromEnv('registry') || npmrc.registry
118
- if (!registry) {
119
- return { registry: DEFAULT_REGISTRY, headers: corepackHeaders() }
120
- }
121
- return { registry, headers: npmrcHeaders(npmrc, registry) }
122
- }
123
-
124
- /** Credentials for Corepack's registry, read the way Corepack reads its own. */
125
- function corepackHeaders () {
126
- const { COREPACK_NPM_TOKEN, COREPACK_NPM_USERNAME, COREPACK_NPM_PASSWORD } = process.env
127
- if (COREPACK_NPM_TOKEN) {
128
- return { authorization: `Bearer ${COREPACK_NPM_TOKEN}` }
129
- }
130
- if (COREPACK_NPM_USERNAME && COREPACK_NPM_PASSWORD) {
131
- return { authorization: `Basic ${base64(`${COREPACK_NPM_USERNAME}:${COREPACK_NPM_PASSWORD}`)}` }
132
- }
133
- return undefined
134
- }
135
-
136
- function settingFromEnv (key) {
137
- const upper = key.toUpperCase()
138
- const { env } = process
139
- return env[`pnpm_config_${key}`] || env[`PNPM_CONFIG_${upper}`] ||
140
- env[`npm_config_${key}`] || env[`NPM_CONFIG_${upper}`] || undefined
141
- }
142
-
143
- /** The user `.npmrc`, with `${VAR}` placeholders expanded. */
144
- function readUserNpmrc () {
145
- return readNpmrc(
146
- settingFromEnv('userconfig') || path.join(os.homedir(), '.npmrc'),
147
- (key, value) => [expandEnv(key), expandEnv(value)]
148
- )
149
- }
150
-
151
- /**
152
- * A project `.npmrc` entry, which is not expanded. One with a `${VAR}`
153
- * placeholder in a registry setting or a credential is dropped, as pnpm drops
154
- * it: a repository must not route the user's environment to a registry.
155
- */
156
- function projectEntry (key, value, file) {
157
- if (!key.includes('${') && !value.includes('${')) {
158
- return [key, value]
159
- }
160
- if (key.startsWith('//') || key === 'registry' || key.endsWith(':registry')) {
161
- console.error(`Ignored ${key} from ${file}: a project .npmrc cannot use environment variables in registry settings or credentials.`)
162
- }
163
- return undefined
164
- }
165
-
166
- /** Where pnpm reads the project `.npmrc`: the workspace root, or else the current directory. */
167
- function projectDir () {
168
- const cwd = process.cwd()
169
- for (let dir = cwd; ; dir = path.dirname(dir)) {
170
- if (fs.existsSync(path.join(dir, 'pnpm-workspace.yaml'))) {
171
- return dir
172
- }
173
- if (path.dirname(dir) === dir) {
174
- return cwd
175
- }
176
- }
177
- }
178
-
179
- /**
180
- * The top-level settings of the `.npmrc` at `file`, each passed through
181
- * `parseEntry`, which returns the `[key, value]` to keep or `undefined`.
182
- */
183
- function readNpmrc (file, parseEntry) {
184
- const settings = Object.create(null)
185
- let text
186
- try {
187
- text = fs.readFileSync(file, 'utf8')
188
- } catch (err) {
189
- if (err.code !== 'ENOENT' && err.code !== 'EISDIR') {
190
- console.error(`Could not read ${file}, so its settings are not used: ${err.message}`)
191
- }
192
- return settings
193
- }
194
- for (const rawLine of text.split(/\r?\n/)) {
195
- const line = rawLine.trim()
196
- // Settings under a `[section]` are not top-level ones.
197
- if (line.startsWith('[')) {
198
- break
199
- }
200
- const separator = line.indexOf('=')
201
- if (separator === -1 || line.startsWith(';') || line.startsWith('#')) {
202
- continue
203
- }
204
- const entry = parseEntry(line.slice(0, separator).trim(), unquote(line.slice(separator + 1).trim()), file)
205
- if (entry != null) {
206
- settings[entry[0]] = entry[1]
207
- }
208
- }
209
- return settings
210
- }
211
-
212
- function unquote (value) {
213
- const quoted = value.length >= 2 && (value[0] === '"' || value[0] === "'") && value.at(-1) === value[0]
214
- return quoted ? value.slice(1, -1) : value
215
- }
216
-
217
- function expandEnv (value) {
218
- return value.replace(/\$\{([^${}]+)\}/g, (_, name) => process.env[name] ?? '')
219
- }
220
-
221
- /**
222
- * The credentials the `.npmrc` holds for `registry`, looked up as pnpm looks
223
- * them up for an `@pnpm` package: the `@pnpm` scope's own credentials first,
224
- * then the registry-wide ones. Each is the longest `//host[:port]/path/`
225
- * prefix of the registry URL that has any, then the same without the port.
226
- * They are sent only over HTTPS or to the local machine.
227
- */
228
- function npmrcHeaders (npmrc, registry) {
229
- const url = new URL(registry.endsWith('/') ? registry : `${registry}/`)
230
- if (url.protocol !== 'https:' && !(url.protocol === 'http:' && isLoopback(url.hostname))) {
231
- return undefined
232
- }
233
- const prefixes = []
234
- for (const host of new Set([url.host, url.hostname])) {
235
- for (let pathname = url.pathname; ; pathname = pathname.slice(0, pathname.lastIndexOf('/', pathname.length - 2) + 1)) {
236
- prefixes.push(`//${host}${pathname}`)
237
- if (pathname === '/') {
238
- break
239
- }
240
- }
241
- }
242
- for (const scope of [':@pnpm', '']) {
243
- for (const prefix of prefixes) {
244
- const authorization = npmrcAuthorization(npmrc, `${prefix}${scope}`)
245
- if (authorization != null) {
246
- return { authorization }
247
- }
248
- }
249
- }
250
- return undefined
251
- }
252
-
253
- function isLoopback (hostname) {
254
- return hostname === 'localhost' || hostname === '[::1]' ||
255
- (net.isIPv4(hostname) && hostname.startsWith('127.'))
256
- }
257
-
258
- function npmrcAuthorization (npmrc, prefix) {
259
- const token = npmrc[`${prefix}:_authToken`]
260
- if (token) {
261
- return `Bearer ${token}`
262
- }
263
- const auth = npmrc[`${prefix}:_auth`]
264
- if (auth) {
265
- return `Basic ${auth}`
266
- }
267
- const username = npmrc[`${prefix}:username`]
268
- const password = npmrc[`${prefix}:_password`]
269
- if (username && password) {
270
- return `Basic ${base64(`${username}:${Buffer.from(password, 'base64').toString('utf8')}`)}`
271
- }
272
- return undefined
273
- }
274
-
275
- function base64 (text) {
276
- return Buffer.from(text, 'utf8').toString('base64')
277
- }
278
-
279
- /**
280
- * Whose signature over the download to trust, following `COREPACK_INTEGRITY_KEYS`
281
- * exactly as Corepack does: npm's own keys when it is unset, the keys it names
282
- * when it holds a key set, and no signature check at all when it is `0` or
283
- * empty — which is the state a registry that re-publishes packages, and
284
- * therefore carries no npm signatures, already has to be in for Corepack to
285
- * have installed this wrapper from it.
286
- */
287
- function signaturePolicy () {
288
- const configured = process.env.COREPACK_INTEGRITY_KEYS
289
- if (configured == null) {
290
- return {}
291
- }
292
- if (configured === '' || configured === '0') {
293
- return { verifySignature: false }
294
- }
295
- let keys
80
+ console.error(`Downloading the pnpm ${version} binary for ${process.platform}-${process.arch}...`)
296
81
  try {
297
- keys = JSON.parse(configured).npm
82
+ await downloadPnpmExecutable({ version, destPath: DOWNLOADED_BINARY, ...options })
298
83
  } catch (err) {
299
- fail(`COREPACK_INTEGRITY_KEYS is not readable as JSON: ${err.message}`)
300
- }
301
- // An absent or malformed `npm` entry would otherwise be passed on as no keys
302
- // at all, which falls back to npm's own — the opposite of what setting the
303
- // variable asked for. An empty set is left alone: it names no key to trust,
304
- // and Corepack reads it the same way, so every download is refused.
305
- if (!Array.isArray(keys)) {
306
- fail('COREPACK_INTEGRITY_KEYS holds no "npm" key set to verify the pnpm binary against.')
84
+ fail(`Could not download the pnpm ${version} binary: ${err.message}`)
307
85
  }
308
- return { keys }
86
+ return DOWNLOADED_BINARY
309
87
  }
310
88
 
311
89
  function fail (message) {