0.0.1 • Published 5 months ago

@meshestra/nestjs-aspect v0.0.1

Weekly downloads
-
License
MIT
Repository
-
Last release
5 months ago

@Meshcraft/aspect

npm License Build Status Coverage Downloads TypeScript Node.js

Powerful Aspect Oriented Programming (AOP) for JavaScript/TypeScript, inspired by Spring AOP

Features

  • ✂️ Clean Separation of Concerns - Isolate cross-cutting concerns from your business logic
  • 🔄 Method Interception - Before, After, Around, AfterReturning, AfterThrowing advice
  • 🎯 Pointcut Expressions - Target specific methods and classes with powerful expressions
  • 🧩 Composable Aspects - Create reusable aspects and combine them effortlessly
  • Performance Optimized - Minimal runtime overhead with smart caching
  • 📏 TypeScript First - Full type safety with excellent IDE support
  • 🔍 Transparent Proxies - Maintains original method signatures and stack traces

Installation

npm install @meshcraft/aspect
# OR
yarn add @meshcraft/aspect
# OR
pnpm add @meshcraft/aspect

Quick Start

import { createAspect, Around, Pointcut } from '@meshcraft/aspect';

// Create a simple logging aspect
const LoggingAspect = createAspect({
  // Define a pointcut that targets service methods
  @Pointcut('execution(*.service.*(..))')
  serviceMethod() {},

  // Apply "around" advice to all methods matching the pointcut
  @Around('serviceMethod()')
  async logMethodExecution(joinPoint) {
    const { method, args, proceed } = joinPoint;
    console.log(`Executing ${method} with arguments:`, args);

    const startTime = performance.now();
    try {
      // Proceed with the original method call
      const result = await proceed();
      console.log(`${method} returned:`, result);
      return result;
    } catch (error) {
      console.error(`${method} threw an error:`, error);
      throw error;
    } finally {
      const endTime = performance.now();
      console.log(`${method} execution time: ${endTime - startTime}ms`);
    }
  }
});

// Create a class with some business logic
class UserService {
  async findById(id: string) {
    // Database operations...
    return { id, name: 'John Doe' };
  }

  async createUser(userData: any) {
    // User creation logic...
    return { id: '123', ...userData };
  }
}

// Apply the aspect to the service
const userService = LoggingAspect.apply(new UserService());

// Use the proxied service - aspects will be applied automatically
await userService.findById('123');
// Console:
// Executing findById with arguments: ['123']
// findById returned: { id: '123', name: 'John Doe' }
// findById execution time: 5.23ms

Core Concepts

Aspects

Aspects are the core containers for cross-cutting concerns. They define pointcuts and advice that describe where and how to apply additional behavior.

Joinpoints

Joinpoints represent specific points in your application's execution where aspects can be applied. In @Meshcraft/aspect, joinpoints are typically method executions.

Pointcuts

Pointcuts are expressions that select specific joinpoints in your code. They determine which methods your aspects will target.

@Pointcut('execution(com.example.service.*.*(..))')
serviceLayer() {}

Advice

Advice defines the behavior to apply at selected joinpoints. @Meshcraft/aspect offers several types of advice:

  • @Before - Executes before the joinpoint
  • @After - Executes after the joinpoint (regardless of outcome)
  • @AfterReturning - Executes after successful completion
  • @AfterThrowing - Executes if the joinpoint throws an exception
  • @Around - Wraps the joinpoint with custom behavior

Advanced Examples

Transaction Management

const TransactionAspect = createAspect({
  @Pointcut('execution(@Transactional *.*(..))')
  transactionalMethod() {},

  @Around('transactionalMethod()')
  async manageTransaction(joinPoint) {
    const tx = await db.beginTransaction();
    try {
      const result = await joinPoint.proceed();
      await tx.commit();
      return result;
    } catch (error) {
      await tx.rollback();
      throw error;
    }
  }
});

// Usage with decorator
class OrderService {
  @Transactional()
  async createOrder(order) {
    // Order creation logic that will be automatically wrapped in a transaction
  }
}

Caching

const CachingAspect = createAspect({
  @Pointcut('execution(@Cacheable *.*(..))')
  cacheableMethod() {},

  @Around('cacheableMethod()')
  async cache(joinPoint) {
    const { target, method, args } = joinPoint;
    const cacheKey = `${target.constructor.name}.${method}(${JSON.stringify(args)})`;

    const cachedValue = await cacheManager.get(cacheKey);
    if (cachedValue) {
      return cachedValue;
    }

    const result = await joinPoint.proceed();
    await cacheManager.set(cacheKey, result);
    return result;
  }
});

Pointcut Expression Syntax

@Meshcraft/aspect provides a powerful expression language for defining pointcuts:

ExpressionDescription
execution(* *.*(..))All methods in all classes
execution(com.example.service.*.*(..))All methods in service classes
execution(* *.find*(..))All methods starting with "find"
execution(@Transactional * *.*(..))Methods with @Transactional annotation
execution(* *.*(String, ..))Methods with String as first parameter
within(com.example.service.*)All joinpoints within service package

Performance Considerations

@Meshcraft/aspect is designed with performance in mind:

  • Pointcut expressions are parsed and optimized at load time
  • Proxies use minimal reflection to maintain performance
  • Advanced caching mechanisms reduce runtime overhead

For production environments, consider:

// Enable production mode to apply additional optimizations
import { configure } from '@meshcraft/aspect';

configure({
  production: process.env.NODE_ENV === 'production',
  cacheSize: 1000
});

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgements

  • Spring Framework - For the original AOP concepts and implementation
  • TypeScript team - For the amazing type system that makes this possible