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

@d1-always/event-manager

v1.0.1

Published

一个高性能、类型安全的事件管理器,专为可靠的事件数据上报而设计。支持队列管理、智能重试、持久化存储等企业级特性。

Readme

@d1-always/event-manager

一个高性能、类型安全的事件管理器,专为可靠的事件数据上报而设计。支持队列管理、智能重试、持久化存储等企业级特性。

npm version License: MIT CI Release Node.js Version TypeScript

✨ 特性

  • 🚀 高性能批处理 - 智能队列管理,支持批量事件处理
  • 🔄 智能重试机制 - 可配置的自动重试,确保数据不丢失
  • 💾 持久化存储 - 支持 localStorage、sessionStorage 或自定义存储
  • 🛡️ 完整类型安全 - 基于 TypeScript,提供完整的类型定义
  • 🎯 参数验证 - 内置必要参数检查,确保数据完整性
  • 🔧 高度可配置 - 灵活的配置选项满足各种业务场景
  • 🌐 国际化支持 - 可自定义错误消息和提示文本
  • 📊 监控友好 - 丰富的事件钩子和统计信息
  • ♻️ 生命周期管理 - 完整的启动/暂停/停止/销毁控制

📦 安装

npm install @d1-always/event-manager
yarn add @d1-always/event-manager
pnpm add @d1-always/event-manager

🚀 快速开始

基础示例

import { EventManager } from '@d1-always/event-manager';

// 定义事件数据类型
interface UserEvent {
  action: string;
  userId: string;
  page?: string;
}

// 定义上报数据类型
interface ReportData {
  event_type: string;
  user_id: string;
  timestamp: number;
  properties: Record<string, any>;
}

// 实现事件管理器
class MyEventManager extends EventManager<UserEvent, ReportData> {
  constructor() {
    super({
      storage: localStorage,
      key: 'user_events',
      max_event_num: 10,
      delay_time: 3,
      debug: true
    });
    
    // 设置事件钩子
    this.setEventHooks({
      onSuccess: (count) => console.log(`✅ 成功上报 ${count} 个事件`),
      onError: (error) => console.error('❌ 上报失败:', error)
    });
  }

  // 构造上报数据
  buildEvent(timestamp: number, eventInfo: UserEvent): ReportData {
    return {
      event_type: eventInfo.action,
      user_id: eventInfo.userId,
      timestamp,
      properties: {
        page: eventInfo.page || window.location.pathname,
        user_agent: navigator.userAgent
      }
    };
  }

  // 实现上报逻辑
  async reportEvent(eventList: EventManager.ReportEvent<ReportData>[]) {
    try {
      const response = await fetch('/api/events', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(eventList.map(e => e.eventInfo))
      });

      if (response.ok) {
        return eventList; // 返回成功的事件
      }
      throw new Error(`HTTP ${response.status}`);
    } catch (error) {
      console.error('上报请求失败:', error);
      return []; // 返回空数组表示全部失败
    }
  }
}

// 创建实例并使用
const eventManager = new MyEventManager();

// 推送事件
eventManager.pushEvent({
  required_keys: ['userId'], // 必要字段检查
  timestamp: Date.now(),
  retry_times: 0,
  eventInfo: {
    action: 'page_view',
    userId: '12345',
    page: '/dashboard'
  }
});

📚 API 文档

构造函数选项

interface Options {
  max_event_num: number;        // 每批最大事件数,默认 10
  max_retry_times: number;      // 最大重试次数,默认 3
  max_queue_len: number;        // 队列最大长度,默认 1000
  max_empty_run_times: number;  // 空队列休眠阈值,默认 10
  delay_time: number;           // 处理间隔(秒),默认 3
  debug?: boolean;              // 调试模式,默认 false
  logLevel?: LogLevel;          // 日志级别,默认 'warn'
  autoStart?: boolean;          // 自动开始,默认 true
  storage?: StorageLike;        // 存储对象(可选)
  key?: string;                 // 存储键名(使用存储时必需)
}

核心方法

事件操作

// 推送单个事件
pushEvent(event: EventInfo<T>): boolean

// 批量推送事件
pushEvents(events: EventInfo<T>[]): number

// 清空队列
clear(): void

生命周期控制

start(): Promise<void>    // 开始处理
pause(): void            // 暂停处理
resume(): void           // 恢复处理
stop(): void             // 停止处理(可恢复)
destroy(): void          // 销毁实例(不可恢复)

