JSPM

@fivexlabs/ng-terminus

1.0.1
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 2
  • Score
    100M100P100Q26518F
  • License MIT

A declarative Angular library for managing RxJS subscriptions and preventing memory leaks

Package Exports

  • @fivexlabs/ng-terminus
  • @fivexlabs/ng-terminus/package.json

Readme

@fivexlabs/ng-terminus

Fivex Labs

Angular Subscription Management Library

Declarative utilities for managing RxJS subscriptions and preventing memory leaks in Angular applications

Made with โค๏ธ by Fivex Labs

npm version License: MIT TypeScript

๐Ÿ’ก Why ng-terminus?

๐Ÿšจ The Problem

Angular applications commonly suffer from memory leaks caused by unmanaged RxJS subscriptions. These issues lead to:

  • ๐Ÿ“ˆ Gradual Performance Degradation: Memory usage increases over time as subscriptions accumulate
  • ๐Ÿ› Difficult-to-Debug Issues: Memory leaks cause seemingly random performance problems
  • ๐Ÿ”„ Repetitive Boilerplate: Manual unsubscribe() calls in every component's ngOnDestroy
  • โŒ Human Error: Easy to forget unsubscription, especially in complex components
  • ๐Ÿง  Cognitive Load: Developers must remember subscription management instead of focusing on business logic

โœ… The Solution

ng-terminus eliminates these issues by providing:

  • ๐ŸŽฏ Zero Memory Leaks: Automatic subscription cleanup tied to Angular's lifecycle
  • ๐Ÿ“ Declarative API: Clean, RxJS-native operators that integrate seamlessly
  • ๐Ÿš€ Developer Experience: Write less code, focus on features, not cleanup
  • ๐Ÿ›ก๏ธ Type Safety: Full TypeScript support prevents runtime errors
  • โšก Performance: Optimized cleanup strategies with minimal overhead

โœจ Features

๐ŸŽฏ Core Features

  • ๐Ÿ”š takeUntilDestroyed Operator: RxJS operator that automatically completes observables when components are destroyed
  • ๐Ÿ“ฆ SubscriptionManager Service: Injectable service for managing multiple subscriptions with automatic cleanup
  • ๐Ÿ›ก๏ธ Type Safety: Full TypeScript support with comprehensive type definitions
  • ๐Ÿš€ Modern Angular: Built for Angular 14+ using the latest DestroyRef patterns
  • โšก Zero Dependencies: Only peer dependencies on Angular and RxJS
  • ๐ŸŒณ Tree Shakable: Optimized for minimal bundle size impact

๐Ÿ”ฅ Advanced Features

  • ๐Ÿ” Debugging Tools: Built-in utilities for tracking subscription lifecycle in development
  • ๐Ÿ› ๏ธ Utility Functions: Helper functions for safe unsubscription and batch management
  • ๐Ÿ“Š Subscription Tracking: Monitor active subscriptions and get insights
  • ๐Ÿ”„ Method Chaining: Fluent API for managing multiple subscriptions
  • โš™๏ธ Flexible Configuration: Support for both automatic and explicit lifecycle management

๐Ÿ“ฆ Installation

npm install @fivexlabs/ng-terminus
# or
yarn add @fivexlabs/ng-terminus

Note: This library requires Angular 14+ and RxJS 7+.

๐Ÿš€ Quick Start

Transform your subscription management with a single operator:

import { Component } from '@angular/core';
import { takeUntilDestroyed } from '@fivexlabs/ng-terminus';
import { DataService } from './data.service';

@Component({
  selector: 'app-user-dashboard',
  template: `<div>Welcome {{ user?.name }}!</div>`
})
export class UserDashboardComponent {
  user: User | null = null;

  constructor(private dataService: DataService) {
    // โœจ This subscription automatically cleans itself up!
    this.dataService.getCurrentUser()
      .pipe(takeUntilDestroyed())
      .subscribe(user => this.user = user);
    
    // โœจ Multiple streams? No problem!
    this.dataService.getNotifications()
      .pipe(
        debounceTime(1000),
        takeUntilDestroyed() // Always last in the pipe
      )
      .subscribe(notifications => this.handleNotifications(notifications));
  }
}

The Service Manager Way

Perfect for complex components with many subscriptions:

import { Component, OnInit } from '@angular/core';
import { SubscriptionManager } from '@fivexlabs/ng-terminus';

@Component({
  selector: 'app-complex-dashboard',
  providers: [SubscriptionManager] // ๐Ÿ”‘ Component-level provider
})
export class ComplexDashboardComponent implements OnInit {
  constructor(
    private dataService: DataService,
    private subManager: SubscriptionManager
  ) {}

