@hyperfrontend/ immutable-api-utils§
Decorators and utilities for creating immutable, tamper-proof object APIs with built-in prototype pollution defense.
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.
Additionally, the library provides safe built-in copies: pre-captured references to JavaScript built-in methods that mitigate prototype pollution attacks when loaded early.
Key Features
@locked()decoratorfor TypeScript classesprevents method overwriting and ensures correct
thisbindingvia
lockedProps()for multiple properties in one callwith
lockedPropertyDescriptors()for custom locking patternsSafe built-in copiesvia secondary entrypoints
captured at module load time before any pollution can occur
Per-instance binding cache
to avoid repeated
.bind()callsZero runtime dependencies
pure JavaScript property descriptor manipulation
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
Object.freeze() is all or nothing: every own property goes read-only, including the ones you meant to keep mutable. lockedProps() locks only the properties you name, non-writable and non-configurable, so they cannot be reassigned, redefined or deleted while the rest of the object stays ordinary. It is shallow, like Object.freeze(): a locked property holding an object still hands out an object you can mutate.
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
// Throws: cannot redefine
Object.defineProperty(config, 'apiKey', { writable: true })
// 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
Two jobs share one mechanism, the property descriptor. One half locks the properties of an object you own; the other half captures the built-ins before anything else can swap them. Every module under built-in-copy/ reads its global exactly once, while it is being evaluated, and re-exports what it found as a named binding: built-in-copy/object reads globalThis.Object, so its keys is the function value Object.keys held at that instant, and a later write to Object.keys lands on the global while the binding goes on answering correctly. parse does the same for JSON.parse.
The limit is worth stating plainly: this mitigates rather than prevents, and import order decides whether it works at all. A module graph evaluates in source order, so a copy is honest only when its import sits above the code that tampers. Put the untrusted import first and the capture reads an already poisoned global, returning exactly the wrong answer the global would. Treat these as entry-point imports:
// these two lines have to be evaluated first
import { keys, hasOwn } from '@hyperfrontend/immutable-api-utils/built-in-copy/object'
import { parse } from '@hyperfrontend/immutable-api-utils/built-in-copy/json'
import './vendor/analytics.js' // whatever this does to Object.keys, keys() is unaffected
There are 24 of these subpaths, each named after the global or family of globals it copies, so the one you want is spelled like the thing you were reaching for: built-in-copy/timers holds setTimeout and queueMicrotask, built-in-copy/console holds log and warn. Members that need a receiver come through as wrappers (hasOwn applies the captured Object.prototype.hasOwnProperty through a captured Reflect.apply), and constructors as create* factories such as createMap. The package root re-exports frozen namespace objects for many of these globals, which reads nicely but pulls in the whole namespace; named bindings from a subpath are what keep the rest out of your bundle.
Three entries do the locking, and they stack. lockedPropertyDescriptors is the primitive: hand it a value and it returns { value, writable: false, configurable: false, enumerable } for you to pass to Object.defineProperty. lockedProps applies that across a list, taking the target object and an array of [key, value] pairs, and returns nothing: it mutates the object you handed it. locked is the class-method decorator and the odd one out, installing an accessor whose getter binds the method to the instance once and caches it under a symbol, and whose setter throws a TypeError.
Every export, on every subpath, is in the API reference.
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 mitigation: Safe built-in copies reduce attack surface when loaded early
- Secure logging: Use safe
consolecopies to prevent tampered log output - Safe timers: Prevent timer functions from being hijacked
- Cross-origin messaging: Secure
postMessagewrappers with captured references
Compatibility
Runs on
- Node.js>=18.0.0supported
- Browserssupported
- Web Workerssupported
built-in-copy/websocket loads without a WebSocket global (Node.js before 22); only construction throws there.
Ships as
- ESMTree-shakeable
- CJSNode and older bundlers
- IIFEHyperfrontendImmutableApiUtils
- UMDHyperfrontendImmutableApiUtils
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 { locked, lockedProps } = HyperfrontendImmutableApiUtils
const { lockedPropertyDescriptors } = HyperfrontendImmutableApiUtils
</script>
Global variable: HyperfrontendImmutableApiUtils
Dependencies
None: zero external dependencies.
Architecture Highlights§
A locked property is defined writable: false and configurable: false, so it cannot be reassigned, deleted, or redefined afterwards. Reassignment fails silently in sloppy mode and throws a TypeError in strict mode, which includes every ES module.
The safe built-in copies are captured at module initialization time. Important: This only works if the module loads before any malicious code runs; it mitigates pollution, not prevents it retroactively.
API Reference§
Module Structure
28 modules · 288 total exports
Tamper-proof copies of the JavaScript built-ins, and the decorators and helpers that lock an object's own properties and descriptors.
ƒ Functions
this binding. Ensures a classic prototype method cannot be overwritten and is always called with the correct
this instance without needing arrow functions. - The method of the prototype is non-configurable and non-enumerable.
- Any attempt to assign to the method throws an error.
- It does not support class fields / arrow functions.
Returns
LockedMethodExample
Locking a class method
class Counter {
private count = 0
\@locked()
increment() {
return ++this.count
}
}◆ Types
type LockedMethod = ( target: object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor< any>) => anytype LockedPropDescriptorsCreator = ( value: unknown, enumerable?: boolean) => PropertyDescriptortype PropertyLock = ( object: object, propertyValuePairs: [ string, any][ ]) => void● Variables
Example
Creating locked property descriptor
const descriptor = lockedPropertyDescriptors('immutable', true)
// => { value: 'immutable', writable: false, configurable: false, enumerable: true }
Object.defineProperty(obj, 'key', descriptor)Example
Locking multiple properties
const config = {}
lockedProps(config, [
['apiUrl', 'https://api.example.com'],
['version', '1.0.0'],
])
config.apiUrl = 'hacked' // throws in strict mode, silently fails otherwiseSafe copies of Array built-in static methods. These references are captured at module initialization time to protect against prototype pollution attacks. Import only what you need for tree-shaking.
● Variables
Safe copies of Console built-in methods. These references are captured at module initialization time to protect against prototype pollution attacks. Import only what you need for tree-shaking.
● Variables
Safe copies of Date built-in via factory function and static methods.
ƒ Functions
● Variables
Example
const timestamp = dateNow()
// => 1704067200000 (example timestamp)Example
const timestamp = dateParse('2024-01-01T00:00:00Z')
// => 1704067200000Example
const timestamp = dateUTC(2024, 0, 1, 12, 0, 0)
// => 1704110400000 (Jan 1, 2024 12:00:00 UTC)Safe copies of encoding built-ins for TextEncoder, TextDecoder, atob, and btoa.
ƒ Functions
atob().Parameters
| Name | Type | Description |
|---|---|---|
§data | string | The base64-encoded string to decode. |
Returns
stringbtoa().Parameters
| Name | Type | Description |
|---|---|---|
§data | string | The string to encode. |
Returns
stringnew TextDecoder().Parameters
Returns
TextDecodernew TextEncoder().Returns
TextEncoder● Variables
Safe copies of the Error built-ins, captured at load so later prototype pollution cannot reach them. Constructors arrive as factory functions, which is the only way to capture a constructor safely.
ƒ Functions
createAggregateError( errors: Iterable< unknown>, message?: string, options?: ErrorOptions): AggregateError
new AggregateError().Parameters
| Name | Type | Description |
|---|---|---|
§errors | Iterable< | An iterable of errors to aggregate. |
§message? | string | Optional error message. |
§options? | ErrorOptions | Optional error options. |
Returns
AggregateErrorExample
Creating AggregateError instance
const results = await Promise.allSettled(promises)
const failures = results.filter(r => r.status === 'rejected').map(r => r.reason)
if (failures.length > 0) {
throw createAggregateError(failures, 'Multiple operations failed')
}new Error().Parameters
| Name | Type | Description |
|---|---|---|
§message? | string | Optional error message. |
§options? | ErrorOptions | Optional error options. |
Returns
ErrorExample
Creating Error instances
const error = createError('Operation failed')
// With cause for error chaining
const wrapped = createError('Request failed', { cause: originalError })new EvalError().Parameters
| Name | Type | Description |
|---|---|---|
§message? | string | Optional error message. |
§options? | ErrorOptions | Optional error options. |
Returns
EvalErrorExample
Creating EvalError instance
throw createEvalError('Dynamic code execution is not allowed')new RangeError().Parameters
| Name | Type | Description |
|---|---|---|
§message? | string | Optional error message. |
§options? | ErrorOptions | Optional error options. |
Returns
RangeErrorExample
Creating RangeError instance
if (index < 0 || index >= array.length) {
throw createRangeError(`Index ${index} out of bounds`)
}new ReferenceError().Parameters
| Name | Type | Description |
|---|---|---|
§message? | string | Optional error message. |
§options? | ErrorOptions | Optional error options. |
Returns
ReferenceErrorExample
Creating ReferenceError instance
if (!(key in registry)) {
throw createReferenceError(`Unknown key: ${key}`)
}new SyntaxError().Parameters
| Name | Type | Description |
|---|---|---|
§message? | string | Optional error message. |
§options? | ErrorOptions | Optional error options. |
Returns
SyntaxErrorExample
Creating SyntaxError instance
if (!isValidJson(input)) {
throw createSyntaxError('Invalid JSON format')
}new TypeError().Parameters
| Name | Type | Description |
|---|---|---|
§message? | string | Optional error message. |
§options? | ErrorOptions | Optional error options. |
Returns
TypeErrorExample
Creating TypeError instance
if (typeof value !== 'string') {
throw createTypeError('Expected a string')
}new URIError().Parameters
| Name | Type | Description |
|---|---|---|
§message? | string | Optional error message. |
§options? | ErrorOptions | Optional error options. |
Returns
URIErrorExample
Creating URIError instance
if (!isValidUtf8(encoded)) {
throw createURIError('Malformed URI sequence')
}● Variables
Safe copies of Function constructor and prototype methods.
ƒ Functions
new Function(). NOTE: Dynamic code generation via Function constructor should be avoided when possible due to security risks similar to eval().
Parameters
| Name | Type | Description |
|---|---|---|
§...args | string[ | Function body and parameter strings. |
Returns
( args: unknown[ ]) => unknown● Variables
Safe copies of JSON built-in methods. These references are captured at module initialization time to protect against prototype pollution attacks. Import only what you need for tree-shaking.
● Variables
Safe Map factory with optional groupBy for ES2024+.
ƒ Functions
new Map().Parameters
Returns
Map< K, V>● Variables
Safe copies of Math built-in methods. These references are captured at module initialization time to protect against prototype pollution attacks. Import only what you need for tree-shaking.
● Variables
Safe copies of the messaging built-ins, captured at load so later prototype pollution cannot reach them: factories for MessageChannel and BroadcastChannel, whose instances carry a safe `postMessage`.
ƒ Functions
Parameters
| Name | Type | Description |
|---|---|---|
§channel | BroadcastChannel | Named broadcast channel for same-origin contexts. |
§message | unknown | The data payload to send, must be structured-cloneable. |
Example
Broadcasting to channel
const channel = new BroadcastChannel('app-updates')
postMessageToBroadcast(channel, { type: 'cache-invalidate' })Parameters
| Name | Type | Description |
|---|---|---|
§port | MessagePort | One end of a two-way communication channel. |
§message | unknown | The data payload to send, must be structured-cloneable. |
§transfer? | Transferable[ | Optional array of Transferable objects. |
Example
Posting message to MessagePort
const channel = new MessageChannel()
postMessageToPort(channel.port1, { status: 'ready' })postMessageToWindow( target: Window, message: unknown, options: PostMessageToWindowOptions): void
Parameters
| Name | Type | Description |
|---|---|---|
§target | Window | Destination window, iframe, or popup context. |
§message | unknown | The data payload to send, must be structured-cloneable. |
§options | PostMessageToWindowOptions | Target origin and optional transferables. |
Example
Posting message to Window
const iframe = document.querySelector('iframe')
postMessageToWindow(iframe.contentWindow, { action: 'refresh' }, {
targetOrigin: 'https://trusted.example.com',
})Parameters
| Name | Type | Description |
|---|---|---|
§target | Worker | Web Worker or service worker instance. |
§message | unknown | The data payload to send, must be structured-cloneable. |
§transfer? | Transferable[ | Optional array of Transferable objects. |
Example
Posting message to Worker
const worker = new Worker('worker.js')
postMessageToWorker(worker, { task: 'process', data: [1, 2, 3] })◈ Interfaces
Properties
● Variables
Safe copies of the Number built-in methods and constants, captured at load so later prototype pollution cannot reach them.
● Variables
Safe copies of Object built-in methods. These references are captured at module initialization time to protect against prototype pollution attacks. Import only what you need for tree-shaking.
ƒ Functions
Parameters
Returns
booleanExample
Checking own properties
const user = { name: 'Alice' }
hasOwn(user, 'name') // => true
hasOwn(user, 'toString') // => false (inherited from prototype)Parameters
| Name | Type | Description |
|---|---|---|
§value | unknown | The value to get the type tag from. |
Returns
stringExample
Getting type tags
typeTag([1, 2, 3]) // => '[object Array]'
typeTag(null) // => '[object Null]'
typeTag(Promise.resolve()) // => '[object Promise]'● Variables
Safe Promise factory and bound static methods.
ƒ Functions
createPromise< T>( executor: ( resolve: ( value: T | PromiseLike< T>) => void, reject: ( reason?: unknown) => void) => void): Promise< T>
new Promise().Parameters
| Name | Type | Description |
|---|---|---|
§executor | ( | The executor function. |
Returns
Promise< T>● Variables
Safe copies of Reflect built-in methods. These references are captured at module initialization time to protect against prototype pollution attacks. Import only what you need for tree-shaking.
● Variables
Safe RegExp factory for protected regex construction.
ƒ Functions
new RegExp().Parameters
Returns
RegExp● Variables
Safe Set factory for protected set construction.
ƒ Functions
● Variables
Safe copies of String built-in static methods. These references are captured at module initialization time to protect against prototype pollution attacks. Import only what you need for tree-shaking.
● Variables
Safe Symbol factory and well-known symbols.
ƒ Functions
Symbol().Parameters
| Name | Type | Description |
|---|---|---|
§description? | string | number | Optional description for the symbol. |
Returns
symbol● Variables
Safe copies of the timer and scheduling built-ins, captured at load so later prototype pollution cannot reach them.
ƒ Functions
Example
Canceling an interval
const intervalId = setInterval(() => console.log('tick'), 1000)
// Stop after some condition
clearInterval(intervalId)Example
Canceling a timeout
const timerId = setTimeout(() => console.log('Never runs'), 5000)
clearTimeout(timerId)Parameters
| Name | Type | Description |
|---|---|---|
§callback | VoidFunction | Function to execute. |
Example
Queuing a microtask
console.log('Start')
queueMicrotask(() => console.log('Microtask'))
console.log('End')
// Output: Start, End, MicrotasksetInterval< TArgs>( callback: ( args: TArgs) => void, delay?: number, ...args: TArgs): Timeout
Parameters
Returns
TimeoutExample
Setting an interval
let count = 0
const intervalId = setInterval(() => {
count++
if (count >= 5) clearInterval(intervalId)
}, 1000)setTimeout< TArgs>( callback: ( args: TArgs) => void, delay?: number, ...args: TArgs): Timeout
Parameters
Returns
TimeoutExample
Setting a timeout
const timerId = setTimeout(() => console.log('Executed'), 1000)
// Pass arguments to callback
setTimeout((name, count) => console.log(name, count), 500, 'items', 5)● Variables
Safe TypedArray and ArrayBuffer factories for protected array construction.
ƒ Functions
new ArrayBuffer().Parameters
| Name | Type | Description |
|---|---|---|
§byteLength | number | The size, in bytes, of the array buffer to create. |
Returns
ArrayBufferExample
Creating ArrayBuffer
const buffer = createArrayBuffer(16)
// => ArrayBuffer { byteLength: 16 }new BigInt64Array().Parameters
| Name | Type | Description |
|---|---|---|
§length | number | The length of the array. |
Returns
BigInt64ArrayExample
Creating BigInt64Array
const bigInts = createBigInt64Array(2)
bigInts[0] = -9223372036854775808n // Min value
bigInts[1] = 9223372036854775807n // Max valuenew BigUint64Array().Parameters
| Name | Type | Description |
|---|---|---|
§length | number | The length of the array. |
Returns
BigUint64ArrayExample
Creating BigUint64Array
const bigUints = createBigUint64Array(2)
bigUints[0] = 0n
bigUints[1] = 18446744073709551615n // Max valuecreateDataView( buffer: ArrayBuffer | SharedArrayBuffer, byteOffset?: number, byteLength?: number): DataView
new DataView().Parameters
| Name | Type | Description |
|---|---|---|
§buffer | ArrayBuffer | SharedArrayBuffer | The ArrayBuffer or SharedArrayBuffer to view. |
§byteOffset? | number | Optional byte offset to start the view. |
§byteLength? | number | Optional byte length of the view. |
Returns
DataViewExample
Creating DataView for binary data
const buffer = createArrayBuffer(8)
const view = createDataView(buffer)
view.setInt32(0, 42, true) // little-endian
view.getInt32(0, true) // => 42new Float32Array().Parameters
| Name | Type | Description |
|---|---|---|
§length | number | The length of the array. |
Returns
Float32ArrayExample
Creating Float32Array
const floats = createFloat32Array(3)
floats[0] = 3.14
floats[1] = -273.15new Float64Array().Parameters
| Name | Type | Description |
|---|---|---|
§length | number | The length of the array. |
Returns
Float64ArrayExample
Creating Float64Array
const doubles = createFloat64Array(2)
doubles[0] = Math.PI
doubles[1] = Number.MAX_VALUEnew Int16Array().Parameters
| Name | Type | Description |
|---|---|---|
§length | number | The length of the array. |
Returns
Int16ArrayExample
Creating Int16Array
const shorts = createInt16Array(2)
shorts[0] = -32768 // Min value
shorts[1] = 32767 // Max valuenew Int32Array().Parameters
| Name | Type | Description |
|---|---|---|
§length | number | The length of the array. |
Returns
Int32ArrayExample
Creating Int32Array
const ints = createInt32Array(2)
ints[0] = -2147483648 // Min value
ints[1] = 2147483647 // Max valuenew Int8Array().Parameters
| Name | Type | Description |
|---|---|---|
§length | number | The length of the array. |
Returns
Int8ArrayExample
Creating Int8Array
const signed = createInt8Array(2)
signed[0] = -128 // Min value
signed[1] = 127 // Max valuenew Uint16Array().Parameters
| Name | Type | Description |
|---|---|---|
§length | number | The length of the array. |
Returns
Uint16ArrayExample
Creating Uint16Array
const shorts = createUint16Array(2)
shorts[0] = 65535 // Max value for 16-bit unsignednew Uint32Array().Parameters
| Name | Type | Description |
|---|---|---|
§length | number | The length of the array. |
Returns
Uint32ArrayExample
Creating Uint32Array
const ints = createUint32Array(4)
ints[0] = 0xFFFFFFFF // Max 32-bit unsigned valuenew Uint8Array(). Supports all standard Uint8Array constructor overloads:
createUint8Array(length)- Creates array of given lengthcreateUint8Array(arrayLike)- Creates from array-like or iterablecreateUint8Array(buffer, byteOffset?, length?)- Creates view over buffer
Parameters
| Name | Type | Description |
|---|---|---|
§length | number | The length of the array to create. |
Returns
Uint8ArrayExample
Creating Uint8Array
const bytes = createUint8Array(4)
bytes[0] = 255
// From array
const fromArray = createUint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f])new Uint8ClampedArray().Parameters
| Name | Type | Description |
|---|---|---|
§length | number | Number of elements to allocate in the clamped array. |
Returns
Uint8ClampedArrayExample
Creating Uint8ClampedArray for pixels
const pixels = createUint8ClampedArray(4) // RGBA
pixels[0] = 300 // Clamped to 255
pixels[1] = -10 // Clamped to 0● Variables
Safe factories for the URL built-ins, URL and URLSearchParams, captured at load so later prototype pollution cannot reach them.
ƒ Functions
new URL().Parameters
Returns
URLExample
Creating URL instances
const absolute = createURL('https://example.com/path?query=1')
const relative = createURL('/api/users', 'https://example.com')
// => URL { href: 'https://example.com/api/users' }createURLSearchParams( init?: string | URLSearchParams | Record< string, string> | Iterable< [ string, string], any, any>): URLSearchParams
new URLSearchParams().Parameters
| Name | Type | Description |
|---|---|---|
§init? | string | URLSearchParams | Record< | Optional initialization value (string, object, or iterable). |
Returns
URLSearchParamsExample
Creating URLSearchParams
const fromString = createURLSearchParams('page=1&limit=10')
const fromObject = createURLSearchParams({ page: '1', limit: '10' })
fromObject.get('page') // => '1'● Variables
URL.canParse().URL.createObjectURL(). Note: This is a browser-only API. In Node.js environments, this will throw.
URL.parse().URL.revokeObjectURL(). Note: This is a browser-only API. In Node.js environments, this will throw.
Safe WeakMap factory for protected weak map construction.
ƒ Functions
new WeakMap().Parameters
Returns
WeakMap< K, V>● Variables
Safe WeakSet factory for protected weak set construction.
ƒ Functions
new WeakSet().Returns
WeakSet< T>● Variables
Safe factory for the WebSocket constructor, captured at load so later prototype pollution cannot reach it.
ƒ Functions
new WebSocket().Parameters
Returns
WebSocketExample
Creating WebSocket connection
const socket = createWebSocket('wss://api.example.com/stream')
socket.onmessage = (event) => console.log(event.data)● Variables
Decorator for binding methods with immutable this context.
ƒ Functions
this binding. Ensures a classic prototype method cannot be overwritten and is always called with the correct
this instance without needing arrow functions. - The method of the prototype is non-configurable and non-enumerable.
- Any attempt to assign to the method throws an error.
- It does not support class fields / arrow functions.
Returns
LockedMethodExample
Locking a class method
class Counter {
private count = 0
\@locked()
increment() {
return ++this.count
}
}◆ Types
type LockedMethod = ( target: object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor< any>) => anyNon-writable, non-configurable property descriptor utilities.
◆ Types
type LockedPropDescriptorsCreator = ( value: unknown, enumerable?: boolean) => PropertyDescriptor● Variables
Example
Creating locked property descriptor
const descriptor = lockedPropertyDescriptors('immutable', true)
// => { value: 'immutable', writable: false, configurable: false, enumerable: true }
Object.defineProperty(obj, 'key', descriptor)Decorator for locking object properties to prevent modification.
◆ Types
type PropertyLock = ( object: object, propertyValuePairs: [ string, any][ ]) => void● Variables
Example
Locking multiple properties
const config = {}
lockedProps(config, [
['apiUrl', 'https://api.example.com'],
['version', '1.0.0'],
])
config.apiUrl = 'hacked' // throws in strict mode, silently fails otherwiseRelated reading§
- How-to
How to harden your code against prototype pollution
Anything else running on the page can add to Object.prototype or reassign a built-in like Array.isArray, and my authorisation checks, serialisers, and dispatch tables inherit the lie without an error anywhere.
- Getting started
Getting Started
Set HyperFrontend up and embed a first feature.
- Architecture
Architecture Guide
How the packages fit together.

