@hyperfrontend/immutable-api-utilsView on npm →@hyperfrontend/immutable-api-utils
Decorators and utilities for creating immutable, tamper-proof object APIs.
What is @hyperfrontend/immutable-api-utils?
@hyperfrontend/immutable-api-utils provides low-level utilities for locking object properties and methods to prevent modification. Using JavaScript's property descriptors (Object.defineProperty), it creates truly immutable APIs where neither values nor method bindings can be altered after definition.
The library offers three approaches: a TypeScript decorator (@locked()) for class methods, a functional API (lockedProps()) for bulk property locking, and descriptor builders (lockedPropertyDescriptors()) for granular control. All utilities enforce non-writable, non-configurable descriptors while maintaining correct this binding through per-instance caching.
Key Features
@locked()decorator for TypeScript classes—prevents method overwriting and ensures correctthisbinding- Bulk property locking via
lockedProps()for multiple properties in one call - Property descriptor creation with
lockedPropertyDescriptors()for custom locking patterns - Per-instance binding cache to avoid repeated
.bind()calls - Zero runtime dependencies - pure JavaScript property descriptor manipulation
Architecture Highlights
The @locked() decorator uses Symbol-based caching to store bound methods per instance, avoiding the performance cost of repeated .bind() calls. Properties are marked configurable: false to prevent deletion or descriptor modification, and writable: false to block reassignment.
Why Use @hyperfrontend/immutable-api-utils?
Prevents Accidental API Tampering in Shared Contexts
When exposing objects to third-party code, plugin systems, or sandboxed environments, you need guarantees that critical methods won't be overwritten. @locked() makes methods truly immutable—attempting reassignment throws a TypeError. This protects public APIs from accidental or malicious modification.
Eliminates this Binding Bugs Without Arrow Functions
Arrow functions in class fields break inheritance and bloat bundle sizes due to per-instance function creation. The @locked() decorator provides correct this binding (like arrow functions) while using efficient prototype methods. Methods are bound once per instance and cached with Symbol keys.
Simplifies Immutable Object Construction
Building frozen objects with Object.freeze() is shallow and doesn't prevent descriptor modification. lockedProps() provides deep immutability for specific properties while allowing controlled mutability elsewhere—ideal for partially frozen configs or API surfaces.
TypeScript-First with Runtime Enforcement
Unlike TypeScript readonly (compile-time only), these utilities enforce immutability at runtime. This catches bugs in JavaScript-land, during deserialization, or when interfacing with dynamically typed code. Type safety and runtime safety in one decorator.
Installation
npm install @hyperfrontend/immutable-api-utils
Quick Start
import { locked, lockedProps, lockedPropertyDescriptors } from '@hyperfrontend/immutable-api-utils'
// Decorator usage: lock methods in classes
class Counter {
private count = 0
@locked()
increment() {
this.count++
return this.count
}
@locked()
getValue() {
return this.count
}
}
const counter = new Counter()
counter.increment() // Works: 1
counter.increment = () => 0 // Throws: Cannot overwrite locked method
// Ensure correct `this` binding even when method is extracted
const { increment } = counter
increment() // Still works correctly, `this` remains bound
// Functional API: lock multiple properties
const config = {}
lockedProps(config, [
['apiKey', 'secret-key-12345'],
['timeout', 5000],
['retries', 3],
])
config.apiKey = 'hacked' // Silent fail in non-strict mode, throws in strict mode
Object.defineProperty(config, 'apiKey', { writable: true }) // Throws: cannot redefine
// Low-level descriptor creation
const obj = {}
Object.defineProperty(obj, 'version', lockedPropertyDescriptors('1.0.0', true))
// Property is non-writable, non-configurable, but enumerable
API Overview
Decorator
@locked()- TypeScript decorator that makes class methods immutable with correctthisbinding
Functions
lockedProps(object, pairs)- Lock multiple properties on an object with key-value pairslockedPropertyDescriptors(value, enumerable?)- Create a locked property descriptor for manual use withObject.defineProperty
Use Cases
- Plugin APIs: Prevent plugins from modifying core library methods
- Sandboxed execution: Expose safe APIs to untrusted code
- Configuration objects: Lock critical config values after initialization
- Public library interfaces: Protect exported classes from mutation
- Event emitters: Prevent handler list manipulation
- Prototype pollution defense: Make critical prototypes tamper-proof
Compatibility
| Platform | Support |
|---|---|
| Browser | ✅ |
| Node.js | ✅ |
| Web Workers | ✅ |
| Deno, Bun, Cloudflare Workers | ✅ |
Output Formats
| Format | File | Tree-Shakeable |
|---|---|---|
| ESM | index.esm.js |
✅ |
| CJS | index.cjs.js |
❌ |
| IIFE | bundle/index.iife.min.js |
❌ |
| UMD | bundle/index.umd.min.js |
❌ |
Bundle size: < 1 KB (minified, self-contained)
CDN Usage
<!-- unpkg -->
<script src="https://unpkg.com/@hyperfrontend/immutable-api-utils"></script>
<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@hyperfrontend/immutable-api-utils"></script>
<script>
const { immutableApi, freezeClass, sealClass } = HyperfrontendImmutableApiUtils
</script>
Global variable: HyperfrontendImmutableApiUtils
Dependencies
None — zero external dependencies.
Part of hyperfrontend
This library is part of the hyperfrontend monorepo.
License
MIT
API Reference
ƒFunctions
locked(): LockedMethod
Creates a decorator that locks a method to prevent overwriting and ensure correct `this` binding.
Returns
LockedMethodA method decorator that locks the method◆Types
LockedMethod
type LockedMethod = (target: object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<any>) => anyLockedPropDescriptorsCreator
type LockedPropDescriptorsCreator = (value: unknown, enumerable?: boolean) => PropertyDescriptorPropertyLock
type PropertyLock = (object: object, propertyValuePairs: [string, any][]) => void●Variables
lockedPropertyDescriptorsLockedPropDescriptorsCreator...lockedPropsPropertyLock...