pnpm-settings-migrator 0.16.0 → 0.18.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.
package/README.md CHANGED
@@ -202,7 +202,8 @@ From 11.28.3, non-null `allowBuilds` values must be maps of booleans or strings.
202
202
  From 11.28.4, non-null `allowUnusedPatches` must be boolean, and non-null
203
203
  `ignoredOptionalDependencies` / `requiredScripts` must be string arrays.
204
204
  From 11.28.5, `httpProxy` and `httpsProxy` must be strings, including when empty;
205
- null is incompatible. Invalid values remain in their sources with diagnostics.
205
+ null is incompatible in 11.28.5. From 11.28.6, null is accepted again and leaves
206
+ the proxy unset. Invalid values remain in their sources with diagnostics.
206
207
 
207
208
  For pnpm 12.9.0 and later stable v12 releases, URL-keyed `registries` entries
208
209
  can set `networkConcurrency` to a positive safe integer, or null to leave it
@@ -232,11 +233,54 @@ For example, targeting 12.11.0 migrates
232
233
  to equivalent workspace YAML. Targeting 12.10.1 retains these settings with
233
234
  diagnostics.
234
235
 
236
+ For pnpm 12.12.0 and later stable v12 releases, `addMissingPeerTypes` accepts
237
+ boolean or null values and `remoteCache` accepts the project organization:
238
+ `{ org: 'acme' }`. Empty and null remote-cache sections are preserved. Server,
239
+ team, signing, and publication fields belong in trusted global configuration
240
+ or environment variables. Objects containing those fields, unknown fields,
241
+ or invalid values remain intact in their source with diagnostics; existing
242
+ incompatible workspace values block migration before writes.
243
+
244
+ `remoteCache` and legacy `sideEffectsCache.remote` are kept independently so
245
+ pnpm applies its field-level precedence. The migrator does not insert defaults
246
+ or rewrite `allowBuilds`: pnpm 12.12 itself now honors explicit build denials
247
+ when `dangerouslyAllowAllBuilds` is true.
248
+
249
+ From 12.12.1, the registry prefix `tool` is reserved. Both `namedRegistries`
250
+ and URL-keyed `registries` using that exact prefix remain in their sources;
251
+ existing workspace declarations block migration. This restriction is
252
+ case-sensitive (`ToOl` remains valid), unlike the existing `pkg` restriction.
253
+
254
+ For pnpm 12.13.0 and later stable v12 releases, `nodePackageMap` accepts
255
+ `mode: off | write-only | load` and `type: standard | loose`. The section and
256
+ its fields may be null to leave them unset. New and legacy
257
+ `nodeExperimentalPackageMap` / `nodePackageMapType` values are preserved so pnpm
258
+ applies the new section's field-level precedence. `storeIndexFormat` accepts
259
+ `sqlite`, `files`, or null; `files` supports stores shared over network filesystems.
260
+ Enum values may use environment placeholders, which are preserved without expansion.
261
+ Older or unconfirmed targets retain these settings in their source.
262
+
263
+ For pnpm 12.14.0 and later stable v12 releases, `sideEffectsCache.exclude`
264
+ accepts package patterns such as `['java', '@native/*', 'example@1']`, an empty
265
+ array, or null. Excluded builds are neither restored nor saved in local or remote
266
+ caches. Cache `read` and `write` values may also use preserved environment
267
+ placeholders. On older targets, the complete cache section stays in its source.
268
+ Invalid nested values and unknown fields also preserve the complete source
269
+ section; existing incompatible workspace values block writes.
270
+
235
271
  pnpm v12 reads `filter` and `filterProd` from CLI arguments or `updateConfig`
236
272
  hooks, not workspace YAML. These settings remain in their source with
237
273
  diagnostics for v12 targets; existing YAML containing them blocks migration.
238
274
  The 12.11.2 hook fix does not add workspace support.
239
275
 
276
+ Other legacy fields ignored by the v12 workspace reader also stay in their
277
+ sources with diagnostics. These include `production`, `dev`, `recursive`,
278
+ `lockfileOnly`, `force`, `depth`, `saveDev`, `fetchingConcurrency`,
279
+ `requiredScripts`, `extraBinPaths`, `extraEnv`, and `workspacePackagePatterns`.
280
+ Existing YAML containing ignored fields blocks migration before writes.
281
+ For example, `production=true` cannot be moved into v12 YAML; use
282
+ `pnpm install --prod` to select production dependencies.
283
+
240
284
  </details>
241
285
 
242
286
  <details>
@@ -273,6 +317,9 @@ Notes:
273
317
  such as `globalDir`, `stateDir`, `configDir`, and `scope` stay in `.npmrc`.
274
318
  - Registry declarations containing embedded credentials or dynamic `${...}`
275
319
  URLs stay in their source with a warning.
320
+ - Invalid registry locations, including relative paths and object property names
321
+ such as `__proto__`, remain in their sources with diagnostics. Registry
322
+ conversion never uses inherited object properties as declarations.
276
323
  - Dynamic `${...}` values in `httpProxy`, `httpsProxy`, `noProxy`, `proxy`, and
277
324
  `noproxy` stay in their source for every target. Project YAML does not expand
278
325
  these placeholders; configure them globally in trusted configuration or use
@@ -297,10 +344,16 @@ Notes:
297
344
  `package.json#pnpm` child keys remain in `package.json`.
298
345
  INI sections in `.npmrc` are retained conservatively; their child names are
299
346
  never treated as root keys during cleanup.
347
+ Bare boolean flags such as `save-exact` and quoted keys such as
348
+ `"save-exact"=true` are cleaned when migrated. Retained sections alone do not
349
+ cause repeated cleanup reports or rewrites.
300
350
  - Runtime migration writes `devEngines.runtime` before removing a workspace
301
351
  runtime declaration. A failed write preserves source settings for retry.
302
352
  If an existing runtime declaration conflicts with a workspace runtime, migration
303
353
  stops before writing any files. An identical Node.js declaration is reused.
354
+ - An `executionEnv` object containing fields besides `nodeVersion` remains
355
+ intact in its source with a warning. Existing workspace declarations with
356
+ such fields block migration before writes, so unmigrated values cannot be lost.
304
357
  - YAML aliases remain when their anchors are unchanged and precede them.
305
358
  If replacement, removal, or sorting would invalidate an alias, its original
306
359
  value is written explicitly so unrelated settings keep their values.
@@ -311,17 +364,18 @@ Notes:
311
364
  - The migrator does not update the `packageManager` version, CI environment variables,
312
365
  shell setup, or pnpm commands in scripts. When `packageManager` still pins pnpm 10,
313
366
  pass `--compatibility v11` explicitly and update the pin separately.
314
- - Compatibility checks cover pnpm 11.28.5 and 12.11.2, including 11.28.0,
315
- 11.28.1, 11.28.2, 11.28.3, 11.28.4, 12.7.0, 12.8.0, 12.8.1, 12.8.2,
316
- 12.9.0, 12.9.1, 12.10.0, 12.10.1, 12.11.0, and 12.11.1.
367
+ - Compatibility checks cover pnpm 11.28.6 and 12.14.0, including 11.28.0,
368
+ 11.28.1, 11.28.2, 11.28.3, 11.28.4, 11.28.5, 12.7.0, 12.8.0, 12.8.1,
369
+ 12.8.2, 12.9.0, 12.9.1, 12.10.0, 12.10.1, 12.11.0, 12.11.1, 12.11.2,
370
+ 12.12.0, 12.12.1, and 12.13.0.
317
371
  Earlier regressions remain at 11.25.0,
318
372
  11.26.0, 11.27.1, 12.2.1, 12.3.4, 12.4.0, 12.4.2, 12.5.0, 12.5.1, and 12.6.0.
319
- As [audited on 2026-10-10](docs/research/pnpm-settings-bump-2026-10-10.md),
320
- npm `latest` is 12.10.1; the highest confirmed stable release is 12.11.2 on
321
- `next-12`. The v11 target is 11.28.5 on `next-11`, while `latest-11` is
322
- 11.28.2. The project is pinned to 12.10.1. These checks verify the documented
373
+ As [audited on 2026-10-17](docs/research/pnpm-settings-bump-2026-10-17.md),
374
+ npm `latest` and the highest confirmed stable release are 12.14.0, also on
375
+ `latest-12` and `next-12`. The v11 target is 11.28.6 on both `latest-11` and
376
+ `next-11`. The project is pinned to 12.14.0. These checks verify the documented
323
377
  fixtures, not every setting in every release. v10 has unit regressions and
324
- source review through 10.34.6; real v10 consumption remains unverified.
378
+ source review through 10.34.7; real v10 consumption remains unverified.
325
379
  The removed `pnpm install --resolution-only` CLI flag is outside this settings
326
380
  migrator's scope; replace it with `pnpm peers check` in scripts before upgrading.
327
381
 
package/dist/cli.mjs CHANGED
@@ -587,6 +587,14 @@ const PNPM_V11_28_5_MINIMUM_VERSION = [
587
587
  5
588
588
  ];
589
589
  /**
590
+ * Release restoring null proxy values as unset configuration.
591
+ */
592
+ const PNPM_V11_28_6_MINIMUM_VERSION = [
593
+ 11,
594
+ 28,
595
+ 6
596
+ ];
597
+ /**
590
598
  * Release adding force, publishing, and reporter settings.
591
599
  */
