@@ -534,9 +534,10 @@ describe('pnpm min-release-age behavior', () => {
534534 } ) ;
535535
536536 // readPnpmPolicy reads pnpm's resolved config via `pnpm config list --json`,
537- // so mock that single spawn rather than any config surface. Keys use pnpm's
538- // kebab-case form; an exclude array mirrors a yaml surface, a comma-joined
539- // string mirrors .npmrc / env. pnpm itself decides which surface won.
537+ // so mock that single spawn rather than any config surface. pnpm 11 reports
538+ // keys camelCase, pnpm 10 kebab-case; each test mocks the form its version
539+ // emits. An exclude array mirrors a yaml surface, a comma-joined string
540+ // mirrors .npmrc / env. pnpm itself decides which surface won.
540541 function mockPnpmConfig ( config : Record < string , unknown > | 'throw' ) {
541542 jest
542543 . spyOn ( require ( 'child_process' ) , 'execSync' )
@@ -633,7 +634,7 @@ describe('pnpm min-release-age behavior', () => {
633634 ) ;
634635
635636 it ( 'v11 >=11.0.4 explicit window auto-enables strict' , async ( ) => {
636- mockPnpmConfig ( { 'minimum-release-age' : 2880 } ) ;
637+ mockPnpmConfig ( { minimumReleaseAge : 2880 } ) ;
637638 const result = await readPnpmPolicy ( '/root' , '11.0.4' ) ;
638639 expect ( result . outcome ) . toBe ( 'active' ) ;
639640 if ( result . outcome === 'active' ) {
@@ -645,8 +646,8 @@ describe('pnpm min-release-age behavior', () => {
645646
646647 it ( 'v11 >=11.0.4 explicit strict:false stays loose' , async ( ) => {
647648 mockPnpmConfig ( {
648- 'minimum-release-age' : 2880 ,
649- 'minimum-release-age-strict' : false ,
649+ minimumReleaseAge : 2880 ,
650+ minimumReleaseAgeStrict : false ,
650651 } ) ;
651652 const result = await readPnpmPolicy ( '/root' , '11.0.4' ) ;
652653 expect ( result . outcome ) . toBe ( 'active' ) ;
@@ -656,7 +657,7 @@ describe('pnpm min-release-age behavior', () => {
656657 } ) ;
657658
658659 it ( 'v11.0.0 explicit window does NOT auto-enable strict' , async ( ) => {
659- mockPnpmConfig ( { 'minimum-release-age' : 2880 } ) ;
660+ mockPnpmConfig ( { minimumReleaseAge : 2880 } ) ;
660661 const result = await readPnpmPolicy ( '/root' , '11.0.0' ) ;
661662 expect ( result . outcome ) . toBe ( 'active' ) ;
662663 if ( result . outcome === 'active' ) {
@@ -665,7 +666,7 @@ describe('pnpm min-release-age behavior', () => {
665666 } ) ;
666667
667668 it ( 'v11.1.3+ writesExcludes true' , async ( ) => {
668- mockPnpmConfig ( { 'minimum-release-age' : 1440 } ) ;
669+ mockPnpmConfig ( { minimumReleaseAge : 1440 } ) ;
669670 const result = await readPnpmPolicy ( '/root' , '11.1.3' ) ;
670671 expect ( result . outcome ) . toBe ( 'active' ) ;
671672 if ( result . outcome === 'active' ) {
@@ -674,7 +675,7 @@ describe('pnpm min-release-age behavior', () => {
674675 } ) ;
675676
676677 it ( 'v11.1.2 writesExcludes false' , async ( ) => {
677- mockPnpmConfig ( { 'minimum-release-age' : 1440 } ) ;
678+ mockPnpmConfig ( { minimumReleaseAge : 1440 } ) ;
678679 const result = await readPnpmPolicy ( '/root' , '11.1.2' ) ;
679680 expect ( result . outcome ) . toBe ( 'active' ) ;
680681 if ( result . outcome === 'active' ) {
@@ -685,8 +686,8 @@ describe('pnpm min-release-age behavior', () => {
685686 // pnpm reports the resolved exclude as a JSON array (set in a yaml surface).
686687 it ( 'honors an exclude array from pnpm config' , async ( ) => {
687688 mockPnpmConfig ( {
688- 'minimum-release-age' : 1440 ,
689- 'minimum-release-age-exclude' : [ 'pkg-a' , 'pkg-b' ] ,
689+ minimumReleaseAge : 1440 ,
690+ minimumReleaseAgeExclude : [ 'pkg-a' , 'pkg-b' ] ,
690691 } ) ;
691692 const result = await readPnpmPolicy ( '/root' , '11.5.2' ) ;
692693 expect ( result . outcome ) . toBe ( 'active' ) ;
@@ -717,15 +718,15 @@ describe('pnpm min-release-age behavior', () => {
717718 // union) is a version-dependent landmine; nx defers rather than crash.
718719 it ( 'invalid exclude entry -> ambiguous (defer to install)' , async ( ) => {
719720 mockPnpmConfig ( {
720- 'minimum-release-age' : 1440 ,
721- 'minimum-release-age-exclude' : [ 'pkg-a@^1.0.0' ] ,
721+ minimumReleaseAge : 1440 ,
722+ minimumReleaseAgeExclude : [ 'pkg-a@^1.0.0' ] ,
722723 } ) ;
723724 const result = await readPnpmPolicy ( '/root' , '11.5.2' ) ;
724725 expect ( result . outcome ) . toBe ( 'ambiguous' ) ;
725726 } ) ;
726727
727728 it ( 'v11 ignoreMissingTime defaults to skip; explicit false errors' , async ( ) => {
728- mockPnpmConfig ( { 'minimum-release-age' : 1440 } ) ;
729+ mockPnpmConfig ( { minimumReleaseAge : 1440 } ) ;
729730 let result = await readPnpmPolicy ( '/root' , '11.5.2' ) ;
730731 expect ( result . outcome ) . toBe ( 'active' ) ;
731732 if ( result . outcome === 'active' ) {
@@ -735,8 +736,8 @@ describe('pnpm min-release-age behavior', () => {
735736 }
736737
737738 mockPnpmConfig ( {
738- 'minimum-release-age' : 1440 ,
739- 'minimum-release-age-ignore-missing-time' : false ,
739+ minimumReleaseAge : 1440 ,
740+ minimumReleaseAgeIgnoreMissingTime : false ,
740741 } ) ;
741742 result = await readPnpmPolicy ( '/root' , '11.5.2' ) ;
742743 expect ( result . outcome ) . toBe ( 'active' ) ;
@@ -746,6 +747,40 @@ describe('pnpm min-release-age behavior', () => {
746747 ) ;
747748 }
748749 } ) ;
750+
751+ // pnpm 11 reports config keys camelCase via `config list --json`; pnpm 10
752+ // reported them kebab-case. Reading only the kebab form dropped every
753+ // explicitly-set value on pnpm 11, so the window fell back to the built-in
754+ // 1440 default (gh-36330).
755+ it ( 'honors a camelCase window from pnpm 11 (auto-enables strict)' , async ( ) => {
756+ mockPnpmConfig ( { minimumReleaseAge : 60 } ) ;
757+ const result = await readPnpmPolicy ( '/root' , '11.13.0' ) ;
758+ expect ( result . outcome ) . toBe ( 'active' ) ;
759+ if ( result . outcome === 'active' ) {
760+ expect ( result . policy . windowMs ) . toBe ( 60 * MINUTE ) ;
761+ const behavior = pnpmBehavior ( result . policy . behavior ) ;
762+ expect ( behavior . strict ) . toBe ( true ) ;
763+ expect ( behavior . looseFallback ) . toBe ( false ) ;
764+ }
765+ } ) ;
766+
767+ it ( 'honors camelCase exclude, strict, and ignoreMissingTime from pnpm 11' , async ( ) => {
768+ mockPnpmConfig ( {
769+ minimumReleaseAge : 2880 ,
770+ minimumReleaseAgeExclude : [ 'pkg-a' ] ,
771+ minimumReleaseAgeStrict : false ,
772+ minimumReleaseAgeIgnoreMissingTime : false ,
773+ } ) ;
774+ const result = await readPnpmPolicy ( '/root' , '11.13.0' ) ;
775+ expect ( result . outcome ) . toBe ( 'active' ) ;
776+ if ( result . outcome === 'active' ) {
777+ expect ( result . policy . windowMs ) . toBe ( 2880 * MINUTE ) ;
778+ expect ( result . policy . isExcluded ( 'pkg-a' , '2.0.0' ) ) . toBe ( true ) ;
779+ const behavior = pnpmBehavior ( result . policy . behavior ) ;
780+ expect ( behavior . strict ) . toBe ( false ) ;
781+ expect ( behavior . missingTimeMap ) . toBe ( 'error' ) ;
782+ }
783+ } ) ;
749784 } ) ;
750785
751786 describe ( 'NO_MATURE release-age wording (pnpm v11 formatTimeAgo buckets)' , ( ) => {
0 commit comments