hono-utils 0.1.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.
@@ -0,0 +1,357 @@
1
+ import * as hono from 'hono';
2
+ import { Env, Context } from 'hono';
3
+ import { Logger, Details } from 'hierarchical-area-logger';
4
+ import * as hono_utils_http_status from 'hono/utils/http-status';
5
+ import { ContentfulStatusCode, SuccessStatusCode, ClientErrorStatusCode } from 'hono/utils/http-status';
6
+ import z$1, { z, ZodObject } from 'zod';
7
+ import { Message, MessageBatch, Queue } from '@cloudflare/workers-types';
8
+ import * as hono_utils_types from 'hono/utils/types';
9
+
10
+ type HonoLoggerVariables = {
11
+ logger: Logger;
12
+ eventId: string;
13
+ };
14
+ /**
15
+ * Logger middleware
16
+ *
17
+ * @description
18
+ * This middleware sets up a logger.
19
+ *
20
+ * @param details - Details to pass to the logger
21
+ * @param parentEventIdHeader - Header name to use for parent event ID
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * app.use('*', logger(
26
+ * {
27
+ * service: 'logger',
28
+ * },
29
+ * parentEventIdHeader: 'parent-event-id',
30
+ * ));
31
+ * ```
32
+ */
33
+ declare const logger: (details: Details, parentEventIdHeader?: string) => hono.MiddlewareHandler<{
34
+ Variables: HonoLoggerVariables;
35
+ }, string, {}, Response>;
36
+
37
+ type WrappableMiddleware<V extends Env> = (c: Context<V>, loggerFunctions: ReturnType<Logger['getArea']> & Pick<Logger, 'eventId' | 'parentEventId' | 'dump'>) => Promise<void>;
38
+ /**
39
+ * Wraps middleware with logger. Logger should be initialized before middleware.
40
+ *
41
+ * @param name - Name of the middleware
42
+ * @param middleware - Middleware function
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * app.use('*', logger(
47
+ * {
48
+ * service: 'logger',
49
+ * },
50
+ * parentEventIdHeader: 'parent-event-id',
51
+ * ));
52
+ *
53
+ * app.use('*', withLogger('middlewareName', (c, { info }) => {
54
+ * info('Middleware initialized');
55
+ * }));
56
+ * ```
57
+ */
58
+ declare const withLogger: <V extends Env>(name: string, middleware: WrappableMiddleware<V>) => hono.MiddlewareHandler<any, string, {}, Response>;
59
+
60
+ /**
61
+ * @description
62
+ * Validator middleware for JSON data.
63
+ *
64
+ * @param schema - Zod schema for validation
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * app.post('/user', jsonValidator(z.object({
69
+ * name: z.string(),
70
+ * age: z.number(),
71
+ * })), (c) => {
72
+ * const { name, age } = await c.req.valid('json');
73
+ * return c.json({ name, age });
74
+ * });
75
+ * ```
76
+ */
77
+ declare const jsonValidator: <T extends z.ZodRawShape>(schema: z.ZodObject<T>) => hono.MiddlewareHandler<any, string, {
78
+ in: {
79
+ json: z.core.$InferObjectOutput<T, {}> extends infer T_1 ? T_1 extends z.core.$InferObjectOutput<T, {}> ? T_1 extends Promise<infer PR> ? PR extends Response | hono.TypedResponse<any, any, any> ? never : PR : T_1 extends Response | hono.TypedResponse<any, any, any> ? never : T_1 : never : never;
80
+ };
81
+ out: {
82
+ json: z.core.$InferObjectOutput<T, {}> extends infer T_2 ? T_2 extends z.core.$InferObjectOutput<T, {}> ? T_2 extends Promise<infer PR> ? PR extends Response | hono.TypedResponse<any, any, any> ? never : PR : T_2 extends Response | hono.TypedResponse<any, any, any> ? never : T_2 : never : never;
83
+ };
84
+ }, z.core.$InferObjectOutput<T, {}> extends infer T_3 ? T_3 extends z.core.$InferObjectOutput<T, {}> ? T_3 extends Promise<infer PR_1> ? PR_1 extends hono.TypedResponse<infer T_4, infer S extends hono_utils_http_status.StatusCode, infer F extends string> ? hono.TypedResponse<T_4, S, F> : PR_1 extends Response ? PR_1 : PR_1 extends undefined ? never : never : T_3 extends hono.TypedResponse<infer T_5, infer S_1 extends hono_utils_http_status.StatusCode, infer F_1 extends string> ? hono.TypedResponse<T_5, S_1, F_1> : T_3 extends Response ? T_3 : T_3 extends undefined ? never : never : never : never>;
85
+
86
+ type HonoIsBotVariables = {
87
+ isBot: boolean;
88
+ };
89
+ /**
90
+ * Middleware to detect and optionally block requests based on Cloudflare's Bot Management score.
91
+ * @param {Object} options - Configuration options for bot detection.
92
+ * @param {number} [options.threshold=49] - The bot score threshold (1-100).
93
+ * Cloudflare scores below this value are treated as bots. A lower score indicates a higher
94
+ * probability that the request is automated.
95
+ * @param {boolean} [options.allowVerifiedBot] - If true, the middleware strictly requires
96
+ * bots to be "verified" (e.g., Googlebot, Bingbot).
97
+ * Note: If this is enabled, non-verified bots will be blocked regardless of their score.
98
+ * @returns {MiddlewareHandler} A Hono middleware handler.
99
+ * @throws {HTTPException} Throws a 401 "Bot detected" error if the score is below the
100
+ * threshold or if a non-verified bot is detected when `allowVerifiedBot` is true.
101
+ * @example
102
+ * ```ts
103
+ * const app = new Hono<{ Variables: HonoIsBotVariables }>();
104
+ * // Block suspected bots with a score lower than 50
105
+ * app.use('/api/*', isBot({ threshold: 50, allowVerifiedBot: true }));
106
+ * ```
107
+ */
108
+ declare const isBot: ({ threshold, allowVerifiedBot, }: {
109
+ threshold: number;
110
+ allowVerifiedBot?: boolean;
111
+ }) => hono.MiddlewareHandler<{
112
+ Variables: HonoIsBotVariables;
113
+ }, string, {}, Response>;
114
+
115
+ type HonoLanguageVariables = {
116
+ language: string;
117
+ };
118
+ type I18nShape = {
119
+ [key: string]: I18nShape | string;
120
+ };
121
+ type HonoI18nVariables = {
122
+ t: ReturnType<typeof setI18n>;
123
+ };
124
+ /**
125
+ * Set i18n
126
+ *
127
+ * @param translations - Translations object
128
+ * @param language - Language
129
+ * @param defaultLanguage - Default language
130
+ */
131
+ declare const setI18n: <T extends I18nShape>(translations: T, language: keyof T, defaultLanguage: string) => (input: string, params?: Record<string, string | number>, overrideLanguage?: keyof T) => string;
132
+ /**
133
+ * I18n Middleware and Queue Factory.
134
+ * @description
135
+ * Sets up the `t` translation function in the Hono context.
136
+ * Note: Requires a preceding middleware to set the `language` variable.
137
+ * @template T - The shape of the translations object.
138
+ * @param translations - The dictionary of translations.
139
+ * @param defaultLanguage - The fallback language code.
140
+ * @returns An object containing the `middleware` for Hono and a `queue` helper for background tasks.
141
+ * @example
142
+ * ```ts
143
+ * const translations = { en: { hi: "Hi {{name}}" }, fr: { hi: "Salut {{name}}" } };
144
+ * // 1. Set language first
145
+ * app.use('*', async (c, next) => {
146
+ * c.set('language', 'en');
147
+ * await next();
148
+ * });
149
+ * // 2. Initialize i18n
150
+ * const { middleware } = i18n(translations, 'en');
151
+ * app.use('*', middleware);
152
+ * // 3. Use in routes
153
+ * app.get('/', (c) => c.text(c.var.t('hi', { name: 'User' })));
154
+ * ```
155
+ */
156
+ declare const i18n: <T extends I18nShape>(translations: T, defaultLanguage: string) => {
157
+ middleware: hono.MiddlewareHandler<{
158
+ Variables: HonoLanguageVariables & HonoI18nVariables;
159
+ }, string, {}, Response>;
160
+ queue: (language?: keyof T) => (input: string, params?: Record<string, string | number>, overrideLanguage?: keyof T | undefined) => string;
161
+ };
162
+
163
+ type DataShape = Record<string, unknown>;
164
+
165
+ type QueueSendParams = {
166
+ parentEventId?: string;
167
+ language?: string;
168
+ };
169
+
170
+ type GenericQueueDataParams = {
171
+ eventId: string;
172
+ } & QueueSendParams;
173
+
174
+ type GenericQueueData<Content extends DataShape> = {
175
+ handler: keyof Content;
176
+ content: Content[keyof Content];
177
+ } & GenericQueueDataParams;
178
+
179
+ interface Variables<Context> {
180
+ context: Context & Pick<Message, 'retry' | 'ack'>;
181
+ metadata: Omit<GenericQueueData<DataShape>, 'content'> &
182
+ Omit<Message, 'body' | 'retry' | 'ack'>;
183
+ }
184
+
185
+ interface Handler<Content, Context> {
186
+ (message: Content, context: Context): Promise<void>;
187
+ }
188
+
189
+ type MessageHandlers<QueueData extends DataShape, Context> = {
190
+ [Term in keyof QueueData]: Handler<QueueData[Term], Variables<Context>>;
191
+ };
192
+
193
+ interface ContextFn<Env, Context> {
194
+ (env: Env, params: GenericQueueDataParams): Context;
195
+ }
196
+
197
+ /**
198
+ * A type-safe wrapper for Cloudflare Worker Queues using Zod for validation.
199
+ * * This class orchestrates the relationship between incoming queue messages and
200
+ * specific logic handlers, ensuring that data is validated at the edge before
201
+ * reaching your business logic.
202
+ *
203
+ * @template Schema - The Zod schema defining the message shapes.
204
+ * @template Environment - The Cloudflare Worker environment bindings (Env).
205
+ * @template Context - The application context derived from the environment and message.
206
+ */
207
+ declare class QueueHandler<Schema extends ZodObject<any>, Environment, Context> {
208
+ private readonly config;
209
+ private readonly handlers;
210
+ /**
211
+ * @param config - Configuration object.
212
+ * @param config.schema - The Zod schema used to parse and validate incoming message content.
213
+ * @param config.setContext - A factory function to generate context (e.g., DB connections, logging) for each message.
214
+ */
215
+ constructor(config: {
216
+ schema: Schema;
217
+ setContext: ContextFn<Environment, Context>;
218
+ });
219
+ /**
220
+ * Registers a specific processing function for a given message type (handlerName).
221
+ * * @template {keyof z.infer<Schema>} HandlerName - A specific key defined in your Zod schema.
222
+ * @param handlerName - The key identifying which handler to use.
223
+ * @param handler - The asynchronous logic to execute when this message type is received.
224
+ * @returns The current instance for fluent chaining.
225
+ */
226
+ addHandler<HandlerName extends keyof z.infer<Schema>>(handlerName: HandlerName, handler: Handler<z.infer<Schema>[HandlerName], Variables<Context>>): this;
227
+ /**
228
+ * Generates the Cloudflare Worker queue consumer function.
229
+ * * This handles the batch processing loop, parses the message body against the
230
+ * schema, injects context, and manages concurrency via `Promise.allSettled`.
231
+ * * @returns An async function compatible with the Cloudflare `queue` export.
232
+ */
233
+ getConsumer(): (batch: MessageBatch<GenericQueueData<z.infer<Schema>>>, env: Environment) => Promise<void>;
234
+ /**
235
+ * Creates a factory function for sending typed messages to a specific queue.
236
+ * * @param queue - The Cloudflare Queue binding (e.g., `env.MY_QUEUE`).
237
+ * @param params - Global parameters to include in every message (e.g., trace IDs).
238
+ * @returns A producer function that accepts a `handler` key and the corresponding typed content.
239
+ */
240
+ getProducer(queue: Queue<unknown>, params: QueueSendParams): <HandlerName extends keyof z.infer<Schema>>(handler: HandlerName, content: z.infer<Schema>[HandlerName]) => Promise<void>;
241
+ }
242
+
243
+ /**
244
+ * Middleware to initialize a Queue producer and attach it to the Hono context.
245
+ * @description
246
+ * This middleware manages Cloudflare Queue interactions. If `finalizeLogHandler` is provided,
247
+ * it will automatically capture the current logger state and send it to the queue after
248
+ * the request is processed (post-`next()`).
249
+ * @param options.name - The name of the Queue binding defined in your `wrangler.toml`.
250
+ * @param options.queueHandler - An instance of your custom QueueHandler.
251
+ * @param [options.finalizeLogHandler] - Optional: The specific message type/key to use when finalizing the log.
252
+ * @example
253
+ * ```ts
254
+ * // OPTIONAL
255
+ * app.use('*', logger(
256
+ * {
257
+ * service: 'logger',
258
+ * },
259
+ * parentEventIdHeader: 'parent-event-id',
260
+ * ));
261
+ *
262
+ * app.use('*', queue({
263
+ * name: 'queue',
264
+ * queueHandler: new QueueHandler(),
265
+ * finalizeLogHandler: 'log'
266
+ * }));
267
+ * ```
268
+ */
269
+ declare const queue: <Schema extends ZodObject<any>, Environment, Context, Key extends keyof z$1.infer<Schema>>({ name, queueHandler, finalizeLogHandler, }: {
270
+ name: string;
271
+ queueHandler: QueueHandler<Schema, Environment, Context>;
272
+ finalizeLogHandler?: Key;
273
+ }) => hono.MiddlewareHandler<{
274
+ Bindings: Record<string, Queue>;
275
+ Variables: HonoLoggerVariables & Record<string, ReturnType<QueueHandler<Schema, Environment, Context>["getProducer"]>>;
276
+ }, string, {}, Response>;
277
+
278
+ type HonoResponseVariables = {
279
+ res: ReturnType<typeof setResponseHandlers>;
280
+ };
281
+ declare const setResponseHandlers: (c: Context) => {
282
+ raw: <T extends {
283
+ status?: ContentfulStatusCode;
284
+ }>(data: T) => Response & hono.TypedResponse<hono_utils_types.JSONParsed<Omit<T, "status">, bigint | readonly bigint[]>, 100 | 102 | 103 | 200 | 201 | 202 | 203 | 206 | 207 | 208 | 226 | 300 | 301 | 302 | 303 | 305 | 306 | 307 | 308 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 | -1, "json">;
285
+ success: <T extends object>(message: string, data?: T, status?: SuccessStatusCode) => Response & hono.TypedResponse<{
286
+ message: string;
287
+ }, 100 | 102 | 103 | 200 | 201 | 202 | 203 | 206 | 207 | 208 | 226 | 300 | 301 | 302 | 303 | 305 | 306 | 307 | 308 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 | -1, "json">;
288
+ error: (message: string, status: ClientErrorStatusCode) => never;
289
+ websocket: (socket: WebSocket) => Response;
290
+ };
291
+ /**
292
+ * Response Middleware
293
+ * @description
294
+ * Injects a `res` object into the Hono context (`c.var.res`).
295
+ * This ensures all outgoing responses follow a consistent schema and include
296
+ * tracing metadata (like `eventId`) automatically.
297
+ * @example
298
+ * ```ts
299
+ * app.use('*', response());
300
+ * ```
301
+ */
302
+ declare const response: hono.MiddlewareHandler<{
303
+ Variables: HonoResponseVariables & HonoLoggerVariables;
304
+ }, string, {}, Response>;
305
+
306
+ /**
307
+ * Derives a secure hex-encoded hash from an input string using PBKDF2-HMAC-SHA256.
308
+ * * @example
309
+ * const passwordHash = await hash("myPassword123", "random-salt-string");
310
+ * * @param {string} input - The plain-text string to hash.
311
+ * @param {string} salt - The salt string used to randomize the hash.
312
+ * @param {number} [iterations=600000] - The CPU cost factor. Default is 600,000.
313
+ * @returns {Promise<string>} A hex-encoded string representing the derived key.
314
+ */
315
+ declare function hash$1(input: string, salt: string, iterations?: number): Promise<string>;
316
+ /**
317
+ * Verifies if an input string matches a stored hash using a constant-time comparison.
318
+ * This prevents timing attacks by ensuring the execution time does not reveal
319
+ * how many characters of the hash were correct.
320
+ * * @param {string} input - The plain-text string to verify.
321
+ * @param {string} salt - The salt used during the original hashing.
322
+ * @param {string} storedHash - The hex-encoded hash previously stored in the database.
323
+ * @param {number} iterations - The iteration count used for the original hash.
324
+ * @returns {Promise<boolean>} Resolves to `true` if the input matches the hash, otherwise `false`.
325
+ */
326
+ declare function verify(input: string, salt: string, storedHash: string, iterations: number): Promise<boolean>;
327
+
328
+ declare const pbkdf2_verify: typeof verify;
329
+ declare namespace pbkdf2 {
330
+ export { hash$1 as hash, pbkdf2_verify as verify };
331
+ }
332
+
333
+ /**
334
+ * Generates a cryptographically strong random salt.
335
+ */
336
+ declare function generateSalt(length?: number): string;
337
+ /**
338
+ * Calculates the SHA hash of the given input string.
339
+ * @param algorithm - The algorithm to be used for hashing. Defaults to 256.
340
+ * @param pepper - An optional string to be prepended to the input before hashing.
341
+ * @param salt - An optional string to be appended to the input before hashing.
342
+ * @returns A Promise that resolves to the hashed string.
343
+ */
344
+ declare function hash({ input, algorithm, pepper, salt, }: {
345
+ input: string;
346
+ algorithm: 'SHA-256' | 'SHA-384' | 'SHA-512';
347
+ pepper?: string;
348
+ salt?: string;
349
+ }): Promise<string>;
350
+
351
+ declare const sha_generateSalt: typeof generateSalt;
352
+ declare const sha_hash: typeof hash;
353
+ declare namespace sha {
354
+ export { sha_generateSalt as generateSalt, sha_hash as hash };
355
+ }
356
+
357
+ export { type ContextFn, type HonoI18nVariables, type HonoIsBotVariables, type HonoLanguageVariables, type HonoLoggerVariables, type HonoResponseVariables, type MessageHandlers, pbkdf2 as PBKDF2, QueueHandler, sha as SHA, type WrappableMiddleware, i18n, isBot, jsonValidator, logger, queue, response, withLogger };