If you want to automate cropping based on your current/any selection do know that the sequence of direction properties of app.activeDocument.selection.bounds are in a different order than the four otherwise-the-same parameters of activeDocument.crop()

//ƒ async crop(e,t=0,r=0,n=0){validateDocument(this);const a=[“left”,“right”,“bottom”,“top”]
// Bounds {_left: 48, _top: 16, _bottom: 1221, _right: 656}

i initially jotted down .crop(b[0], b[1], b[2], b[3]) because it made sense for them to line up like that but this is what the developer console is reporting at the present moment

I was just looking at crop, thinking bounds should be optional. In the case of absent bounds, it should act on the current selection bounds – just like how it works via the UI.

We really need to add an example for crop. The first argument is the Bounds object.
.crop(b) in your example.

//ƒ async crop(e,t=0,r=0,n=0){> validateDocument(this);const a=[“left”,“right”,“bottom”,“top”]

// Bounds {_left: 48, _top: 16, _bottom: 1221, _right: 656}

const a = app.activeDocument;
const b = a.selection.bounds;
if (b) {
a.crop([b[0], b[3], b[2], b[1]]);
} else {
alert(“Please make a selection before running the script.”);

only because it was invited but i wasnt debugging, & it works as it is meant to when i used it. it just bothers me why, how, & that the different argument pattern exists.