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 🙏

© 2026 – Pkg Stats / Ryan Hefner

browser-storage-lru-cleaner

v0.0.22

Published

A TypeScript SDK for automatic browser storage cleanup using LRU algorithm

Readme

Browser Storage LRU Cleaner

🧹 一个基于LRU算法的浏览器存储自动清理SDK,支持localStorage和IndexedDB,通过代理模式实现业务无感知的自动清理。

🚀 特性

  • 🔄 LRU算法: 内置最近最少使用算法,智能清理不常用数据
  • 🔌 可扩展: 支持自定义清理策略,算法与SDK解耦
  • 📦 多存储支持: 同时支持localStorage和IndexedDB
  • 🎭 代理模式: 通过Proxy和Object.defineProperty实现透明代理
  • ⚡ 业务无感知: 自动拦截存储操作,无需修改业务代码
  • 📊 智能清理: 根据配置的容量阈值自动触发清理
  • 🔧 灵活配置: 支持清理阈值、清理比例、排除键等配置
  • 📈 统计监控: 提供详细的存储使用统计和健康检查

📋 目录

📦 安装

npm install browser-storage-lru-cleaner

或者使用yarn:

yarn add browser-storage-lru-cleaner

🚀 快速开始

localStorage 清理器

import { createLocalStorageCleaner } from 'browser-storage-lru-cleaner';

// 创建清理器实例
const cleaner = createLocalStorageCleaner({
  maxStorageSize: 5 * 1024 * 1024, // 5MB
  cleanupThreshold: 0.8, // 80%时开始清理
  cleanupRatio: 0.3, // 清理30%的数据
  autoCleanup: true, // 启用自动清理
  debug: true // 开启调试模式
});

// 安装代理,开始监控
cleaner.installProxy();

// 现在正常使用localStorage,清理器会自动工作
localStorage.setItem('user_data', JSON.stringify({ name: 'John' }));
localStorage.getItem('user_data'); // 会被记录访问

IndexedDB 清理器

import { createIndexedDBCleaner } from 'browser-storage-lru-cleaner';

// 创建IndexedDB清理器
const cleaner = createIndexedDBCleaner('MyApp', 'keyValueStore', {
  maxStorageSize: 10 * 1024 * 1024, // 10MB
  cleanupThreshold: 0.9,
  autoCleanup: true
});

// IndexedDB操作会被自动监控
await cleaner.getAdapter().setItem('large_data', jsonData);

自定义策略

import { StorageCleaner, LocalStorageAdapter } from 'browser-storage-lru-cleaner';

// 实现自定义清理策略
class CustomStrategy implements ICleanupStrategy {
  getName() {
    return 'Custom';
  }

  recordAccess(key: string) {
    // 自定义访问记录逻辑
  }

  getKeysToCleanup(allKeys: string[], currentSize: number, maxSize: number) {
    // 自定义清理逻辑
    return keysToCleanup;
  }

  cleanup(keys: string[]) {
    // 清理后的处理
  }
}

// 使用自定义策略
const cleaner = new StorageCleaner(
  new LocalStorageAdapter(),
  {
    strategy: new CustomStrategy(),
    maxStorageSize: 5 * 1024 * 1024
  }
);

📚 API文档

StorageCleaner

主要的SDK类,提供存储清理功能。

构造函数

constructor(adapter: IStorageAdapter, config?: Partial<IStorageCleanerConfig>)

主要方法

| 方法 | 描述 | 返回值 | |------|------|--------| | installProxy() | 安装代理,开始监控存储操作 | void | | uninstallProxy() | 卸载代理,停止监控 | void | | manualCleanup() | 手动触发清理 | Promise<void> | | getStats() | 获取存储统计信息 | IStorageStats | | checkHealth() | 检查存储健康状态 | Promise<HealthResult> | | updateConfig(config) | 更新配置 | void | | destroy() | 销毁实例,清理资源 | void |

便捷函数

// 创建localStorage清理器
createLocalStorageCleaner(config?: Partial<IStorageCleanerConfig>): StorageCleaner

// 创建IndexedDB清理器
createIndexedDBCleaner(
  dbName?: string,
  storeName?: string,
  config?: Partial<IStorageCleanerConfig>
): StorageCleaner

⚙️ 配置选项

interface IStorageCleanerConfig {
  // 最大存储大小(字节)
  maxStorageSize: number;

  // 清理阈值(0-1之间,表示达到最大容量的百分比时开始清理)
  cleanupThreshold: number;