  ngOnInit() {
    // โœจ Add multiple subscriptions - all cleaned up automatically!
    this.subManager.add(
      this.dataService.getUserData().subscribe(data => this.handleUserData(data)),
      this.dataService.getAnalytics().subscribe(analytics => this.updateCharts(analytics)),
      interval(30000).subscribe(() => this.refreshData()),
      this.websocketService.connect().subscribe(message => this.handleMessage(message))
    );

    console.log(`Managing ${this.subManager.activeCount} subscriptions`);
  }
  
  // ๐ŸŽ‰ No ngOnDestroy needed! Zero boilerplate!
}

๐Ÿ”ฅ Advanced Features Showcase

๐Ÿ” Subscription Debugging

Track subscription lifecycle in development with powerful debugging tools:

import { SubscriptionDebugger } from '@fivexlabs/ng-terminus';

// Enable debugging in development
if (!environment.production) {
  SubscriptionDebugger.configure({
    enableLogging: true,
    logPrefix: '[MyApp Subscriptions]',
    captureStackTrace: true
  });
}

// Now all subscription lifecycle events will be logged!

๐Ÿ› ๏ธ Utility Functions

Handle edge cases and complex scenarios with ease:

import { 
  safeUnsubscribe, 
  createManagedObservable,
  manageManyObservables 
} from '@fivexlabs/ng-terminus';

@Component({...})
export class AdvancedComponent {
  constructor(private http: HttpClient) {
    // โœจ Safe unsubscription - never throws
    let subscription: Subscription | undefined;
    safeUnsubscribe(subscription); // Won't throw if undefined

    // โœจ Functional approach to managed observables
    const managedData$ = createManagedObservable(
      this.http.get('/api/data'),
      inject(DestroyRef)
    );

    // โœจ Batch manage multiple observables
    const [users$, posts$, comments$] = manageManyObservables([
      this.http.get('/api/users'),
      this.http.get('/api/posts'),
      this.http.get('/api/comments')
    ]);
  }
}

๐Ÿ“Š Subscription Tracking

Monitor and analyze your subscription usage:

@Component({
  providers: [SubscriptionManager]
})
export class MonitoredComponent {
  constructor(private subManager: SubscriptionManager) {
    // Add subscriptions
    this.subManager.add(
      stream1$.subscribe(),
      stream2$.subscribe()
    );

    // โœจ Monitor subscription health
    console.log(`Active subscriptions: ${this.subManager.activeCount}`);
    console.log(`Has active subscriptions: ${this.subManager.hasActiveSubscriptions}`);
    
    // โœจ Method chaining for fluent API
    this.subManager
      .add(stream3$.subscribe())
      .add(stream4$.subscribe());
  }
}

๐Ÿ“š Documentation

Basic Subscription Management

The foundation of ng-terminus is built on two core patterns:

// Pattern 1: Operator-based (Recommended)
import { takeUntilDestroyed } from '@fivexlabs/ng-terminus';

data$.pipe(takeUntilDestroyed()).subscribe();

// Pattern 2: Service-based (For complex scenarios)
import { SubscriptionManager } from '@fivexlabs/ng-terminus';

constructor(private subManager: SubscriptionManager) {
  this.subManager.add(subscription1, subscription2);
}

takeUntilDestroyed Operator

Automatic DestroyRef Injection

The simplest and most common usage - no configuration needed:

@Component({...})
export class SimpleComponent {
  constructor(private dataService: DataService) {
    // โœจ DestroyRef is automatically injected
    this.dataService.getData()
      .pipe(takeUntilDestroyed())
      .subscribe(data => console.log(data));
  }
}

Explicit DestroyRef Usage

For advanced scenarios where you need control:

@Component({...})
export class AdvancedComponent {
  private destroyRef = inject(DestroyRef);

  constructor(private dataService: DataService) {
    this.dataService.getData()
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe(data => console.log(data));
  }
}

Service Usage

Use in services for background tasks:

@Injectable()
export class BackgroundService {
  private destroyRef = inject(DestroyRef);

  constructor(private http: HttpClient) {
    // โœจ Auto-cleanup when service is destroyed
    interval(60000)
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe(() => this.performHealthCheck());
  }
}

SubscriptionManager Service

Always provide SubscriptionManager at the component level for proper lifecycle management:

@Component({
  selector: 'app-dashboard',
  providers: [SubscriptionManager] // ๐Ÿ”‘ Key: Component-level provider
})
export class DashboardComponent implements OnInit {
  constructor(private subManager: SubscriptionManager) {}

