npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@ng-catbee/storage

v21.0.1

Published

A modern, type-safe Angular library for simplified interaction with web storage APIs (localStorage and sessionStorage) — fully compatible with Server-Side Rendering (SSR) and offering advanced features like JSON storage, boolean/number parsing, enum valid

Readme

@ng-catbee/storage

Catbee Storage for Angular

A modern, type-safe Angular library for simplified interaction with web storage APIs (localStorage and sessionStorage) — fully compatible with Server-Side Rendering (SSR) and offering advanced features like JSON storage, boolean/number parsing, enum validation, reactive observables, and configurable encoding strategies.

📦 Demo

Stackblitz

✨ Features

  • 🔒 Type-Safe - Full TypeScript support with generics
  • 📦 Dual Storage - Separate services for localStorage and sessionStorage
  • 🎯 Advanced Getters - Built-in support for JSON, arrays, booleans, numbers, and enums
  • 🔄 Reactive - Observable-based change detection with RxJS
  • 🌐 SSR Compatible - Gracefully handles server-side rendering
  • 🎨 Configurable Encoding - Base64, custom, or no encoding
  • Bulk Operations - Get/set multiple items at once
  • 📦 Zero Dependencies: Lightweight with no external dependencies

🛠️ Installation

npm install @ng-catbee/storage

🔧 Configuration (Optional)

Standalone apps:

import { provideCatbeeStorage } from '@ng-catbee/storage';

export const appConfig: ApplicationConfig = {
  providers: [
    provideCatbeeStorage({
      {
        common: {
          encoding: 'default' // 'default', 'base64', 'custom'
        },
        localStorage: { 
          encoding: 'custom',
          customEncode: (value: string) => `catbee-${value}`, // Replace with your custom logic
          customDecode: (value: string) => value.replace('catbee-', '') // Replace with your custom logic
        },
      }
    })
  ]
};

Module-based apps:

import { CatbeeStorageModule } from '@ng-catbee/storage';

@NgModule({
  imports: [
    CatbeeStorageModule.forRoot({
      common: {
        encoding: 'base64' // 'default', 'base64', 'custom'
      },
      localStorage: { 
        encoding: 'custom',
        customEncode: (value: string) => `catbee-${value}`, // Replace with your custom logic
        customDecode: (value: string) => value.replace('catbee-', '') // Replace with your custom logic
      },
    })
  ]
})
export class AppModule { }

⚡ Quick Start

import { Component, inject } from '@angular/core';
import { CatbeeLocalStorageService, CatbeeSessionStorageService } from '@ng-catbee/storage';

interface UserSettings {
  theme: string;
  language: string;
}

@Component({
  selector: 'app-root',
  template: `
    <button (click)="save()">Save</button>
    <button (click)="load()">Load</button>
    <p>{{ data }}</p>
  `
})
export class AppComponent {
  private localStorage = inject(CatbeeLocalStorageService);
  private sessionStorage = inject(CatbeeSessionStorageService);
  data = '';

  save() {
    // localStorage (persists across sessions)
    this.localStorage.set('username', 'john_doe');
    this.localStorage.setJson('settings', { theme: 'dark', language: 'en' });
    
    // sessionStorage (cleared when tab closes)
    this.sessionStorage.set('tempToken', 'abc123');
  }

  load() {
    const username = this.localStorage.get('username') ?? 'Guest';
    const settings = this.localStorage.getJsonWithDefault<UserSettings>('settings', { theme: 'light', language: 'en' });
    this.data = `User: ${username}, Theme: ${settings.theme}`;
  }
}

Type-Safe Methods

@Component({ selector: 'app-typed' })
export class TypedComponent {
  private localStorage = inject(CatbeeLocalStorageService);

  examples() {
    // Boolean (recognizes: true/false, 1/0, yes/no, on/off)
    this.localStorage.set('darkMode', 'true');
    const isDark = this.localStorage.getBooleanWithDefault('darkMode', false); // true

    // Number (handles integers and floats)
    this.localStorage.set('count', '42');
    const count = this.localStorage.getNumberWithDefault('count', 0); // 42

    // Enum with validation
    type Theme = 'light' | 'dark';
    const theme = this.localStorage.getEnumWithDefault('theme', 'light', ['light', 'dark']);

    // Arrays
    this.localStorage.setArray('tags', ['angular', 'typescript']);
    const tags = this.localStorage.getArrayWithDefault<string>('tags', []);
    
    // Nullable returns (when you don't want auto-set defaults)
    const maybeNumber = this.localStorage.getNumber('optionalCount'); // number | null
    const maybeBoolean = this.localStorage.getBoolean('optionalFlag'); // boolean | null
    const maybeEnum = this.localStorage.getEnum<Theme>('optionalTheme', ['light', 'dark']); // Theme | null
  }
}

Reactive Storage with Observables

import { Component, inject, OnInit } from '@angular/core';
import { CatbeeLocalStorageService } from '@ng-catbee/storage';

@Component({
  selector: 'app-reactive',
  template: `
    <div>Theme: {{ currentTheme }}</div>
    <button (click)="changeTheme()">Toggle Theme</button>
  `,
})
export class ReactiveComponent implements OnInit {
  private localStorage = inject(CatbeeLocalStorageService);
  currentTheme = 'light';