  // 清理比例(0-1之间,表示每次清理释放的空间比例)
  cleanupRatio: number;

  // 访问记录的最大保存时间(毫秒)
  maxAccessAge: number;

  // 是否启用自动清理
  autoCleanup: boolean;

  // 清理策略
  strategy?: ICleanupStrategy;

  // 调试模式
  debug?: boolean;

  // 排除的键(不会被清理)
  excludeKeys?: string[];
}

默认配置

const DEFAULT_CONFIG = {
  maxStorageSize: 5 * 1024 * 1024, // 5MB
  cleanupThreshold: 0.8, // 80%
  cleanupRatio: 0.3, // 30%
  maxAccessAge: 7 * 24 * 60 * 60 * 1000, // 7天
  autoCleanup: true,
  debug: false,
  excludeKeys: []
};

🏗️ 技术设计

架构概览

┌─────────────────────────────────────────────────────────────┐
│                    Browser Storage LRU Cleaner             │
├─────────────────────────────────────────────────────────────┤
│  StorageCleaner (主控制器)                                   │
│  ├── 代理管理 (Proxy Management)                            │
│  ├── 配置管理 (Config Management)                           │
│  ├── 统计监控 (Stats Monitoring)                            │
│  └── 健康检查 (Health Check)                                │
├─────────────────────────────────────────────────────────────┤
│  清理策略层 (Cleanup Strategy Layer)                        │
│  ├── LRUStrategy (默认LRU算法)                              │
│  ├── ICleanupStrategy (策略接口)                            │
│  └── 自定义策略 (Custom Strategies)                         │
├─────────────────────────────────────────────────────────────┤
│  存储适配器层 (Storage Adapter Layer)                       │
│  ├── LocalStorageAdapter                                   │
│  ├── IndexedDBAdapter                                      │
│  └── IStorageAdapter (适配器接口)                           │
├─────────────────────────────────────────────────────────────┤
│  工具层 (Utility Layer)                                     │
│  ├── 数据压缩 (Data Compression)                            │
│  ├── 大小计算 (Size Calculation)                            │
│  ├── 防抖节流 (Debounce/Throttle)                          │
│  └── 浏览器兼容性检查 (Browser Compatibility)               │
└─────────────────────────────────────────────────────────────┘

核心设计原则

1. 代理模式 (Proxy Pattern)

通过JavaScript Proxy和Object.defineProperty实现透明代理:

// localStorage代理实现
const proxiedStorage = new Proxy(originalLocalStorage, {
  get(target, prop, receiver) {
    if (prop === 'getItem') {
      return function(key: string) {
        const result = target.getItem(key);
        if (result !== null) {
          // 记录访问,更新LRU
          self.strategy.recordAccess(key);
        }
        return result;
      };
    }

    if (prop === 'setItem') {
      return function(key: string, value: string) {
        // 检查是否需要清理
        if (self.config.autoCleanup) {
          self.checkAndCleanup(requiredSpace);
        }

        target.setItem(key, value);
        self.strategy.recordAccess(key);
      };
    }

    return Reflect.get(target, prop, receiver);
  }
});

// 替换全局localStorage
Object.defineProperty(window, 'localStorage', {
  value: proxiedStorage,
  writable: true,
  configurable: true
});

2. LRU算法实现

基于访问时间和频次的LRU算法:

interface IAccessRecord {
  lastAccess: number;    // 最后访问时间
  accessCount: number;   // 访问次数
  size: number;          // 数据大小
}

// LRU排序逻辑
sortKeysByLRU(keys: string[]): string[] {
  return keys.sort((a, b) => {
    const recordA = this.accessRecords[a];
    const recordB = this.accessRecords[b];

    // 优先级:无记录 > 访问时间早 > 访问次数少
    if (!recordA && !recordB) return 0;
    if (!recordA) return -1;
    if (!recordB) return 1;

    const timeDiff = recordA.lastAccess - recordB.lastAccess;
    if (timeDiff !== 0) return timeDiff;

    return recordA.accessCount - recordB.accessCount;
  });
}

3. 数据压缩存储

访问记录使用压缩格式存储,减少元数据占用:

// 压缩前:
{
  "user_data": {
    "lastAccess": 1640995200000,
    "accessCount": 5,
    "size": 1024
  }
}

// 压缩后:
{
  "user_data": [1640995200000, 5, 1024]
}

4. 智能清理策略

多层次的清理触发机制:

  1. 容量阈值触发: 达到设定容量百分比时自动清理
  2. 写入前检查: 新数据写入前预检查空间
  3. 时间过期清理: 定期清理过期的访问记录
  4. 手动触发: 提供手动清理接口
