{"_id":"@bantai-dev/with-rate-limit","_rev":"5-28d8c5bde7d4bd2d55dddfe0ee401c9c","name":"@bantai-dev/with-rate-limit","dist-tags":{"latest":"1.2.0"},"versions":{"0.5.0":{"name":"@bantai-dev/with-rate-limit","version":"0.5.0","keywords":["rate-limiting","policy-engine","policy-library","bantai"],"author":{"name":"Jun","email":"bosquejun@gmail.com"},"license":"MIT","_id":"@bantai-dev/with-rate-limit@0.5.0","maintainers":[{"name":"jun-paul.i.bosque","email":"bosque.junpaul@gmail.com"}],"homepage":"https://github.com/bosquejun/bantai#readme","bugs":{"url":"https://github.com/bosquejun/bantai/issues"},"dist":{"shasum":"706394dccf09066e9b151b9e1b97684d3506c346","tarball":"https://registry.npmjs.org/@bantai-dev/with-rate-limit/-/with-rate-limit-0.5.0.tgz","fileCount":6,"integrity":"sha512-Vgz0Y5Pc0pIEBr6C++qPK7E8B6skbgzmMKBLBpJIkuUVDDev2Hs2N/VHuL8tfLSfFenG/w9dixgGXodp4aCeRA==","signatures":[{"sig":"MEQCIEvtZWriFn9i/gWIZ1uB1Hi/EowshSLP0d1WmFdi/N1pAiAbsSno+5YrbJ0Oz/RU3yna3CtsdLkPzbFWHTFAPxrf5w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":71515},"main":"./dist/index.js","type":"module","_from":"file:bantai-dev-with-rate-limit-0.5.0.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"tsup","test:watch":"vitest","check-types":"tsc --noEmit","test:coverage":"vitest --coverage"},"_npmUser":{"name":"jun-paul.i.bosque","email":"bosque.junpaul@gmail.com"},"_resolved":"/tmp/b45b8f2234b8f5a8932dfdfa91d50b0a/bantai-dev-with-rate-limit-0.5.0.tgz","_integrity":"sha512-Vgz0Y5Pc0pIEBr6C++qPK7E8B6skbgzmMKBLBpJIkuUVDDev2Hs2N/VHuL8tfLSfFenG/w9dixgGXodp4aCeRA==","repository":{"url":"git+https://github.com/bosquejun/bantai.git","type":"git","directory":"packages/with-rate-limit"},"_npmVersion":"10.8.2","description":"Rate limiting extension for @bantai-dev/core","directories":{},"_nodeVersion":"20.20.0","dependencies":{"ms":"^2.1.3","zod":"^4.3.5","@bantai-dev/core":"0.5.0","@bantai-dev/with-storage":"0.5.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","vitest":"^2.1.8","@types/ms":"^2.1.0","typescript":"5.9.2","tsconfig-paths":"^4.2.0","@bantai-dev/typescript-config":"0.0.0"},"peerDependencies":{"zod":"^4.3.5"},"_npmOperationalInternal":{"tmp":"tmp/with-rate-limit_0.5.0_1770174228212_0.641308249296392","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@bantai-dev/with-rate-limit","version":"0.6.0","keywords":["rate-limiting","policy-engine","policy-library","bantai"],"author":{"name":"Jun","email":"bosquejun@gmail.com"},"license":"MIT","_id":"@bantai-dev/with-rate-limit@0.6.0","maintainers":[{"name":"jun-paul.i.bosque","email":"bosque.junpaul@gmail.com"}],"homepage":"https://github.com/bosquejun/bantai#readme","bugs":{"url":"https://github.com/bosquejun/bantai/issues"},"dist":{"shasum":"0ed423924785871a4d1ff8a4be82fc294919e735","tarball":"https://registry.npmjs.org/@bantai-dev/with-rate-limit/-/with-rate-limit-0.6.0.tgz","fileCount":6,"integrity":"sha512-wDHyuIjZSsVLDjxYZHL0S7sSki6MRL5KfzqejcL/z7JPwV25eXMYzGX42FmMcTuwf6O7vBcS638XD2WVSDLw4A==","signatures":[{"sig":"MEUCIQDgiIoFrLnuWXzGLRc7+21oNCijMTM3xuq3HFM0Kfs/4QIgTSF3qt3CvbNYWAk0Qkm6otmu+d87hXLCs5le1VGQ39s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":72684},"main":"./dist/index.js","type":"module","_from":"file:bantai-dev-with-rate-limit-0.6.0.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"tsup","test:watch":"vitest","check-types":"tsc --noEmit","test:coverage":"vitest --coverage"},"_npmUser":{"name":"jun-paul.i.bosque","email":"bosque.junpaul@gmail.com"},"_resolved":"/tmp/ff1e968062919ea79c939d3e4482d172/bantai-dev-with-rate-limit-0.6.0.tgz","_integrity":"sha512-wDHyuIjZSsVLDjxYZHL0S7sSki6MRL5KfzqejcL/z7JPwV25eXMYzGX42FmMcTuwf6O7vBcS638XD2WVSDLw4A==","repository":{"url":"git+https://github.com/bosquejun/bantai.git","type":"git","directory":"packages/with-rate-limit"},"_npmVersion":"10.8.2","description":"Rate limiting extension for @bantai-dev/core","directories":{},"_nodeVersion":"20.20.0","dependencies":{"ms":"^2.1.3","zod":"^4.3.5","@bantai-dev/core":"0.6.0","@bantai-dev/with-storage":"0.6.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","vitest":"^2.1.8","@types/ms":"^2.1.0","typescript":"5.9.2","tsconfig-paths":"^4.2.0","@bantai-dev/typescript-config":"0.0.0"},"peerDependencies":{"zod":"^4.3.5"},"_npmOperationalInternal":{"tmp":"tmp/with-rate-limit_0.6.0_1770221179720_0.44964307220055866","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@bantai-dev/with-rate-limit","version":"1.0.0","keywords":["rate-limiting","policy-engine","policy-library","bantai"],"author":{"name":"Jun","email":"bosquejun@gmail.com"},"license":"MIT","_id":"@bantai-dev/with-rate-limit@1.0.0","maintainers":[{"name":"jun-paul.i.bosque","email":"bosque.junpaul@gmail.com"}],"homepage":"https://github.com/bosquejun/bantai#readme","bugs":{"url":"https://github.com/bosquejun/bantai/issues"},"dist":{"shasum":"b70c6c6b8e931901f551dc4b38081bac0bb540ff","tarball":"https://registry.npmjs.org/@bantai-dev/with-rate-limit/-/with-rate-limit-1.0.0.tgz","fileCount":6,"integrity":"sha512-u/m+aW2E4yaNf8wnQU+d0+fN0TovmPDobl7zBbZR8PZmBhA278D80NOVSCIgBCxIq4g2fZ//zlSEeKcaTjKH0g==","signatures":[{"sig":"MEUCICi2SGLCSmyH9e1u9Zk6VaCMXU6Os6P2ofMALFSU2y6FAiEAs/r+MSic5je/UQgACuNxKBo/cfGfHPI8ARpPxI02pi8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":72684},"main":"./dist/index.js","type":"module","_from":"file:bantai-dev-with-rate-limit-1.0.0.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"tsup","test:watch":"vitest","check-types":"tsc --noEmit","test:coverage":"vitest --coverage"},"_npmUser":{"name":"jun-paul.i.bosque","email":"bosque.junpaul@gmail.com"},"_resolved":"/tmp/e15e5a3d71e2806c585438832fbe99c4/bantai-dev-with-rate-limit-1.0.0.tgz","_integrity":"sha512-u/m+aW2E4yaNf8wnQU+d0+fN0TovmPDobl7zBbZR8PZmBhA278D80NOVSCIgBCxIq4g2fZ//zlSEeKcaTjKH0g==","repository":{"url":"git+https://github.com/bosquejun/bantai.git","type":"git","directory":"packages/with-rate-limit"},"_npmVersion":"10.8.2","description":"Rate limiting extension for @bantai-dev/core","directories":{},"_nodeVersion":"20.20.0","dependencies":{"ms":"^2.1.3","zod":"^4.3.5","@bantai-dev/core":"1.0.0","@bantai-dev/with-storage":"1.0.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","vitest":"^2.1.8","@types/ms":"^2.1.0","typescript":"5.9.2","tsconfig-paths":"^4.2.0","@bantai-dev/typescript-config":"0.0.0"},"peerDependencies":{"zod":"^4.3.5"},"_npmOperationalInternal":{"tmp":"tmp/with-rate-limit_1.0.0_1770276183679_0.9960333912972796","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@bantai-dev/with-rate-limit","version":"1.1.0","keywords":["rate-limiting","policy-engine","policy-library","bantai"],"author":{"name":"Jun","email":"bosquejun@gmail.com"},"license":"MIT","_id":"@bantai-dev/with-rate-limit@1.1.0","maintainers":[{"name":"jun-paul.i.bosque","email":"bosque.junpaul@gmail.com"}],"homepage":"https://bantai.vercel.app/","bugs":{"url":"https://github.com/bosquejun/bantai/issues"},"dist":{"shasum":"3b16959eabcbb5dea9fca7409af4df2d54702582","tarball":"https://registry.npmjs.org/@bantai-dev/with-rate-limit/-/with-rate-limit-1.1.0.tgz","fileCount":6,"integrity":"sha512-Z8QOSgttddqNvyDNee/FXV3ubNNBPsnBFrb6RqZiiIDpW0ziYAZdF606hdxh+avo80dFfmx5L53sPAn9eA5qFQ==","signatures":[{"sig":"MEYCIQCSoOpAXWXdq/KFRU56KERt6Xl5ZMHKg6fQxXP7EiB3qQIhAOcUsMX4BT9I660FWVGGtYyPy5oPQeEk1uTc1J1Cj+Rn","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":84114},"main":"./dist/index.js","type":"module","_from":"file:bantai-dev-with-rate-limit-1.1.0.tgz","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"test":"vitest run","build":"tsup","test:watch":"vitest","check-types":"tsc --noEmit","test:coverage":"vitest --coverage"},"_npmUser":{"name":"jun-paul.i.bosque","email":"bosque.junpaul@gmail.com"},"_resolved":"/tmp/06335293364f753337868a70c28d6561/bantai-dev-with-rate-limit-1.1.0.tgz","_integrity":"sha512-Z8QOSgttddqNvyDNee/FXV3ubNNBPsnBFrb6RqZiiIDpW0ziYAZdF606hdxh+avo80dFfmx5L53sPAn9eA5qFQ==","repository":{"url":"git+https://github.com/bosquejun/bantai.git","type":"git","directory":"packages/with-rate-limit"},"_npmVersion":"10.8.2","description":"Rate limiting extension for @bantai-dev/core","directories":{},"_nodeVersion":"20.20.0","dependencies":{"ms":"^2.1.3","zod":"^4.3.5","@bantai-dev/core":"1.1.0","@bantai-dev/with-storage":"1.1.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","vitest":"^2.1.8","@types/ms":"^2.1.0","typescript":"5.9.2","tsconfig-paths":"^4.2.0","@bantai-dev/typescript-config":"0.0.0"},"peerDependencies":{"zod":"^4.3.5"},"_npmOperationalInternal":{"tmp":"tmp/with-rate-limit_1.1.0_1770306049362_0.9214171418505139","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@bantai-dev/with-rate-limit","version":"1.2.0","description":"Rate limiting extension for @bantai-dev/core","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"repository":{"type":"git","url":"git+https://github.com/bosquejun/bantai.git","directory":"packages/with-rate-limit"},"homepage":"https://bantai.vercel.app/","bugs":{"url":"https://github.com/bosquejun/bantai/issues"},"keywords":["rate-limiting","policy-engine","policy-library","rate-limiting-extension","bantai"],"author":{"name":"Jun","email":"bosquejun@gmail.com"},"license":"MIT","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"devDependencies":{"@types/ms":"^2.1.0","tsconfig-paths":"^4.2.0","tsup":"^8.5.1","typescript":"5.9.2","vitest":"^2.1.8","@bantai-dev/typescript-config":"0.0.0"},"dependencies":{"ms":"^2.1.3","zod":"^4.3.5","@bantai-dev/core":"1.2.0","@bantai-dev/with-storage":"1.2.0"},"peerDependencies":{"zod":"^4.3.5"},"scripts":{"build":"tsup","check-types":"tsc --noEmit","test":"vitest run","test:watch":"vitest","test:coverage":"vitest --coverage"},"_id":"@bantai-dev/with-rate-limit@1.2.0","_integrity":"sha512-HDQgvYlpyS5FY4gHjMDYkql9bQs4I6N551he1ngsWj2UHHorFeul8gObMku3NFznAWSxYUojQNP9ESl5al6MGA==","_resolved":"/tmp/a8cf4bdd65d321c800be805b304784ed/bantai-dev-with-rate-limit-1.2.0.tgz","_from":"file:bantai-dev-with-rate-limit-1.2.0.tgz","_nodeVersion":"20.20.0","_npmVersion":"10.8.2","dist":{"integrity":"sha512-HDQgvYlpyS5FY4gHjMDYkql9bQs4I6N551he1ngsWj2UHHorFeul8gObMku3NFznAWSxYUojQNP9ESl5al6MGA==","shasum":"44ed51d6610818c92d43ee363b73f68eca5519a8","tarball":"https://registry.npmjs.org/@bantai-dev/with-rate-limit/-/with-rate-limit-1.2.0.tgz","fileCount":6,"unpackedSize":81266,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIEdqioSYJUFs603oKQvbpCwd46ls0b9gMLkUn5+NsMqOAiEAtXG/xvx2S6YOiiEufklGVoz/52fzKfHIJk5g4qM0R/w="}]},"_npmUser":{"name":"jun-paul.i.bosque","email":"bosque.junpaul@gmail.com"},"directories":{},"maintainers":[{"name":"jun-paul.i.bosque","email":"bosque.junpaul@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/with-rate-limit_1.2.0_1770561884082_0.3326748203275418"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-04T03:03:48.132Z","modified":"2026-02-08T14:44:44.361Z","0.5.0":"2026-02-04T03:03:48.353Z","0.6.0":"2026-02-04T16:06:19.899Z","1.0.0":"2026-02-05T07:23:03.841Z","1.1.0":"2026-02-05T15:40:49.613Z","1.2.0":"2026-02-08T14:44:44.223Z"},"bugs":{"url":"https://github.com/bosquejun/bantai/issues"},"author":{"name":"Jun","email":"bosquejun@gmail.com"},"license":"MIT","homepage":"https://bantai.vercel.app/","keywords":["rate-limiting","policy-engine","policy-library","rate-limiting-extension","bantai"],"repository":{"type":"git","url":"git+https://github.com/bosquejun/bantai.git","directory":"packages/with-rate-limit"},"description":"Rate limiting extension for @bantai-dev/core","maintainers":[{"name":"jun-paul.i.bosque","email":"bosque.junpaul@gmail.com"}],"readme":"# @bantai-dev/with-rate-limit\n\n> Rate limiting extension for @bantai-dev/core\n\nAdd rate limiting capabilities to your Bantai contexts with support for multiple rate limiting strategies including fixed-window, sliding-window, and token-bucket algorithms.\n\n**Website**: [https://bantai.vercel.app/](https://bantai.vercel.app/)\n\n## Installation\n\n```bash\nnpm install @bantai-dev/with-rate-limit @bantai-dev/core @bantai-dev/with-storage zod\n# or\npnpm add @bantai-dev/with-rate-limit @bantai-dev/core @bantai-dev/with-storage zod\n# or\nyarn add @bantai-dev/with-rate-limit @bantai-dev/core @bantai-dev/with-storage zod\n```\n\n**Note**: `@bantai-dev/core`, `@bantai-dev/with-storage`, and `zod` are peer dependencies and must be installed separately.\n\n## Quick Start\n\n```typescript\nimport { z } from 'zod';\nimport { defineContext, definePolicy, evaluatePolicy, allow } from '@bantai-dev/core';\nimport { createMemoryStorage } from '@bantai-dev/with-storage';\nimport {\n  withRateLimit,\n  defineRateLimitRule,\n  rateLimit,\n} from '@bantai-dev/with-rate-limit';\n\n// 1. Define your base context\nconst apiContext = defineContext(\n  z.object({\n    userId: z.string(),\n    endpoint: z.string(),\n  })\n);\n\n// 2. Extend context with rate limiting\n// generateKey will automatically create keys from your input\nconst rateLimitedContext = withRateLimit(apiContext, {\n  storage: createMemoryStorage(rateLimit.storageSchema),\n  generateKey: (input) => `api:${input.userId}:${input.endpoint}`,\n  defaultValues: {\n    rateLimit: {\n      type: 'fixed-window',\n      limit: 100,\n      period: '1h',\n    },\n  },\n});\n\n// 3. Define a rate limiting rule using defineRateLimitRule\n// This automatically handles rate limit checking and incrementing\nconst rateLimitRule = defineRateLimitRule(\n  rateLimitedContext,\n  'check-rate-limit',\n  async (input) => {\n    // Your business logic here\n    // Rate limit is already checked and will be incremented on allow\n    // input.currentLimit contains the rate limit check result\n    console.log(`Remaining: ${input.currentLimit.remaining}`);\n    return allow({ reason: 'Request allowed' });\n  },\n  {\n    config: {\n      limit: 100,\n      period: '1h',\n      type: 'fixed-window',\n    },\n  }\n);\n\n// 4. Define policy\nconst apiPolicy = definePolicy(\n  rateLimitedContext,\n  'api-rate-limit-policy',\n  [rateLimitRule],\n  {\n    defaultStrategy: 'preemptive',\n  }\n);\n\n// 5. Evaluate policy\n// The generateKey function will create the key automatically\nconst result = await evaluatePolicy(apiPolicy, {\n  userId: 'user123',\n  endpoint: '/api/search',\n});\n```\n\n## Rate Limiting Strategies\n\n### Fixed Window\n\nFixed window rate limiting divides time into discrete windows. All requests within a window count toward the limit, and the counter resets at the start of each new window.\n\n**Use cases**: Simple rate limiting, API quotas, basic throttling\n\n```typescript\n{\n  type: 'fixed-window',\n  key: 'user:123',\n  limit: 100,\n  period: '1h', // Supports ms format: '1h', '30m', '5s', etc.\n}\n```\n\n### Sliding Window\n\nSliding window rate limiting tracks individual request timestamps. Only requests within the current window count toward the limit, providing smoother rate limiting.\n\n**Use cases**: More accurate rate limiting, preventing burst traffic\n\n```typescript\n{\n  type: 'sliding-window',\n  key: 'user:123',\n  limit: 100,\n  period: '1h',\n}\n```\n\n### Token Bucket\n\nToken bucket rate limiting uses a bucket that refills at a constant rate. Requests consume tokens, and requests are allowed when tokens are available.\n\n**Use cases**: Burst handling, smooth rate limiting with refill\n\n```typescript\n{\n  type: 'token-bucket',\n  key: 'user:123',\n  limit: 10000,\n  period: '1d', // Refills to full capacity (10k tokens) over 1 day\n  cost: 1, // Optional: tokens consumed per request (default: 1)\n}\n```\n\n**Note**: The `period` is a time period (e.g. `\"1d\"`, `\"1h\"`, `\"30m\"`) representing the time to refill from empty to full capacity. The refill rate is automatically calculated as `limit / period`. For example, `limit: 10_000` and `period: '1d'` means the bucket can hold 10,000 tokens and refills at a rate of 10,000 tokens per day.\n\n**Cost**: The `cost` parameter (optional, default: 1) specifies how many tokens/requests each operation consumes. This allows you to implement variable-cost rate limiting where different operations consume different amounts. For example, a simple API call might cost 1, while a complex operation might cost 5. This parameter works for all rate limiting strategies.\n\n## API Reference\n\n### `withRateLimit(context, options?)`\n\nExtends a Bantai context with rate limiting capabilities. Adds `rateLimit` schema fields and tools to the context.\n\n**Parameters:**\n- `context`: A Bantai context definition\n- `options`:\n  - `storage?`: A storage adapter implementing `RateLimitStorage` interface\n  - `defaultValues?`: Default values for rate limit configuration\n  - `generateKey?`: Optional function to generate rate limit keys dynamically from context input. If provided, this function will be used when `rateLimit.key` is not specified in the input.\n\n**Returns:** Extended context with rate limiting capabilities\n\n**Example with generateKey:**\n\n```typescript\nimport { createMemoryStorage } from '@bantai-dev/with-storage';\nimport { withRateLimit, rateLimit } from '@bantai-dev/with-rate-limit';\n\nconst rateLimitedContext = withRateLimit(apiContext, {\n  storage: createMemoryStorage(rateLimit.storageSchema),\n  generateKey: (input) => `api:${input.userId}:${input.endpoint}`,\n  defaultValues: {\n    rateLimit: {\n      type: 'fixed-window',\n      limit: 100,\n      period: '1h',\n    },\n  },\n});\n```\n\nWhen using `defineRateLimitRule`, if `rateLimit.key` is not provided in the input, the `generateKey` function will be used automatically.\n\n### `defineRateLimitRule(context, name, evaluate, options)`\n\nA helper function that automatically handles rate limit checking and incrementing. This simplifies rule creation by handling the rate limit logic for you.\n\n**Parameters:**\n- `context`: A context extended with rate limiting capabilities via `withRateLimit`\n- `name`: Unique name for the rule\n- `evaluate`: Your rule evaluation function. The function receives:\n  - `input`: The context input with an additional `currentLimit` property containing the rate limit check result\n  - `context`: The evaluation context with tools\n- `options`: Configuration object\n  - `config`: Rate limit configuration (required). This will be merged with any `rateLimit` config from the input.\n  - `onAllow?`: Optional hook called after rate limit is incremented (if your rule allows)\n  - `onDeny?`: Optional hook called if your rule denies\n\n**Returns:** `RuleDefinition<TContext, TName>`\n\n**How it works:**\n\n1. Checks the rate limit before evaluating your rule\n2. If rate limit is exceeded, returns `deny` immediately\n3. If rate limit passes, evaluates your rule with `currentLimit` available in the input\n4. On allow, automatically increments the rate limit counter\n5. Calls your optional hooks\n\n**Key resolution order:**\n\n1. If `rateLimit.key` is provided in the input, use it\n2. Otherwise, if `generateKey` function is available, use it\n3. Otherwise, fall back to `'unknown-key'`\n\n**Example:**\n```typescript\nconst rateLimitRule = defineRateLimitRule(\n  rateLimitedContext,\n  'api-rule',\n  async (input) => {\n    // input.currentLimit contains the rate limit check result\n    console.log(`Remaining: ${input.currentLimit.remaining}`);\n    \n    // Your business logic here\n    return allow({ reason: 'Request processed' });\n  },\n  {\n    config: {\n      limit: 100,\n      period: '1h',\n      type: 'fixed-window',\n    },\n    onAllow: async (result, input) => {\n      // Optional: Additional logic after rate limit increment\n      console.log(`Request allowed for ${input.userId}`);\n    },\n  }\n);\n```\n\n### `rateLimit.checkRateLimit(storage, config, clock?)`\n\nChecks if a rate limit would be exceeded without incrementing the counter. Use this in your rule's `evaluate` function when not using `defineRateLimitRule`.\n\n**Parameters:**\n- `storage`: Storage adapter implementing `RateLimitStorage`\n- `config`: Rate limit configuration object\n- `clock?`: Optional clock function for testing (defaults to `Date.now`)\n\n**Returns:** `Promise<RateLimitCheckResult>`\n\n**RateLimitCheckResult:**\n```typescript\n{\n  allowed: boolean;\n  remaining: number;\n  resetAt: number; // Unix timestamp in milliseconds\n  reason?: string;\n}\n```\n\n### `rateLimit.incrementRateLimit(storage, config, clock?)`\n\nIncrements the rate limit counter. Use this in your rule's `onAllow` hook when not using `defineRateLimitRule`.\n\n**Parameters:**\n- `storage`: Storage adapter implementing `RateLimitStorage`\n- `config`: Rate limit configuration object\n- `clock?`: Optional clock function for testing (defaults to `Date.now`)\n\n**Returns:** `Promise<void>`\n\n## Storage Integration\n\nThe rate limiting extension requires a storage adapter. You can use:\n\n- **Memory storage** (development/testing): `createMemoryStorage` from this package\n- **Redis storage** (production): `createRedisStorage` from `@bantai-dev/storage-redis`\n- **Custom storage**: Implement the `StorageAdapter` interface from `@bantai-dev/with-storage`\n\n### Using Redis Storage\n\n```typescript\nimport { createRedisStorage } from '@bantai-dev/storage-redis';\nimport { rateLimit } from '@bantai-dev/with-rate-limit';\n\nconst redisStorage = createRedisStorage(\n  { url: process.env.REDIS_URL },\n  rateLimit.storageSchema\n);\n\nconst rateLimitedContext = withRateLimit(apiContext, {\n  storage: redisStorage,\n});\n```\n\n## Examples\n\n### Per-User Rate Limiting\n\n```typescript\nimport { defineRule, allow, deny } from '@bantai-dev/core';\n\nconst userRateLimitRule = defineRule(\n  rateLimitedContext,\n  'user-rate-limit',\n  async (input, { tools }) => {\n    const result = await tools.rateLimit.checkRateLimit(\n      tools.storage,\n      {\n        key: `user:${input.userId}`,\n        type: 'fixed-window',\n        limit: 1000,\n        period: '24h',\n      }\n    );\n\n    return result.allowed ? allow({ reason: 'Rate limit OK' }) : deny({ reason: result.reason });\n  },\n  {\n    onAllow: async (result, input, { tools }) => {\n      await tools.rateLimit.incrementRateLimit(\n        tools.storage,\n        {\n          key: `user:${input.userId}`,\n          type: 'fixed-window',\n          limit: 1000,\n          period: '24h',\n        }\n      );\n    },\n  }\n);\n```\n\n### Endpoint-Specific Rate Limits\n\n```typescript\nimport { defineRule, allow, deny } from '@bantai-dev/core';\n\nconst endpointLimits = {\n  '/api/auth/login': { limit: 5, period: '15m' },\n  '/api/payment': { limit: 10, period: '1m' },\n  '/api/search': { limit: 100, period: '1m' },\n};\n\nconst endpointRateLimitRule = defineRule(\n  rateLimitedContext,\n  'endpoint-rate-limit',\n  async (input, { tools }) => {\n    const config = endpointLimits[input.endpoint] || { limit: 50, period: '1h' };\n    \n    const result = await tools.rateLimit.checkRateLimit(\n      tools.storage,\n      {\n        key: `endpoint:${input.endpoint}:${input.userId}`,\n        type: 'sliding-window',\n        limit: config.limit,\n        period: config.period,\n      }\n    );\n\n    return result.allowed ? allow({ reason: 'Rate limit OK' }) : deny({ reason: result.reason });\n  },\n  {\n    onAllow: async (result, input, { tools }) => {\n      const config = endpointLimits[input.endpoint] || { limit: 50, period: '1h' };\n      \n      await tools.rateLimit.incrementRateLimit(\n        tools.storage,\n        {\n          key: `endpoint:${input.endpoint}:${input.userId}`,\n          type: 'sliding-window',\n          limit: config.limit,\n          period: config.period,\n        }\n      );\n    },\n  }\n);\n```\n\n### Tier-Based Rate Limiting\n\n```typescript\nimport { defineRule, allow, deny } from '@bantai-dev/core';\n\nconst tierLimits = {\n  free: { limit: 100, period: '1h' },\n  premium: { limit: 1000, period: '1h' },\n  enterprise: { limit: 10000, period: '1h' },\n};\n\nconst tierRateLimitRule = defineRule(\n  rateLimitedContext,\n  'tier-rate-limit',\n  async (input, { tools }) => {\n    const config = tierLimits[input.userTier];\n    \n    const result = await tools.rateLimit.checkRateLimit(\n      tools.storage,\n      {\n        key: `tier:${input.userTier}:${input.userId}`,\n        type: 'token-bucket',\n        limit: config.limit,\n        period: '1h', // Refills to full capacity over 1 hour\n      }\n    );\n\n    return result.allowed ? allow({ reason: 'Rate limit OK' }) : deny({ reason: result.reason });\n  },\n  {\n    onAllow: async (result, input, { tools }) => {\n      const config = tierLimits[input.userTier];\n      \n      await tools.rateLimit.incrementRateLimit(\n        tools.storage,\n        {\n          key: `tier:${input.userTier}:${input.userId}`,\n          type: 'token-bucket',\n          limit: config.limit,\n          period: '1h', // Refills to full capacity over 1 hour\n        }\n      );\n    },\n  }\n);\n```\n\n## Type Safety\n\nThe package provides full TypeScript type safety:\n\n- **Context extension**: Type-safe context merging with rate limit fields\n- **Config validation**: Zod schemas validate rate limit configurations\n- **Storage types**: Type-safe storage adapter interface\n- **Result types**: Typed rate limit check results\n\n## Requirements\n\n- Node.js >= 20.9.0\n- TypeScript >= 5.0\n- Zod >= 4.3.5\n- @bantai-dev/core\n- @bantai-dev/with-storage\n\n## Links\n\n- **Website**: [https://bantai.vercel.app/](https://bantai.vercel.app/)\n- **GitHub Repository**: [https://github.com/bosquejun/bantai](https://github.com/bosquejun/bantai)\n- **npm Package**: [https://www.npmjs.com/package/@bantai-dev/with-rate-limit](https://www.npmjs.com/package/@bantai-dev/with-rate-limit)\n\n## License\n\nMIT\n\n","readmeFilename":"README.md"}