Interfaces

Proxy System

Learn about the powerful proxy system from the core interface which enable module-aware communication in Antelopejs.

Common Features

The Antelopejs proxy system is a collection of specialized utilities from the core interface that facilitate communication between modules while ensuring proper lifecycle management. These proxies provide robust patterns for handling asynchronous function calls, events, and registration-based systems with automatic cleanup when modules are unloaded.

All proxy types in Antelopejs share these key characteristics:

  • Module awareness: Track which module is responsible for registering handlers or callbacks
  • Automatic cleanup: Prevent memory leaks by removing handlers when modules are unloaded
  • Type safety: Full TypeScript support for improved developer experience
  • Loose coupling: Enable communication between modules without tight dependencies

AsyncProxy

AsyncProxy manages asynchronous function calls between modules, handling the lifecycle of callbacks and automatically cleaning up when modules are unloaded.

Key Features

  • Delayed execution: Queue function calls until a callback is attached
  • Promise-based API: Works seamlessly with modern async/await patterns

Basic Usage

Creating an AsyncProxy

import { AsyncProxy } from '@ajs/core/beta';

// Create an async proxy for a function that takes a string and returns a number
const calculateProxy = new AsyncProxy<(input: string) => number>();

Attaching a Callback

// The module providing the functionality attaches a callback
calculateProxy.onCall((input: string) => {
  return input.length * 2;
});

Calling the Proxy

// The calling module uses the proxy
async function processInput(text: string) {
  const result = await calculateProxy.call(text);
  console.log(`Result: ${result}`);
}

// This will work even if called before a callback is attached
// The call will be queued until onCall is invoked
processInput("hello"); // Will eventually output: "Result: 10"

How AsyncProxy Works

AsyncProxy maintains an internal queue of function calls that haven't been processed yet. When a callback is attached via onCall(), any queued calls are immediately executed.

When a module attaches a callback without specifying manual detachment, the AsyncProxy registers itself with the module system. When that module is unloaded, all its attached proxies are automatically detached, preventing dangling callbacks and memory leaks.

EventProxy

EventProxy provides a module-aware event system that automatically cleans up event handlers when modules are unloaded, preventing memory leaks and ensuring proper separation between components.

Key Features

  • Event propagation: Efficiently broadcasts events to multiple handlers
  • Lightweight: Minimal overhead for high-performance event handling

Basic Usage

Creating an EventProxy

import { EventProxy } from '@ajs/core/beta';

// Create an event proxy with a typed event handler signature
const userUpdated = new EventProxy<(userId: string, newData: UserData) => void>();

Registering an Event Handler

// Register a handler for the event
userUpdated.register((userId, userData) => {
  console.log(`User ${userId} updated: ${JSON.stringify(userData)}`);
});

Emitting an Event

// Emit the event to all registered handlers
userUpdated.emit('user123', { name: 'Jane Doe', email: '[email protected]' });

Manually Unregistering an Event Handler

// Store the handler reference
const handler = (userId, userData) => {
  console.log(`User ${userId} updated: ${JSON.stringify(userData)}`);
};

// Register it
userUpdated.register(handler);

// Later, unregister it
userUpdated.unregister(handler);

How EventProxy Works

When an event handler is registered, EventProxy automatically detects which module is registering the handler. It stores this information along with the handler function.

When a module is unloaded, all EventProxy instances in the system automatically remove any handlers that were registered by that module, preventing "zombie" handlers and memory leaks.

RegisteringProxy

RegisteringProxy is specialized for managing registration-based systems in a module-aware context, providing a clean way to register and unregister handlers while ensuring proper cleanup.

Key Features

  • Registration queuing: Can handle registrations before the implementation is available

Basic Usage

Creating a RegisteringProxy

import { RegisteringProxy } from '@ajs/core/beta';

// Create a registering proxy with a typed registration function
// The registration function takes an ID and a handler function
const middlewareProxy = new RegisteringProxy<(id: string, handler: (req: Request, res: Response, next: Function) => void) => void>();

Setting Up Registration and Unregistration Callbacks

// Set up the actual registration function
middlewareProxy.onRegister((id, handler) => {
  console.log(`Registering middleware: ${id}`);
  app.use(handler);
});

// Set up the unregistration function
middlewareProxy.onUnregister((id) => {
  console.log(`Unregistering middleware: ${id}`);
  // Logic to remove the middleware from the app
});

Registering a Handler

// Register a middleware
const middlewareId = 'auth-middleware';
middlewareProxy.register(middlewareId, (req, res, next) => {
  // Authentication logic
  next();
});

Manually Unregistering a Handler

// Unregister a middleware
middlewareProxy.unregister('auth-middleware');

How RegisteringProxy Works

RegisteringProxy maintains a map of registered items, along with information about which module registered each item. When a module is unloaded, RegisteringProxy automatically unregisters any items that were registered by that module.

The proxy also handles the case where registrations happen before the actual implementation is attached, queueing registrations and applying them once the implementation is available.

Advanced Usage

Manual Detachment

All proxy types allow for manual detachment when needed:

// AsyncProxy
asyncProxy.detach();

// RegisteringProxy
registeringProxy.detach();

Preventing Automatic Detachment

For cases where you want to keep callbacks attached even when the providing module is unloaded:

// For AsyncProxy
asyncProxy.onCall((input) => {
  // Implementation
}, true); // true = manual detachment required

// For RegisteringProxy
registeringProxy.onRegister((id, handler) => {
  // Registration logic
}, true); // true = manual detachment required

Complete Usage Example

For a comprehensive example of how to use these proxies in real-world scenarios, please refer to the Export Interfaces chapter which demonstrates how to create and implement interfaces using the proxy system.