https://github.com/karmaniverous/controlled-proxy
Control & modify the behavior of any object non-destructively at runtime.
https://github.com/karmaniverous/controlled-proxy
proxy typescript
Last synced: 6 months ago
JSON representation
Control & modify the behavior of any object non-destructively at runtime.
- Host: GitHub
- URL: https://github.com/karmaniverous/controlled-proxy
- Owner: karmaniverous
- Created: 2024-10-15T07:27:56.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2024-11-18T16:19:56.000Z (about 1 year ago)
- Last Synced: 2025-07-07T07:05:18.662Z (6 months ago)
- Topics: proxy, typescript
- Language: TypeScript
- Homepage: https://docs.karmanivero.us/controlled-proxy/
- Size: 1.32 MB
- Stars: 7
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
Awesome Lists containing this project
README
# controlled-proxy

**_`controlledProxy` allows the behavior of any object to be modified & controlled non-destructively at runtime._**
The developer can:
- Alter the proxy's endpoint controls at runtime.
- Specify a context-aware handler for disabled endpoints, also at runtime.
- Create multiple proxies of an underlying object, each controlled differently.
- Inject proxies into dependent code & control them from the outside.
Easy use case:
- You have a utility library with extensive logging.
- You consume that library from an application that uses a custom logger like `winston`.
- You want your utility library also to log to `winston`.
- You normally want debug logging from the utility library disabled, _even when it is enabed in the outer application_, but you want to enable it selectively to help debug the outer app.
> [API Documentation](https://docs.karmanivero.us/controlled-proxy/) • [CHANGELOG](https://github.com/karmaniverous/controlled-proxy/tree/main/CHANGELOG.md)
## Installation
```bash
npm install @karmaniverous/controlled-proxy
```
## Basic Usage
The `controlledProxy` function creates a type-safe proxy of any `object`.
The [`options`](https://docs.karmanivero.us/controlled-proxy/interfaces/controlled_proxy.ControlledProxyOptions.html) parameter is an object with the following properties:
| Property | Type | Default | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `defaultControls` | `Record` | `{}` | A map of controlled property keys to boolean values. When this value is `true` or the property is uncontrolled, the property will behave normally. When this value is false, the property will execute the disabled member handler or return `undefined`. |
| `defaultDisabledMemberHandler` | [`DisabledMemberHandler`](https://docs.karmanivero.us/controlled-proxy/types/controlled_proxy.DisabledMemberHandler.html) | `() => undefined` | A function that is called when a disabled controlled property is accessed. |
| `target` | `object` | _required_ | The object to proxy. |
### Example
```ts
import { controlledProxy } from '@karmaniverous/controlled-proxy';
// Create a controlled console logger. Info messages are disabled by default.
const controlledConsoleLogger = controlledProxy({
defaultControls: { debug: true, info: false },
target: console,
});
// Log messages.
controlledConsoleLogger.debug('debug log');
controlledConsoleLogger.info('info log');
// > debug log
```
## Runtime Control
The proxy object has two special properties, keyed with symbols that can be imported from the package:
| Property | Type | Description |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[controlProp]` | `Record` | A map of controlled property keys to boolean values. When this value is `true` or the property is uncontrolled, the property will behave normally. When this value is false, the property will execute the disabled member handler or return `undefined`. |
| `[disabledMemberHandlerProp]` | [`DisabledMemberHandler`](https://docs.karmanivero.us/controlled-proxy/types/controlled_proxy.DisabledMemberHandler.html) | A function that is called when a disabled controlled property is accessed. Defaults to `() => undefined`. |
### Example
```ts
import {
controlledProxy,
controlProp,
disabledMemberHandlerProp,
} from '@karmaniverous/controlled-proxy';
// Create a controlled console logger. Info messages are disabled by default.
const controlledConsoleLogger = controlledProxy({
defaultControls: { debug: true, info: false },
target: console,
});
// Disable debug messages & enable info messages at runtime.
controlledConsoleLogger[controlProp].debug = false;
controlledConsoleLogger[controlProp].info = true;
// Log messages.
controlledConsoleLogger.debug('debug log');
controlledConsoleLogger.info('info log');
// > info log
// Change the disabled member handler.
controlledConsoleLogger[disabledMemberHandlerProp] = (
target: Console,
prop: PropertyKey,
) => target.log(`Accessed disabled member: ${prop.toString()}`);
// Log messages again.
controlledConsoleLogger.debug('debug log');
controlledConsoleLogger.info('info log');
// > Accessed disabled member: debug
// > info log
```
## Proxy Injection
Here's an example of the real power of the library: **let's inject a controlled proxy into a class!**
### Example
```ts
import { controlledProxy, controlProp } from '@karmaniverous/controlled-proxy';
// Create a class that accepts a proxied logger as a constructor argument.
class MyClass {
// Proxied logger must be compatible with console.debug & console.info.
constructor(private logger: Pick) {}
// Exercise the proxied logger.
myMethod() {
this.logger.debug('debug log');
this.logger.info('info log');
}
}
// Create a controlled console logger, with all messages enabled by default
// and a custom disabled member handler.
const controlledConsoleLogger = controlledProxy({
defaultControls: { debug: false, info: true },
defaultDisabledMemberHandler: (target: Console, prop: PropertyKey) =>
target.log(`Accessed disabled member: ${prop.toString()}`),
target: console,
});
// Instantiate the class with the controlled console logger.
const myConsoleInstance = new MyClass(controlledConsoleLogger);
// Disable console debug messages at runtime.
controlledConsoleLogger[controlProp].debug = false;
// Exercise the proxied console logger from within the class.
myConsoleInstance.myMethod();
// > Accessed disabled member: debug
// > info log
// Create an equivalent controlled winston logger, with all messages enabled by
// default and a custom disabled member handler.
import { createLogger, type Logger } from 'winston';
const controlledWinstonLogger = controlledProxy({
defaultControls: { debug: true, info: true },
defaultDisabledMemberHandler: (target: Logger, prop: PropertyKey) =>
target.log('warn', `Accessed disabled member: ${prop.toString()}`),
target: createLogger(),
});
// Instantiate the class again with the controlled winston logger.
const myWinstonInstance = new MyClass(controlledWinstonLogger);
// Disable winston debug messages at runtime.
controlledWinstonLogger[controlProp].debug = false;
// Exercise the proxied winston logger from within the class.
myWinstonInstance.myMethod();
// > [winston] { "level":"warn", "message":"Accessed disabled member: debug" }
// > [winston] { "level":"info", "message":"info log" }
```
---
Built for you with ❤️ on Bali! Find more great tools & templates on [my GitHub Profile](https://github.com/karmaniverous).