@workkit/hono 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -0
- package/dist/index.d.ts +81 -2
- package/dist/index.js +74 -3
- package/dist/index.js.map +6 -4
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -78,6 +78,44 @@ app.get("/", (c) => {
|
|
|
78
78
|
|
|
79
79
|
- **`cacheResponse(options)`** — Cache responses using the Cache API. Options: `ttl`, `vary?`
|
|
80
80
|
|
|
81
|
+
### Tiered Rate Limiting
|
|
82
|
+
|
|
83
|
+
- **`tieredRateLimit(options)`** — Apply different rate limits based on user tier. Uses `@workkit/ratelimit` tiered limiter under the hood.
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
app.use("/api/*", tieredRateLimit({
|
|
87
|
+
namespace: env.RATE_LIMIT_KV,
|
|
88
|
+
tiers: { free: { limit: 100 }, pro: { limit: 10000 } },
|
|
89
|
+
window: "1h",
|
|
90
|
+
keyFn: (c) => c.req.header("CF-Connecting-IP") ?? "unknown",
|
|
91
|
+
tierFn: (c) => getUserTier(c),
|
|
92
|
+
}))
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Quota Middleware
|
|
96
|
+
|
|
97
|
+
- **`quotaLimit(options)`** — Enforce multi-window quota limits (e.g. 10/hour + 100/day). Returns 429 with quota breakdown when exceeded.
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
app.use("/api/*", quotaLimit({
|
|
101
|
+
namespace: env.RATE_LIMIT_KV,
|
|
102
|
+
limits: [
|
|
103
|
+
{ window: "1h", limit: 10 },
|
|
104
|
+
{ window: "1d", limit: 100 },
|
|
105
|
+
],
|
|
106
|
+
keyFn: (c) => c.req.header("CF-Connecting-IP") ?? "unknown",
|
|
107
|
+
}))
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Cache Jitter
|
|
111
|
+
|
|
112
|
+
The `cacheResponse` middleware supports a `jitter` option to randomize TTL per response, preventing thundering herd when many cache entries expire simultaneously:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
app.use("/api/public/*", cacheResponse({ ttl: 300, jitter: 30 }))
|
|
116
|
+
// Actual TTL will be 270-330s (±30s random offset)
|
|
117
|
+
```
|
|
118
|
+
|
|
81
119
|
### Helpers
|
|
82
120
|
|
|
83
121
|
- **`getEnv(c)`** — Get validated env from Hono context (shorthand for `c.get("workkit:env")`)
|
package/dist/index.d.ts
CHANGED
|
@@ -72,6 +72,45 @@ interface CacheOptions {
|
|
|
72
72
|
cache?: Cache;
|
|
73
73
|
/** HTTP methods to cache (defaults to ['GET']) */
|
|
74
74
|
methods?: string[];
|
|
75
|
+
/** Jitter in seconds — actual TTL varies by ±jitter to prevent thundering herd */
|
|
76
|
+
jitter?: number;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Options for the tieredRateLimit middleware.
|
|
80
|
+
*/
|
|
81
|
+
interface TieredRateLimitOptions {
|
|
82
|
+
/** KV namespace for storing rate limit counters */
|
|
83
|
+
namespace: KVNamespace;
|
|
84
|
+
/** Tier definitions — e.g. { free: { limit: 100 }, pro: { limit: 10000 } } */
|
|
85
|
+
tiers: Record<string, {
|
|
86
|
+
limit: number;
|
|
87
|
+
}>;
|
|
88
|
+
/** Window duration — e.g. '1m', '1h', '1d' */
|
|
89
|
+
window: string;
|
|
90
|
+
/** Function to extract the rate limit key from context */
|
|
91
|
+
keyFn: (c: Context) => string | Promise<string>;
|
|
92
|
+
/** Function to determine the user's tier from context */
|
|
93
|
+
tierFn: (c: Context) => string | Promise<string>;
|
|
94
|
+
/** Default tier to fall back to for unknown tiers */
|
|
95
|
+
defaultTier?: string;
|
|
96
|
+
/** Custom response when rate limited (optional) */
|
|
97
|
+
onRateLimited?: (c: Context) => Response | Promise<Response>;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Options for the quotaLimit middleware.
|
|
101
|
+
*/
|
|
102
|
+
interface QuotaLimitOptions {
|
|
103
|
+
/** KV namespace for storing quota counters */
|
|
104
|
+
namespace: KVNamespace;
|
|
105
|
+
/** Array of window/limit pairs — e.g. [{ window: '1h', limit: 10 }, { window: '1d', limit: 100 }] */
|
|
106
|
+
limits: Array<{
|
|
107
|
+
window: string;
|
|
108
|
+
limit: number;
|
|
109
|
+
}>;
|
|
110
|
+
/** Function to extract the quota key from context */
|
|
111
|
+
keyFn: (c: Context) => string | Promise<string>;
|
|
112
|
+
/** Custom response when quota exceeded (optional) */
|
|
113
|
+
onQuotaExceeded?: (c: Context) => Response | Promise<Response>;
|
|
75
114
|
}
|
|
76
115
|
/**
|
|
77
116
|
* Hono environment type with workkit context variables.
|
|
@@ -153,6 +192,46 @@ declare function parseDuration(duration: string): number;
|
|
|
153
192
|
declare function fixedWindow(options: FixedWindowOptions): RateLimiter;
|
|
154
193
|
import { MiddlewareHandler as MiddlewareHandler3 } from "hono";
|
|
155
194
|
/**
|
|
195
|
+
* Tiered rate limiting middleware for Hono.
|
|
196
|
+
*
|
|
197
|
+
* Applies different rate limits based on user tier (e.g. free, pro, enterprise).
|
|
198
|
+
* Uses `tiered()` from `@workkit/ratelimit` under the hood.
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* ```ts
|
|
202
|
+
* app.use('/api/*', tieredRateLimit({
|
|
203
|
+
* namespace: env.RATE_LIMIT_KV,
|
|
204
|
+
* tiers: { free: { limit: 100 }, pro: { limit: 10000 } },
|
|
205
|
+
* window: '1h',
|
|
206
|
+
* keyFn: (c) => c.req.header('CF-Connecting-IP') ?? 'unknown',
|
|
207
|
+
* tierFn: (c) => getUserTier(c),
|
|
208
|
+
* }))
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
declare function tieredRateLimit(options: TieredRateLimitOptions): MiddlewareHandler3;
|
|
212
|
+
import { MiddlewareHandler as MiddlewareHandler4 } from "hono";
|
|
213
|
+
/**
|
|
214
|
+
* Quota middleware for Hono.
|
|
215
|
+
*
|
|
216
|
+
* Enforces multi-window quota limits (e.g. 10/hour + 100/day).
|
|
217
|
+
* Uses `quota()` from `@workkit/ratelimit` under the hood.
|
|
218
|
+
* Returns 429 with quota breakdown when exceeded.
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```ts
|
|
222
|
+
* app.use('/api/*', quotaLimit({
|
|
223
|
+
* namespace: env.RATE_LIMIT_KV,
|
|
224
|
+
* limits: [
|
|
225
|
+
* { window: '1h', limit: 10 },
|
|
226
|
+
* { window: '1d', limit: 100 },
|
|
227
|
+
* ],
|
|
228
|
+
* keyFn: (c) => c.req.header('CF-Connecting-IP') ?? 'unknown',
|
|
229
|
+
* }))
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
declare function quotaLimit(options: QuotaLimitOptions): MiddlewareHandler4;
|
|
233
|
+
import { MiddlewareHandler as MiddlewareHandler5 } from "hono";
|
|
234
|
+
/**
|
|
156
235
|
* Cache middleware for Hono — caches responses using the Cache API.
|
|
157
236
|
*
|
|
158
237
|
* Only caches successful (2xx) responses. Serves cached responses on cache hit.
|
|
@@ -165,7 +244,7 @@ import { MiddlewareHandler as MiddlewareHandler3 } from "hono";
|
|
|
165
244
|
* })
|
|
166
245
|
* ```
|
|
167
246
|
*/
|
|
168
|
-
declare function cacheResponse(options: CacheOptions):
|
|
247
|
+
declare function cacheResponse(options: CacheOptions): MiddlewareHandler5;
|
|
169
248
|
import { EnvSchema as EnvSchema3, InferEnv as InferEnv2 } from "@workkit/env";
|
|
170
249
|
import { Context as Context2 } from "hono";
|
|
171
250
|
/**
|
|
@@ -183,4 +262,4 @@ import { Context as Context2 } from "hono";
|
|
|
183
262
|
* ```
|
|
184
263
|
*/
|
|
185
264
|
declare function getEnv<T extends EnvSchema3>(c: Context2<WorkkitEnv<T>>): InferEnv2<T>;
|
|
186
|
-
export { workkitErrorHandler, workkit, rateLimit, parseDuration, getEnv, fixedWindow, cacheResponse, WorkkitOptions, WorkkitEnv, RateLimiter, RateLimitResult, RateLimitOptions, FixedWindowOptions, ErrorHandlerOptions, CacheOptions };
|
|
265
|
+
export { workkitErrorHandler, workkit, tieredRateLimit, rateLimit, quotaLimit, parseDuration, getEnv, fixedWindow, cacheResponse, WorkkitOptions, WorkkitEnv, TieredRateLimitOptions, RateLimiter, RateLimitResult, RateLimitOptions, QuotaLimitOptions, FixedWindowOptions, ErrorHandlerOptions, CacheOptions };
|
package/dist/index.js
CHANGED
|
@@ -116,9 +116,77 @@ function fixedWindow(options) {
|
|
|
116
116
|
}
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
|
+
// src/tiered-rate-limit.ts
|
|
120
|
+
import { tiered } from "@workkit/ratelimit";
|
|
121
|
+
function tieredRateLimit(options) {
|
|
122
|
+
const { namespace, tiers, window, keyFn, tierFn, onRateLimited, defaultTier } = options;
|
|
123
|
+
const limiter = tiered({
|
|
124
|
+
namespace,
|
|
125
|
+
tiers,
|
|
126
|
+
window,
|
|
127
|
+
defaultTier
|
|
128
|
+
});
|
|
129
|
+
return async (c, next) => {
|
|
130
|
+
const key = await keyFn(c);
|
|
131
|
+
const tier = await tierFn(c);
|
|
132
|
+
let result;
|
|
133
|
+
try {
|
|
134
|
+
result = await limiter.check(key, tier);
|
|
135
|
+
} catch (err) {
|
|
136
|
+
return c.json({ error: "Internal server error" }, 500);
|
|
137
|
+
}
|
|
138
|
+
c.header("X-RateLimit-Limit", String(result.limit));
|
|
139
|
+
c.header("X-RateLimit-Remaining", String(result.remaining));
|
|
140
|
+
c.header("X-RateLimit-Reset", String(Math.ceil(result.resetAt.getTime() / 1000)));
|
|
141
|
+
if (!result.allowed) {
|
|
142
|
+
const retryAfterSeconds = Math.max(1, Math.ceil((result.resetAt.getTime() - Date.now()) / 1000));
|
|
143
|
+
c.header("Retry-After", String(retryAfterSeconds));
|
|
144
|
+
if (onRateLimited) {
|
|
145
|
+
return onRateLimited(c);
|
|
146
|
+
}
|
|
147
|
+
return c.json({
|
|
148
|
+
error: "Rate limit exceeded",
|
|
149
|
+
retryAfter: retryAfterSeconds
|
|
150
|
+
}, 429);
|
|
151
|
+
}
|
|
152
|
+
await next();
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
// src/quota-limit.ts
|
|
156
|
+
import { quota } from "@workkit/ratelimit";
|
|
157
|
+
function quotaLimit(options) {
|
|
158
|
+
const { namespace, limits, keyFn, onQuotaExceeded } = options;
|
|
159
|
+
const limiter = quota({
|
|
160
|
+
namespace,
|
|
161
|
+
limits: limits.map((l) => ({
|
|
162
|
+
window: l.window,
|
|
163
|
+
limit: l.limit
|
|
164
|
+
}))
|
|
165
|
+
});
|
|
166
|
+
return async (c, next) => {
|
|
167
|
+
const key = await keyFn(c);
|
|
168
|
+
const result = await limiter.check(key);
|
|
169
|
+
c.header("X-RateLimit-Limit", String(result.limit));
|
|
170
|
+
c.header("X-RateLimit-Remaining", String(result.remaining));
|
|
171
|
+
c.header("X-RateLimit-Reset", String(Math.ceil(result.resetAt.getTime() / 1000)));
|
|
172
|
+
if (!result.allowed) {
|
|
173
|
+
const retryAfterSeconds = Math.max(1, Math.ceil((result.resetAt.getTime() - Date.now()) / 1000));
|
|
174
|
+
c.header("Retry-After", String(retryAfterSeconds));
|
|
175
|
+
if (onQuotaExceeded) {
|
|
176
|
+
return onQuotaExceeded(c);
|
|
177
|
+
}
|
|
178
|
+
return c.json({
|
|
179
|
+
error: "Quota exceeded",
|
|
180
|
+
retryAfter: retryAfterSeconds,
|
|
181
|
+
quotas: result.quotas
|
|
182
|
+
}, 429);
|
|
183
|
+
}
|
|
184
|
+
await next();
|
|
185
|
+
};
|
|
186
|
+
}
|
|
119
187
|
// src/cache.ts
|
|
120
188
|
function cacheResponse(options) {
|
|
121
|
-
const { ttl, keyFn, methods = ["GET"] } = options;
|
|
189
|
+
const { ttl, keyFn, methods = ["GET"], jitter } = options;
|
|
122
190
|
return async (c, next) => {
|
|
123
191
|
if (!methods.includes(c.req.method)) {
|
|
124
192
|
await next();
|
|
@@ -144,7 +212,8 @@ function cacheResponse(options) {
|
|
|
144
212
|
statusText: cloned.statusText,
|
|
145
213
|
headers: new Headers(cloned.headers)
|
|
146
214
|
});
|
|
147
|
-
|
|
215
|
+
const actualTtl = jitter ? Math.max(1, ttl + Math.floor(Math.random() * jitter * 2) - jitter) : ttl;
|
|
216
|
+
cachedResponse.headers.set("Cache-Control", `s-maxage=${actualTtl}`);
|
|
148
217
|
const putPromise = cache.put(cacheRequest, cachedResponse);
|
|
149
218
|
try {
|
|
150
219
|
c.executionCtx.waitUntil(putPromise);
|
|
@@ -165,12 +234,14 @@ function getEnv(c) {
|
|
|
165
234
|
export {
|
|
166
235
|
workkitErrorHandler,
|
|
167
236
|
workkit,
|
|
237
|
+
tieredRateLimit,
|
|
168
238
|
rateLimit,
|
|
239
|
+
quotaLimit,
|
|
169
240
|
parseDuration,
|
|
170
241
|
getEnv,
|
|
171
242
|
fixedWindow,
|
|
172
243
|
cacheResponse
|
|
173
244
|
};
|
|
174
245
|
|
|
175
|
-
//# debugId=
|
|
246
|
+
//# debugId=80A4AD10CFC3AED664756E2164756E21
|
|
176
247
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["src/middleware.ts", "src/error-handler.ts", "src/rate-limit.ts", "src/cache.ts", "src/helpers.ts"],
|
|
3
|
+
"sources": ["src/middleware.ts", "src/error-handler.ts", "src/rate-limit.ts", "src/tiered-rate-limit.ts", "src/quota-limit.ts", "src/cache.ts", "src/helpers.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"import { parseEnv } from \"@workkit/env\";\nimport type { EnvSchema, InferEnv } from \"@workkit/env\";\nimport type { MiddlewareHandler } from \"hono\";\nimport type { WorkkitEnv, WorkkitOptions } from \"./types\";\n\n/**\n * Main workkit middleware — validates environment bindings on first request\n * and stores the parsed, typed env in Hono's context.\n *\n * Validation runs once (on the first request) and the result is cached\n * for subsequent requests within the same Worker invocation.\n *\n * @example\n * ```ts\n * const app = new Hono()\n * app.use(workkit({ env: { API_KEY: z.string().min(1) } }))\n * app.get('/', (c) => {\n * const env = c.get('workkit:env') // typed\n * })\n * ```\n */\nexport function workkit<T extends EnvSchema>(\n\toptions: WorkkitOptions<T>,\n): MiddlewareHandler<WorkkitEnv<T>> {\n\tlet cachedEnv: InferEnv<T> | null = null;\n\n\treturn async (c, next) => {\n\t\tif (!cachedEnv) {\n\t\t\tconst rawEnv = c.env as Record<string, unknown>;\n\t\t\tcachedEnv = await parseEnv(rawEnv, options.env);\n\t\t}\n\n\t\tc.set(\"workkit:env\", cachedEnv);\n\t\tc.set(\"workkit:envValidated\", true);\n\n\t\tawait next();\n\t};\n}\n",
|
|
6
6
|
"import { InternalError, RateLimitError, type WorkkitError, isWorkkitError } from \"@workkit/errors\";\nimport type { ErrorHandler } from \"hono\";\nimport type { ErrorHandlerOptions } from \"./types\";\n\n/**\n * Hono error handler that converts WorkkitErrors to proper HTTP responses.\n *\n * - WorkkitError instances → structured JSON with their status code\n * - RateLimitError → includes Retry-After header\n * - ValidationError → includes issues array\n * - Unknown errors → 500 Internal Server Error\n *\n * @example\n * ```ts\n * app.onError(workkitErrorHandler({\n * includeStack: false,\n * onError: (err, c) => console.error(err),\n * }))\n * ```\n */\nexport function workkitErrorHandler(options: ErrorHandlerOptions = {}): ErrorHandler {\n\tconst { includeStack = false, onError } = options;\n\n\treturn async (err, c) => {\n\t\tif (onError) {\n\t\t\ttry {\n\t\t\t\tawait onError(err, c);\n\t\t\t} catch {\n\t\t\t\t// Don't let error callback failures break the response\n\t\t\t}\n\t\t}\n\n\t\tif (isWorkkitError(err)) {\n\t\t\treturn workkitErrorToResponse(err, includeStack);\n\t\t}\n\n\t\t// Wrap unknown errors as InternalError\n\t\tconst wrapped = new InternalError(\n\t\t\terr instanceof Error ? err.message : \"An unexpected error occurred\",\n\t\t\t{ cause: err },\n\t\t);\n\n\t\treturn workkitErrorToResponse(wrapped, includeStack);\n\t};\n}\n\nfunction workkitErrorToResponse(error: WorkkitError, includeStack: boolean): Response {\n\tconst body: Record<string, unknown> = {\n\t\terror: {\n\t\t\tcode: error.code,\n\t\t\tmessage: error.message,\n\t\t\tstatusCode: error.statusCode,\n\t\t},\n\t};\n\n\t// Include validation issues if present\n\tif (\"issues\" in error && Array.isArray((error as any).issues)) {\n\t\t(body.error as any).issues = (error as any).issues;\n\t}\n\n\tif (includeStack && error.stack) {\n\t\t(body.error as any).stack = error.stack;\n\t}\n\n\tconst headers: Record<string, string> = {\n\t\t\"Content-Type\": \"application/json\",\n\t};\n\n\t// Set Retry-After header for rate limit errors\n\tif (error instanceof RateLimitError && error.retryAfterMs) {\n\t\theaders[\"Retry-After\"] = String(Math.ceil(error.retryAfterMs / 1000));\n\t}\n\n\treturn new Response(JSON.stringify(body), {\n\t\tstatus: error.statusCode,\n\t\theaders,\n\t});\n}\n",
|
|
7
7
|
"import { RateLimitError } from \"@workkit/errors\";\nimport type { MiddlewareHandler } from \"hono\";\nimport type { FixedWindowOptions, RateLimitOptions, RateLimitResult, RateLimiter } from \"./types\";\n\n/**\n * Rate limit middleware for Hono.\n *\n * @example\n * ```ts\n * app.use('/api/*', rateLimit({\n * limiter: fixedWindow({ namespace: env.KV, limit: 100, window: '1m' }),\n * keyFn: (c) => c.req.header('CF-Connecting-IP') ?? 'unknown',\n * }))\n * ```\n */\nexport function rateLimit(options: RateLimitOptions): MiddlewareHandler {\n\tconst { limiter, keyFn, onRateLimited } = options;\n\n\treturn async (c, next) => {\n\t\tconst key = await keyFn(c);\n\t\tconst result = await limiter.check(key);\n\n\t\t// Set rate limit headers regardless of outcome\n\t\tc.header(\"X-RateLimit-Limit\", String(result.remaining + (result.allowed ? 0 : 1)));\n\t\tc.header(\"X-RateLimit-Remaining\", String(result.remaining));\n\t\tc.header(\"X-RateLimit-Reset\", String(Math.ceil(result.resetAt / 1000)));\n\n\t\tif (!result.allowed) {\n\t\t\tif (onRateLimited) {\n\t\t\t\treturn onRateLimited(c, result);\n\t\t\t}\n\n\t\t\tconst retryAfterMs = result.resetAt - Date.now();\n\t\t\tthrow new RateLimitError(\"Rate limit exceeded\", retryAfterMs > 0 ? retryAfterMs : undefined);\n\t\t}\n\n\t\tawait next();\n\t};\n}\n\n/**\n * Parse a duration string like '1m', '5m', '1h', '1d' into milliseconds.\n */\nexport function parseDuration(duration: string): number {\n\tconst match = duration.match(/^(\\d+)(s|m|h|d)$/);\n\tif (!match) {\n\t\tthrow new Error(`Invalid duration format: \"${duration}\". Use e.g. '1m', '5m', '1h', '1d'.`);\n\t}\n\n\tconst value = Number.parseInt(match[1]!, 10);\n\tconst unit = match[2]!;\n\n\tswitch (unit) {\n\t\tcase \"s\":\n\t\t\treturn value * 1000;\n\t\tcase \"m\":\n\t\t\treturn value * 60 * 1000;\n\t\tcase \"h\":\n\t\t\treturn value * 60 * 60 * 1000;\n\t\tcase \"d\":\n\t\t\treturn value * 24 * 60 * 60 * 1000;\n\t\tdefault:\n\t\t\tthrow new Error(`Unknown duration unit: \"${unit}\"`);\n\t}\n}\n\n/**\n * Creates a fixed-window rate limiter backed by KV.\n *\n * Each window is stored as a KV key with an expiration TTL.\n * Uses KV's eventual consistency — suitable for soft rate limiting,\n * not cryptographic precision.\n *\n * @example\n * ```ts\n * const limiter = fixedWindow({\n * namespace: env.RATE_LIMIT_KV,\n * limit: 100,\n * window: '1m',\n * })\n * ```\n */\nexport function fixedWindow(options: FixedWindowOptions): RateLimiter {\n\tconst { namespace, limit, window: windowStr, prefix = \"rl:\" } = options;\n\tconst windowMs = parseDuration(windowStr);\n\n\treturn {\n\t\tasync check(key: string): Promise<RateLimitResult> {\n\t\t\tconst now = Date.now();\n\t\t\tconst windowStart = Math.floor(now / windowMs) * windowMs;\n\t\t\tconst resetAt = windowStart + windowMs;\n\t\t\tconst kvKey = `${prefix}${key}:${windowStart}`;\n\n\t\t\tconst current = await namespace.get(kvKey);\n\t\t\tconst count = current ? Number.parseInt(current, 10) : 0;\n\n\t\t\tif (count >= limit) {\n\t\t\t\treturn { allowed: false, remaining: 0, resetAt };\n\t\t\t}\n\n\t\t\t// Increment counter with TTL equal to window duration (in seconds, rounded up)\n\t\t\tconst ttlSeconds = Math.ceil(windowMs / 1000);\n\t\t\tawait namespace.put(kvKey, String(count + 1), {\n\t\t\t\texpirationTtl: ttlSeconds,\n\t\t\t});\n\n\t\t\treturn { allowed: true, remaining: limit - count - 1, resetAt };\n\t\t},\n\t};\n}\n",
|
|
8
|
-
"import type { MiddlewareHandler } from \"hono\";\nimport type {
|
|
8
|
+
"import { tiered } from \"@workkit/ratelimit\";\nimport type { MiddlewareHandler } from \"hono\";\nimport type { TieredRateLimitOptions } from \"./types\";\n\n/**\n * Tiered rate limiting middleware for Hono.\n *\n * Applies different rate limits based on user tier (e.g. free, pro, enterprise).\n * Uses `tiered()` from `@workkit/ratelimit` under the hood.\n *\n * @example\n * ```ts\n * app.use('/api/*', tieredRateLimit({\n * namespace: env.RATE_LIMIT_KV,\n * tiers: { free: { limit: 100 }, pro: { limit: 10000 } },\n * window: '1h',\n * keyFn: (c) => c.req.header('CF-Connecting-IP') ?? 'unknown',\n * tierFn: (c) => getUserTier(c),\n * }))\n * ```\n */\nexport function tieredRateLimit(options: TieredRateLimitOptions): MiddlewareHandler {\n\tconst { namespace, tiers, window, keyFn, tierFn, onRateLimited, defaultTier } = options;\n\n\t// Create the tiered limiter once — reused across all requests\n\tconst limiter = tiered({\n\t\tnamespace,\n\t\ttiers,\n\t\twindow: window as `${number}${\"s\" | \"m\" | \"h\" | \"d\"}`,\n\t\tdefaultTier,\n\t});\n\n\treturn async (c, next) => {\n\t\tconst key = await keyFn(c);\n\t\tconst tier = await tierFn(c);\n\n\t\tlet result: Awaited<ReturnType<typeof limiter.check>>;\n\t\ttry {\n\t\t\tresult = await limiter.check(key, tier);\n\t\t} catch (err) {\n\t\t\t// If tier resolution fails (unknown tier, no default), return 500\n\t\t\treturn c.json({ error: \"Internal server error\" }, 500);\n\t\t}\n\n\t\t// Set rate limit headers\n\t\tc.header(\"X-RateLimit-Limit\", String(result.limit));\n\t\tc.header(\"X-RateLimit-Remaining\", String(result.remaining));\n\t\tc.header(\"X-RateLimit-Reset\", String(Math.ceil(result.resetAt.getTime() / 1000)));\n\n\t\tif (!result.allowed) {\n\t\t\tconst retryAfterSeconds = Math.max(\n\t\t\t\t1,\n\t\t\t\tMath.ceil((result.resetAt.getTime() - Date.now()) / 1000),\n\t\t\t);\n\t\t\tc.header(\"Retry-After\", String(retryAfterSeconds));\n\n\t\t\tif (onRateLimited) {\n\t\t\t\treturn onRateLimited(c);\n\t\t\t}\n\n\t\t\treturn c.json(\n\t\t\t\t{\n\t\t\t\t\terror: \"Rate limit exceeded\",\n\t\t\t\t\tretryAfter: retryAfterSeconds,\n\t\t\t\t},\n\t\t\t\t429,\n\t\t\t);\n\t\t}\n\n\t\tawait next();\n\t};\n}\n",
|
|
9
|
+
"import { quota } from \"@workkit/ratelimit\";\nimport type { MiddlewareHandler } from \"hono\";\nimport type { QuotaLimitOptions } from \"./types\";\n\n/**\n * Quota middleware for Hono.\n *\n * Enforces multi-window quota limits (e.g. 10/hour + 100/day).\n * Uses `quota()` from `@workkit/ratelimit` under the hood.\n * Returns 429 with quota breakdown when exceeded.\n *\n * @example\n * ```ts\n * app.use('/api/*', quotaLimit({\n * namespace: env.RATE_LIMIT_KV,\n * limits: [\n * { window: '1h', limit: 10 },\n * { window: '1d', limit: 100 },\n * ],\n * keyFn: (c) => c.req.header('CF-Connecting-IP') ?? 'unknown',\n * }))\n * ```\n */\nexport function quotaLimit(options: QuotaLimitOptions): MiddlewareHandler {\n\tconst { namespace, limits, keyFn, onQuotaExceeded } = options;\n\n\t// Create the quota limiter once — reused across all requests\n\tconst limiter = quota({\n\t\tnamespace,\n\t\tlimits: limits.map((l) => ({\n\t\t\twindow: l.window as `${number}${\"s\" | \"m\" | \"h\" | \"d\"}`,\n\t\t\tlimit: l.limit,\n\t\t})),\n\t});\n\n\treturn async (c, next) => {\n\t\tconst key = await keyFn(c);\n\t\tconst result = await limiter.check(key);\n\n\t\t// Set rate limit headers\n\t\tc.header(\"X-RateLimit-Limit\", String(result.limit));\n\t\tc.header(\"X-RateLimit-Remaining\", String(result.remaining));\n\t\tc.header(\"X-RateLimit-Reset\", String(Math.ceil(result.resetAt.getTime() / 1000)));\n\n\t\tif (!result.allowed) {\n\t\t\tconst retryAfterSeconds = Math.max(\n\t\t\t\t1,\n\t\t\t\tMath.ceil((result.resetAt.getTime() - Date.now()) / 1000),\n\t\t\t);\n\t\t\tc.header(\"Retry-After\", String(retryAfterSeconds));\n\n\t\t\tif (onQuotaExceeded) {\n\t\t\t\treturn onQuotaExceeded(c);\n\t\t\t}\n\n\t\t\treturn c.json(\n\t\t\t\t{\n\t\t\t\t\terror: \"Quota exceeded\",\n\t\t\t\t\tretryAfter: retryAfterSeconds,\n\t\t\t\t\tquotas: result.quotas,\n\t\t\t\t},\n\t\t\t\t429,\n\t\t\t);\n\t\t}\n\n\t\tawait next();\n\t};\n}\n",
|
|
10
|
+
"import type { MiddlewareHandler } from \"hono\";\nimport type { CacheOptions } from \"./types\";\n\n/**\n * Cache middleware for Hono — caches responses using the Cache API.\n *\n * Only caches successful (2xx) responses. Serves cached responses on cache hit.\n * Uses Cloudflare's Cache API by default.\n *\n * @example\n * ```ts\n * app.get('/api/data', cacheResponse({ ttl: 300 }), async (c) => {\n * return c.json(await fetchData())\n * })\n * ```\n */\nexport function cacheResponse(options: CacheOptions): MiddlewareHandler {\n\tconst { ttl, keyFn, methods = [\"GET\"], jitter } = options;\n\n\treturn async (c, next) => {\n\t\t// Only cache specified methods\n\t\tif (!methods.includes(c.req.method)) {\n\t\t\tawait next();\n\t\t\treturn;\n\t\t}\n\n\t\tconst cacheKey = keyFn ? keyFn(c) : c.req.url;\n\t\tconst cacheRequest = new Request(cacheKey);\n\n\t\t// Try to get the cache instance\n\t\tconst cache = options.cache ?? (typeof caches !== \"undefined\" ? caches.default : null);\n\t\tif (!cache) {\n\t\t\t// No cache available, skip caching\n\t\t\tawait next();\n\t\t\treturn;\n\t\t}\n\n\t\t// Check for cached response\n\t\tconst cached = await cache.match(cacheRequest);\n\t\tif (cached) {\n\t\t\treturn cached;\n\t\t}\n\n\t\t// Execute handler\n\t\tawait next();\n\n\t\t// Only cache successful responses\n\t\tconst response = c.res;\n\t\tif (response.status >= 200 && response.status < 300) {\n\t\t\t// Clone the response and add cache headers\n\t\t\tconst cloned = response.clone();\n\t\t\tconst cachedResponse = new Response(cloned.body, {\n\t\t\t\tstatus: cloned.status,\n\t\t\t\tstatusText: cloned.statusText,\n\t\t\t\theaders: new Headers(cloned.headers),\n\t\t\t});\n\t\t\tconst actualTtl = jitter\n\t\t\t\t? Math.max(1, ttl + Math.floor(Math.random() * jitter * 2) - jitter)\n\t\t\t\t: ttl;\n\t\t\tcachedResponse.headers.set(\"Cache-Control\", `s-maxage=${actualTtl}`);\n\n\t\t\t// Store in cache — use waitUntil if available, otherwise await directly\n\t\t\tconst putPromise = cache.put(cacheRequest, cachedResponse);\n\t\t\ttry {\n\t\t\t\tc.executionCtx.waitUntil(putPromise);\n\t\t\t} catch {\n\t\t\t\t// executionCtx not available (e.g., in tests), await directly\n\t\t\t\tawait putPromise;\n\t\t\t}\n\t\t}\n\t};\n}\n",
|
|
9
11
|
"import type { EnvSchema, InferEnv } from \"@workkit/env\";\nimport type { Context } from \"hono\";\nimport type { WorkkitEnv } from \"./types\";\n\n/**\n * Get the validated, typed environment from Hono context.\n *\n * Requires the workkit() middleware to have run first.\n * Throws if env has not been validated yet.\n *\n * @example\n * ```ts\n * app.get('/test', (c) => {\n * const env = getEnv(c)\n * return c.json({ key: env.API_KEY })\n * })\n * ```\n */\nexport function getEnv<T extends EnvSchema>(c: Context<WorkkitEnv<T>>): InferEnv<T> {\n\tconst validated = c.get(\"workkit:envValidated\");\n\tif (!validated) {\n\t\tthrow new Error(\n\t\t\t\"workkit:env is not available. Did you forget to add the workkit() middleware?\",\n\t\t);\n\t}\n\treturn c.get(\"workkit:env\");\n}\n"
|
|
10
12
|
],
|
|
11
|
-
"mappings": "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
|
|
12
|
-
"debugId": "
|
|
13
|
+
"mappings": "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",
|
|
14
|
+
"debugId": "80A4AD10CFC3AED664756E2164756E21",
|
|
13
15
|
"names": []
|
|
14
16
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workkit/hono",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Hono middleware that integrates workkit utilities — env validation, error handling, rate limiting, caching",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bikash Dash <beeeku>",
|
|
@@ -35,8 +35,9 @@
|
|
|
35
35
|
"clean": "rm -rf dist"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@workkit/errors": "
|
|
39
|
-
"@workkit/env": "
|
|
38
|
+
"@workkit/errors": "^1.0.1",
|
|
39
|
+
"@workkit/env": "^0.1.1",
|
|
40
|
+
"@workkit/ratelimit": "^0.2.0"
|
|
40
41
|
},
|
|
41
42
|
"peerDependencies": {
|
|
42
43
|
"hono": ">=4.0.0"
|