  ngOnInit() {
    // Watch changes to a specific key
    this.localStorage.watch('theme').subscribe(value => {
      this.currentTheme = value ?? 'light';
      console.log('Theme changed to:', value);
    });
    
    // Watch all storage changes
    this.localStorage.watchAll().subscribe(event => {
      console.log('Storage changed:', event.key, event.newValue);
    });
  }

  changeTheme() {
    const newTheme = this.currentTheme === 'light' ? 'dark' : 'light';
    this.localStorage.set('theme', newTheme);
    // Observable will automatically emit the new value
  }
}

Reactive & Advanced Features

@Component({ selector: 'app-advanced' })
export class AdvancedComponent implements OnInit {
  private localStorage = inject(CatbeeLocalStorageService);

  ngOnInit() {
    // Watch changes to specific key
    this.localStorage.watch('theme').subscribe(value => {
      console.log('Theme changed:', value);
    });
  }

  examples() {
    // Get with default (auto-sets if missing)
    const theme = this.localStorage.getWithDefault('theme', 'light', ['light', 'dark']);

    // Conditional set
    this.localStorage.setIfNotExists('firstVisit', new Date().toISOString());

    // Bulk operations
    this.localStorage.multiSet({ username: 'john', theme: 'dark' });
    const values = this.localStorage.multiGet(['username', 'theme']);

    // Delete operations
    this.localStorage.deleteMany(['key1', 'key2']);
    this.localStorage.clear();

    // Storage info
    const size = this.localStorage.size();
  }
}

📚 API Reference

Basic Methods

| Method | Description | |--------|-------------| | set(key: string, value: string, skipEncoding?: boolean): void | Store a string value | | get(key: string, skipDecoding?: boolean): string \| null | Get a string value | | delete(key: string): void | Remove an item | | has(key: string): boolean | Check if key exists | | clear(): void | Remove all items | | keys(): string[] | Get all keys | | values(): (string \| null)[] | Get all values | | entries(): [string, string \| null][] | Get all key-value pairs |

Core Methods

| Method | Description | |--------|-------------| | setJson<T>(key, value): void | Store JSON value | | getJson<T>(key, skipDecoding?): T \| null | Get and parse JSON | | getJsonWithDefault<T>(key, defaultValue): T | Get JSON with auto-set default | | setArray<T>(key, value): void | Store array | | getArray<T>(key, skipDecoding?): T[] \| null | Get and parse array | | getArrayWithDefault<T>(key, defaultValue?): T[] | Get array with auto-set default | | getBoolean(key, skipDecoding?) | Parse boolean | | getBooleanWithDefault(key, defaultValue) | Parse boolean with auto-set default | | getNumber(key, skipDecoding?) | Parse number | | getNumberWithDefault(key, defaultValue) | Parse number with auto-set default | | getEnum<T>(key, enumValues, skipDecoding?) | Get validated enum | | getEnumWithDefault<T>(key, defaultValue, enumValues) | Get enum with auto-set default |

Advanced

| Method | Description | |--------|-------------| | setIfNotExists(key: string, value: string): void | Set only if missing | | getWithDefault(key: string, defaultValue: string, allowedValues?: string[]): string | Get with validation and auto-set default | | updateJson<T>(key: string, updates: Partial<T>, defaultValue: T): void | Partial JSON update | | multiGet(keys: string[]): Map<string, string \| null> | Get multiple values at once | | multiSet(entries: Record<string, string>): void | Set multiple values at once | | deleteMany(keys: string[]): void | Delete multiple keys | | watch(key: string): Observable<string \| null> | Watch key changes | | watchAll(): Observable<{key: string \| null, oldValue: string \| null, newValue: string \| null}> | Watch all changes | | size(): number | Total size in bytes |

🎯 Use Case: Shopping Cart

import { Injectable, inject } from '@angular/core';
import { CatbeeLocalStorageService } from '@ng-catbee/storage';

interface CartItem {
  id: number;
  name: string;
  price: number;
  quantity: number;
}

@Injectable({ providedIn: 'root' })
export class CartService {
  private localStorage = inject(CatbeeLocalStorageService);
  private readonly CART_KEY = 'shopping-cart';

  getCart(): CartItem[] {
    return this.localStorage.getArrayWithDefault<CartItem>(this.CART_KEY, []);
  }

  addItem(item: CartItem) {
    const cart = this.getCart();
    const existing = cart.find(i => i.id === item.id);
    existing ? existing.quantity += item.quantity : cart.push(item);
    this.localStorage.setArray(this.CART_KEY, cart);
  }

  removeItem(itemId: number) {
    const cart = this.getCart().filter(i => i.id !== itemId);
    this.localStorage.setArray(this.CART_KEY, cart);
  }

  getTotalPrice(): number {
    return this.getCart().reduce((sum, item) => sum + item.price * item.quantity, 0);
  }
}

📖 Documentation

💡 Full documentation available at https://catbee.npm.hprasath.com

📜 License

MIT © Catbee Technologies (see the LICENSE file for the full text)

🔗 Links