From using immer[1], I learned about Object.freeze(), which immer uses under the hood.
JavaScript doesn't provide a nice way to extend the language syntax, unlike some other programming languages. So most DSLs end up looking like imperative code but do pure mutations underneath. I understand, for the reader it's confusing, because unless you know that it's a DSL you never know whether the mutations are pure or not.
Compare that with Haskell, with its support for custom operators, allows for some quite succinct code to express deep mutations (cf. lens library)