  ngOnInit() {
    // โœจ Add multiple subscriptions at once
    this.subManager.add(
      this.userService.getProfile().subscribe(profile => this.profile = profile),
      this.notificationService.getAlerts().subscribe(alerts => this.alerts = alerts),
      interval(30000).subscribe(() => this.refreshData())
    );
  }
  
  // โœจ Automatic cleanup on component destroy - no ngOnDestroy needed!
}

Dynamic Subscription Management

Add and remove subscriptions based on user interactions:

@Component({
  providers: [SubscriptionManager]
})
export class InteractiveComponent {
  private currentStreamSub?: Subscription;

  constructor(private subManager: SubscriptionManager) {}

  startMonitoring(streamId: string) {
    // โœจ Add new subscription
    this.currentStreamSub = this.dataService.getStream(streamId).subscribe();
    this.subManager.add(this.currentStreamSub);
  }

  stopMonitoring() {
    // โœจ Remove specific subscription
    if (this.currentStreamSub) {
      this.subManager.remove(this.currentStreamSub);
      this.currentStreamSub.unsubscribe();
    }
  }

  getStatus() {
    return `Managing ${this.subManager.activeCount} active subscriptions`;
  }
}

Method Chaining

Fluent API for readable subscription management:

ngOnInit() {
  this.subManager
    .add(stream1$.subscribe())
    .add(stream2$.subscribe())
    .add(stream3$.subscribe());
}

Module Import (Optional)

If you want to use SubscriptionManager globally across your application:

import { NgModule } from '@angular/core';
import { NgTerminusModule } from '@fivexlabs/ng-terminus';

@NgModule({
  imports: [NgTerminusModule.forRoot()],
  // ...
})
export class AppModule { }

// For feature modules
@NgModule({
  imports: [NgTerminusModule.forFeature()],
  // ...
})
export class FeatureModule { }

Advanced Usage

Combining with RxJS Operators

Always place takeUntilDestroyed as the last operator in your pipe:

this.dataService.getSearchResults(query)
  .pipe(
    debounceTime(300),
    distinctUntilChanged(),
    switchMap(query => this.http.get(`/api/search?q=${query}`)),
    map(response => response.data),
    takeUntilDestroyed() // โœจ Always last
  )
  .subscribe(results => this.searchResults = results);

Error Handling

ng-terminus plays well with RxJS error handling:

this.dataService.getRiskyData()
  .pipe(
    retry(3),
    catchError(error => of([])),
    takeUntilDestroyed()
  )
  .subscribe(data => this.handleData(data));

Utility Functions

Advanced utilities for edge cases and complex scenarios:

Safe Unsubscription

import { safeUnsubscribe } from '@fivexlabs/ng-terminus';

let subscription: Subscription | undefined;
safeUnsubscribe(subscription); // โœจ Never throws

Functional Observable Management

import { createManagedObservable, manageManyObservables } from '@fivexlabs/ng-terminus';

// Single observable
const managed$ = createManagedObservable(this.http.get('/api/data'));

// Multiple observables
const [users$, posts$] = manageManyObservables([
  this.http.get('/api/users'),
  this.http.get('/api/posts')
]);

๐Ÿงช Testing

npm test

๐Ÿ“– API Reference

Core Operators

Function Parameters Returns Description
takeUntilDestroyed<T> destroyRef?: DestroyRef OperatorFunction<T, T> Automatically completes observable on component destroy
untilDestroyed<T> None OperatorFunction<T, T> Alias for takeUntilDestroyed() with auto-injection

Services

Service Key Methods Description
SubscriptionManager add(), remove(), activeCount Manages multiple subscriptions with automatic cleanup

Utility Functions

Function Parameters Returns Description
safeUnsubscribe Subscription | null | undefined boolean Safely unsubscribe without errors
createManagedObservable<T> Observable<T>, DestroyRef? Observable<T> Create auto-managed observable
manageManyObservables<T> T[], DestroyRef? T[] Batch manage multiple observables

๐Ÿ“‹ Changelog

See CHANGELOG.md for a detailed history of all changes and new features.

๐Ÿค Contributing

We welcome contributions! Please see our Contributing Guide for details.

  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.

๐Ÿข About Fivex Labs

Fivex Labs is a technology company focused on building innovative tools and libraries for modern web development. We believe in creating solutions that are both powerful and developer-friendly.

Other Libraries by Fivex Labs

Visit us at fivexlabs.com to learn more about our work and other open-source projects.


Made with โค๏ธ by Fivex Labs

ยฉ 2025 Fivex Labs. All rights reserved.