{"_id":"@alonye/rate-limiter-nest","_rev":"2-6671ffb415a42bb05b86bbb5c6774c5d","name":"@alonye/rate-limiter-nest","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@alonye/rate-limiter-nest","version":"1.0.0","keywords":["nestjs","rate-limiter","redis","opentelemetry","distributed","queue","throttling"],"_id":"@alonye/rate-limiter-nest@1.0.0","maintainers":[{"name":"alonye","email":"alon.yeroushalmi@entrata.com"}],"dist":{"shasum":"526c0fbeb02c8f957d29668fb993bc3fa6be2643","tarball":"https://registry.npmjs.org/@alonye/rate-limiter-nest/-/rate-limiter-nest-1.0.0.tgz","fileCount":8,"integrity":"sha512-sPj5tdXICE9ZqXtnSUmfIN2BAVZmY3w6wM2l1Elmox37KZn72ZoDPqi1BgBi9EEWj/AS1LVppfssvG9YO4Cphw==","signatures":[{"sig":"MEQCIASZbQ4m+2YiKSm7X+HZM0BU8d1EI92FP+4/yJKXzDleAiADn3LalpQB99udsKRWGzax4Q+jtY2cAQSlam+7x0BAYQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":15564},"main":"./dist/index.cjs","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"621132e2de682fb5a21a6b36ca80e054f47c9fae","scripts":{"dev":"tsup --watch","lint":"eslint src --ext .ts","test":"jest","build":"tsup","clean":"rimraf dist"},"_npmUser":{"name":"alonye","email":"alon.yeroushalmi@entrata.com"},"_npmVersion":"10.9.2","description":"NestJS adapter for the rate limiter core package","directories":{},"sideEffects":false,"_nodeVersion":"22.17.1","dependencies":{"@alonye/rate-limiter-core":"workspace:*"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.0.0","tsup":"^8.0.0","eslint":"^8.0.0","rimraf":"^5.0.0","ts-jest":"^29.0.0","typescript":"^5.0.0","@types/jest":"^29.5.0","@nestjs/core":"^10.0.0","@nestjs/common":"^10.0.0","@nestjs/config":"^3.0.0","@nestjs/testing":"^10.0.0","@typescript-eslint/parser":"^6.0.0","@typescript-eslint/eslint-plugin":"^6.0.0"},"peerDependencies":{"ioredis":"^5.0.0","@nestjs/core":"^8.0.0 || ^9.0.0 || ^10.0.0","@nestjs/common":"^8.0.0 || ^9.0.0 || ^10.0.0","@nestjs/config":"^1.0.0 || ^2.0.0 || ^3.0.0","@alonye/rate-limiter-core":"^1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/rate-limiter-nest_1.0.0_1757516028917_0.6140089675654727","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@alonye/rate-limiter-nest","version":"1.0.1","description":"NestJS adapter for the rate limiter core package","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","require":"./dist/index.cjs","types":"./dist/index.d.ts"}},"sideEffects":false,"scripts":{"build":"tsup","dev":"tsup --watch","test":"jest","lint":"eslint src --ext .ts","clean":"rimraf dist"},"keywords":["nestjs","rate-limiter","redis","opentelemetry","distributed","queue","throttling"],"peerDependencies":{"@nestjs/common":"^8.0.0 || ^9.0.0 || ^10.0.0","@nestjs/config":"^1.0.0 || ^2.0.0 || ^3.0.0","@nestjs/core":"^8.0.0 || ^9.0.0 || ^10.0.0","ioredis":"^5.0.0","@alonye/rate-limiter-core":"^1.0.0"},"dependencies":{"@alonye/rate-limiter-core":"^1.0.0"},"devDependencies":{"@nestjs/common":"^10.0.0","@nestjs/config":"^3.0.0","@nestjs/core":"^10.0.0","@nestjs/testing":"^10.0.0","@types/jest":"^29.5.0","@typescript-eslint/eslint-plugin":"^6.0.0","@typescript-eslint/parser":"^6.0.0","eslint":"^8.0.0","jest":"^29.0.0","rimraf":"^5.0.0","ts-jest":"^29.0.0","tsup":"^8.0.0","typescript":"^5.0.0"},"_id":"@alonye/rate-limiter-nest@1.0.1","gitHead":"621132e2de682fb5a21a6b36ca80e054f47c9fae","_nodeVersion":"22.17.1","_npmVersion":"10.9.2","dist":{"integrity":"sha512-n3VYfKYRs545jGWuqHVoVww0VHY4553ToWuNcjp/5tH9bHMrFwlr2BllB8mPRhkR4TJ2qtXSrI1QOJ3aCxEWdA==","shasum":"fc158c8933238924c99aecd0436512ec3dfdacfa","tarball":"https://registry.npmjs.org/@alonye/rate-limiter-nest/-/rate-limiter-nest-1.0.1.tgz","fileCount":8,"unpackedSize":15559,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFRmeRyLVEKP5mlb+hyidl8RKwiJ8/+yEJAKZs5kqrIXAiBPZwcmqor5ZCB/ecC5jqtGEriDLWP6cxwhryLRr0hBBA=="}]},"_npmUser":{"name":"alonye","email":"alon.yeroushalmi@entrata.com"},"directories":{},"maintainers":[{"name":"alonye","email":"alon.yeroushalmi@entrata.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/rate-limiter-nest_1.0.1_1757516153665_0.5254866091272705"},"_hasShrinkwrap":false}},"time":{"created":"2025-09-10T14:53:48.805Z","modified":"2025-09-10T14:55:54.055Z","1.0.0":"2025-09-10T14:53:49.087Z","1.0.1":"2025-09-10T14:55:53.846Z"},"keywords":["nestjs","rate-limiter","redis","opentelemetry","distributed","queue","throttling"],"description":"NestJS adapter for the rate limiter core package","maintainers":[{"name":"alonye","email":"alon.yeroushalmi@entrata.com"}],"readme":"# @eli-plus/rate-limiter-nest\n\nNestJS adapter for the rate limiter core package.\n\n## Features\n\n- **Dependency Injection**: Seamless integration with NestJS DI container\n- **Configuration Integration**: Works with NestJS ConfigService\n- **Lifecycle Management**: Automatic startup and shutdown handling\n- **Logger Integration**: Uses NestJS Logger for consistent logging\n- **Telemetry Support**: OpenTelemetry integration\n\n## Installation\n\n```bash\nnpm install @eli-plus/rate-limiter-nest @eli-plus/rate-limiter-core\n```\n\n## Usage\n\n### Basic Setup\n\n```typescript\nimport { Module } from '@nestjs/common';\nimport { ConfigModule, ConfigService } from '@nestjs/config';\nimport {\n  RateLimiterModule,\n  RateLimiterService,\n} from '@eli-plus/rate-limiter-nest';\nimport Redis from 'ioredis';\n\n@Module({\n  imports: [\n    ConfigModule.forRoot(),\n    RateLimiterModule.forRoot({\n      use: 'flexible',\n      redis: new Redis({\n        host: 'localhost',\n        port: 6379,\n      }),\n      profiles: {\n        google: {\n          profile: 'google',\n          points: 100,\n          durationSec: 60,\n        },\n        microsoft: {\n          profile: 'microsoft',\n          points: 50,\n          durationSec: 60,\n        },\n      },\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\n### Async Configuration\n\n```typescript\nimport { Module } from '@nestjs/common';\nimport { ConfigModule, ConfigService } from '@nestjs/config';\nimport {\n  RateLimiterModule,\n  RateLimiterService,\n} from '@eli-plus/rate-limiter-nest';\nimport Redis from 'ioredis';\n\n@Module({\n  imports: [\n    ConfigModule.forRoot(),\n    RateLimiterModule.forRootAsync({\n      use: 'flexible',\n      useFactory: (configService: ConfigService) => ({\n        redis: new Redis({\n          host: configService.get('REDIS_HOST'),\n          port: configService.get('REDIS_PORT'),\n        }),\n        profiles: {\n          google: {\n            profile: 'google',\n            points: configService.get('GOOGLE_RATE_LIMIT_POINTS'),\n            durationSec: configService.get('GOOGLE_RATE_LIMIT_DURATION'),\n          },\n          microsoft: {\n            profile: 'microsoft',\n            points: configService.get('MICROSOFT_RATE_LIMIT_POINTS'),\n            durationSec: configService.get('MICROSOFT_RATE_LIMIT_DURATION'),\n          },\n        },\n        claimLockTtlSec: configService.get('RATE_LIMITER_CLAIM_LOCK_TTL'),\n        resultTtlSec: configService.get('RATE_LIMITER_RESULT_TTL'),\n      }),\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\n### Using the Service\n\n```typescript\nimport { Injectable } from '@nestjs/common';\nimport { RateLimiterService } from '@eli-plus/rate-limiter-nest';\n\n@Injectable()\nexport class EmailService {\n  constructor(private readonly rateLimiter: RateLimiterService) {}\n\n  async sendEmail(emailData: EmailData): Promise<EmailResult> {\n    return await this.rateLimiter.schedule({\n      namespace: 'sendingEmail',\n      bucketKey: emailData.from,\n      profile: emailData.vendor,\n      jobId: emailData.messageId,\n      payloadJson: JSON.stringify(emailData),\n    });\n  }\n}\n```\n\n### Custom Job Executor\n\n```typescript\nimport { Injectable } from '@nestjs/common';\nimport { JobExecutor } from '@eli-plus/rate-limiter-core';\n\n@Injectable()\nexport class EmailJobExecutor implements JobExecutor {\n  constructor(private readonly emailService: EmailService) {}\n\n  async execute(descriptor: any): Promise<unknown> {\n    const payload = JSON.parse(descriptor.payloadJson);\n    return await this.emailService.doSendEmail(payload);\n  }\n}\n\n@Module({\n  providers: [\n    EmailJobExecutor,\n    {\n      provide: 'JOB_EXECUTOR',\n      useExisting: EmailJobExecutor,\n    },\n  ],\n})\nexport class EmailModule {}\n```\n\n## API Reference\n\n### RateLimiterModule\n\n#### forRoot(options)\n\nSynchronously configure the rate limiter module.\n\n**Options:**\n\n- `use`: Rate limiter type ('flexible' | 'bottleneck')\n- `redis`: Redis instance\n- `profiles`: Rate limit profiles\n- `claimLockTtlSec?`: Lock TTL in seconds (default: 30)\n- `resultTtlSec?`: Result TTL in seconds (default: 30)\n- `telemetry?`: Telemetry instance\n\n#### forRootAsync(options)\n\nAsynchronously configure the rate limiter module.\n\n**Options:**\n\n- `use`: Rate limiter type ('flexible' | 'bottleneck')\n- `imports?`: Additional modules to import\n- `inject?`: Dependencies to inject\n- `useFactory`: Factory function for configuration\n\n### RateLimiterService\n\n#### schedule<T>(opts: ScheduleOpts): Promise<T>\n\nSchedule a job for rate-limited execution.\n\n**Parameters:**\n\n- `opts.namespace`: Job namespace (e.g., 'sendingEmail')\n- `opts.bucketKey`: Rate limiter bucket key (e.g., sender email)\n- `opts.profile`: Rate limit profile (e.g., 'google', 'microsoft')\n- `opts.jobId`: Unique job identifier\n- `opts.payloadJson`: Job payload as JSON string\n- `opts.priority?`: Job priority (default: MEDIUM)\n\n## Configuration\n\n### Environment Variables\n\n```bash\nREDIS_HOST=localhost\nREDIS_PORT=6379\nREDIS_PASSWORD=your-password\nGOOGLE_RATE_LIMIT_POINTS=100\nGOOGLE_RATE_LIMIT_DURATION=60\nMICROSOFT_RATE_LIMIT_POINTS=50\nMICROSOFT_RATE_LIMIT_DURATION=60\nRATE_LIMITER_CLAIM_LOCK_TTL=30\nRATE_LIMITER_RESULT_TTL=30\n```\n\n### Rate Limit Profiles\n\n```typescript\nconst profiles = {\n  google: {\n    profile: 'google',\n    points: 100, // 100 requests per window\n    durationSec: 60, // 60 second window\n  },\n  microsoft: {\n    profile: 'microsoft',\n    points: 50, // 50 requests per window\n    durationSec: 60, // 60 second window\n  },\n};\n```\n\n## Lifecycle Management\n\nThe rate limiter service automatically handles:\n\n- **Startup**: Initializes Redis connections and starts dispatcher\n- **Shutdown**: Gracefully stops dispatcher and closes connections\n- **Error Handling**: Logs errors and provides fallback behavior\n\n## Testing\n\n```typescript\nimport { Test, TestingModule } from '@nestjs/testing';\nimport { RateLimiterService } from '@eli-plus/rate-limiter-nest';\n\ndescribe('EmailService', () => {\n  let service: EmailService;\n  let rateLimiter: RateLimiterService;\n\n  beforeEach(async () => {\n    const module: TestingModule = await Test.createTestingModule({\n      providers: [\n        EmailService,\n        {\n          provide: RateLimiterService,\n          useValue: {\n            schedule: jest.fn(),\n          },\n        },\n      ],\n    }).compile();\n\n    service = module.get<EmailService>(EmailService);\n    rateLimiter = module.get<RateLimiterService>(RateLimiterService);\n  });\n\n  it('should send email with rate limiting', async () => {\n    const mockResult = { success: true };\n    jest.spyOn(rateLimiter, 'schedule').mockResolvedValue(mockResult);\n\n    const result = await service.sendEmail({\n      from: 'test@example.com',\n      to: 'recipient@example.com',\n      subject: 'Test',\n      body: 'Test message',\n    });\n\n    expect(result).toEqual(mockResult);\n    expect(rateLimiter.schedule).toHaveBeenCalledWith({\n      namespace: 'sendingEmail',\n      bucketKey: 'test@example.com',\n      profile: 'google',\n      jobId: expect.any(String),\n      payloadJson: expect.any(String),\n    });\n  });\n});\n```\n\n## License\n\nMIT License\n","readmeFilename":"README.md"}