hono-utils 0.3.0 → 0.3.2

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/dist/index.d.cts CHANGED
@@ -4,7 +4,7 @@ import { Logger, Details } from 'hierarchical-area-logger';
4
4
  import * as hono_utils_http_status from 'hono/utils/http-status';
5
5
  import { ContentfulStatusCode, SuccessStatusCode, ClientErrorStatusCode, StatusCode } from 'hono/utils/http-status';
6
6
  import z$1, { z, ZodObject } from 'zod';
7
- import { Message, MessageBatch, Queue } from '@cloudflare/workers-types';
7
+ import { Message, MessageBatch, Queue, Iso3166Alpha2Code, ContinentCode } from '@cloudflare/workers-types';
8
8
  import * as hono_utils_types from 'hono/utils/types';
9
9
  import { hc, ClientResponse } from 'hono/client';
10
10
 
@@ -304,6 +304,110 @@ declare const response: hono.MiddlewareHandler<{
304
304
  Variables: HonoResponseVariables & HonoLoggerVariables;
305
305
  }, string, {}, Response>;
306
306
 
307
+ type CFData = {
308
+ isEUCountry: boolean;
309
+ latitude: string;
310
+ longitude: string;
311
+ city: string;
312
+ country: Iso3166Alpha2Code | 'T1';
313
+ continent: ContinentCode;
314
+ region?: string;
315
+ regionCode?: string;
316
+ postalCode: string;
317
+ timezone: string;
318
+ colo: string;
319
+ asn?: number;
320
+ };
321
+ type UserAgentData = {
322
+ browser: {
323
+ name: string;
324
+ version: string;
325
+ };
326
+ device: {
327
+ model: string;
328
+ vendor: string;
329
+ type: UAParser.IDevice['type'] | 'desktop';
330
+ };
331
+ engine: {
332
+ name: string;
333
+ version: string;
334
+ };
335
+ os: {
336
+ name: string;
337
+ version: string;
338
+ };
339
+ cpu: string;
340
+ };
341
+ type HonoClientInfoVariables = {
342
+ client: CFData & {
343
+ ip: string;
344
+ userAgent: string;
345
+ securityHash: string;
346
+ } & UserAgentData;
347
+ };
348
+ /**
349
+ * Hono middleware that extracts and injects comprehensive client information
350
+ * into the request context, including geolocation data from Cloudflare and
351
+ * parsed User-Agent details.
352
+ * @param {Object} [config] - Optional configuration for the middleware.
353
+ * @param {Function} [config.securityHashString] - A custom function to generate the seed string
354
+ * used for the security hash. Receives Cloudflare and User-Agent data as input.
355
+ * Defaults to `${city}${country}${continent}`.
356
+ * @param {string} [config.hashSecretBinding] - The name of the environment variable
357
+ * containing the hash secret. Defaults to 'HASH_SECRET'.
358
+ * @returns {MiddlewareHandler} A Hono middleware handler.
359
+ * @throws {Error} Throws an error if Cloudflare (`req.raw.cf`) data is missing.
360
+ * @example
361
+ * ```ts
362
+ * const app = new Hono<{ Variables: HonoClientInfoVariables }>();
363
+ *
364
+ * app.use('*', clientInfo());
365
+ *
366
+ * app.get('/me', (c) => {
367
+ * const client = c.get('client');
368
+ * return c.json({
369
+ * location: `${client.city}, ${client.country}`,
370
+ * device: client.device.type
371
+ * });
372
+ * });
373
+ * ```
374
+ */
375
+ declare const clientInfo: (config?: {
376
+ hashSecretBinding?: string;
377
+ securityHashString?: (content: CFData & UserAgentData & {
378
+ ip: string;
379
+ }) => string;
380
+ }) => hono.MiddlewareHandler<{
381
+ Bindings: Record<string, string>;
382
+ Variables: HonoClientInfoVariables;
383
+ }, string, {}, Response>;
384
+
385
+ /**
386
+ * A Hono middleware factory that hydrates a context variable based on environment bindings or other variables.
387
+ * @template {Record<string, unknown>} Bindings - The environment bindings (e.g., Cloudflare Env).
388
+ * @template {Record<string, unknown>} Variables - The Hono context variables.
389
+ * @template HidratedResult - The resulting type of the hydrated variable.
390
+ * @param {Object} options - The configuration options for hydration.
391
+ * @param {keyof Variables} options.variableName - The name of the variable in the context to be set.
392
+ * @param {(c: Bindings & { get: <T>(key: keyof Variables) => T }) => HidratedResult} options.hydrate -
393
+ * A function that takes the environment and a getter, returning the hydrated value.
394
+ * @returns {import("hono").MiddlewareHandler} A Hono middleware that performs the hydration and calls next().
395
+ * @example
396
+ * const middleware = hydrateVariable({
397
+ * variableName: 'userProfile',
398
+ * hydrate: (c) => new UserProfile(c.USER_ID)
399
+ * });
400
+ */
401
+ declare const hydrateVariable: <Bindings extends Record<string, unknown>, Variables extends Record<string, unknown>, HidratedResult>({ variableName, hydrate, }: {
402
+ variableName: keyof Variables;
403
+ hydrate: (c: Bindings & {
404
+ get: <T>(key: keyof Variables) => T;
405
+ }) => HidratedResult;
406
+ }) => hono.MiddlewareHandler<{
407
+ Bindings: Bindings;
408
+ Variables: Record<string, HidratedResult>;
409
+ }, string, {}, Response>;
410
+
307
411
  /**
308
412
  * Derives a secure hex-encoded hash from an input string using PBKDF2-HMAC-SHA256.
309
413
  * * @example
@@ -375,4 +479,4 @@ interface CreateTypedClientOptions {
375
479
  }
376
480
  declare const createTypedClient: <TApp extends Hono<any, any, any>>(options: CreateTypedClientOptions) => <TSuccessData>(fn: (c: ReturnType<typeof hc<TApp>>) => Promise<ClientResponse<TSuccessData>>) => Promise<TSuccessData>;
377
481
 
378
- export { type ContextFn, type CreateTypedClientOptions, type HonoI18nVariables, type HonoIsBotVariables, type HonoLanguageVariables, type HonoLoggerVariables, type HonoResponseVariables, type MessageHandlers, pbkdf2 as PBKDF2, QueueHandler, sha as SHA, type WrappableMiddleware, createTypedClient, i18n, isBot, jsonValidator, logger, onError, onNotFound, queue, response, withLogger };
482
+ export { type ContextFn, type CreateTypedClientOptions, type HonoClientInfoVariables, type HonoI18nVariables, type HonoIsBotVariables, type HonoLanguageVariables, type HonoLoggerVariables, type HonoResponseVariables, type MessageHandlers, pbkdf2 as PBKDF2, QueueHandler, sha as SHA, type WrappableMiddleware, clientInfo, createTypedClient, hydrateVariable, i18n, isBot, jsonValidator, logger, onError, onNotFound, queue, response, withLogger };
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ import { Logger, Details } from 'hierarchical-area-logger';
4
4
  import * as hono_utils_http_status from 'hono/utils/http-status';
5
5
  import { ContentfulStatusCode, SuccessStatusCode, ClientErrorStatusCode, StatusCode } from 'hono/utils/http-status';
6
6
  import z$1, { z, ZodObject } from 'zod';
7
- import { Message, MessageBatch, Queue } from '@cloudflare/workers-types';
7
+ import { Message, MessageBatch, Queue, Iso3166Alpha2Code, ContinentCode } from '@cloudflare/workers-types';
8
8
  import * as hono_utils_types from 'hono/utils/types';
9
9
  import { hc, ClientResponse } from 'hono/client';
10
10
 
@@ -304,6 +304,110 @@ declare const response: hono.MiddlewareHandler<{
304
304
  Variables: HonoResponseVariables & HonoLoggerVariables;
305
305
  }, string, {}, Response>;
306
306
 
307
+ type CFData = {
308
+ isEUCountry: boolean;
309
+ latitude: string;
310
+ longitude: string;
311
+ city: string;
312
+ country: Iso3166Alpha2Code | 'T1';
313
+ continent: ContinentCode;
314
+ region?: string;
315
+ regionCode?: string;
316
+ postalCode: string;
317
+ timezone: string;
318
+ colo: string;
319
+ asn?: number;
320
+ };
321
+ type UserAgentData = {
322
+ browser: {
323
+ name: string;
324
+ version: string;
325
+ };
326
+ device: {
327
+ model: string;
328
+ vendor: string;
329
+ type: UAParser.IDevice['type'] | 'desktop';
330
+ };
331
+ engine: {
332
+ name: string;
333
+ version: string;
334
+ };
335
+ os: {
336
+ name: string;
337
+ version: string;
338
+ };
339
+ cpu: string;
340
+ };
341
+ type HonoClientInfoVariables = {
342
+ client: CFData & {
343
+ ip: string;
344
+ userAgent: string;
345
+ securityHash: string;
346
+ } & UserAgentData;
347
+ };
348
+ /**
349
+ * Hono middleware that extracts and injects comprehensive client information
350
+ * into the request context, including geolocation data from Cloudflare and
351
+ * parsed User-Agent details.
352
+ * @param {Object} [config] - Optional configuration for the middleware.
353
+ * @param {Function} [config.securityHashString] - A custom function to generate the seed string
354
+ * used for the security hash. Receives Cloudflare and User-Agent data as input.
355
+ * Defaults to `${city}${country}${continent}`.
356
+ * @param {string} [config.hashSecretBinding] - The name of the environment variable
357
+ * containing the hash secret. Defaults to 'HASH_SECRET'.
358
+ * @returns {MiddlewareHandler} A Hono middleware handler.
359
+ * @throws {Error} Throws an error if Cloudflare (`req.raw.cf`) data is missing.
360
+ * @example
361
+ * ```ts
362
+ * const app = new Hono<{ Variables: HonoClientInfoVariables }>();
363
+ *
364
+ * app.use('*', clientInfo());
365
+ *
366
+ * app.get('/me', (c) => {
367
+ * const client = c.get('client');
368
+ * return c.json({
369
+ * location: `${client.city}, ${client.country}`,
370
+ * device: client.device.type
371
+ * });
372
+ * });
373
+ * ```
374
+ */
375
+ declare const clientInfo: (config?: {
376
+ hashSecretBinding?: string;
377
+ securityHashString?: (content: CFData & UserAgentData & {
378
+ ip: string;
379
+ }) => string;
380
+ }) => hono.MiddlewareHandler<{
381
+ Bindings: Record<string, string>;
382
+ Variables: HonoClientInfoVariables;
383
+ }, string, {}, Response>;
384
+
385
+ /**
386
+ * A Hono middleware factory that hydrates a context variable based on environment bindings or other variables.
387
+ * @template {Record<string, unknown>} Bindings - The environment bindings (e.g., Cloudflare Env).
388
+ * @template {Record<string, unknown>} Variables - The Hono context variables.
389
+ * @template HidratedResult - The resulting type of the hydrated variable.
390
+ * @param {Object} options - The configuration options for hydration.
391
+ * @param {keyof Variables} options.variableName - The name of the variable in the context to be set.
392
+ * @param {(c: Bindings & { get: <T>(key: keyof Variables) => T }) => HidratedResult} options.hydrate -
393
+ * A function that takes the environment and a getter, returning the hydrated value.
394
+ * @returns {import("hono").MiddlewareHandler} A Hono middleware that performs the hydration and calls next().
395
+ * @example
396
+ * const middleware = hydrateVariable({
397
+ * variableName: 'userProfile',
398
+ * hydrate: (c) => new UserProfile(c.USER_ID)
399
+ * });
400
+ */
401
+ declare const hydrateVariable: <Bindings extends Record<string, unknown>, Variables extends Record<string, unknown>, HidratedResult>({ variableName, hydrate, }: {
402
+ variableName: keyof Variables;
403
+ hydrate: (c: Bindings & {
404
+ get: <T>(key: keyof Variables) => T;
405
+ }) => HidratedResult;
406
+ }) => hono.MiddlewareHandler<{
407
+ Bindings: Bindings;
408
+ Variables: Record<string, HidratedResult>;
409
+ }, string, {}, Response>;
410
+
307
411
  /**
308
412
  * Derives a secure hex-encoded hash from an input string using PBKDF2-HMAC-SHA256.
309
413
  * * @example
@@ -375,4 +479,4 @@ interface CreateTypedClientOptions {
375
479
  }
376
480
  declare const createTypedClient: <TApp extends Hono<any, any, any>>(options: CreateTypedClientOptions) => <TSuccessData>(fn: (c: ReturnType<typeof hc<TApp>>) => Promise<ClientResponse<TSuccessData>>) => Promise<TSuccessData>;
377
481
 
378
- export { type ContextFn, type CreateTypedClientOptions, type HonoI18nVariables, type HonoIsBotVariables, type HonoLanguageVariables, type HonoLoggerVariables, type HonoResponseVariables, type MessageHandlers, pbkdf2 as PBKDF2, QueueHandler, sha as SHA, type WrappableMiddleware, createTypedClient, i18n, isBot, jsonValidator, logger, onError, onNotFound, queue, response, withLogger };
482
+ export { type ContextFn, type CreateTypedClientOptions, type HonoClientInfoVariables, type HonoI18nVariables, type HonoIsBotVariables, type HonoLanguageVariables, type HonoLoggerVariables, type HonoResponseVariables, type MessageHandlers, pbkdf2 as PBKDF2, QueueHandler, sha as SHA, type WrappableMiddleware, clientInfo, createTypedClient, hydrateVariable, i18n, isBot, jsonValidator, logger, onError, onNotFound, queue, response, withLogger };
package/dist/index.js CHANGED
@@ -13957,6 +13957,10 @@ var response = createMiddleware6(async (c, next) => {
13957
13957
  await next();
13958
13958
  });
13959
13959
 
13960
+ // src/middleware/clientInfo.ts
13961
+ import { createMiddleware as createMiddleware7 } from "hono/factory";
13962
+ import { UAParser } from "ua-parser-js";
13963
+
13960
13964
  // src/crypto/pbkdf2.ts
13961
13965
  var pbkdf2_exports = {};
13962
13966
  __export(pbkdf2_exports, {
@@ -14038,6 +14042,91 @@ async function hash3({
14038
14042
  return hashedString;
14039
14043
  }
14040
14044
 
14045
+ // src/middleware/clientInfo.ts
14046
+ var clientInfo = (config2) => createMiddleware7(async ({ env, req, set: set2 }, next) => {
14047
+ if (!req.raw.cf) {
14048
+ throw new Error("Cloudflare data is not available");
14049
+ }
14050
+ const hashSecret = env[config2?.hashSecretBinding ?? "HASH_SECRET"];
14051
+ if (!hashSecret) {
14052
+ throw new Error("Hash secret is not available");
14053
+ }
14054
+ const {
14055
+ latitude,
14056
+ longitude,
14057
+ city,
14058
+ country,
14059
+ continent,
14060
+ colo,
14061
+ asn,
14062
+ isEUCountry,
14063
+ region,
14064
+ regionCode,
14065
+ postalCode,
14066
+ timezone
14067
+ } = req.raw.cf;
14068
+ const cf = {
14069
+ latitude,
14070
+ longitude,
14071
+ city,
14072
+ country,
14073
+ continent,
14074
+ colo,
14075
+ asn,
14076
+ isEUCountry: Boolean(isEUCountry),
14077
+ region,
14078
+ regionCode,
14079
+ postalCode,
14080
+ timezone
14081
+ };
14082
+ const userAgent = req.raw.headers.get("user-agent") ?? "Unknown";
14083
+ const parser = new UAParser(userAgent);
14084
+ const userAgentParsed = parser.getResult();
14085
+ const { browser, device, engine, os, cpu } = userAgentParsed;
14086
+ const ua = {
14087
+ browser: {
14088
+ name: browser.name ?? "Unknown",
14089
+ version: browser.version ?? "Unknown"
14090
+ },
14091
+ device: {
14092
+ model: device.model ?? "Unknown",
14093
+ vendor: device.vendor ?? "Unknown",
14094
+ type: device.type ?? "desktop"
14095
+ },
14096
+ engine: {
14097
+ name: engine.name ?? "Unknown",
14098
+ version: engine.version ?? "Unknown"
14099
+ },
14100
+ os: {
14101
+ name: os.name ?? "Unknown",
14102
+ version: os.version ?? "Unknown"
14103
+ },
14104
+ cpu: cpu.architecture ?? "Unknown"
14105
+ };
14106
+ const ip = req.raw.headers.get("CF-Connecting-IP") ?? "127.0.0.1";
14107
+ const clientContent = { ...cf, ...ua, ip };
14108
+ set2("client", {
14109
+ ...clientContent,
14110
+ userAgent,
14111
+ securityHash: await sha_exports.hash({
14112
+ input: config2?.securityHashString?.(clientContent) ?? `${ip}${city}${country}${userAgent}`,
14113
+ algorithm: "SHA-512",
14114
+ pepper: hashSecret
14115
+ })
14116
+ });
14117
+ next();
14118
+ });
14119
+
14120
+ // src/middleware/hydrateVariable.ts
14121
+ import { createMiddleware as createMiddleware8 } from "hono/factory";
14122
+ var hydrateVariable = ({
14123
+ variableName,
14124
+ hydrate
14125
+ }) => createMiddleware8(async ({ set: set2, env, get }, next) => {
14126
+ set2(variableName, hydrate({ ...env, get }));
14127
+ await next();
14128
+ });
14129
+
14041
14130
  // src/queue/QueueHandler.ts
14042
14131
  import { init } from "@paralleldrive/cuid2";
14043
14132
  var QueueHandler = class {
@@ -14190,7 +14279,9 @@ export {
14190
14279
  pbkdf2_exports as PBKDF2,
14191
14280
  QueueHandler,
14192
14281
  sha_exports as SHA,
14282
+ clientInfo,
14193
14283
  createTypedClient,
14284
+ hydrateVariable,
14194
14285
  i18n,
14195
14286
  isBot,
14196
14287
  jsonValidator,