592
600
  const PNPM_V12_7_MINIMUM_VERSION = [
@@ -627,6 +635,38 @@ const PNPM_V12_11_MINIMUM_VERSION = [
627
635
  0
628
636
  ];
629
637
  /**
638
+ * Release introducing remote cache configuration and missing peer types.
639
+ */
640
+ const PNPM_V12_12_MINIMUM_VERSION = [
641
+ 12,
642
+ 12,
643
+ 0
644
+ ];
645
+ /**
646
+ * Release reserving the tool registry prefix.
647
+ */
648
+ const PNPM_V12_12_1_MINIMUM_VERSION = [
649
+ 12,
650
+ 12,
651
+ 1
652
+ ];
653
+ /**
654
+ * Release introducing package map options and file-based store indexes.
655
+ */
656
+ const PNPM_V12_13_MINIMUM_VERSION = [
657
+ 12,
658
+ 13,
659
+ 0
660
+ ];
661
+ /**
662
+ * Release introducing side-effects cache exclusions.
663
+ */
664
+ const PNPM_V12_14_MINIMUM_VERSION = [
665
+ 12,
666
+ 14,
667
+ 0
668
+ ];
669
+ /**
630
670
  * Workspace settings first consumed by pnpm v12 in 12.11.0.
631
671
  */
632
672
  const PNPM_V12_11_WORKSPACE_SETTINGS = [
@@ -652,6 +692,16 @@ const PNPM_V12_7_WORKSPACE_SETTINGS = [
652
692
  * @see https://github.com/pnpm/pnpm/blob/v12.4.0/pnpm/crates/config/src/workspace_yaml.rs
653
693
  */
654
694
  const PNPM_SETTINGS_CAPABILITIES = [
695
+ {
696
+ minimumVersion: PNPM_V12_13_MINIMUM_VERSION,
697
+ workspaceFields: ["nodePackageMap", "storeIndexFormat"],
698
+ taskFields: []
699
+ },
700
+ {
701
+ minimumVersion: PNPM_V12_12_MINIMUM_VERSION,
702
+ workspaceFields: ["addMissingPeerTypes", "remoteCache"],
703
+ taskFields: []
704
+ },
655
705
  {
656
706
  minimumVersion: PNPM_V12_11_MINIMUM_VERSION,
657
707
  workspaceFields: PNPM_V12_11_WORKSPACE_SETTINGS,
@@ -733,9 +783,82 @@ const PNPM_V12_ONLY_WORKSPACE_SETTINGS = [
733
783
  "globalShims"
734
784
  ];
735
785
  /**
786
+ * Legacy fields absent from pnpm v12's workspace reader through 12.14.0.
787
+ * Removed aliases with explicit migration replacements are kept separately.
788
+ *
789
+ * @see https://github.com/pnpm/pnpm/blob/v12.14.0/pnpm/crates/config/src/workspace_yaml/settings.rs
790
+ */
791
+ const PNPM_V12_IGNORED_WORKSPACE_SETTINGS = [
792
+ "access",
793
+ "allowNew",
794
+ "allowSameVersion",
795
+ "auditIgnorePrune",
796
+ "autoConfirmAllPrompts",
797
+ "binLinks",
798
+ "commitHooks",
799
+ "cpu",
800
+ "depth",
801
+ "description",
802
+ "dev",
803
+ "dryRun",
804
+ "enablePnp",
805
+ "extraBinPaths",
806
+ "extraEnv",
807
+ "fetchingConcurrency",
808
+ "force",
809
+ "git",
810
+ "gitTagVersion",
811
+ "global",
812
+ "globalPath",
813
+ "globalPrefix",
814
+ "ignoreCurrentSpecifiers",
815
+ "json",
816
+ "libc",
817
+ "localAddress",
818
+ "lockfileOnly",
819
+ "long",
820
+ "message",
821
+ "npmPath",
822
+ "only",
823
+ "os",
824
+ "packDestination",
825
+ "packGzipLevel",
826
+ "parseable",
827
+ "pnpmExecPath",
828
+ "preserveAbsolutePaths",
829
+ "production",
830
+ "publishBranch",
831
+ "recursive",
832
+ "registriesByPrefix",
833
+ "registriesByScope",
834
+ "registryOptionsByUrl",
835
+ "requiredScripts",
836
+ "runtime",
837
+ "save",
838
+ "saveDev",
839
+ "saveOptional",
840
+ "saveProd",
841
+ "sideEffectsCacheRead",
842
+ "sideEffectsCacheWrite",
843
+ "signGitTag",
844
+ "strictSsl",
845
+ "tag",
846
+ "tryLoadDefaultPnpmfile",
847
+ "umask",
848
+ "useGitBranchLockfile",
849
+ "useLockfile",
850
+ "useRunningStoreServer",
851
+ "version",
852
+ "workspacePackagePatterns",
853
+ "workspacePackages",
854
+ "workspacePrefix",
855
+ "workspaceRoot",
856
+ "yes"
857
+ ];
858
+ /**
736
859
  * Lookup used to remove v11-only fields from the v12 workspace allowlist.
737
860
  */
738
- const v11OnlyWorkspaceSettings = new Set(PNPM_V11_ONLY_WORKSPACE_SETTINGS);
861
+ const v11OnlyWorkspaceSettings = /* @__PURE__ */ new Set([...PNPM_V11_ONLY_WORKSPACE_SETTINGS, ...PNPM_V12_IGNORED_WORKSPACE_SETTINGS]);
739
862
  /**
740
863
  * Fields accepted by registry declarations with pnpm 12.9 concurrency limits.
741
864
  */
@@ -770,7 +893,7 @@ const PROJECT_REFUSED_SETTINGS = new Set(PNPM_PROJECT_REFUSED_SETTINGS);
770
893
  /**
771
894
  * Fields reported as incompatible when selecting settings for v12.
772
895
  */
773
- const PNPM_V11_ONLY_SETTINGS = new Set(PNPM_V11_ONLY_WORKSPACE_SETTINGS);
896
+ const PNPM_V11_ONLY_SETTINGS = /* @__PURE__ */ new Set([...PNPM_V11_ONLY_WORKSPACE_SETTINGS, ...PNPM_V12_IGNORED_WORKSPACE_SETTINGS]);
774
897
  /**
775
898
  * Allowed workspace fields for the v11 target.
776
899
  */
@@ -1119,6 +1242,32 @@ function formatSettingsIssues(options) {
1119
1242
  return warnings;
1120
1243
  }
1121
1244
  //#endregion
1245
+ //#region src/features/settings/registries.ts
1246
+ /**
1247
+ * Check that a registry location is a URL rather than an object property name.
1248
+ *
1249
+ * @param value - Untrusted registry location
1250
+ *
1251
+ * @returns Whether the location is an absolute or scheme-relative URL
1252
+ */
1253
+ function isRegistryUrl(value) {
1254
+ return isString(value) && URL.canParse(value.startsWith("//") ? `https:${value}` : value);
1255
+ }
1256
+ /**
1257
+ * Validate locations in legacy registry maps and URL-keyed declarations.
1258
+ *
1259
+ * @param key - Canonical workspace setting name
1260
+ * @param value - Untrusted registry configuration
1261
+ *
1262
+ * @returns Whether the complete setting must be retained in its source
1263
+ */
1264
+ function hasInvalidRegistryLocations(key, value) {
1265
+ if (!["namedRegistries", "registries"].includes(key) || value === null) return false;
1266
+ if (!isPlainObject(value)) return true;
1267
+ if (key === "namedRegistries") return Object.values(value).some((url) => !isRegistryUrl(url));
1268
+ return Object.entries(value).some(([url, entry]) => isString(entry) ? !isRegistryUrl(entry) : !isRegistryUrl(url));
1269
+ }
1270
+ //#endregion
1122
1271
  //#region src/features/settings/permissions.ts
1123
1272
  /**
1124
1273
  * Validate a dependency's capability map without dropping unknown permissions.
@@ -1152,6 +1301,65 @@ function hasInvalidPermissionSettings(key, value) {
1152
1301
  //#endregion
1153
1302
  //#region src/features/settings/pnpm-v12-values.ts
1154
1303
  /**
1304
+ * Validate the package-map section without dropping future or misspelled keys.
1305
+ *
1306
+ * @param value - Untrusted package-map value
1307
+ *
1308
+ * @returns Whether the section is outside the pnpm 12.13 schema
1309
+ */
1310
+ function hasInvalidNodePackageMap(value) {
1311
+ if (value === null) return false;
1312
+ if (!value || typeof value !== "object" || isArray(value)) return true;
1313
+ return Object.entries(value).some(([field, entry]) => {
1314
+ let values = [];
1315
+ if (field === "mode") values = [
1316
+ "off",
1317
+ "write-only",
1318
+ "load"
1319
+ ];
1320
+ else if (field === "type") values = ["standard", "loose"];
1321
+ return values.length === 0 || entry !== null && (!isString(entry) || !entry.includes("${") && !values.includes(entry));
1322
+ });
1323
+ }
1324
+ /**
1325
+ * Validate cache exclusions and retain their whole section on older targets.
1326
+ *
1327
+ * @param value - Untrusted cache section
1328
+ * @param target - Confirmed release controlling exclusion support
1329
+ *
1330
+ * @returns Whether a section containing exclusions cannot migrate
1331
+ */
1332
+ function hasInvalidCacheExclusions(value, target) {
1333
+ if (!Object.hasOwn(value, "exclude")) return false;
1334
+ if (!supportsMinimumVersion(target.version, PNPM_V12_14_MINIMUM_VERSION)) return true;
1335
+ return Object.entries(value).some(([field, entry]) => {
1336
+ if (field === "exclude") return entry !== null && (!isArray(entry) || !entry.every(isString));
1337
+ if (field === "read" || field === "write") return entry !== null && !isBoolean(entry) && !(isString(entry) && entry.includes("${"));
1338
+ if (field !== "remote") return true;
1339
+ if (entry === null) return false;
1340
+ if (!entry || typeof entry !== "object" || isArray(entry)) return true;
1341
+ return Object.entries(entry).some(([key, setting]) => {
1342
+ if (key === "org" || key === "organization") return !isString(setting);
1343
+ return key !== "packages" || !isArray(setting) || !setting.every(isString);
1344
+ });
1345
+ });
1346
+ }
1347
+ /**
1348
+ * Check the value expansions introduced by pnpm 12.13 and 12.14.
1349
+ *
1350
+ * @param key - Canonical setting name
1351
+ * @param value - Untrusted setting value
1352
+ * @param target - Confirmed target release
1353
+ *
1354
+ * @returns Whether a package-map, store-index, or cache value is incompatible
1355
+ */
1356
+ function hasIncompatiblePackageMapOrCache(key, value, target) {
1357
+ if (key === "nodePackageMap") return !supportsMinimumVersion(target.version, PNPM_V12_13_MINIMUM_VERSION) || hasInvalidNodePackageMap(value);
1358
+ if (key === "storeIndexFormat") return !supportsMinimumVersion(target.version, PNPM_V12_13_MINIMUM_VERSION) || value !== null && (!isString(value) || !value.includes("${") && !["sqlite", "files"].includes(value));
1359
+ if (key === "sideEffectsCache" && value && typeof value === "object") return hasInvalidCacheExclusions(value, target);
1360
+ return false;
1361
+ }
1362
+ /**
1155
1363
  * Check a loaded linker object without discarding unsupported options.
1156
1364
  *
1157
1365
  * @param value - Untrusted nodeLinker object
@@ -1241,6 +1449,7 @@ function hasInvalidLinkerOrLockfileScalarValue(key, value) {
1241
1449
  * @returns Whether the setting cannot be used by the target release
1242
1450
  */
1243
1451
  function hasIncompatibleV12SettingValue(key, value, target) {
1452
+ if (hasIncompatiblePackageMapOrCache(key, value, target)) return true;
1244
1453
  const supportsV12_10 = supportsMinimumVersion(target.version, PNPM_V12_10_MINIMUM_VERSION);
1245
1454
  if (key === "nodeLinker" && value === "loaded") return !supportsV12_10;
1246
1455
  if (key === "failIfNoMatch" && target.compatibility === "v12") return value !== null && !isBoolean(value);
@@ -1263,6 +1472,22 @@ function hasIncompatibleV12SettingValue(key, value, target) {
1263
1472
  return false;
1264
1473
  }
1265
1474
  //#endregion
1475
+ //#region src/features/settings/remote-cache.ts
1476
+ /**
1477
+ * Accept only the project-owned remote cache organization.
1478
+ * Keep machine configuration and unknown fields intact in their source.
1479
+ *
1480
+ * @param key - Canonical workspace setting name
1481
+ * @param value - Untrusted incoming or existing workspace value
1482
+ *
1483
+ * @returns Whether the remote cache setting cannot migrate to project YAML
1484
+ */
1485
+ function hasInvalidRemoteCacheValue(key, value) {
1486
+ if (key !== "remoteCache" || value === null) return false;
1487
+ if (!value || typeof value !== "object" || isArray(value)) return true;
1488
+ return Object.entries(value).some(([field, entry]) => field !== "org" || entry !== null && !isString(entry));
1489
+ }
1490
+ //#endregion
1266
1491
  //#region src/features/settings/versioned-values.ts
1267
1492
  /**
1268
1493
  * Bounds of the signed 32-bit integer used by pnpm's task priority.
@@ -1279,7 +1504,10 @@ const MAX_TASK_PRIORITY = 2147483647;
1279
1504
  * @returns Whether pnpm rejects the value after migration
1280
1505
  */
1281
1506
  function hasInvalidV11PatchValue(key, value, target) {
1282
- if (["httpProxy", "httpsProxy"].includes(key) && supportsMinimumVersion(target.version, PNPM_V11_28_5_MINIMUM_VERSION)) return !isString(value);
1507
+ if (["httpProxy", "httpsProxy"].includes(key) && supportsMinimumVersion(target.version, PNPM_V11_28_5_MINIMUM_VERSION)) {
1508
+ if (value === null && supportsMinimumVersion(target.version, PNPM_V11_28_6_MINIMUM_VERSION)) return false;
1509
+ return !isString(value);
1510
+ }
1283
1511
  if (value === null || value === void 0) return false;
1284
1512
  if (key === "allowBuilds" && supportsMinimumVersion(target.version, PNPM_V11_28_3_MINIMUM_VERSION)) return typeof value !== "object" || isArray(value) || Object.values(value).some((entry) => !isBoolean(entry) && !isString(entry));
1285
1513
  if (supportsMinimumVersion(target.version, PNPM_V11_28_4_MINIMUM_VERSION)) {
@@ -1311,7 +1539,7 @@ function hasInvalidScalarValue(key, value, target) {
1311
1539
  "ndjson",
1312
1540
  "silent"
1313
1541
  ].includes(value);
1314
- if (key === "provenance") return value !== null && !isBoolean(value);
1542
+ if (key === "provenance" || key === "addMissingPeerTypes") return value !== null && !isBoolean(value);
1315
1543
  if (key === "progress") return !isBoolean(value);
1316
1544
  if (key === "tagVersionPrefix") return !isString(value);
1317
1545
  if (key === "loglevel") return !isString(value) || !value.includes("${") && ![
@@ -1341,6 +1569,17 @@ function hasInvalidReleaseValue(key, value, target) {
1341
1569
  return false;
1342
1570
  }
1343
1571
  /**
1572
+ * Check release-specific reserved aliases without broadening case sensitivity.
1573
+ *
1574
+ * @param prefix - Registry alias as declared
1575
+ * @param target - Confirmed target version
1576
+ *
1577
+ * @returns Whether the alias shadows a protocol introduced by these releases
1578
+ */
1579
+ function isReservedRegistryPrefix(prefix, target) {
1580
+ return prefix.toLowerCase() === "pkg" && supportsMinimumVersion(target.version, PNPM_V12_5_MINIMUM_VERSION) || prefix === "tool" && supportsMinimumVersion(target.version, PNPM_V12_12_1_MINIMUM_VERSION);
1581
+ }
1582
+ /**
1344
1583
  * Check value shapes that changed within a pnpm major release.
1345
1584
  *
1346
1585
  * Reject the whole setting so cleanup cannot discard unsupported nested values.
@@ -1352,7 +1591,7 @@ function hasInvalidReleaseValue(key, value, target) {
1352
1591
  * @returns Whether the value uses a shape unsupported by the selected release
1353
1592
  */
1354
1593
  function hasIncompatibleSettingValue(key, value, target) {
1355
- if (hasInvalidPermissionSettings(key, value) || hasInvalidReleaseValue(key, value, target) || hasInvalidV11PatchValue(key, value, target) || hasIncompatibleV12SettingValue(key, value, target)) return true;
1594
+ if (hasInvalidPermissionSettings(key, value) || hasInvalidRemoteCacheValue(key, value) || hasInvalidReleaseValue(key, value, target) || hasInvalidV11PatchValue(key, value, target) || hasIncompatibleV12SettingValue(key, value, target)) return true;
1356
1595
  if (hasInvalidScalarValue(key, value, target)) return true;
1357
1596
  const supportsV12_5 = supportsMinimumVersion(target.version, PNPM_V12_5_MINIMUM_VERSION);
1358
1597
  if (key === "supportedArchitectures" && isArray(value)) return !supportsV12_5;
@@ -1370,19 +1609,42 @@ function hasIncompatibleSettingValue(key, value, target) {
1370
1609
  "constraints"
1371
1610
  ].some((field) => Object.hasOwn(value, field));
1372
1611
  }
1373
- if (key === "namedRegistries" && supportsV12_5) return Object.keys(value).some((prefix) => prefix.toLowerCase() === "pkg");
1612
+ if (key === "namedRegistries") return Object.keys(value).some((prefix) => isReservedRegistryPrefix(prefix, target));
1374
1613
  if (key === "registries") return Object.values(value).some((entry) => {
1375
1614
  if (!entry || typeof entry !== "object") return false;
1376
1615
  if (Object.hasOwn(entry, "ecosystem") && !supportsV12_5) return true;
1377
1616
  if (Object.hasOwn(entry, "packages") && !supportsMinimumVersion(target.version, PNPM_V12_5_1_MINIMUM_VERSION)) return true;
1378
1617
  const prefix = Reflect.get(entry, "prefix");
1379
- return supportsV12_5 && isString(prefix) && prefix.toLowerCase() === "pkg";
1618
+ return isString(prefix) && isReservedRegistryPrefix(prefix, target);
1380
1619
  });
1381
1620
  return false;
1382
1621
  }
1383
1622
  //#endregion
1384
1623
  //#region src/features/settings/schema.ts
1385
1624
  /**
1625
+ * Detect legacy runtime fields without a destination in pnpm v11 and newer.
1626
+ *
1627
+ * @param key - Canonical setting name
1628
+ * @param value - Untrusted legacy runtime declaration
1629
+ * @param compatibility - Target major controlling runtime conversion
1630
+ *
1631
+ * @returns Whether converting the runtime would discard sibling fields
1632
+ */
1633
+ function hasUnmigratableRuntimeFields(key, value, compatibility) {
1634
+ return Boolean(key === "executionEnv" && compatibility !== "v10" && value && typeof value === "object" && Object.keys(value).some((field) => field !== "nodeVersion"));
1635
+ }
1636
+ /**
1637
+ * Check task declarations against the fields consumed by the target release.
1638
+ *
1639
+ * @param value - Untrusted task declarations
1640
+ * @param taskSettings - Supported task field names
1641
+ *
1642
+ * @returns Whether any declared task field is unsupported
1643
+ */
1644
+ function hasUnsupportedTaskFields(value, taskSettings) {
1645
+ return Boolean(value && typeof value === "object" && Object.values(value).some((task) => task && typeof task === "object" && Object.keys(task).some((field) => !taskSettings.has(field))));
1646
+ }
1647
+ /**
1386
1648
  * Create an empty collection of schema selection issues.
1387
1649
  *
1388
1650
  * @returns Empty issue lists for every supported issue category
@@ -1487,8 +1749,8 @@ function resolveSettingIssue({ allowedFields, target, key, npmrc, value }) {
1487
1749
  if (compatibility !== "v10" && PROJECT_REFUSED_SETTINGS.has(key)) return "refused";
1488
1750
  if (REGISTRY_SETTINGS.has(key) && containsUnsafeRegistryValue(value)) return "unsafe";
1489
1751
  if (PROXY_SETTINGS.has(key) && isString(value) && value.includes("${")) return "unsafe";
1490
- if (hasIncompatibleSettingValue(key, value, target)) return "incompatible";
1491
- if (key === "tasks" && value && typeof value === "object" && Object.values(value).some((task) => task && typeof task === "object" && Object.keys(task).some((field) => !taskSettings.has(field)))) return "incompatible";
1752
+ if (hasInvalidRegistryLocations(key, value) || hasUnmigratableRuntimeFields(key, value, compatibility) || hasIncompatibleSettingValue(key, value, target)) return "incompatible";
1753
+ if (key === "tasks" && hasUnsupportedTaskFields(value, taskSettings)) return "incompatible";
1492
1754
  if (!workspaceSettings.has(key)) return isSettingFromAnotherVersion(key, compatibility) ? "incompatible" : "unknown";
1493
1755
  if (allowedFields && !allowedFields.has(key)) return "unsupported";
1494
1756
  }
@@ -1897,8 +2159,17 @@ function getNpmrcLineKey(line) {
1897
2159
  const trimmed = line.trim();
1898
2160
  if (!trimmed || trimmed.startsWith(";") || trimmed.startsWith("#")) return;
1899
2161
  const index = trimmed.indexOf("=");
1900
- if (index <= 0) return;
1901
- return normalizeNpmrcKey(trimmed.slice(0, index));
2162
+ if (index === 0) return;
2163
+ let key = (index < 0 ? trimmed : trimmed.slice(0, index)).trim();
2164
+ if (key.startsWith("\"") && key.endsWith("\"") || key.startsWith("'") && key.endsWith("'")) {
2165
+ if (key.startsWith("'")) key = key.slice(1, -1);
2166
+ try {
2167
+ const parsed = JSON.parse(key);
2168
+ if (!isString(parsed)) return;
2169
+ key = parsed;
2170
+ } catch {}
2171
+ } else key = key.replace(/\\(?<escaped>[\\;#])|[;#].*$/gu, (_, escaped) => escaped ?? "");
2172
+ return normalizeNpmrcKey(key);
1902
2173
  }
1903
2174
  /**
1904
2175
  * Check whether a `.npmrc` key is auth/registry-related and should stay in
@@ -1925,7 +2196,7 @@ function isNpmrcAuthOrRegistryKey(key) {
1925
2196
  * @param compatibility - Concrete pnpm compatibility target
1926
2197
  * @param migratedKeys - Original `.npmrc` keys that were migrated
1927
2198
  *
1928
- * @returns A promise that resolves when the file has been pruned
2199
+ * @returns Whether the file was changed or removed
1929
2200
  *
1930
2201
  * @throws {Error} When file read/write operations fail
1931
2202
  *
@@ -1938,18 +2209,25 @@ async function pruneNpmrc(path, compatibility, migratedKeys) {
1938
2209
  const migratedKeySet = new Set(migratedKeys.map(normalizeNpmrcKey));
1939
2210
  const content = await fsReadFile(path);
1940
2211
  let inSection = false;
1941
- const updatedContent = content.split(/\r\n|[\r\n]/u).filter((line) => {
2212
+ let removed = false;
2213
+ const lines = content.split(/\r\n|[\r\n]/u).filter((line) => {
1942
2214
  if (/^\[[^\]]*\]\s*$/u.test(line.replace(/^\uFEFF/u, ""))) inSection = true;
1943
2215
  if (inSection) return true;
1944
2216
  const key = getNpmrcLineKey(line);
1945
2217
  if (!key) return true;
1946
- return !migratedKeySet.has(key);
1947
- }).join("\n").trimEnd();
2218
+ if (migratedKeySet.has(key)) {
2219
+ removed = true;
2220
+ return false;
2221
+ }
2222
+ return true;
2223
+ });
2224
+ if (!removed) return false;
2225
+ const updatedContent = lines.join("\n").trimEnd();
1948
2226
  if (compatibility !== "v10" && !updatedContent.trim()) {
1949
2227
  await fsRemoveFile(path);
1950
- return;
2228
+ return true;
1951
2229
  }
1952
- await fsWriteFile(path, updatedContent);
2230
+ return fsWriteFileIfChanged(path, updatedContent);
1953
2231
  }
1954
2232
  /**
1955
2233
  * Read `.npmrc` and return settings that should be migrated into workspace config.
@@ -2488,10 +2766,10 @@ async function persistMigration(options) {
2488
2766
  if (!cleanNpmrc) return result;
2489
2767
  const pruneTasks = projectNpmrcs.projects.flatMap((project) => {
2490
2768
  const appliedKeys = selectAppliedProjectKeys(project, finalSettings);
2491
- return appliedKeys.length ? [pruneNpmrc(project.npmrcPath, compatibility, appliedKeys).then(() => project.npmrcPath)] : [];
2769
+ return appliedKeys.length ? [pruneNpmrc(project.npmrcPath, compatibility, appliedKeys).then((changed) => changed ? project.npmrcPath : void 0)] : [];
2492
2770
  });
2493
- if (npmrcExists && appliedNpmrcKeys.length) pruneTasks.push(pruneNpmrc(npmrcPath, compatibility, appliedNpmrcKeys).then(() => npmrcPath));
2494
- const cleanedNpmrcPaths = await Promise.all(pruneTasks);
2771
+ if (npmrcExists && appliedNpmrcKeys.length) pruneTasks.push(pruneNpmrc(npmrcPath, compatibility, appliedNpmrcKeys).then((changed) => changed ? npmrcPath : void 0));
2772
+ const cleanedNpmrcPaths = (await Promise.all(pruneTasks)).filter(isString);
2495
2773
  result.changedFiles.push(...cleanedNpmrcPaths);
2496
2774
  result.sourceSettingsCleaned ||= cleanedNpmrcPaths.length > 0;
2497
2775
  return result;
@@ -2587,9 +2865,9 @@ function normalizeSideEffectsCache(settings) {
2587
2865
  * @returns Nothing; the URL's declaration is created or updated in place
2588
2866
  */
2589
2867
  function addRegistryScope(declarations, url, scope) {
2590
- declarations[url] ??= {};
2591
- const declaration = declarations[url];
2868
+ const declaration = declarations.get(url) ?? {};
2592
2869
  declaration.scopes = unique([...declaration.scopes ?? [], scope]);
2870
+ declarations.set(url, declaration);
2593
2871
  }
2594
2872
  /**
2595
2873
  * Find source URLs with multiple active aliases across the merged settings.
@@ -2618,9 +2896,9 @@ function findRegistryAliasConflicts(prefixesByUrl, namedRegistries, canonicalPre
2618
2896
  function normalizeNamedRegistries(settings, warnings, canonicalSettings) {
2619
2897
  const { namedRegistries } = settings;
2620
2898
  if (!namedRegistries) return;
2621
- const declarations = {};
2899
+ const declarations = /* @__PURE__ */ new Map();
2622
2900
  for (const [key, value] of Object.entries(settings.registries ?? {})) if (isString(value)) addRegistryScope(declarations, value, key === "default" ? "@" : key);
2623
- else declarations[key] = { ...value };
2901
+ else declarations.set(key, { ...value });
2624
2902
  const canonicalPrefixes = new Set(Object.values(canonicalSettings.registries ?? {}).flatMap((value) => !isString(value) && isString(value?.prefix) ? [value.prefix] : []));
2625
2903
  const prefixesByUrl = /* @__PURE__ */ new Map();
2626
2904
  const registryAliases = Object.entries(namedRegistries).filter(([prefix]) => !canonicalPrefixes.has(prefix));
@@ -2635,17 +2913,17 @@ function normalizeNamedRegistries(settings, warnings, canonicalSettings) {
2635
2913
  return;
2636
2914
  }
2637
2915
  for (const [url, [prefix]] of prefixesByUrl) {
2638
- declarations[url] ??= {};
2639
- const declaration = declarations[url];
2640
- const canonicalDeclaration = canonicalSettings.registries?.[url];
2916
+ const declaration = declarations.get(url) ?? {};
2917
+ const canonicalDeclaration = canonicalSettings.registries && Object.hasOwn(canonicalSettings.registries, url) ? canonicalSettings.registries[url] : void 0;
2641
2918
  const declaredPrefix = (isString(canonicalDeclaration) ? void 0 : canonicalDeclaration?.prefix) ?? declaration.prefix;
2642
2919
  if (isString(declaredPrefix) && declaredPrefix !== prefix) {
2643
2920
  warnings.push(`namedRegistries was kept because ${url} already declares prefix ${declaredPrefix}.`);
2644
2921
  return;
2645
2922
  }
2646
2923
  declaration.prefix = prefix;
2924
+ declarations.set(url, declaration);
2647
2925
  }
2648
- settings.registries = declarations;
2926
+ settings.registries = Object.fromEntries(declarations);
2649
2927
  Reflect.deleteProperty(settings, "namedRegistries");
2650
2928
  }
2651
2929
  /**
@@ -3207,7 +3485,7 @@ const bold = getColor("bold");
3207
3485
  //#endregion
3208
3486
  //#region package.json
3209
3487
  var name = "pnpm-settings-migrator";
3210
- var version = "0.16.0";
3488
+ var version = "0.18.0";
3211
3489
  //#endregion
3212
3490
  //#region src/cli/options.ts
3213
3491
  /**
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Document } from "yaml";
2
- import { PnpmSettings, RegistryDeclaration } from "@pnpm/types";
2
+ import { PnpmSettings, RegistryDeclaration, SideEffectsCacheSettings } from "@pnpm/types";
3
3
  //#region src/types/options.d.ts
4
4
  /**
5
5
  * Compatibility target used by the migrator.
@@ -259,6 +259,27 @@ interface PnpmSkillsSettings {
259
259
  * Settings supported by pnpm v12, subject to the resolved version capabilities.
260
260
  */
261
261
  interface PnpmSettingsV12 {
262
+ /**
263
+ * Package-map generation and loading options (pnpm 12.13+).
264
+ */
265
+ nodePackageMap?: PnpmNodePackageMapSettings | null;
266
+ /**
267
+ * Store package index backend; files supports network shares (pnpm 12.13+).
268
+ */
269
+ storeIndexFormat?: 'sqlite' | 'files' | null;
270
+ /**
271
+ * Build cache policy, including exclusions from pnpm 12.14 onward.
272
+ */
273
+ sideEffectsCache?: boolean | PnpmSideEffectsCacheSettings;
274
+ /**
275
+ * Link available companion @types packages as optional peers (pnpm 12.12+).
276
+ * Null leaves pnpm's default unchanged.
277
+ */
278
+ addMissingPeerTypes?: boolean | null;
279
+ /**
280
+ * Project-owned remote cache configuration (pnpm 12.12+).
281
+ */
282
+ remoteCache?: PnpmRemoteCacheSettings | null;
262
283
  /**
263
284
  * Per-dependency build and agent skill permissions (pnpm 12.11+).
264
285
  */
@@ -353,6 +374,41 @@ interface PnpmSettingsV12 {
353
374
  */
354
375
  tasks?: Record<string, PnpmTaskSettings>;
355
376
  }
377
+ /**
378
+ * Package-map section introduced in pnpm 12.13. Explicit fields take precedence
379
+ * over nodeExperimentalPackageMap and nodePackageMapType in the same source.
380
+ */
381
+ interface PnpmNodePackageMapSettings {
382
+ /**
383
+ * Disable the map, write it for tools, or also load it in Node.js scripts.
384
+ * Null leaves the legacy setting or pnpm's default effective.
385
+ */
386
+ mode?: 'off' | 'write-only' | 'load' | null;
387
+ /**
388
+ * Map format; null leaves the legacy setting or default effective.
389
+ */
390
+ type?: 'standard' | 'loose' | null;
391
+ }
392
+ /**
393
+ * Structured build cache options with pnpm 12.14 package exclusions.
394
+ */
395
+ interface PnpmSideEffectsCacheSettings extends SideEffectsCacheSettings {
396
+ /**
397
+ * Package name, scope, or name@version patterns whose builds are neither
398
+ * restored nor saved; null leaves exclusions unset.
399
+ */
400
+ exclude?: string[] | null;
401
+ }
402
+ /**
403
+ * Remote cache options allowed in project YAML. Server and signing settings
404
+ * belong in trusted global configuration or environment variables.
405
+ */
406
+ interface PnpmRemoteCacheSettings {
407
+ /**
408
+ * Organization owning shared artifacts; null leaves it unset.
409
+ */
410
+ org?: string | null;
411
+ }
356
412
  //#endregion
357
413
  //#region src/types/workspace.d.ts
358
414
  /**
@@ -473,6 +529,14 @@ interface PnpmSettingsDeprecated {
473
529
  * Settings introduced in pnpm v11.
474
530
  */
475
531
  interface PnpmSettingsV11 {
532
+ /**
533
+ * HTTP proxy; null leaves the proxy unset except in pnpm 11.28.5.
534
+ */
535
+ httpProxy?: string | null;
536
+ /**
537
+ * HTTPS proxy; null leaves the proxy unset except in pnpm 11.28.5.
538
+ */
539
+ httpsProxy?: string | null;
476
540
  /**
477
541
  * Publish provenance preference. Consumed from workspace configuration by
478
542
  * pnpm v11 and pnpm v12 starting with 12.11. Null leaves it unset.
@@ -522,7 +586,7 @@ interface PnpmSettingsV11 {
522
586
  /**
523
587
  * `pnpm-workspace.yaml` types.
524
588
  */
525
- type PnpmWorkspace = Omit<PnpmSettings, 'lockfile' | 'nodeLinker' | 'provenance' | 'registries' | 'supportedArchitectures' | 'tasks'> & PnpmSettingsDeprecated & PnpmSettingsV11 & PnpmSettingsV12 & PnpmWorkspaceLegacy;
589
+ type PnpmWorkspace = Omit<PnpmSettings, 'httpProxy' | 'httpsProxy' | 'lockfile' | 'nodeLinker' | 'provenance' | 'registries' | 'sideEffectsCache' | 'supportedArchitectures' | 'tasks'> & PnpmSettingsDeprecated & PnpmSettingsV11 & PnpmSettingsV12 & PnpmWorkspaceLegacy;
526
590
  //#endregion
527
591
  //#region src/types/package-json.d.ts
528
592
  /**
@@ -730,4 +794,4 @@ export declare function migratePnpmSettings(rawOptions?: Options): Promise<Migra
730
794
  */
731
795
  export declare function resolveOptions(options?: Options): ResolvedOptions;
732
796
  //#endregion
733
- export type { CompatibilityTarget, GlobalShimPolicy, MergeStrategy, MigrationResult, NpmRC, Options, PackageJson, PackageManagerEngine, PnpmCargoSettings, PnpmLoadedNodeLinkerSettings, PnpmLockfileSettings, PnpmPackagePermissions, PnpmPythonSettings, PnpmRegistryDeclaration, PnpmSettingsDeprecated, PnpmSettingsV11, PnpmSettingsV12, PnpmSkillsSettings, PnpmTaskSettings, PnpmWorkspace, PnpmWorkspaceLegacy, ResolvedOptions, RuntimeEngine, SettingsChange };
797
+ export type { CompatibilityTarget, GlobalShimPolicy, MergeStrategy, MigrationResult, NpmRC, Options, PackageJson, PackageManagerEngine, PnpmCargoSettings, PnpmLoadedNodeLinkerSettings, PnpmLockfileSettings, PnpmNodePackageMapSettings, PnpmPackagePermissions, PnpmPythonSettings, PnpmRegistryDeclaration, PnpmRemoteCacheSettings, PnpmSettingsDeprecated, PnpmSettingsV11, PnpmSettingsV12, PnpmSideEffectsCacheSettings, PnpmSkillsSettings, PnpmTaskSettings, PnpmWorkspace, PnpmWorkspaceLegacy, ResolvedOptions, RuntimeEngine, SettingsChange };
package/dist/index.mjs CHANGED
@@ -585,6 +585,14 @@ const PNPM_V11_28_5_MINIMUM_VERSION = [
585
585
  5
586
586
  ];
587
587
  /**
588
+ * Release restoring null proxy values as unset configuration.
589
+ */
590
+ const PNPM_V11_28_6_MINIMUM_VERSION = [
591
+ 11,
592
+ 28,
593
+ 6
594
+ ];
595
+ /**
588
596
  * Release adding force, publishing, and reporter settings.
589
597
  */
590
598
  const PNPM_V12_7_MINIMUM_VERSION = [
@@ -625,6 +633,38 @@ const PNPM_V12_11_MINIMUM_VERSION = [
625
633
  0
626
634
  ];
627
635
  /**
636
+ * Release introducing remote cache configuration and missing peer types.
637
+ */
638
+ const PNPM_V12_12_MINIMUM_VERSION = [
639
+ 12,
640
+ 12,
641
+ 0
642
+ ];
643
+ /**
644
+ * Release reserving the tool registry prefix.
645
+ */
646
+ const PNPM_V12_12_1_MINIMUM_VERSION = [
647
+ 12,
648
+ 12,
649
+ 1
650
+ ];
651
+ /**
652
+ * Release introducing package map options and file-based store indexes.
653
+ */
654
+ const PNPM_V12_13_MINIMUM_VERSION = [
655
+ 12,
656
+ 13,
657
+ 0
658
+ ];
659
+ /**
660
+ * Release introducing side-effects cache exclusions.
661
+ */
662
+ const PNPM_V12_14_MINIMUM_VERSION = [
663
+ 12,
664
+ 14,
665
+ 0
666
+ ];
667
+ /**
628
668
  * Workspace settings first consumed by pnpm v12 in 12.11.0.
629
669
  */
630
670
  const PNPM_V12_11_WORKSPACE_SETTINGS = [
@@ -650,6 +690,16 @@ const PNPM_V12_7_WORKSPACE_SETTINGS = [
650
690
  * @see https://github.com/pnpm/pnpm/blob/v12.4.0/pnpm/crates/config/src/workspace_yaml.rs
651
691
  */
652
692
  const PNPM_SETTINGS_CAPABILITIES = [
693
+ {
694
+ minimumVersion: PNPM_V12_13_MINIMUM_VERSION,
695
+ workspaceFields: ["nodePackageMap", "storeIndexFormat"],
696
+ taskFields: []
697
+ },
698
+ {
699
+ minimumVersion: PNPM_V12_12_MINIMUM_VERSION,
700
+ workspaceFields: ["addMissingPeerTypes", "remoteCache"],
701
+ taskFields: []
702
+ },
653
703
  {
654
704
  minimumVersion: PNPM_V12_11_MINIMUM_VERSION,
655
705
  workspaceFields: PNPM_V12_11_WORKSPACE_SETTINGS,
@@ -731,9 +781,82 @@ const PNPM_V12_ONLY_WORKSPACE_SETTINGS = [
731
781
  "globalShims"
732
782
  ];
733
783
  /**
784
+ * Legacy fields absent from pnpm v12's workspace reader through 12.14.0.
785
+ * Removed aliases with explicit migration replacements are kept separately.
786
+ *
787
+ * @see https://github.com/pnpm/pnpm/blob/v12.14.0/pnpm/crates/config/src/workspace_yaml/settings.rs
788
+ */
789
+ const PNPM_V12_IGNORED_WORKSPACE_SETTINGS = [
790
+ "access",
791
+ "allowNew",
792
+ "allowSameVersion",
793
+ "auditIgnorePrune",
794
+ "autoConfirmAllPrompts",
795
+ "binLinks",
796
+ "commitHooks",
797
+ "cpu",
798
+ "depth",
799
+ "description",
800
+ "dev",
801
+ "dryRun",
802
+ "enablePnp",
803
+ "extraBinPaths",
804
+ "extraEnv",
805
+ "fetchingConcurrency",
806
+ "force",
807
+ "git",
808
+ "gitTagVersion",
809
+ "global",
810
+ "globalPath",
811
+ "globalPrefix",
812
+ "ignoreCurrentSpecifiers",
813
+ "json",
814
+ "libc",
815
+ "localAddress",
816
+ "lockfileOnly",
817
+ "long",
818
+ "message",
819
+ "npmPath",
820
+ "only",
821
+ "os",
822
+ "packDestination",
823
+ "packGzipLevel",
824
+ "parseable",
825
+ "pnpmExecPath",
826
+ "preserveAbsolutePaths",
827
+ "production",
828
+ "publishBranch",
829
+ "recursive",
830
+ "registriesByPrefix",
831
+ "registriesByScope",
832
+ "registryOptionsByUrl",
833
+ "requiredScripts",
834
+ "runtime",
835
+ "save",
836
+ "saveDev",
837
+ "saveOptional",
838
+ "saveProd",
839
+ "sideEffectsCacheRead",
840
+ "sideEffectsCacheWrite",
841
+ "signGitTag",
842
+ "strictSsl",
843
+ "tag",
844
+ "tryLoadDefaultPnpmfile",
845
+ "umask",
846
+ "useGitBranchLockfile",
847
+ "useLockfile",
848
+ "useRunningStoreServer",
849
+ "version",
850
+ "workspacePackagePatterns",
851
+ "workspacePackages",
852
+ "workspacePrefix",
853
+ "workspaceRoot",
854
+ "yes"
855
+ ];
856
+ /**
734
857
  * Lookup used to remove v11-only fields from the v12 workspace allowlist.
735
858
  */
736
- const v11OnlyWorkspaceSettings = new Set(PNPM_V11_ONLY_WORKSPACE_SETTINGS);
859
+ const v11OnlyWorkspaceSettings = /* @__PURE__ */ new Set([...PNPM_V11_ONLY_WORKSPACE_SETTINGS, ...PNPM_V12_IGNORED_WORKSPACE_SETTINGS]);
737
860
  /**
738
861
  * Fields accepted by registry declarations with pnpm 12.9 concurrency limits.
739
862
  */
@@ -768,7 +891,7 @@ const PROJECT_REFUSED_SETTINGS = new Set(PNPM_PROJECT_REFUSED_SETTINGS);
768
891
  /**
769
892
  * Fields reported as incompatible when selecting settings for v12.
770
893
  */
771
- const PNPM_V11_ONLY_SETTINGS = new Set(PNPM_V11_ONLY_WORKSPACE_SETTINGS);
894
+ const PNPM_V11_ONLY_SETTINGS = /* @__PURE__ */ new Set([...PNPM_V11_ONLY_WORKSPACE_SETTINGS, ...PNPM_V12_IGNORED_WORKSPACE_SETTINGS]);
772
895
  /**
773
896
  * Allowed workspace fields for the v11 target.
774
897
  */
@@ -1112,6 +1235,32 @@ function formatSettingsIssues(options) {
1112
1235
  return warnings;
1113
1236
  }
1114
1237
  //#endregion
1238
+ //#region src/features/settings/registries.ts
1239
+ /**
1240
+ * Check that a registry location is a URL rather than an object property name.
1241
+ *
1242
+ * @param value - Untrusted registry location
1243
+ *
1244
+ * @returns Whether the location is an absolute or scheme-relative URL
1245
+ */
1246
+ function isRegistryUrl(value) {
1247
+ return isString(value) && URL.canParse(value.startsWith("//") ? `https:${value}` : value);
1248
+ }
1249
+ /**
1250
+ * Validate locations in legacy registry maps and URL-keyed declarations.
1251
+ *
1252
+ * @param key - Canonical workspace setting name
1253
+ * @param value - Untrusted registry configuration
1254
+ *
1255
+ * @returns Whether the complete setting must be retained in its source
1256
+ */
1257
+ function hasInvalidRegistryLocations(key, value) {
1258
+ if (!["namedRegistries", "registries"].includes(key) || value === null) return false;
1259
+ if (!isPlainObject(value)) return true;
1260
+ if (key === "namedRegistries") return Object.values(value).some((url) => !isRegistryUrl(url));
1261
+ return Object.entries(value).some(([url, entry]) => isString(entry) ? !isRegistryUrl(entry) : !isRegistryUrl(url));
1262
+ }
1263
+ //#endregion
1115
1264
  //#region src/features/settings/permissions.ts
1116
1265
  /**
1117
1266
  * Validate a dependency's capability map without dropping unknown permissions.
@@ -1145,6 +1294,65 @@ function hasInvalidPermissionSettings(key, value) {
1145
1294
  //#endregion
1146
1295
  //#region src/features/settings/pnpm-v12-values.ts
1147
1296
  /**
1297
+ * Validate the package-map section without dropping future or misspelled keys.
1298
+ *
1299
+ * @param value - Untrusted package-map value
1300
+ *
1301
+ * @returns Whether the section is outside the pnpm 12.13 schema
1302
+ */
1303
+ function hasInvalidNodePackageMap(value) {
1304
+ if (value === null) return false;
1305
+ if (!value || typeof value !== "object" || isArray(value)) return true;
1306
+ return Object.entries(value).some(([field, entry]) => {
1307
+ let values = [];
1308
+ if (field === "mode") values = [
1309
+ "off",
1310
+ "write-only",
1311
+ "load"
1312
+ ];
1313
+ else if (field === "type") values = ["standard", "loose"];
1314
+ return values.length === 0 || entry !== null && (!isString(entry) || !entry.includes("${") && !values.includes(entry));
1315
+ });
1316
+ }
1317
+ /**
1318
+ * Validate cache exclusions and retain their whole section on older targets.
1319
+ *
1320
+ * @param value - Untrusted cache section
1321
+ * @param target - Confirmed release controlling exclusion support
1322
+ *
1323
+ * @returns Whether a section containing exclusions cannot migrate
1324
+ */
1325
+ function hasInvalidCacheExclusions(value, target) {
1326
+ if (!Object.hasOwn(value, "exclude")) return false;
1327
+ if (!supportsMinimumVersion(target.version, PNPM_V12_14_MINIMUM_VERSION)) return true;
1328
+ return Object.entries(value).some(([field, entry]) => {
1329
+ if (field === "exclude") return entry !== null && (!isArray(entry) || !entry.every(isString));
1330
+ if (field === "read" || field === "write") return entry !== null && !isBoolean(entry) && !(isString(entry) && entry.includes("${"));
1331
+ if (field !== "remote") return true;
1332
+ if (entry === null) return false;
1333
+ if (!entry || typeof entry !== "object" || isArray(entry)) return true;
1334
+ return Object.entries(entry).some(([key, setting]) => {
1335
+ if (key === "org" || key === "organization") return !isString(setting);
1336
+ return key !== "packages" || !isArray(setting) || !setting.every(isString);
1337
+ });
1338
+ });
1339
+ }
1340
+ /**
1341
+ * Check the value expansions introduced by pnpm 12.13 and 12.14.
1342
+ *
1343
+ * @param key - Canonical setting name
1344
+ * @param value - Untrusted setting value
1345
+ * @param target - Confirmed target release
1346
+ *
1347
+ * @returns Whether a package-map, store-index, or cache value is incompatible
1348
+ */
1349
+ function hasIncompatiblePackageMapOrCache(key, value, target) {
1350
+ if (key === "nodePackageMap") return !supportsMinimumVersion(target.version, PNPM_V12_13_MINIMUM_VERSION) || hasInvalidNodePackageMap(value);
1351
+ if (key === "storeIndexFormat") return !supportsMinimumVersion(target.version, PNPM_V12_13_MINIMUM_VERSION) || value !== null && (!isString(value) || !value.includes("${") && !["sqlite", "files"].includes(value));
1352
+ if (key === "sideEffectsCache" && value && typeof value === "object") return hasInvalidCacheExclusions(value, target);
1353
+ return false;
1354
+ }
1355
+ /**
1148
1356
  * Check a loaded linker object without discarding unsupported options.
1149
1357
  *
1150
1358
  * @param value - Untrusted nodeLinker object
@@ -1234,6 +1442,7 @@ function hasInvalidLinkerOrLockfileScalarValue(key, value) {
1234
1442
  * @returns Whether the setting cannot be used by the target release
1235
1443
  */
1236
1444
  function hasIncompatibleV12SettingValue(key, value, target) {
1445
+ if (hasIncompatiblePackageMapOrCache(key, value, target)) return true;
1237
1446
  const supportsV12_10 = supportsMinimumVersion(target.version, PNPM_V12_10_MINIMUM_VERSION);
1238
1447
  if (key === "nodeLinker" && value === "loaded") return !supportsV12_10;
1239
1448
  if (key === "failIfNoMatch" && target.compatibility === "v12") return value !== null && !isBoolean(value);
@@ -1256,6 +1465,22 @@ function hasIncompatibleV12SettingValue(key, value, target) {
1256
1465
  return false;
1257
1466
  }
1258
1467
  //#endregion
1468
+ //#region src/features/settings/remote-cache.ts
1469
+ /**
1470
+ * Accept only the project-owned remote cache organization.
1471
+ * Keep machine configuration and unknown fields intact in their source.
1472
+ *
1473
+ * @param key - Canonical workspace setting name
1474
+ * @param value - Untrusted incoming or existing workspace value
1475
+ *
1476
+ * @returns Whether the remote cache setting cannot migrate to project YAML
1477
+ */
1478
+ function hasInvalidRemoteCacheValue(key, value) {
1479
+ if (key !== "remoteCache" || value === null) return false;
1480
+ if (!value || typeof value !== "object" || isArray(value)) return true;
1481
+ return Object.entries(value).some(([field, entry]) => field !== "org" || entry !== null && !isString(entry));
1482
+ }
1483
+ //#endregion
1259
1484
  //#region src/features/settings/versioned-values.ts
1260
1485
  /**
1261
1486
  * Bounds of the signed 32-bit integer used by pnpm's task priority.
@@ -1272,7 +1497,10 @@ const MAX_TASK_PRIORITY = 2147483647;
1272
1497
  * @returns Whether pnpm rejects the value after migration
1273
1498
  */
1274
1499
  function hasInvalidV11PatchValue(key, value, target) {
1275
- if (["httpProxy", "httpsProxy"].includes(key) && supportsMinimumVersion(target.version, PNPM_V11_28_5_MINIMUM_VERSION)) return !isString(value);
1500
+ if (["httpProxy", "httpsProxy"].includes(key) && supportsMinimumVersion(target.version, PNPM_V11_28_5_MINIMUM_VERSION)) {
1501
+ if (value === null && supportsMinimumVersion(target.version, PNPM_V11_28_6_MINIMUM_VERSION)) return false;
1502
+ return !isString(value);
1503
+ }
1276
1504
  if (value === null || value === void 0) return false;
1277
1505
  if (key === "allowBuilds" && supportsMinimumVersion(target.version, PNPM_V11_28_3_MINIMUM_VERSION)) return typeof value !== "object" || isArray(value) || Object.values(value).some((entry) => !isBoolean(entry) && !isString(entry));
1278
1506
  if (supportsMinimumVersion(target.version, PNPM_V11_28_4_MINIMUM_VERSION)) {
@@ -1304,7 +1532,7 @@ function hasInvalidScalarValue(key, value, target) {
1304
1532
  "ndjson",
1305
1533
  "silent"
1306
1534
  ].includes(value);
1307
- if (key === "provenance") return value !== null && !isBoolean(value);
1535
+ if (key === "provenance" || key === "addMissingPeerTypes") return value !== null && !isBoolean(value);
1308
1536
  if (key === "progress") return !isBoolean(value);
1309
1537
  if (key === "tagVersionPrefix") return !isString(value);
1310
1538
  if (key === "loglevel") return !isString(value) || !value.includes("${") && ![
@@ -1334,6 +1562,17 @@ function hasInvalidReleaseValue(key, value, target) {
1334
1562
  return false;
1335
1563
  }
1336
1564
  /**
1565
+ * Check release-specific reserved aliases without broadening case sensitivity.
1566
+ *
1567
+ * @param prefix - Registry alias as declared
1568
+ * @param target - Confirmed target version
1569
+ *
1570
+ * @returns Whether the alias shadows a protocol introduced by these releases
1571
+ */
1572
+ function isReservedRegistryPrefix(prefix, target) {
1573
+ return prefix.toLowerCase() === "pkg" && supportsMinimumVersion(target.version, PNPM_V12_5_MINIMUM_VERSION) || prefix === "tool" && supportsMinimumVersion(target.version, PNPM_V12_12_1_MINIMUM_VERSION);
1574
+ }
1575
+ /**
1337
1576
  * Check value shapes that changed within a pnpm major release.
1338
1577
  *
1339
1578
  * Reject the whole setting so cleanup cannot discard unsupported nested values.
@@ -1345,7 +1584,7 @@ function hasInvalidReleaseValue(key, value, target) {
1345
1584
  * @returns Whether the value uses a shape unsupported by the selected release
1346
1585
  */
1347
1586
  function hasIncompatibleSettingValue(key, value, target) {
1348
- if (hasInvalidPermissionSettings(key, value) || hasInvalidReleaseValue(key, value, target) || hasInvalidV11PatchValue(key, value, target) || hasIncompatibleV12SettingValue(key, value, target)) return true;
1587
+ if (hasInvalidPermissionSettings(key, value) || hasInvalidRemoteCacheValue(key, value) || hasInvalidReleaseValue(key, value, target) || hasInvalidV11PatchValue(key, value, target) || hasIncompatibleV12SettingValue(key, value, target)) return true;
1349
1588
  if (hasInvalidScalarValue(key, value, target)) return true;
1350
1589
  const supportsV12_5 = supportsMinimumVersion(target.version, PNPM_V12_5_MINIMUM_VERSION);
1351
1590
  if (key === "supportedArchitectures" && isArray(value)) return !supportsV12_5;
@@ -1363,19 +1602,42 @@ function hasIncompatibleSettingValue(key, value, target) {
1363
1602
  "constraints"
1364
1603
  ].some((field) => Object.hasOwn(value, field));
1365
1604
  }
1366
- if (key === "namedRegistries" && supportsV12_5) return Object.keys(value).some((prefix) => prefix.toLowerCase() === "pkg");
1605
+ if (key === "namedRegistries") return Object.keys(value).some((prefix) => isReservedRegistryPrefix(prefix, target));
1367
1606
  if (key === "registries") return Object.values(value).some((entry) => {
1368
1607
  if (!entry || typeof entry !== "object") return false;
1369
1608
  if (Object.hasOwn(entry, "ecosystem") && !supportsV12_5) return true;
1370
1609
  if (Object.hasOwn(entry, "packages") && !supportsMinimumVersion(target.version, PNPM_V12_5_1_MINIMUM_VERSION)) return true;
1371
1610
  const prefix = Reflect.get(entry, "prefix");
1372
- return supportsV12_5 && isString(prefix) && prefix.toLowerCase() === "pkg";
1611
+ return isString(prefix) && isReservedRegistryPrefix(prefix, target);
1373
1612
  });
1374
1613
  return false;
1375
1614
  }
1376
1615
  //#endregion
1377
1616
  //#region src/features/settings/schema.ts
1378
1617
  /**
1618
+ * Detect legacy runtime fields without a destination in pnpm v11 and newer.
1619
+ *
1620
+ * @param key - Canonical setting name
1621
+ * @param value - Untrusted legacy runtime declaration
1622
+ * @param compatibility - Target major controlling runtime conversion
1623
+ *
1624
+ * @returns Whether converting the runtime would discard sibling fields
1625
+ */
1626
+ function hasUnmigratableRuntimeFields(key, value, compatibility) {
1627
+ return Boolean(key === "executionEnv" && compatibility !== "v10" && value && typeof value === "object" && Object.keys(value).some((field) => field !== "nodeVersion"));
1628
+ }
1629
+ /**
1630
+ * Check task declarations against the fields consumed by the target release.
1631
+ *
1632
+ * @param value - Untrusted task declarations
1633
+ * @param taskSettings - Supported task field names
1634
+ *
1635
+ * @returns Whether any declared task field is unsupported
1636
+ */
1637
+ function hasUnsupportedTaskFields(value, taskSettings) {
1638
+ return Boolean(value && typeof value === "object" && Object.values(value).some((task) => task && typeof task === "object" && Object.keys(task).some((field) => !taskSettings.has(field))));
1639
+ }
1640
+ /**
1379
1641
  * Create an empty collection of schema selection issues.
1380
1642
  *
1381
1643
  * @returns Empty issue lists for every supported issue category
@@ -1480,8 +1742,8 @@ function resolveSettingIssue({ allowedFields, target, key, npmrc, value }) {
1480
1742
  if (compatibility !== "v10" && PROJECT_REFUSED_SETTINGS.has(key)) return "refused";
1481
1743
  if (REGISTRY_SETTINGS.has(key) && containsUnsafeRegistryValue(value)) return "unsafe";
1482
1744
  if (PROXY_SETTINGS.has(key) && isString(value) && value.includes("${")) return "unsafe";
1483
- if (hasIncompatibleSettingValue(key, value, target)) return "incompatible";
1484
- if (key === "tasks" && value && typeof value === "object" && Object.values(value).some((task) => task && typeof task === "object" && Object.keys(task).some((field) => !taskSettings.has(field)))) return "incompatible";
1745
+ if (hasInvalidRegistryLocations(key, value) || hasUnmigratableRuntimeFields(key, value, compatibility) || hasIncompatibleSettingValue(key, value, target)) return "incompatible";
1746
+ if (key === "tasks" && hasUnsupportedTaskFields(value, taskSettings)) return "incompatible";
1485
1747
  if (!workspaceSettings.has(key)) return isSettingFromAnotherVersion(key, compatibility) ? "incompatible" : "unknown";
1486
1748
  if (allowedFields && !allowedFields.has(key)) return "unsupported";
1487
1749
  }
@@ -1890,8 +2152,17 @@ function getNpmrcLineKey(line) {
1890
2152
  const trimmed = line.trim();
1891
2153
  if (!trimmed || trimmed.startsWith(";") || trimmed.startsWith("#")) return;
1892
2154
  const index = trimmed.indexOf("=");
1893
- if (index <= 0) return;
1894
- return normalizeNpmrcKey(trimmed.slice(0, index));
2155
+ if (index === 0) return;
2156
+ let key = (index < 0 ? trimmed : trimmed.slice(0, index)).trim();
2157
+ if (key.startsWith("\"") && key.endsWith("\"") || key.startsWith("'") && key.endsWith("'")) {
2158
+ if (key.startsWith("'")) key = key.slice(1, -1);
2159
+ try {
2160
+ const parsed = JSON.parse(key);
2161
+ if (!isString(parsed)) return;
2162
+ key = parsed;
2163
+ } catch {}
2164
+ } else key = key.replace(/\\(?<escaped>[\\;#])|[;#].*$/gu, (_, escaped) => escaped ?? "");
2165
+ return normalizeNpmrcKey(key);
1895
2166
  }
1896
2167
  /**
1897
2168
  * Check whether a `.npmrc` key is auth/registry-related and should stay in
@@ -1918,7 +2189,7 @@ function isNpmrcAuthOrRegistryKey(key) {
1918
2189
  * @param compatibility - Concrete pnpm compatibility target
1919
2190
  * @param migratedKeys - Original `.npmrc` keys that were migrated
1920
2191
  *
1921
- * @returns A promise that resolves when the file has been pruned
2192
+ * @returns Whether the file was changed or removed
1922
2193
  *
1923
2194
  * @throws {Error} When file read/write operations fail
1924
2195
  *
@@ -1931,18 +2202,25 @@ async function pruneNpmrc(path, compatibility, migratedKeys) {
1931
2202
  const migratedKeySet = new Set(migratedKeys.map(normalizeNpmrcKey));
1932
2203
  const content = await fsReadFile(path);
1933
2204
  let inSection = false;
1934
- const updatedContent = content.split(/\r\n|[\r\n]/u).filter((line) => {
2205
+ let removed = false;
2206
+ const lines = content.split(/\r\n|[\r\n]/u).filter((line) => {
1935
2207
  if (/^\[[^\]]*\]\s*$/u.test(line.replace(/^\uFEFF/u, ""))) inSection = true;
1936
2208
  if (inSection) return true;
1937
2209
  const key = getNpmrcLineKey(line);
1938
2210
  if (!key) return true;
1939
- return !migratedKeySet.has(key);
1940
- }).join("\n").trimEnd();
2211
+ if (migratedKeySet.has(key)) {
2212
+ removed = true;
2213
+ return false;
2214
+ }
2215
+ return true;
2216
+ });
2217
+ if (!removed) return false;
2218
+ const updatedContent = lines.join("\n").trimEnd();
1941
2219
  if (compatibility !== "v10" && !updatedContent.trim()) {
1942
2220
  await fsRemoveFile(path);
1943
- return;
2221
+ return true;
1944
2222
  }
1945
- await fsWriteFile(path, updatedContent);
2223
+ return fsWriteFileIfChanged(path, updatedContent);
1946
2224
  }
1947
2225
  /**
1948
2226
  * Read `.npmrc` and return settings that should be migrated into workspace config.
@@ -2481,10 +2759,10 @@ async function persistMigration(options) {
2481
2759
  if (!cleanNpmrc) return result;
2482
2760
  const pruneTasks = projectNpmrcs.projects.flatMap((project) => {
2483
2761
  const appliedKeys = selectAppliedProjectKeys(project, finalSettings);
2484
- return appliedKeys.length ? [pruneNpmrc(project.npmrcPath, compatibility, appliedKeys).then(() => project.npmrcPath)] : [];
2762
+ return appliedKeys.length ? [pruneNpmrc(project.npmrcPath, compatibility, appliedKeys).then((changed) => changed ? project.npmrcPath : void 0)] : [];
2485
2763
  });
2486
- if (npmrcExists && appliedNpmrcKeys.length) pruneTasks.push(pruneNpmrc(npmrcPath, compatibility, appliedNpmrcKeys).then(() => npmrcPath));
2487
- const cleanedNpmrcPaths = await Promise.all(pruneTasks);
2764
+ if (npmrcExists && appliedNpmrcKeys.length) pruneTasks.push(pruneNpmrc(npmrcPath, compatibility, appliedNpmrcKeys).then((changed) => changed ? npmrcPath : void 0));
2765
+ const cleanedNpmrcPaths = (await Promise.all(pruneTasks)).filter(isString);
2488
2766
  result.changedFiles.push(...cleanedNpmrcPaths);
2489
2767
  result.sourceSettingsCleaned ||= cleanedNpmrcPaths.length > 0;
2490
2768
  return result;
@@ -2580,9 +2858,9 @@ function normalizeSideEffectsCache(settings) {
2580
2858
  * @returns Nothing; the URL's declaration is created or updated in place
2581
2859
  */
2582
2860
  function addRegistryScope(declarations, url, scope) {
2583
- declarations[url] ??= {};
2584
- const declaration = declarations[url];
2861
+ const declaration = declarations.get(url) ?? {};
2585
2862
  declaration.scopes = unique([...declaration.scopes ?? [], scope]);
2863
+ declarations.set(url, declaration);
2586
2864
  }
2587
2865
  /**
2588
2866
  * Find source URLs with multiple active aliases across the merged settings.
@@ -2611,9 +2889,9 @@ function findRegistryAliasConflicts(prefixesByUrl, namedRegistries, canonicalPre
2611
2889
  function normalizeNamedRegistries(settings, warnings, canonicalSettings) {
2612
2890
  const { namedRegistries } = settings;
2613
2891
  if (!namedRegistries) return;
2614
- const declarations = {};
2892
+ const declarations = /* @__PURE__ */ new Map();
2615
2893
  for (const [key, value] of Object.entries(settings.registries ?? {})) if (isString(value)) addRegistryScope(declarations, value, key === "default" ? "@" : key);
2616
- else declarations[key] = { ...value };
2894
+ else declarations.set(key, { ...value });
2617
2895
  const canonicalPrefixes = new Set(Object.values(canonicalSettings.registries ?? {}).flatMap((value) => !isString(value) && isString(value?.prefix) ? [value.prefix] : []));
2618
2896
  const prefixesByUrl = /* @__PURE__ */ new Map();
2619
2897
  const registryAliases = Object.entries(namedRegistries).filter(([prefix]) => !canonicalPrefixes.has(prefix));
@@ -2628,17 +2906,17 @@ function normalizeNamedRegistries(settings, warnings, canonicalSettings) {
2628
2906
  return;
2629
2907
  }
2630
2908
  for (const [url, [prefix]] of prefixesByUrl) {
2631
- declarations[url] ??= {};
2632
- const declaration = declarations[url];
2633
- const canonicalDeclaration = canonicalSettings.registries?.[url];
2909
+ const declaration = declarations.get(url) ?? {};
2910
+ const canonicalDeclaration = canonicalSettings.registries && Object.hasOwn(canonicalSettings.registries, url) ? canonicalSettings.registries[url] : void 0;
2634
2911
  const declaredPrefix = (isString(canonicalDeclaration) ? void 0 : canonicalDeclaration?.prefix) ?? declaration.prefix;
2635
2912
  if (isString(declaredPrefix) && declaredPrefix !== prefix) {
2636
2913
  warnings.push(`namedRegistries was kept because ${url} already declares prefix ${declaredPrefix}.`);
2637
2914
  return;
2638
2915
  }
2639
2916
  declaration.prefix = prefix;
2917
+ declarations.set(url, declaration);
2640
2918
  }
2641
- settings.registries = declarations;
2919
+ settings.registries = Object.fromEntries(declarations);
2642
2920
  Reflect.deleteProperty(settings, "namedRegistries");
2643
2921
  }
2644
2922
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pnpm-settings-migrator",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "description": "Move pnpm settings from `pnpm` field in `package.json` and `.npmrc` file to `pnpm-workspace.yaml`",
5
5
  "keywords": [
6
6
  "migrator",
@@ -44,7 +44,7 @@
44
44
  "dependencies": {
45
45
  "@ntnyq/utils": "^0.23.0",
46
46
  "@pnpm/types": "^1102.1.1",
47
- "cac": "^7.0.0",
47
+ "cac": "^7.1.0",
48
48
  "camelcase-keys": "^10.0.3",
49
49
  "consola": "^3.4.2",
50
50
  "defu": "^6.1.7",
@@ -56,17 +56,17 @@
56
56
  "yaml": "^2.9.1"
57
57
  },
58
58
  "devDependencies": {
59
- "@types/node": "^26.6.4",
59
+ "@types/node": "^26.6.5",
60
60
  "bumpp": "^12.3.0",
61
61
  "husky": "^9.1.7",
62
62
  "nano-staged": "^1.0.2",
63
63
  "npm-run-all2": "^9.0.3",
64
- "oxfmt": "^0.72.0",
65
- "oxlint": "^1.87.0",
66
- "tsdown": "^0.23.0",
64
+ "oxfmt": "^0.73.0",
65
+ "oxlint": "^1.88.0",
66
+ "tsdown": "^0.23.1",
67
67
  "tsx": "^4.23.15",
68
68
  "typescript": "^7.0.2",
69
- "vitest": "^5.0.3"
69
+ "vitest": "^5.0.4"
70
70
  },
71
71
  "nano-staged": {
72
72
  "*.{js,ts,mjs,tsx}": "oxlint --fix",