@@ -2,6 +2,8 @@ import type { Context, Middleware } from "../../../core/src";
22
33/**
44 * Secure IP extraction configuration
5+ *
6+ * @interface IpExtractionConfig
57 */
68export interface IpExtractionConfig {
79 /**
@@ -47,10 +49,17 @@ export interface IpExtractionConfig {
4749 logWarnings ?: boolean ;
4850}
4951
52+ /**
53+ * Cloud provider configuration type
54+ * @internal
55+ */
56+ type CloudProviderConfig = Partial < IpExtractionConfig > ;
57+
5058/**
5159 * Default secure configurations for common cloud providers
60+ * @internal
5261 */
53- const CLOUD_CONFIGS : Record < string , Partial < IpExtractionConfig > > = {
62+ const CLOUD_CONFIGS : Record < string , CloudProviderConfig > = {
5463 aws : {
5564 trustProxy : true ,
5665 trustedHeaders : [ "x-forwarded-for" ] ,
@@ -109,22 +118,38 @@ const CLOUD_CONFIGS: Record<string, Partial<IpExtractionConfig>> = {
109118} ;
110119
111120/**
112- * IP extraction middleware that populates ctx.clientIp
121+ * IP extraction middleware factory
122+ *
123+ * Creates middleware that securely extracts the client IP address from requests,
124+ * handling various proxy configurations and preventing IP spoofing attacks.
125+ *
126+ * @template T - Context type parameter
127+ * @param {IpExtractionConfig | IpExtractionPreset } [config="direct"] - Configuration object or preset name
128+ * @returns {Middleware<T> } IP extraction middleware
113129 *
114130 * @example
115131 * ```typescript
116- * // Using presets
132+ * // Direct connection (no proxy)
133+ * app.use(ipExtract("direct"));
134+ *
135+ * // Behind Cloudflare
117136 * app.use(ipExtract("cloudflare"));
118137 *
119- * // Using configuration
138+ * // Custom configuration
120139 * app.use(ipExtract({
121140 * trustProxy: true,
122141 * trustedProxies: ["10.0.0.0/8"],
123- * trustedHeaders: ["x-real-ip"]
142+ * trustedHeaders: ["x-real-ip"],
143+ * logWarnings: true
124144 * }));
125145 *
126- * // Direct connection
127- * app.use(ipExtract("direct"));
146+ * // Access extracted IP
147+ * app.get("/api/info", (ctx) => {
148+ * const clientIp = ctx.clientIp;
149+ * // or
150+ * const ip = getClientIp(ctx);
151+ * return ctx.json({ ip });
152+ * });
128153 * ```
129154 */
130155export function ipExtract < T extends Record < string , unknown > = Record < string , unknown > > (
@@ -168,15 +193,38 @@ export function ipExtract<T extends Record<string, unknown> = Record<string, unk
168193}
169194
170195/**
171- * Helper to get client IP from context (for use in other middlewares)
172- * This provides backward compatibility and type safety
196+ * Helper function to get client IP from context
197+ *
198+ * Provides a type-safe way to access the client IP that was
199+ * extracted by the ipExtract middleware.
200+ *
201+ * @template T - Context type parameter
202+ * @param {Context<T> } ctx - Request context
203+ * @returns {string | undefined } The client IP address or undefined
204+ *
205+ * @example
206+ * ```typescript
207+ * app.use(ipExtract("cloudflare"));
208+ *
209+ * app.get("/api/log", (ctx) => {
210+ * const ip = getClientIp(ctx);
211+ * console.log(`Request from: ${ip}`);
212+ * return ctx.text("OK");
213+ * });
214+ * ```
173215 */
174216export function getClientIp < T extends Record < string , unknown > > ( ctx : Context < T > ) : string | undefined {
175217 return ctx . clientIp ;
176218}
177219
178220/**
179221 * Securely extract client IP based on configuration
222+ *
223+ * @internal
224+ * @template T - Context type parameter
225+ * @param {Context<T> } ctx - Request context
226+ * @param {Required<IpExtractionConfig> } config - Resolved configuration
227+ * @returns {string | undefined } Extracted IP address
180228 */
181229function secureExtractClientIp < T extends Record < string , unknown > > ( ctx : Context < T > , config : Required < IpExtractionConfig > ) : string | undefined {
182230 // 1. If not trusting proxies, only use direct connection IP
@@ -219,6 +267,15 @@ function secureExtractClientIp<T extends Record<string, unknown>>(ctx: Context<T
219267
220268/**
221269 * Parse X-Forwarded-For header securely
270+ *
271+ * Extracts the client IP from an X-Forwarded-For header chain,
272+ * with protection against overly long chains.
273+ *
274+ * @internal
275+ * @param {string } value - X-Forwarded-For header value
276+ * @param {number } maxChain - Maximum chain length to accept
277+ * @param {boolean } logWarnings - Whether to log warnings
278+ * @returns {string | undefined } Extracted IP or undefined
222279 */
223280function parseXForwardedFor ( value : string , maxChain : number , logWarnings : boolean ) : string | undefined {
224281 const ips = value
@@ -252,6 +309,11 @@ function parseXForwardedFor(value: string, maxChain: number, logWarnings: boolea
252309
253310/**
254311 * Check if IP is in trusted list (supports CIDR notation)
312+ *
313+ * @internal
314+ * @param {string } ip - IP address to check
315+ * @param {string[] } trustedList - List of trusted IPs/CIDRs
316+ * @returns {boolean } True if IP is trusted
255317 */
256318function isIpTrusted ( ip : string , trustedList : string [ ] ) : boolean {
257319 const normalizedIp = normalizeIp ( ip ) ;
@@ -272,6 +334,11 @@ function isIpTrusted(ip: string, trustedList: string[]): boolean {
272334
273335/**
274336 * Check if IP is within CIDR range
337+ *
338+ * @internal
339+ * @param {string } ip - IP address to check
340+ * @param {string } cidr - CIDR range (e.g., "10.0.0.0/8")
341+ * @returns {boolean } True if IP is within range
275342 */
276343function isIpInCidr ( ip : string , cidr : string ) : boolean {
277344 const [ range , prefixLength ] = cidr . split ( "/" ) ;
@@ -295,15 +362,25 @@ function isIpInCidr(ip: string, cidr: string): boolean {
295362}
296363
297364/**
298- * Convert IPv4 to number for CIDR calculation
365+ * Convert IPv4 address to number for CIDR calculation
366+ *
367+ * @internal
368+ * @param {string } ip - IPv4 address
369+ * @returns {number } Numeric representation
299370 */
300371function ipv4ToNumber ( ip : string ) : number {
301372 const parts = ip . split ( "." ) . map ( ( p ) => parseInt ( p , 10 ) ) ;
302373 return ( ( parts [ 0 ] << 24 ) | ( parts [ 1 ] << 16 ) | ( parts [ 2 ] << 8 ) | parts [ 3 ] ) >>> 0 ;
303374}
304375
305376/**
306- * Check if IPv6 is in CIDR range (simplified)
377+ * Check if IPv6 is in CIDR range (simplified implementation)
378+ *
379+ * @internal
380+ * @param {string } ip - IPv6 address
381+ * @param {string } range - IPv6 CIDR range
382+ * @param {number } prefix - Prefix length
383+ * @returns {boolean } True if IP is within range
307384 */
308385function isIpv6InCidr ( ip : string , range : string , prefix : number ) : boolean {
309386 // This is a simplified check - for production use, consider a library
@@ -319,7 +396,11 @@ function isIpv6InCidr(ip: string, range: string, prefix: number): boolean {
319396}
320397
321398/**
322- * Basic IPv6 normalization (expand :: notation)
399+ * Basic IPv6 normalization
400+ *
401+ * @internal
402+ * @param {string } ip - IPv6 address
403+ * @returns {string } Normalized IPv6 address
323404 */
324405function normalizeIpv6 ( ip : string ) : string {
325406 // Remove any zone index
@@ -331,7 +412,11 @@ function normalizeIpv6(ip: string): string {
331412}
332413
333414/**
334- * Check if string is IPv4
415+ * Check if string is a valid IPv4 address
416+ *
417+ * @internal
418+ * @param {string } ip - String to check
419+ * @returns {boolean } True if valid IPv4
335420 */
336421function isIpv4 ( ip : string ) : boolean {
337422 const ipv4Regex = / ^ ( \d { 1 , 3 } \. ) { 3 } \d { 1 , 3 } $ / ;
@@ -345,7 +430,11 @@ function isIpv4(ip: string): boolean {
345430}
346431
347432/**
348- * Check if string is IPv6
433+ * Check if string is a valid IPv6 address
434+ *
435+ * @internal
436+ * @param {string } ip - String to check
437+ * @returns {boolean } True if valid IPv6
349438 */
350439function isIpv6 ( ip : string ) : boolean {
351440 // Basic IPv6 validation
@@ -356,13 +445,23 @@ function isIpv6(ip: string): boolean {
356445
357446/**
358447 * Validate IP address format (IPv4 and IPv6)
448+ *
449+ * @internal
450+ * @param {string } ip - String to validate
451+ * @returns {boolean } True if valid IP address
359452 */
360453function isValidIp ( ip : string ) : boolean {
361454 return isIpv4 ( ip ) || isIpv6 ( ip ) ;
362455}
363456
364457/**
365- * Normalize IP address
458+ * Normalize IP address format
459+ *
460+ * Removes IPv6 prefixes, ports, brackets, and other formatting.
461+ *
462+ * @internal
463+ * @param {string } ip - IP address to normalize
464+ * @returns {string } Normalized IP address
366465 */
367466function normalizeIp ( ip : string ) : string {
368467 if ( ! ip ) return ip ;
@@ -387,14 +486,36 @@ function normalizeIp(ip: string): string {
387486}
388487
389488/**
390- * Create a secure IP extractor with your configuration
489+ * Function type for IP extraction
490+ * @callback IpExtractor
491+ * @template T
492+ * @param {Context<T> } ctx - Request context
493+ * @returns {string | undefined } Extracted IP
391494 */
392- function createSecureIpExtractor ( config : Required < IpExtractionConfig > ) {
495+ type IpExtractor = < T extends Record < string , unknown > > ( ctx : Context < T > ) => string | undefined ;
496+
497+ /**
498+ * Create a secure IP extractor function with configuration
499+ *
500+ * @internal
501+ * @param {Required<IpExtractionConfig> } config - Configuration
502+ * @returns {IpExtractor } IP extractor function
503+ */
504+ function createSecureIpExtractor ( config : Required < IpExtractionConfig > ) : IpExtractor {
393505 return < T extends Record < string , unknown > > ( ctx : Context < T > ) => secureExtractClientIp ( ctx , config ) ;
394506}
395507
396508/**
397- * Example configurations for common scenarios
509+ * Predefined configurations for common deployment scenarios
510+ *
511+ * @example
512+ * ```typescript
513+ * // Use a preset
514+ * app.use(ipExtract("cloudflare"));
515+ *
516+ * // Access preset configuration
517+ * const cloudflareConfig = IP_EXTRACTION_PRESETS.cloudflare;
518+ * ```
398519 */
399520export const IP_EXTRACTION_PRESETS = {
400521 /**
@@ -472,5 +593,7 @@ export const IP_EXTRACTION_PRESETS = {
472593 } as IpExtractionConfig ,
473594} as const ;
474595
475- // Export types
596+ /**
597+ * Type for available IP extraction presets
598+ */
476599export type IpExtractionPreset = keyof typeof IP_EXTRACTION_PRESETS ;
0 commit comments