配置管理

// 更新配置
updateOptions(options: Partial<Options>): void

// 获取当前配置
getOptions(): Readonly<Options>

// 设置事件钩子
setEventHooks(hooks: Partial<EventHooks<T>>): void

// 设置消息文本(国际化)
setMessages(messages: Partial<Messages>): void

状态监控

// 获取运行状态
getStats(): Stats

interface Stats {
  queueLength: number;     // 队列长度
  emptyRunTimes: number;   // 空运行次数
  isRunning: boolean;      // 是否运行中
  isPaused: boolean;       // 是否已暂停
  isDestroyed: boolean;    // 是否已销毁
}

抽象方法(需实现)

// 构造上报数据
abstract buildEvent(timestamp: number, eventInfo: T): R;

// 执行事件上报
abstract reportEvent(events: ReportEvent<R>[]): Promise<ReportEvent<R>[]>;

事件钩子

interface EventHooks<T> {
  onSuccess?: (count: number) => void;                    // 上报成功
  onError?: (error: string, events: EventInfo<T>[]) => void;  // 发生错误
  onMaxRetryReached?: (events: EventInfo<T>[]) => void;   // 达到最大重试次数
  onQueueFull?: (event: EventInfo<T>) => void;           // 队列已满
}

📖 使用场景

1. 用户行为追踪

interface UserAction {
  event: string;
  userId: string;
  elementId?: string;
}

class BehaviorTracker extends EventManager<UserAction, any> {
  constructor() {
    super({
      storage: localStorage,
      key: 'behavior_events',
      max_event_num: 20,
      delay_time: 1
    });
  }

  buildEvent(timestamp: number, { event, userId, elementId }: UserAction) {
    return {
      event_name: event,
      user_id: userId,
      element_id: elementId,
      timestamp,
      page_url: location.href
    };
  }

  async reportEvent(events: EventManager.ReportEvent<any>[]) {
    const response = await fetch('/api/analytics', {
      method: 'POST',
      body: JSON.stringify(events.map(e => e.eventInfo))
    });
    return response.ok ? events : [];
  }

  // 便捷方法
  trackClick(elementId: string, userId: string) {
    this.pushEvent({
      required_keys: ['userId'],
      timestamp: Date.now(),
      retry_times: 0,
      eventInfo: { event: 'click', userId, elementId }
    });
  }
}

const tracker = new BehaviorTracker();
tracker.trackClick('submit-btn', 'user123');

2. 错误日志上报

interface ErrorEvent {
  error: Error;
  userId?: string;
  context?: Record<string, any>;
}

class ErrorReporter extends EventManager<ErrorEvent, any> {
  constructor() {
    super({
      max_retry_times: 5,  // 错误日志重试更多次
      max_event_num: 5,
      delay_time: 0.5,     // 更快上报
      logLevel: 'error'
    });
  }

  buildEvent(timestamp: number, { error, userId, context }: ErrorEvent) {
    return {
      level: 'error',
      message: error.message,
      stack: error.stack,
      user_id: userId,
      url: location.href,
      timestamp,
      context
    };
  }

  async reportEvent(events: EventManager.ReportEvent<any>[]) {
    const response = await fetch('/api/errors', {
      method: 'POST',
      body: JSON.stringify(events.map(e => e.eventInfo))
    });
    return response.ok ? events : [];
  }

  logError(error: Error, userId?: string, context?: Record<string, any>) {
    this.pushEvent({
      required_keys: [],
      timestamp: Date.now(),
      retry_times: 0,
      eventInfo: { error, userId, context }
    });
  }
}

// 全局错误监听
const errorReporter = new ErrorReporter();
window.addEventListener('error', (e) => {
  errorReporter.logError(new Error(e.message));
});

3. 业务指标收集

interface Metric {
  name: string;
  value: number;
  tags?: Record<string, string>;
}

class MetricsCollector extends EventManager<Metric, any> {
  constructor() {
    super({
      max_event_num: 50,
      delay_time: 10,      // 10秒批量上报
      storage: localStorage,
      key: 'metrics'
    });
  }

  buildEvent(timestamp: number, { name, value, tags }: Metric) {
    return {
      metric_name: name,
      value,
      timestamp,
      tags: { ...tags, host: location.hostname }
    };
  }

  async reportEvent(events: EventManager.ReportEvent<any>[]) {
    const response = await fetch('/api/metrics', {
      method: 'POST',
      body: JSON.stringify(events.map(e => e.eventInfo))
    });
    return response.ok ? events : [];
  }

