@pnpm/registry-access.commands 1100.2.16 → 1100.3.1

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 (2) hide show
  1. package/lib/distTag.js +83 -22
  2. package/package.json +25 -16
package/lib/distTag.js CHANGED
@@ -1,8 +1,12 @@
1
+ import readline from 'node:readline';
2
+ import { input } from '@inquirer/prompts';
1
3
  import { docsUrl } from '@pnpm/cli.utils';
2
4
  import { pickRegistryForPackage } from '@pnpm/config.pick-registry-for-package';
3
5
  import { PnpmError } from '@pnpm/error';
6
+ import { globalInfo, globalWarn } from '@pnpm/logger';
4
7
  import { createGetAuthHeaderByURI } from '@pnpm/network.auth-header';
5
8
  import { createFetchFromRegistry } from '@pnpm/network.fetch';
9
+ import { SyntheticOtpError, withOtpHandling, } from '@pnpm/network.web-auth';
6
10
  import npa from '@pnpm/npm-package-arg';
7
11
  import { setDistTag } from '@pnpm/registry-access.client';
8
12
  import { renderHelp } from 'render-help';
@@ -103,15 +107,21 @@ async function distTagAdd(opts, params) {
103
107
  const registryUrl = pickRegistryForPackage(opts.registries ?? { default: 'https://registry.npmjs.org/' }, packageName);
104
108
  const authHeader = getAuthHeaderForRegistry(opts.configByUri, registryUrl);
105
109
  const fetchFromRegistry = createFetchFromRegistry(opts);
106
- const otp = opts.cliOptions?.otp;
107
- await setDistTag({
108
- packageName,
109
- version,
110
- distTag: tag,
111
- registryUrl,
112
- authHeader,
113
- fetchFromRegistry,
114
- otp,
110
+ const cliOtp = opts.cliOptions?.otp;
111
+ const authType = cliOtp ? 'legacy' : 'web';
112
+ await withOtpHandling({
113
+ context: createOtpContext(opts),
114
+ fetchOptions: WEB_AUTH_FETCH_OPTIONS,
115
+ operation: (otp) => setDistTag({
116
+ packageName,
117
+ version,
118
+ distTag: tag,
119
+ registryUrl,
120
+ authHeader,
121
+ authType,
122
+ fetchFromRegistry,
123
+ otp: otp ?? cliOtp,
124
+ }),
115
125
  });
116
126
  return `+${tag}: ${packageName}@${version}`;
117
127
  }
@@ -127,24 +137,67 @@ async function distTagRm(opts, params) {
127
137
  const registryUrl = pickRegistryForPackage(opts.registries ?? { default: 'https://registry.npmjs.org/' }, packageName);
128
138
  const authHeader = getAuthHeaderForRegistry(opts.configByUri, registryUrl);
129
139
  const fetchFromRegistry = createFetchFromRegistry(opts);
130
- const otp = opts.cliOptions?.otp;
140
+ const cliOtp = opts.cliOptions?.otp;
131
141
  // First check the tag exists
132
142
  const distTags = await fetchDistTags(packageName, registryUrl, fetchFromRegistry, authHeader);
133
143
  if (!(tag in distTags)) {
134
144
  throw new PnpmError('DIST_TAG_NOT_FOUND', `dist-tag "${tag}" is not set on package "${packageName}"`);
135
145
  }
136
146
  const distTagUrl = getDistTagUrl(packageName, registryUrl, tag);
147
+ const authType = cliOtp ? 'legacy' : 'web';
148
+ await withOtpHandling({
149
+ context: createOtpContext(opts),
150
+ fetchOptions: WEB_AUTH_FETCH_OPTIONS,
151
+ operation: (otp) => deleteDistTag({
152
+ authHeader,
153
+ authType,
154
+ distTagUrl,
155
+ fetchFromRegistry,
156
+ otp: otp ?? cliOtp,
157
+ tag,
158
+ }),
159
+ });
160
+ return `-${tag}: ${packageName}@${distTags[tag]}`;
161
+ }
162
+ async function deleteDistTag({ authHeader, authType, distTagUrl, fetchFromRegistry, otp, tag, }) {
137
163
  const response = await fetchFromRegistry(distTagUrl, {
138
164
  authHeaderValue: authHeader,
139
165
  method: 'DELETE',
140
166
  headers: {
167
+ 'npm-auth-type': authType,
141
168
  ...(otp ? { 'npm-otp': otp } : {}),
142
169
  },
143
170
  });
144
- if (!response.ok) {
145
- await throwRegistryError(response, `remove dist-tag "${tag}" from`);
171
+ if (response.ok)
172
+ return;
173
+ const body = await response.text();
174
+ const action = `remove dist-tag "${tag}" from`;
175
+ if (response.status === 401) {
176
+ throw parseAuthError(body, action);
146
177
  }
147
- return `-${tag}: ${packageName}@${distTags[tag]}`;
178
+ if (response.status === 403) {
179
+ throw new PnpmError('FORBIDDEN', `You do not have permission to ${action} this package. ${body}`);
180
+ }
181
+ throw new PnpmError('REGISTRY_ERROR', `Failed to ${action} package: ${response.status} ${response.statusText}. ${body}`);
182
+ }
183
+ function parseAuthError(body, action) {
184
+ let parsed;
185
+ try {
186
+ parsed = JSON.parse(body);
187
+ }
188
+ catch {
189
+ parsed = undefined;
190
+ }
191
+ if (parsed != null && typeof parsed === 'object' && 'authUrl' in parsed && 'doneUrl' in parsed) {
192
+ return new SyntheticOtpError({
193
+ authUrl: typeof parsed.authUrl === 'string' ? parsed.authUrl : undefined,
194
+ doneUrl: typeof parsed.doneUrl === 'string' ? parsed.doneUrl : undefined,
195
+ });
196
+ }
197
+ if (/one-time pass/i.test(body)) {
198
+ return new SyntheticOtpError(undefined);
199
+ }
200
+ return new PnpmError('UNAUTHORIZED', `You must be logged in to ${action} packages. ${body}`);
148
201
  }
149
202
  function getAuthHeaderForRegistry(configByUri, registryUrl) {
150
203
  const getAuthHeader = createGetAuthHeaderByURI(configByUri ?? {});
@@ -168,14 +221,22 @@ async function fetchDistTags(packageName, registryUrl, fetchFromRegistry, authHe
168
221
  }
169
222
  return await response.json();
170
223
  }
171
- async function throwRegistryError(response, action) {
172
- const errorBody = await response.text();
173
- if (response.status === 401) {
174
- throw new PnpmError('UNAUTHORIZED', `You must be logged in to ${action} packages. ${errorBody}`);
175
- }
176
- if (response.status === 403) {
177
- throw new PnpmError('FORBIDDEN', `You do not have permission to ${action} this package. ${errorBody}`);
178
- }
179
- throw new PnpmError('REGISTRY_ERROR', `Failed to ${action} package: ${response.status} ${response.statusText}. ${errorBody}`);
224
+ const WEB_AUTH_FETCH_OPTIONS = {
225
+ method: 'GET',
226
+ };
227
+ // `withOtpHandling` polls `doneUrl` through `context.fetch`, so it must inherit
228
+ // the command's proxy/TLS/`configByUri` config — otherwise the write succeeds
229
+ // but the web-auth retry fails in custom-network environments.
230
+ function createOtpContext(opts) {
231
+ return {
232
+ Date,
233
+ createReadlineInterface: readline.createInterface.bind(null, { input: process.stdin }),
234
+ enquirer: { input },
235
+ fetch: createFetchFromRegistry(opts),
236
+ globalInfo,
237
+ globalWarn,
238
+ process,
239
+ setTimeout,
240
+ };
180
241
  }
181
242
  //# sourceMappingURL=distTag.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/registry-access.commands",
3
- "version": "1100.2.16",
3
+ "version": "1100.3.1",
4
4
  "description": "Commands for managing packages on the registry",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -8,7 +8,10 @@
8
8
  ],
9
9
  "license": "MIT",
10
10
  "funding": "https://opencollective.com/pnpm",
11
- "repository": "https://github.com/pnpm/pnpm/tree/main/registry-access/commands",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/pnpm/pnpm/tree/main/registry-access/commands"
14
+ },
12
15
  "homepage": "https://github.com/pnpm/pnpm/tree/main/registry-access/commands#readme",
13
16
  "bugs": {
14
17
  "url": "https://github.com/pnpm/pnpm/issues"
@@ -24,32 +27,38 @@
24
27
  "!*.map"
25
28
  ],
26
29
  "dependencies": {
30
+ "@inquirer/prompts": "^8.4.3",
27
31
  "@pnpm/npm-package-arg": "^2.0.0",
28
32
  "chalk": "^5.6.2",
29
33
  "ramda": "npm:@pnpm/ramda@0.28.1",
30
34
  "render-help": "^2.0.0",
31
35
  "semver": "^7.8.1",
32
- "@pnpm/cli.utils": "1101.0.8",
33
- "@pnpm/config.pick-registry-for-package": "1100.0.6",
36
+ "@pnpm/config.reader": "1101.6.0",
37
+ "@pnpm/network.auth-header": "1101.1.0",
38
+ "@pnpm/network.fetch": "1100.1.0",
34
39
  "@pnpm/error": "1100.0.0",
35
- "@pnpm/config.reader": "1101.4.1",
36
- "@pnpm/network.auth-header": "1101.0.0",
37
- "@pnpm/registry-access.client": "1100.0.1",
38
- "@pnpm/network.fetch": "1100.0.7",
39
- "@pnpm/types": "1101.2.0",
40
- "@pnpm/resolving.registry.types": "1100.0.5"
40
+ "@pnpm/config.pick-registry-for-package": "1100.0.7",
41
+ "@pnpm/cli.utils": "1101.0.9",
42
+ "@pnpm/resolving.registry.types": "1100.1.1",
43
+ "@pnpm/types": "1101.3.0",
44
+ "@pnpm/network.web-auth": "1101.1.0",
45
+ "@pnpm/registry-access.client": "1100.1.1"
46
+ },
47
+ "peerDependencies": {
48
+ "@pnpm/logger": "^1001.0.1"
41
49
  },
42
50
  "devDependencies": {
43
51
  "@jest/globals": "30.3.0",
44
- "@pnpm/registry-mock": "6.0.0",
45
52
  "@types/ramda": "0.31.1",
46
53
  "@types/semver": "7.7.1",
47
54
  "execa": "npm:safe-execa@0.3.0",
48
- "@pnpm/prepare": "1100.0.11",
49
- "@pnpm/registry-access.commands": "1100.2.16",
50
- "@pnpm/testing.mock-agent": "1100.0.7",
51
- "@pnpm/releasing.commands": "1100.3.1",
52
- "@pnpm/testing.command-defaults": "1100.0.1"
55
+ "@pnpm/prepare": "1100.0.13",
56
+ "@pnpm/registry-access.commands": "1100.3.1",
57
+ "@pnpm/logger": "1100.0.0",
58
+ "@pnpm/releasing.commands": "1100.4.2",
59
+ "@pnpm/testing.mock-agent": "1101.0.0",
60
+ "@pnpm/testing.command-defaults": "1100.0.3",
61
+ "@pnpm/testing.registry-mock": "1100.0.3"
53
62
  },
54
63
  "engines": {
55
64
  "node": ">=22.13"