(existing, nodes, { pin = true } = {})
| 198 | // - `changes` is a list of `{ key, change }` entries describing edits |
| 199 | // - `warning` is an optional message to surface to the user |
| 200 | const applyApprovalForPackage = (existing, nodes, { pin = true } = {}) => { |
| 201 | const allowScripts = { ...existing } |
| 202 | const changes = [] |
| 203 | |
| 204 | if (!Array.isArray(nodes) || nodes.length === 0) { |
| 205 | return { allowScripts, changes } |
| 206 | } |
| 207 | |
| 208 | const sample = nodes[0] |
| 209 | const name = nameKeyFor(sample) |
| 210 | |
| 211 | // Deny-wins: any existing false that targets any installed version aborts. |
| 212 | for (const node of nodes) { |
| 213 | for (const [key, value] of Object.entries(allowScripts)) { |
| 214 | if (value === false && keyTargetsNode(key, node)) { |
| 215 | /* istanbul ignore next: name fallback covers the empty-name edge case */ |
| 216 | const subject = name || 'this package' |
| 217 | return { |
| 218 | allowScripts, |
| 219 | changes, |
| 220 | warning: denyWarning(key, subject, name), |
| 221 | } |
| 222 | } |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | if (!pin) { |
| 227 | // Name-only mode: collapse any single-version pins for this package |
| 228 | // into a single name-only entry. |
| 229 | for (const key of Object.keys(allowScripts)) { |
| 230 | if ( |
| 231 | keyTargetsNode(key, sample) && |
| 232 | key !== name && |
| 233 | isSingleVersionPin(key) && |
| 234 | allowScripts[key] === true |
| 235 | ) { |
| 236 | delete allowScripts[key] |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | /* istanbul ignore else: name === null is the no-identity path tested separately */ |
| 241 | if (name && allowScripts[name] !== true) { |
| 242 | allowScripts[name] = true |
| 243 | changes.push({ key: name, change: 'added' }) |
| 244 | } |
| 245 | return { allowScripts, changes } |
| 246 | } |
| 247 | |
| 248 | // Pin mode. For each currently installed version, write a single-version |
| 249 | // pin if one is not already in place. Stale single-version pins for this |
| 250 | // package are removed. Per the RFC's pin-mismatch table, an existing |
| 251 | // name-only entry (`pkg: true`) is replaced by `pkg@x.y.z: true` once |
| 252 | // every installed version has a pin. |
| 253 | const installedKeys = new Set(nodes.map(versionedKeyFor).filter(Boolean)) |
| 254 | |
| 255 | for (const key of Object.keys(allowScripts)) { |
| 256 | if ( |
| 257 | keyTargetsNode(key, sample) && |
no test coverage detected
searching dependent graphs…