checkAndCleanup(requiredSpace: number = 0): Promise<void> {
  const currentSize = await this.adapter.getStorageSize();
  const threshold = this.config.maxStorageSize * this.config.cleanupThreshold;

  if (currentSize + requiredSpace > threshold) {
    await this.cleanup(requiredSpace);
  }
}

性能优化

1. 防抖处理

访问记录的保存使用防抖,避免频繁写入:

private saveAccessRecordsDebounced = Utils.debounce(() => {
  this.saveAccessRecords();
}, 1000);

2. 异步操作

所有可能阻塞的操作都设计为异步:

async updateKeySizes(keys: string[]): Promise<void> {
  for (const key of keys) {
    if (this.accessRecords[key]) {
      const size = await this.storageAdapter.getItemSize(key);
      this.accessRecords[key].size = size;
    }
  }
}

3. 最小清理原则

只清理必要的数据,保证新数据能够成功插入:

const spaceToFree = Math.max(
  currentSize - (maxSize * (1 - cleanupRatio)), // 清理到目标容量
  requiredSpace // 或者释放足够的空间
);

兼容性设计

1. 浏览器支持检查

static checkBrowserSupport(): {
  localStorage: boolean;
  indexedDB: boolean;
  proxy: boolean;
} {
  return {
    localStorage: typeof Storage !== 'undefined' && !!window.localStorage,
    indexedDB: typeof window !== 'undefined' && !!window.indexedDB,
    proxy: typeof Proxy !== 'undefined'
  };
}

2. 优雅降级

当代理不可用时,提供手动清理模式:

if (!Utils.checkBrowserSupport().proxy) {
  console.warn('Proxy not supported, falling back to manual mode');
  // 提供手动清理接口
}

错误处理

1. 存储异常处理

try {
  this.originalLocalStorage.setItem(key, value);
} catch (error) {
  if (error.name === 'QuotaExceededError') {
    // 存储空间不足,触发清理
    await this.cleanup(requiredSpace);
    // 重试
    this.originalLocalStorage.setItem(key, value);
  } else {
    throw error;
  }
}

2. 数据恢复

访问记录损坏时的恢复机制:

try {
  this.accessRecords = Utils.decompressAccessRecords(data);
} catch (error) {
  console.warn('Failed to load access records, starting fresh');
  this.accessRecords = {};
}

💡 使用示例

基础使用

import { createLocalStorageCleaner } from 'browser-storage-lru-cleaner';

// 1. 创建清理器
const cleaner = createLocalStorageCleaner({
  maxStorageSize: 2 * 1024 * 1024, // 2MB
  cleanupThreshold: 0.8,
  autoCleanup: true,
  debug: true
});

// 2. 安装代理
cleaner.installProxy();

// 3. 正常使用localStorage,清理器会自动工作
localStorage.setItem('user_profile', JSON.stringify({
  id: 1,
  name: 'John Doe',
  preferences: { theme: 'dark' }
}));

// 4. 监控存储状态
setInterval(() => {
  const stats = cleaner.getStats();
  console.log(`存储使用率: ${Math.round(stats.usageRatio * 100)}%`);
}, 5000);

高级配置

import { StorageCleaner, LocalStorageAdapter, LRUStrategy } from 'browser-storage-lru-cleaner';

// 自定义LRU策略配置
const customStrategy = new LRUStrategy(adapter, {
  maxAccessAge: 3 * 24 * 60 * 60 * 1000, // 3天过期
  excludeKeys: ['app_config', 'user_token'], // 排除关键数据
  debug: true
});

// 创建清理器
const cleaner = new StorageCleaner(new LocalStorageAdapter(), {
  maxStorageSize: 10 * 1024 * 1024, // 10MB
  cleanupThreshold: 0.85, // 85%时清理
  cleanupRatio: 0.4, // 清理40%
  strategy: customStrategy,
  excludeKeys: ['critical_data', 'session_info']
});

cleaner.installProxy();

React集成示例

import React, { useEffect, useState } from 'react';
import { createLocalStorageCleaner } from 'browser-storage-lru-cleaner';

