From 6d10b92618d7bbb7a0eb2d116eb3dc5c139b297c Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Wed, 27 May 2026 09:50:29 -0700 Subject: [PATCH] fix!: default opts.access to null to preserve registry behavior Closes #9414. BREAKING CHANGE: `opts.access` now defaults to `null` instead of `'public'`. With `null`, libnpmpublish no longer sets an explicit access level in the publish payload, so new scoped packages are created as `restricted` (registry default) and republishes preserve the existing access level. Callers that want to force public access must now pass `access: 'public'` explicitly. --- workspaces/libnpmpublish/README.md | 9 +++++++-- workspaces/libnpmpublish/lib/publish.js | 2 +- workspaces/libnpmpublish/test/publish.js | 10 ++++++---- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/workspaces/libnpmpublish/README.md b/workspaces/libnpmpublish/README.md index 4daac34feaad1..69b747d92dcfc 100644 --- a/workspaces/libnpmpublish/README.md +++ b/workspaces/libnpmpublish/README.md @@ -44,8 +44,13 @@ A couple of options of note: defaults to `latest`. * `opts.access` - tells the registry whether this package should be - published as `public` or `restricted`. Only applies to scoped - packages. Defaults to `public`. + published as `'public'` or `'restricted'`. May also be `null`, which + preserves the existing access level on already-published packages and + defers to the registry's default for new packages (the registry treats + scoped packages as `restricted` and unscoped packages as `public` by + default). Only `'restricted'` and `null` are meaningful for scoped + packages; `'restricted'` is rejected for unscoped packages. Defaults to + `null`. * `opts.token` - can be passed in and will be used as the authentication token for the registry. For other ways to pass in auth details, see the diff --git a/workspaces/libnpmpublish/lib/publish.js b/workspaces/libnpmpublish/lib/publish.js index cfe85d2d29f57..414e07b78bf72 100644 --- a/workspaces/libnpmpublish/lib/publish.js +++ b/workspaces/libnpmpublish/lib/publish.js @@ -23,7 +23,7 @@ Remove the 'private' field from the package.json to publish it.`), // spec is used to pick the appropriate registry/auth combo const spec = npa.resolve(manifest.name, manifest.version) opts = { - access: 'public', + access: null, algorithms: ['sha512'], defaultTag: 'latest', ...opts, diff --git a/workspaces/libnpmpublish/test/publish.js b/workspaces/libnpmpublish/test/publish.js index 0a38f73f59f12..389c2a8fe98b3 100644 --- a/workspaces/libnpmpublish/test/publish.js +++ b/workspaces/libnpmpublish/test/publish.js @@ -57,7 +57,7 @@ t.test('basic publish - no npmVersion', async t => { }, }, }, - access: 'public', + access: null, _attachments: { 'libnpmpublish-test-1.0.0.tgz': { content_type: 'application/octet-stream', @@ -110,7 +110,7 @@ t.test('scoped publish', async t => { }, }, }, - access: 'public', + access: null, _attachments: { '@npmcli/libnpmpublish-test-1.0.0.tgz': { content_type: 'application/octet-stream', @@ -302,7 +302,7 @@ t.test('other error code', async t => { const packument = { name: 'libnpmpublish', description: 'some stuff', - access: 'public', + access: null, _id: 'libnpmpublish', 'dist-tags': { latest: '1.0.0', @@ -546,6 +546,7 @@ t.test('publish existing package with provenance in gha', async t => { const ret = await publish(manifest, tarData, { ...opts, + access: 'public', provenance: true, fulcioURL: fulcioURL, rekorURL: rekorURL, @@ -766,7 +767,7 @@ t.test('user-supplied provenance - success', async t => { }, }, }, - access: 'public', + access: null, _attachments: { '@npmcli/libnpmpublish-test-1.0.0.tgz': { content_type: 'application/octet-stream', @@ -1091,6 +1092,7 @@ t.test('publish existing package with provenance in gitlab', async t => { const ret = await publish(manifest, tarData, { ...opts, + access: 'public', provenance: true, fulcioURL: fulcioURL, rekorURL: rekorURL,