Skip to content

Commit 786944e

Browse files
committed
Improve JSDOCs for ip-extract middleware
1 parent 179ff72 commit 786944e

File tree

1 file changed

+142
-19
lines changed

1 file changed

+142
-19
lines changed

packages/middleware/src/utils/ip-extract.ts

Lines changed: 142 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import type { Context, Middleware } from "../../../core/src";
22

33
/**
44
* Secure IP extraction configuration
5+
*
6+
* @interface IpExtractionConfig
57
*/
68
export 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
*/
130155
export 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
*/
174216
export 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
*/
181229
function 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
*/
223280
function 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
*/
256318
function 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
*/
276343
function 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
*/
300371
function 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
*/
308385
function 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
*/
324405
function 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
*/
336421
function 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
*/
350439
function 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
*/
360453
function 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
*/
367466
function 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
*/
399520
export 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+
*/
476599
export type IpExtractionPreset = keyof typeof IP_EXTRACTION_PRESETS;

0 commit comments

Comments
 (0)