Not elegant, but I wonder whether existing Layer Masks may merit consideration.
Edited: updated to the code to exclude Shape Layers with Strokes


// change shape layer vector masks to layer masks;
// 2024, use it at your own risk;
if (app.documents.length > 0) {
var theLayers = collectShapeLayers();
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
// process layers;
for (var m = 0; m < theLayers.length; m++) {
// if layer has no stroke;
if (theLayers[m][3] == false) {
selectLayerByID(theLayers[m][1], false);
// load as selection, apply as mask and delete vector mask;
var idchannel = stringIDToTypeID( "channel" );
var idpath = stringIDToTypeID( "path" );
var idnull = stringIDToTypeID( "null" );
var idvectorMask = stringIDToTypeID( "vectorMask" );
// =======================================================
var desc5 = new ActionDescriptor();
var ref1 = new ActionReference();
ref1.putProperty( idchannel, stringIDToTypeID( "selection" ) );
desc5.putReference( idnull, ref1 );
var ref2 = new ActionReference();
ref2.putEnumerated( idpath, idpath, idvectorMask );
ref2.putEnumerated( stringIDToTypeID( "layer" ), stringIDToTypeID( "ordinal" ), stringIDToTypeID( "targetEnum" ) );
desc5.putReference( stringIDToTypeID( "to" ), ref2 );
desc5.putInteger( stringIDToTypeID( "version" ), 1 );
desc5.putBoolean( stringIDToTypeID( "vectorMaskParams" ), true );
executeAction( stringIDToTypeID( "set" ), desc5, DialogModes.NO );
if (theLayers[m][2] == false) {
var desc7 = new ActionDescriptor();
desc7.putClass( stringIDToTypeID( "new" ), idchannel );
var ref3 = new ActionReference();
var idmask = stringIDToTypeID( "mask" );
ref3.putEnumerated( idchannel, idchannel, idmask );
desc7.putReference( stringIDToTypeID( "at" ), ref3 );
desc7.putEnumerated( stringIDToTypeID( "using" ), stringIDToTypeID( "userMaskEnabled" ), stringIDToTypeID( "revealSelection" ) );
executeAction( stringIDToTypeID( "make" ), desc7, DialogModes.NO );
} else {
var desc10 = new ActionDescriptor();
var ref6 = new ActionReference();
ref6.putEnumerated( idchannel, idchannel, stringIDToTypeID( "mask" ) );
desc10.putReference( stringIDToTypeID( "null" ), ref6 );
var idmakeVisible = stringIDToTypeID( "makeVisible" );
desc10.putBoolean( idmakeVisible, false );
executeAction( stringIDToTypeID( "select" ), desc10, DialogModes.NO );
// =======================================================
executeAction( stringIDToTypeID( "inverse" ), undefined, DialogModes.NO );
// =======================================================
var desc18 = new ActionDescriptor();
var ref8 = new ActionReference();
ref8.putProperty( stringIDToTypeID( "color" ), stringIDToTypeID( "colors" ) );
desc18.putReference( stringIDToTypeID( "null" ), ref8 );
executeAction( stringIDToTypeID( "reset" ), desc18, DialogModes.NO );
// =======================================================
var desc5 = new ActionDescriptor();
var ref2 = new ActionReference();
ref2.putProperty( stringIDToTypeID( "color" ), stringIDToTypeID( "colors" ) );
desc5.putReference( stringIDToTypeID( "null" ), ref2 );
executeAction( stringIDToTypeID( "exchange" ), desc5, DialogModes.NO );
// =======================================================
var desc13 = new ActionDescriptor();
desc13.putEnumerated( stringIDToTypeID( "using" ), stringIDToTypeID( "fillContents" ), stringIDToTypeID( "foregroundColor" ) );
desc13.putUnitDouble( stringIDToTypeID( "opacity" ),stringIDToTypeID( "percentUnit" ), 100.000000 );
desc13.putEnumerated( stringIDToTypeID( "mode" ), stringIDToTypeID( "blendMode" ), stringIDToTypeID( "normal" ) );
executeAction( stringIDToTypeID( "fill" ), desc13, DialogModes.NO );
};
// =======================================================
var desc9 = new ActionDescriptor();
var ref4 = new ActionReference();
ref4.putEnumerated( idpath, idpath, idvectorMask );
ref4.putEnumerated( stringIDToTypeID( "layer" ), stringIDToTypeID( "ordinal" ), stringIDToTypeID( "targetEnum" ) );
desc9.putReference( idnull, ref4 );
executeAction( stringIDToTypeID( "delete" ), desc9, DialogModes.NO );
};
};
// reset;
app.preferences.rulerUnits = originalRulerUnits;
};
////////////////////////////////////
////// collect shape layers //////
function collectShapeLayers () {
// get number of layers;
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
var applicationDesc = executeActionGet(ref);
var theNumber = applicationDesc.getInteger(stringIDToTypeID("numberOfLayers"));
// process the layers;
var theLayers = new Array;
for (var m = 0; m <= theNumber; m++) {
try {
var ref = new ActionReference();
ref.putIndex( charIDToTypeID( "Lyr " ), m);
var layerDesc = executeActionGet(ref);
var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
var isBackground = layerDesc.getBoolean(stringIDToTypeID("background"));
// if not layer group collect values;
if (layerSet != "layerSectionEnd" && layerSet != "layerSectionStart" && isBackground != true) {
var theName = layerDesc.getString(stringIDToTypeID('name'));
var theID = layerDesc.getInteger(stringIDToTypeID('layerID'));
var theIndex = layerDesc.getInteger(stringIDToTypeID('itemIndex'));
var theKind = layerDesc.getInteger(stringIDToTypeID('layerKind'));
var hasLayerMask = layerDesc.hasKey(charIDToTypeID("Usrs"));
var hasStroke = layerDesc.hasKey(stringIDToTypeID("AGMStrokeStyleInfo"));
if (hasStroke == true) {hasStroke = layerDesc.getObjectValue(stringIDToTypeID("AGMStrokeStyleInfo")).getBoolean(stringIDToTypeID("strokeEnabled"))};
if (theKind == 4) {
theLayers.push([theName, theID, hasLayerMask, hasStroke])
}
};
}
catch (e) {};
};
return theLayers;
};
////// based on code by mike hale, via paul riggott //////
function selectLayerByID(id,add){
add = undefined ? add = false:add
var ref = new ActionReference();
ref.putIdentifier(charIDToTypeID("Lyr "), id);
var desc = new ActionDescriptor();
desc.putReference(charIDToTypeID("null"), ref );
if(add) desc.putEnumerated( stringIDToTypeID( "selectionModifier" ), stringIDToTypeID( "selectionModifierType" ), stringIDToTypeID( "addToSelection" ) );
desc.putBoolean( charIDToTypeID( "MkVs" ), false );
try{
executeAction(charIDToTypeID("slct"), desc, DialogModes.NO );
}catch(e){
alert(e.message);
}
};