const StorageMonitor: React.FC = () => {
  const [cleaner] = useState(() => createLocalStorageCleaner({
    maxStorageSize: 5 * 1024 * 1024,
    debug: process.env.NODE_ENV === 'development'
  }));

  const [stats, setStats] = useState(cleaner.getStats());

  useEffect(() => {
    // 安装代理
    cleaner.installProxy();

    // 定期更新统计
    const interval = setInterval(() => {
      setStats(cleaner.getStats());
    }, 1000);

    return () => {
      clearInterval(interval);
      cleaner.destroy();
    };
  }, [cleaner]);

  const handleManualCleanup = async () => {
    await cleaner.manualCleanup();
    setStats(cleaner.getStats());
  };

  return (
    <div>
      <h3>存储监控</h3>
      <p>使用率: {Math.round(stats.usageRatio * 100)}%</p>
      <p>项目数: {stats.itemCount}</p>
      <p>清理次数: {stats.cleanupCount}</p>
      <button onClick={handleManualCleanup}>手动清理</button>
    </div>
  );
};

Vue集成示例

import { defineComponent, ref, onMounted, onUnmounted } from 'vue';
import { createLocalStorageCleaner } from 'browser-storage-lru-cleaner';

export default defineComponent({
  name: 'StorageManager',
  setup() {
    const cleaner = createLocalStorageCleaner({
      maxStorageSize: 3 * 1024 * 1024,
      cleanupThreshold: 0.9
    });

    const stats = ref(cleaner.getStats());
    let interval: NodeJS.Timeout;

    onMounted(() => {
      cleaner.installProxy();

      interval = setInterval(() => {
        stats.value = cleaner.getStats();
      }, 2000);
    });

    onUnmounted(() => {
      clearInterval(interval);
      cleaner.destroy();
    });

    const checkHealth = async () => {
      const health = await cleaner.checkHealth();
      console.log('健康检查:', health);
    };

    return {
      stats,
      checkHealth,
      manualCleanup: () => cleaner.manualCleanup()
    };
  }
});

IndexedDB使用示例

import { createIndexedDBCleaner } from 'browser-storage-lru-cleaner';

// 创建IndexedDB清理器
const cleaner = createIndexedDBCleaner('MyApp', 'cache', {
  maxStorageSize: 50 * 1024 * 1024, // 50MB
  cleanupThreshold: 0.8
});

// 获取适配器进行操作
const adapter = cleaner.getAdapter();

// 存储大量数据
async function cacheApiResponse(url: string, data: any) {
  const key = `api_cache_${btoa(url)}`;
  const value = JSON.stringify({
    data,
    timestamp: Date.now(),
    url
  });

  await adapter.setItem(key, value);
}

// 读取缓存
async function getCachedResponse(url: string) {
  const key = `api_cache_${btoa(url)}`;
  const cached = await adapter.getItem(key);

  if (cached) {
    const parsed = JSON.parse(cached);
    // 检查是否过期(1小时)
    if (Date.now() - parsed.timestamp < 60 * 60 * 1000) {
      return parsed.data;
    }
  }

  return null;
}

🎯 最佳实践

1. 配置建议

// 移动端配置(存储空间有限)
const mobileConfig = {
  maxStorageSize: 2 * 1024 * 1024, // 2MB
  cleanupThreshold: 0.7, // 70%时清理
  cleanupRatio: 0.5, // 清理50%
  maxAccessAge: 3 * 24 * 60 * 60 * 1000 // 3天
};

// 桌面端配置(存储空间充足)
const desktopConfig = {
  maxStorageSize: 20 * 1024 * 1024, // 20MB
  cleanupThreshold: 0.9, // 90%时清理
  cleanupRatio: 0.3, // 清理30%
  maxAccessAge: 14 * 24 * 60 * 60 * 1000 // 14天
};

// 根据设备类型选择配置
const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
const config = isMobile ? mobileConfig : desktopConfig;

2. 关键数据保护

const cleaner = createLocalStorageCleaner({
  // 排除关键业务数据
  excludeKeys: [
    'user_token',
    'app_config',
    'user_preferences',
    'session_data'
  ],
  maxStorageSize: 5 * 1024 * 1024
});

// 或者使用前缀保护
const protectedPrefixes = ['auth_', 'config_', 'critical_'];
const isProtectedKey = (key: string) =>
  protectedPrefixes.some(prefix => key.startsWith(prefix));

3. 错误处理

try {
  cleaner.installProxy();
} catch (error) {
  console.error('Failed to install storage cleaner:', error);

  // 降级处理
  if (error.message.includes('Proxy')) {
    console.warn('Proxy not supported, using manual cleanup mode');
    // 定期手动清理
    setInterval(() => {
      cleaner.manualCleanup();
    }, 5 * 60 * 1000); // 5分钟
  }
}

4. 性能监控

