proxied
Create a proxy function that can intercept and modify function calls, parameters, and return values.
Basic Usage
typescript
import { proxied } from 'radash'
const originalFn = (a: number, b: number) => a + b
const proxiedFn = proxied(originalFn, {
before: (args) => console.log('Before call:', args),
after: (result) => console.log('After call:', result)
})
console.log(proxiedFn(2, 3)) // Before call: [2, 3], After call: 5, 5
Syntax
typescript
function proxied<T extends (...args: any[]) => any>(
fn: T,
handlers: {
before?: (args: Parameters<T>) => void | Parameters<T>
after?: (result: ReturnType<T>) => void | ReturnType<T>
error?: (error: any) => void | ReturnType<T>
}
): T
Parameters
fn
(function): The original function to proxyhandlers
(object): Proxy handlersbefore
(function): Called before the original function, can modify argumentsafter
(function): Called after the original function, can modify return valueerror
(function): Called when the original function throws an error
Return Value
Returns a proxied function with the same signature as the original function.
Examples
Basic Function Proxy
typescript
import { proxied } from 'radash'
const add = (a: number, b: number) => a + b
const loggedAdd = proxied(add, {
before: (args) => console.log('Adding:', args),
after: (result) => console.log('Result:', result)
})
console.log(loggedAdd(5, 3)) // Adding: [5, 3], Result: 8, 8
Parameter Modification
typescript
import { proxied } from 'radash'
const multiply = (a: number, b: number) => a * b
const validatedMultiply = proxied(multiply, {
before: (args) => {
const [a, b] = args
if (a < 0 || b < 0) {
throw new Error('Negative numbers not allowed')
}
return args
}
})
console.log(validatedMultiply(2, 3)) // 6
// validatedMultiply(-1, 3) // Error: Negative numbers not allowed
Return Value Modification
typescript
import { proxied } from 'radash'
const getUser = (id: number) => ({ id, name: `User ${id}` })
const cachedGetUser = proxied(getUser, {
after: (result) => {
console.log('Caching result:', result)
return { ...result, cached: true }
}
})
console.log(cachedGetUser(1)) // Caching result: { id: 1, name: 'User 1' }, { id: 1, name: 'User 1', cached: true }
Error Handling
typescript
import { proxied } from 'radash'
const divide = (a: number, b: number) => {
if (b === 0) throw new Error('Division by zero')
return a / b
}
const safeDivide = proxied(divide, {
error: (error) => {
console.error('Division error:', error.message)
return 0
}
})
console.log(safeDivide(10, 2)) // 5
console.log(safeDivide(10, 0)) // Division error: Division by zero, 0
Async Function Proxy
typescript
import { proxied } from 'radash'
const fetchUser = async (id: number) => {
const response = await fetch(`https://api.example.com/users/${id}`)
return response.json()
}
const loggedFetchUser = proxied(fetchUser, {
before: (args) => console.log('Fetching user:', args[0]),
after: (result) => console.log('User fetched:', result),
error: (error) => {
console.error('Fetch error:', error.message)
return { error: 'User not found' }
}
})
const user = await loggedFetchUser(1)
Performance Monitoring
typescript
import { proxied } from 'radash'
const expensiveOperation = (data: number[]) => {
return data.reduce((sum, num) => sum + num, 0)
}
const monitoredOperation = proxied(expensiveOperation, {
before: (args) => {
console.time('operation')
return args
},
after: (result) => {
console.timeEnd('operation')
return result
}
})
console.log(monitoredOperation([1, 2, 3, 4, 5])) // operation: 0.123ms, 15
Validation Proxy
typescript
import { proxied } from 'radash'
const createUser = (name: string, age: number, email: string) => ({
id: Date.now(),
name,
age,
email
})
const validatedCreateUser = proxied(createUser, {
before: (args) => {
const [name, age, email] = args
if (!name || name.length < 2) {
throw new Error('Name must be at least 2 characters')
}
if (age < 0 || age > 150) {
throw new Error('Age must be between 0 and 150')
}
if (!email.includes('@')) {
throw new Error('Invalid email format')
}
return args
}
})
try {
const user = validatedCreateUser('Alice', 25, 'alice@example.com')
console.log('User created:', user)
} catch (error) {
console.error('Validation failed:', error.message)
}
Caching Proxy
typescript
import { proxied } from 'radash'
const cache = new Map()
const expensiveCalculation = (x: number, y: number) => {
// Simulate expensive operation
return Math.pow(x, y) + Math.sqrt(x + y)
}
const cachedCalculation = proxied(expensiveCalculation, {
before: (args) => {
const key = JSON.stringify(args)
if (cache.has(key)) {
console.log('Cache hit for:', args)
return { cached: true, result: cache.get(key) }
}
return args
},
after: (result) => {
const key = JSON.stringify(arguments)
cache.set(key, result)
console.log('Cached result for:', arguments)
return result
}
})
console.log(cachedCalculation(2, 3)) // 8.236, Cached result for: [2, 3]
console.log(cachedCalculation(2, 3)) // Cache hit for: [2, 3], 8.236
Logging Proxy
typescript
import { proxied } from 'radash'
const processData = (data: any[]) => {
return data.filter(item => item > 0).map(item => item * 2)
}
const loggedProcessData = proxied(processData, {
before: (args) => {
console.log('Processing data:', args[0])
return args
},
after: (result) => {
console.log('Processed result:', result)
return result
},
error: (error) => {
console.error('Processing error:', error.message)
return []
}
})
console.log(loggedProcessData([1, -2, 3, 0, 5]))
// Processing data: [1, -2, 3, 0, 5]
// Processed result: [2, 6, 10]
// [2, 6, 10]
Rate Limiting Proxy
typescript
import { proxied } from 'radash'
const callCounts = new Map()
const apiCall = async (endpoint: string) => {
const response = await fetch(endpoint)
return response.json()
}
const rateLimitedApiCall = proxied(apiCall, {
before: (args) => {
const endpoint = args[0]
const count = callCounts.get(endpoint) || 0
if (count >= 10) {
throw new Error(`Rate limit exceeded for ${endpoint}`)
}
callCounts.set(endpoint, count + 1)
return args
},
error: (error) => {
console.error('API call failed:', error.message)
return { error: error.message }
}
})
// Usage
try {
const data = await rateLimitedApiCall('https://api.example.com/data')
console.log('API response:', data)
} catch (error) {
console.error('Rate limit error:', error.message)
}
Authentication Proxy
typescript
import { proxied } from 'radash'
const protectedResource = (userId: number, action: string) => {
return `User ${userId} performed ${action}`
}
const authenticatedResource = proxied(protectedResource, {
before: (args) => {
const [userId, action] = args
// Simulate authentication check
if (!userId || userId <= 0) {
throw new Error('Unauthorized: Invalid user ID')
}
if (!action || action.length === 0) {
throw new Error('Invalid action')
}
return args
},
error: (error) => {
console.error('Authentication error:', error.message)
return { error: error.message, authorized: false }
}
})
console.log(authenticatedResource(1, 'read')) // User 1 performed read
console.log(authenticatedResource(0, 'write')) // Authentication error: Unauthorized: Invalid user ID, { error: 'Unauthorized: Invalid user ID', authorized: false }
Transformation Proxy
typescript
import { proxied } from 'radash'
const formatText = (text: string, format: string) => {
switch (format) {
case 'uppercase': return text.toUpperCase()
case 'lowercase': return text.toLowerCase()
case 'titlecase': return text.replace(/\w\S*/g, txt => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase())
default: return text
}
}
const enhancedFormatText = proxied(formatText, {
before: (args) => {
let [text, format] = args
// Normalize text
text = text.trim()
// Validate format
if (!['uppercase', 'lowercase', 'titlecase'].includes(format)) {
format = 'titlecase' // Default format
}
return [text, format]
},
after: (result) => {
// Add formatting info
return {
original: arguments[0],
formatted: result,
format: arguments[1]
}
}
})
console.log(enhancedFormatText(' hello world ', 'uppercase'))
// { original: ' hello world ', formatted: 'HELLO WORLD', format: 'uppercase' }
Notes
- Interception: Can intercept function calls before and after execution
- Modification: Can modify arguments and return values
- Error handling: Can catch and handle errors gracefully
- Performance: Minimal overhead for simple proxies
- Composition: Can be composed with other functional utilities
Differences from Other Methods
proxied
: Creates function proxies with interception capabilitiesmemo
: Caches function resultsdebounce
: Delays function executionthrottle
: Limits function execution frequency
Performance
- Time Complexity: O(1) for proxy creation
- Memory: Minimal overhead for function wrapping
- Use Cases: Logging, validation, caching, monitoring, authentication