* Make a javascript object immutable (assuring it cannot be changed from the current value) * * All attributes of that object are made immutable, including properties of contained * objects, recursively. * * This operation cannot be undone. * * Example: * *
(object)
| 157 | * @return object {Object} - The original object is returned - for chaining. |
| 158 | */ |
| 159 | static makeImmutable(object) { |
| 160 | if (Buffer.isBuffer(object)) { |
| 161 | return object; |
| 162 | } |
| 163 | |
| 164 | for (let propertyName of Object.keys(object)) { |
| 165 | let value = object[propertyName]; |
| 166 | |
| 167 | if (value instanceof RawConfig) { |
| 168 | Object.defineProperty(object, propertyName, { |
| 169 | value: value.resolve(), |
| 170 | writable: false, |
| 171 | configurable: false |
| 172 | }); |
| 173 | } else if (Array.isArray(value)) { |
| 174 | // Ensure object items of this array are also immutable. |
| 175 | for (let item of value) { |
| 176 | if (this.isObject(item) || Array.isArray(item)) { |
| 177 | this.makeImmutable(item); |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | Object.defineProperty(object, propertyName, { |
| 182 | value: Object.freeze(value) |
| 183 | }); |
| 184 | } else { |
| 185 | // Call recursively if an object. |
| 186 | if (this.isObject(value)) { |
| 187 | // Create a proxy, to capture user updates of configuration options, and throw an exception for awareness, as per: |
| 188 | // https://github.com/lorenwest/node-config/issues/514 |
| 189 | value = new Proxy(this.makeImmutable(value), { |
| 190 | get(target, property, receiver) { |
| 191 | // Bypass proxy receiver for properties directly on the target (e.g., RegExp.prototype.source) |
| 192 | // or properties that are not functions to prevent errors related to internal object methods. |
| 193 | if (Object.hasOwn(target, property) || (property in target && typeof target[property] !== 'function')) { |
| 194 | return Reflect.get(target, property); |
| 195 | } |
| 196 | |
| 197 | // Otherwise, use the proxy receiver to handle the property access |
| 198 | const ref = Reflect.get(target, property, receiver); |
| 199 | |
| 200 | // Binds the method's `this` context to the target object (e.g., Date.prototype.toISOString) |
| 201 | // to ensure it behaves correctly when called on the proxy. |
| 202 | if (typeof ref === 'function') { |
| 203 | return ref.bind(target); |
| 204 | } |
| 205 | return ref; |
| 206 | }, |
| 207 | set(target, name) { |
| 208 | const message = (Reflect.has(target, name) ? 'update' : 'add'); |
| 209 | // Notify the user. |
| 210 | throw Error(`Can not ${message} runtime configuration property: "${name}". Configuration objects are immutable unless ALLOW_CONFIG_MUTATIONS is set.`) |
| 211 | } |
| 212 | }); |
| 213 | } |
| 214 | |
| 215 | // Check if property already has writable: false and configurable: false |
| 216 | const currentDescriptor = Object.getOwnPropertyDescriptor(object, propertyName); |
no test coverage detected