// 监控清理效果
cleaner.getStrategy().getAccessStats = function() {
  const records = Object.values(this.accessRecords);
  return {
    totalRecords: records.length,
    averageAccessCount: records.reduce((sum, r) => sum + r.accessCount, 0) / records.length,
    oldestAccess: Math.min(...records.map(r => r.lastAccess)),
    newestAccess: Math.max(...records.map(r => r.lastAccess))
  };
};

// 定期报告
setInterval(() => {
  const stats = cleaner.getStats();
  const accessStats = cleaner.getStrategy().getAccessStats();

  console.log('Storage Stats:', {
    usageRatio: Math.round(stats.usageRatio * 100) + '%',
    itemCount: stats.itemCount,
    cleanupCount: stats.cleanupCount,
    avgAccess: Math.round(accessStats.averageAccessCount)
  });
}, 30000);

5. 数据迁移

// 版本升级时的数据迁移
function migrateStorageData() {
  const version = localStorage.getItem('storage_version');

  if (!version || version < '2.0') {
    // 清理旧版本的元数据
    Object.keys(localStorage).forEach(key => {
      if (key.startsWith('__old_lru_')) {
        localStorage.removeItem(key);
      }
    });

    localStorage.setItem('storage_version', '2.0');
  }
}

// 在初始化前执行迁移
migrateStorageData();
const cleaner = createLocalStorageCleaner(config);

❓ 常见问题

Q: 代理会影响性能吗?

A: 代理的性能影响很小。我们使用了以下优化:

  • 防抖处理减少频繁的元数据写入
  • 异步操作避免阻塞主线程
  • 压缩存储减少元数据占用
  • 智能清理避免不必要的操作

Q: 如何处理存储配额超限?

A: SDK提供多层保护:

try {
  localStorage.setItem(key, value);
} catch (error) {
  if (error.name === 'QuotaExceededError') {
    // 自动触发清理
    await cleaner.manualCleanup();
    // 重试操作
    localStorage.setItem(key, value);
  }
}

Q: 可以在Web Worker中使用吗?

A: 目前不支持Web Worker,因为:

  • Web Worker中没有DOM和window对象
  • 无法访问localStorage(可以使用IndexedDB)
  • 代理模式依赖全局对象替换

Q: 如何自定义清理策略?

A: 实现ICleanupStrategy接口:

class TimeBasedStrategy implements ICleanupStrategy {
  getName() { return 'TimeBased'; }

  recordAccess(key: string) {
    // 记录访问时间
  }

  getKeysToCleanup(allKeys: string[], currentSize: number, maxSize: number) {
    // 返回超过时间阈值的键
    return expiredKeys;
  }

  cleanup(keys: string[]) {
    // 清理后处理
  }
}

Q: 支持哪些浏览器?

A: 支持所有现代浏览器:

  • Chrome 49+
  • Firefox 18+
  • Safari 10+
  • Edge 12+
  • 移动端浏览器

Q: 数据会丢失吗?

A: 不会意外丢失重要数据:

  • 通过excludeKeys保护关键数据
  • LRU算法优先清理不常用数据
  • 提供手动控制清理时机
  • 支持清理前的确认回调

Q: 如何调试清理过程?

A: 启用调试模式:

const cleaner = createLocalStorageCleaner({
  debug: true // 启用详细日志
});

// 监听清理事件
cleaner.on('cleanup', (event) => {
  console.log('清理事件:', event);
});

🔧 开发

构建项目

# 安装依赖
npm install

# 构建
npm run build

# 开发模式
npm run dev

# 运行测试
npm test

# 启动演示
npm run demo

项目结构

src/
├── interfaces/          # TypeScript接口定义
├── strategies/          # 清理策略实现
│   └── lru-strategy.ts
├── adapters/           # 存储适配器
│   ├── localstorage-adapter.ts
│   └── indexeddb-adapter.ts
├── utils/              # 工具函数
├── storage-cleaner.ts  # 主要SDK类
└── index.ts           # 入口文件

demo/                  # 演示页面
├── index.html
└── demo.js

tests/                 # 测试文件
└── *.test.ts

📄 许可证

MIT License - 详见 LICENSE 文件

🤝 贡献

欢迎提交Issue和Pull Request!

  1. Fork 项目
  2. 创建特性分支 (git checkout -b feature/AmazingFeature)
  3. 提交更改 (git commit -m 'Add some AmazingFeature')
  4. 推送到分支 (git push origin feature/AmazingFeature)
  5. 打开Pull Request

📞 支持


⭐ 如果这个项目对你有帮助,请给个Star!