  recordTiming(operation: string, duration: number) {
    this.pushEvent({
      required_keys: [],
      timestamp: Date.now(),
      retry_times: 0,
      eventInfo: {
        name: 'operation_duration',
        value: duration,
        tags: { operation }
      }
    });
  }
}

const metrics = new MetricsCollector();
const start = performance.now();
// ... 执行操作
metrics.recordTiming('api_call', performance.now() - start);

🔧 高级配置

自定义存储

class CustomStorage implements EventManager.StorageLike {
  private cache = new Map<string, string>();

  get(key: string): string | null {
    return this.cache.get(key) || null;
  }

  set(key: string, value: string): void {
    this.cache.set(key, value);
    // 可添加持久化逻辑
    this.saveToDatabase(key, value);
  }

  private async saveToDatabase(key: string, value: string) {
    // 自定义持久化逻辑
  }
}

多环境配置

const isDev = process.env.NODE_ENV === 'development';

const eventManager = new MyEventManager({
  debug: isDev,
  logLevel: isDev ? 'debug' : 'warn',
  delay_time: isDev ? 1 : 5,
  max_event_num: isDev ? 3 : 20
});

条件上报

class ConditionalEventManager extends EventManager<any, any> {
  private enabled = true;

  async reportEvent(events: EventManager.ReportEvent<any>[]) {
    if (!this.enabled) return [];
    
    // 根据其他条件决定是否上报
    const shouldReport = await this.checkReportingConditions();
    if (!shouldReport) return [];

    // 执行实际上报
    return this.performReport(events);
  }

  setEnabled(enabled: boolean) {
    this.enabled = enabled;
    if (!enabled) this.pause();
    else this.resume();
  }

  private async checkReportingConditions(): Promise<boolean> {
    // 自定义条件判断逻辑
    return true;
  }
}

📋 最佳实践

  1. 合理设置队列大小

    // 根据业务量调整,避免内存溢出
    max_queue_len: 1000
  2. 优化上报频率

    // 平衡及时性和性能
    delay_time: 3,        // 3秒间隔
    max_event_num: 10     // 每批10个事件
  3. 错误处理

    this.setEventHooks({
      onError: (error, events) => {
        // 记录错误日志
        console.error('Event error:', error);
        // 可选:发送到监控系统
        sendToMonitoring(error, events);
      }
    });
  4. 资源清理

    // 组件销毁时清理资源
    componentWillUnmount() {
      this.eventManager.destroy();
    }
  5. 开发调试

    if (process.env.NODE_ENV === 'development') {
      eventManager.updateOptions({ debug: true, logLevel: 'debug' });
    }

🔄 CI/CD 流程

自动化工作流

项目采用 GitHub Actions 实现全自动 CI/CD 流程:

🔍 PR 检查流程

  • 触发条件: PR 创建/更新
  • 检查项目:
    • TypeScript 类型检查
    • 项目构建测试
    • 包结构验证
    • 自动化测试

🚀 自动发布流程

  • 触发条件: 合并到 main 分支
  • 发布条件: package.json 版本号变更
  • 自动执行:
    1. 检查版本是否已发布
    2. 构建和测试
    3. 发布到 NPM
    4. 创建 GitHub Release
    5. 发送通知

📦 发布新版本

  1. 更新版本号

    # 手动更新 package.json 中的 version
    vim package.json
  2. 提交到 main 分支

    git add package.json
    git commit -m "chore: bump version to x.x.x"
    git push origin main
  3. 自动发布

    • GitHub Actions 会自动检测版本变更
    • 执行构建、测试和发布流程
    • 发布成功后创建 GitHub Release

🔧 配置要求

为了让 CI/CD 正常工作,需要在 GitHub 仓库中配置:

  • NPM_TOKEN: NPM 发布令牌(Repository Secrets)
  • GITHUB_TOKEN: GitHub API 令牌(自动提供)

🤝 贡献

我们欢迎任何形式的贡献!请查看 贡献指南 了解详细信息。

快速贡献流程

  1. Fork 项目仓库
  2. 创建功能分支:git checkout -b feature/amazing-feature
  3. 提交更改:git commit -m 'feat: add amazing feature'
  4. 推送分支:git push origin feature/amazing-feature
  5. 创建 Pull Request

📄 许可证

MIT License


需要帮助? 请查看 Issues 或提交新的问题。