z-shield 0.1.0 → 0.1.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.
Files changed (52) hide show
  1. package/README.md +123 -6
  2. package/dist/block-manager.d.ts +134 -0
  3. package/dist/block-manager.d.ts.map +1 -0
  4. package/dist/block-manager.js +425 -0
  5. package/dist/block-manager.js.map +1 -0
  6. package/dist/cli.d.ts +141 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +784 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/config.d.ts +15 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +148 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/detector.d.ts +56 -0
  15. package/dist/detector.d.ts.map +1 -0
  16. package/dist/detector.js +81 -0
  17. package/dist/detector.js.map +1 -0
  18. package/dist/index.d.ts +29 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +120 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/logger.d.ts +141 -0
  23. package/dist/logger.d.ts.map +1 -0
  24. package/dist/logger.js +347 -0
  25. package/dist/logger.js.map +1 -0
  26. package/dist/middleware.d.ts +68 -0
  27. package/dist/middleware.d.ts.map +1 -0
  28. package/dist/middleware.js +272 -0
  29. package/dist/middleware.js.map +1 -0
  30. package/dist/next.d.ts +73 -0
  31. package/dist/next.d.ts.map +1 -0
  32. package/dist/next.js +486 -0
  33. package/dist/next.js.map +1 -0
  34. package/dist/rate-limiter.d.ts +75 -0
  35. package/dist/rate-limiter.d.ts.map +1 -0
  36. package/dist/rate-limiter.js +153 -0
  37. package/dist/rate-limiter.js.map +1 -0
  38. package/dist/types.d.ts +199 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +6 -0
  41. package/dist/types.js.map +1 -0
  42. package/next.d.ts +1 -0
  43. package/next.js +2 -0
  44. package/package.json +34 -5
  45. package/src/block-manager.js +0 -326
  46. package/src/cli.js +0 -680
  47. package/src/config.js +0 -165
  48. package/src/detector.js +0 -88
  49. package/src/index.js +0 -97
  50. package/src/logger.js +0 -279
  51. package/src/middleware.js +0 -288
  52. package/src/rate-limiter.js +0 -186
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RateLimiter = void 0;
4
+ const block_manager_1 = require("./block-manager");
5
+ /**
6
+ * RateLimiter - Bounded In-Memory Request Counter
7
+ *
8
+ * Algorithm: Fixed Window with Per-IP Dynamic Reset Time
9
+ * - On an IP's first request, we establish a time window (now -> now + windowMs).
10
+ * - Every subsequent request within the window increments the counter.
11
+ * - When the window expires, the counter resets to 1 with a new window.
12
+ *
13
+ * Memory Protection (DoS defense):
14
+ * - JavaScript's Map preserves insertion order.
15
+ * - By deleting and re-setting accessed keys, the Map functions as an LRU (Least Recently Used) cache.
16
+ * - If the number of tracked IPs reaches `maxTrackedIps`, we evict the oldest entry.
17
+ * This ensures memory consumption is strictly bounded and cannot crash the Node.js process.
18
+ */
19
+ class RateLimiter {
20
+ windowMs;
21
+ maxRequests;
22
+ maxTrackedIps;
23
+ records;
24
+ /**
25
+ * @param options - Limiter options or resolved configuration
26
+ */
27
+ constructor(options = {}) {
28
+ this.windowMs = options.windowMs || 60000;
29
+ this.maxRequests = options.maxRequests || 100;
30
+ this.maxTrackedIps = options.maxTrackedIps || 10000;
31
+ this.records = new Map();
32
+ }
33
+ /**
34
+ * Records an incoming request from an IP address.
35
+ *
36
+ * @param ip - Client IP address
37
+ * @param now - Current timestamp (injectable for testing)
38
+ * @returns Current rate limit status
39
+ */
40
+ record(ip, now = Date.now()) {
41
+ const cleanIp = (0, block_manager_1.normalizeIp)(ip);
42
+ let entry = this.records.get(cleanIp);
43
+ // If an existing entry has expired, treat it as a fresh window
44
+ if (entry && now >= entry.resetTime) {
45
+ entry = undefined;
46
+ }
47
+ if (entry) {
48
+ // Existing active window: increment count
49
+ entry.count += 1;
50
+ // Refresh LRU order: delete and re-set so this IP is marked as most recently used
51
+ this.records.delete(cleanIp);
52
+ this.records.set(cleanIp, entry);
53
+ }
54
+ else {
55
+ // New window or expired window: only evict if adding a NEW IP at capacity
56
+ if (!this.records.has(cleanIp) && this.records.size >= this.maxTrackedIps) {
57
+ this._evictOldest(now);
58
+ }
59
+ entry = {
60
+ count: 1,
61
+ resetTime: now + this.windowMs
62
+ };
63
+ // Delete first to ensure refreshed LRU order in Map
64
+ this.records.delete(cleanIp);
65
+ this.records.set(cleanIp, entry);
66
+ }
67
+ const remaining = Math.max(0, this.maxRequests - entry.count);
68
+ const isExceeded = entry.count > this.maxRequests;
69
+ return {
70
+ count: entry.count,
71
+ remaining,
72
+ resetTime: entry.resetTime,
73
+ isExceeded
74
+ };
75
+ }
76
+ /**
77
+ * Inspects current status for an IP without incrementing its counter.
78
+ *
79
+ * @param ip - Client IP address
80
+ * @param now - Current timestamp
81
+ * @returns Current rate limit status
82
+ */
83
+ get(ip, now = Date.now()) {
84
+ const cleanIp = (0, block_manager_1.normalizeIp)(ip);
85
+ const entry = this.records.get(cleanIp);
86
+ if (!entry || now >= entry.resetTime) {
87
+ return {
88
+ count: 0,
89
+ remaining: this.maxRequests,
90
+ resetTime: now + this.windowMs,
91
+ isExceeded: false
92
+ };
93
+ }
94
+ return {
95
+ count: entry.count,
96
+ remaining: Math.max(0, this.maxRequests - entry.count),
97
+ resetTime: entry.resetTime,
98
+ isExceeded: entry.count > this.maxRequests
99
+ };
100
+ }
101
+ /**
102
+ * Resets counter for a specific IP.
103
+ * @param ip - Client IP address
104
+ */
105
+ reset(ip) {
106
+ const cleanIp = (0, block_manager_1.normalizeIp)(ip);
107
+ this.records.delete(cleanIp);
108
+ }
109
+ /**
110
+ * Clears all tracked IP records from memory.
111
+ */
112
+ clear() {
113
+ this.records.clear();
114
+ }
115
+ /**
116
+ * Number of IPs currently stored in memory.
117
+ */
118
+ get size() {
119
+ return this.records.size;
120
+ }
121
+ /**
122
+ * Scans and removes any expired counters from memory.
123
+ * Because Map maintains insertion order, oldest entries are checked first.
124
+ *
125
+ * @param now - Current timestamp
126
+ * @returns Count of removed expired entries
127
+ */
128
+ pruneExpired(now = Date.now()) {
129
+ let pruned = 0;
130
+ for (const [ip, entry] of this.records.entries()) {
131
+ if (now >= entry.resetTime) {
132
+ this.records.delete(ip);
133
+ pruned++;
134
+ }
135
+ }
136
+ return pruned;
137
+ }
138
+ /**
139
+ * Evicts the oldest entry to maintain bounded memory.
140
+ * Evicts the least recently used entry to strictly uphold memory bound.
141
+ *
142
+ * @param _now - Current timestamp
143
+ */
144
+ _evictOldest(_now) {
145
+ const iterator = this.records.keys();
146
+ const firstKey = iterator.next().value;
147
+ if (firstKey !== undefined) {
148
+ this.records.delete(firstKey);
149
+ }
150
+ }
151
+ }
152
+ exports.RateLimiter = RateLimiter;
153
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"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","sourcesContent":["import { normalizeIp } from './block-manager';\nimport type { RateLimitStatus, ZShieldConfig, ZShieldOptions } from './types';\n\nexport interface RateLimiterOptions {\n windowMs?: number;\n maxRequests?: number;\n maxTrackedIps?: number;\n}\n\ninterface CounterEntry {\n count: number;\n resetTime: number;\n}\n\n/**\n * RateLimiter - Bounded In-Memory Request Counter\n *\n * Algorithm: Fixed Window with Per-IP Dynamic Reset Time\n * - On an IP's first request, we establish a time window (now -> now + windowMs).\n * - Every subsequent request within the window increments the counter.\n * - When the window expires, the counter resets to 1 with a new window.\n *\n * Memory Protection (DoS defense):\n * - JavaScript's Map preserves insertion order.\n * - By deleting and re-setting accessed keys, the Map functions as an LRU (Least Recently Used) cache.\n * - If the number of tracked IPs reaches `maxTrackedIps`, we evict the oldest entry.\n * This ensures memory consumption is strictly bounded and cannot crash the Node.js process.\n */\nexport class RateLimiter {\n readonly windowMs: number;\n readonly maxRequests: number;\n readonly maxTrackedIps: number;\n private readonly records: Map<string, CounterEntry>;\n\n /**\n * @param options - Limiter options or resolved configuration\n */\n constructor(options: RateLimiterOptions | ZShieldConfig | ZShieldOptions = {}) {\n this.windowMs = options.windowMs || 60000;\n this.maxRequests = options.maxRequests || 100;\n this.maxTrackedIps = options.maxTrackedIps || 10000;\n\n this.records = new Map<string, CounterEntry>();\n }\n\n /**\n * Records an incoming request from an IP address.\n *\n * @param ip - Client IP address\n * @param now - Current timestamp (injectable for testing)\n * @returns Current rate limit status\n */\n record(ip: string, now: number = Date.now()): RateLimitStatus {\n const cleanIp = normalizeIp(ip);\n\n let entry = this.records.get(cleanIp);\n\n // If an existing entry has expired, treat it as a fresh window\n if (entry && now >= entry.resetTime) {\n entry = undefined;\n }\n\n if (entry) {\n // Existing active window: increment count\n entry.count += 1;\n\n // Refresh LRU order: delete and re-set so this IP is marked as most recently used\n this.records.delete(cleanIp);\n this.records.set(cleanIp, entry);\n } else {\n // New window or expired window: only evict if adding a NEW IP at capacity\n if (!this.records.has(cleanIp) && this.records.size >= this.maxTrackedIps) {\n this._evictOldest(now);\n }\n\n entry = {\n count: 1,\n resetTime: now + this.windowMs\n };\n\n // Delete first to ensure refreshed LRU order in Map\n this.records.delete(cleanIp);\n this.records.set(cleanIp, entry);\n }\n\n const remaining = Math.max(0, this.maxRequests - entry.count);\n const isExceeded = entry.count > this.maxRequests;\n\n return {\n count: entry.count,\n remaining,\n resetTime: entry.resetTime,\n isExceeded\n };\n }\n\n /**\n * Inspects current status for an IP without incrementing its counter.\n *\n * @param ip - Client IP address\n * @param now - Current timestamp\n * @returns Current rate limit status\n */\n get(ip: string, now: number = Date.now()): RateLimitStatus {\n const cleanIp = normalizeIp(ip);\n const entry = this.records.get(cleanIp);\n\n if (!entry || now >= entry.resetTime) {\n return {\n count: 0,\n remaining: this.maxRequests,\n resetTime: now + this.windowMs,\n isExceeded: false\n };\n }\n\n return {\n count: entry.count,\n remaining: Math.max(0, this.maxRequests - entry.count),\n resetTime: entry.resetTime,\n isExceeded: entry.count > this.maxRequests\n };\n }\n\n /**\n * Resets counter for a specific IP.\n * @param ip - Client IP address\n */\n reset(ip: string): void {\n const cleanIp = normalizeIp(ip);\n this.records.delete(cleanIp);\n }\n\n /**\n * Clears all tracked IP records from memory.\n */\n clear(): void {\n this.records.clear();\n }\n\n /**\n * Number of IPs currently stored in memory.\n */\n get size(): number {\n return this.records.size;\n }\n\n /**\n * Scans and removes any expired counters from memory.\n * Because Map maintains insertion order, oldest entries are checked first.\n *\n * @param now - Current timestamp\n * @returns Count of removed expired entries\n */\n pruneExpired(now: number = Date.now()): number {\n let pruned = 0;\n for (const [ip, entry] of this.records.entries()) {\n if (now >= entry.resetTime) {\n this.records.delete(ip);\n pruned++;\n }\n }\n return pruned;\n }\n\n /**\n * Evicts the oldest entry to maintain bounded memory.\n * Evicts the least recently used entry to strictly uphold memory bound.\n *\n * @param _now - Current timestamp\n */\n private _evictOldest(_now: number): void {\n const iterator = this.records.keys();\n const firstKey = iterator.next().value;\n\n if (firstKey !== undefined) {\n this.records.delete(firstKey);\n }\n }\n}\n"]}
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Z-Shield Core Type Definitions
3
+ */
4
+ import type { RateLimiter } from './rate-limiter';
5
+ import type { Detector } from './detector';
6
+ import type { BlockManager, BlockInfo } from './block-manager';
7
+ import type { Logger } from './logger';
8
+ export type ZShieldMode = 'monitor' | 'protect';
9
+ export interface ZShieldOptions {
10
+ mode?: ZShieldMode;
11
+ windowMs?: number;
12
+ maxRequests?: number;
13
+ maxTrackedIps?: number;
14
+ autoBlock?: boolean;
15
+ blockDurationMs?: number;
16
+ blockDuration?: string | number;
17
+ maxBlockedIps?: number;
18
+ trustProxy?: boolean;
19
+ logDir?: string;
20
+ logFilename?: string;
21
+ appName?: string;
22
+ excludeRoutes?: string[] | readonly string[];
23
+ maxLogSizeBytes?: number;
24
+ maxLogFiles?: number;
25
+ maxLogQueueSize?: number;
26
+ onLogError?: ((err: Error, event: Record<string, unknown>) => void) | null;
27
+ }
28
+ export interface ZShieldConfig {
29
+ readonly mode: ZShieldMode;
30
+ readonly windowMs: number;
31
+ readonly maxRequests: number;
32
+ readonly maxTrackedIps: number;
33
+ readonly autoBlock: boolean;
34
+ readonly blockDurationMs: number;
35
+ readonly blockDuration?: string | number;
36
+ readonly maxBlockedIps: number;
37
+ readonly trustProxy: boolean;
38
+ readonly logDir: string;
39
+ readonly logFilename: string;
40
+ readonly appName: string;
41
+ readonly excludeRoutes: readonly string[];
42
+ readonly maxLogSizeBytes: number;
43
+ readonly maxLogFiles: number;
44
+ readonly maxLogQueueSize: number;
45
+ readonly onLogError: ((err: Error, event: Record<string, unknown>) => void) | null;
46
+ }
47
+ export interface RateLimitStatus {
48
+ count: number;
49
+ remaining: number;
50
+ resetTime: number;
51
+ isExceeded: boolean;
52
+ }
53
+ export interface BlockRecord {
54
+ ip: string;
55
+ blockedAt: number;
56
+ expiresAt: number;
57
+ reason: string;
58
+ durationMs: number;
59
+ }
60
+ export type { BlockInfo } from './block-manager';
61
+ export type SecurityEventType = 'rate_limit' | 'manual_block' | 'manual_unblock' | 'suspicious_activity' | string;
62
+ export type SecurityEventAction = 'blocked' | 'warning' | 'audit' | 'unblocked' | string;
63
+ export interface SecurityEvent {
64
+ timestamp: string;
65
+ appName: string;
66
+ ip: string;
67
+ type: SecurityEventType;
68
+ action: SecurityEventAction;
69
+ route?: string;
70
+ method?: string;
71
+ reason?: string;
72
+ details?: Record<string, unknown>;
73
+ [key: string]: unknown;
74
+ }
75
+ export interface DetectionResult {
76
+ status: 'normal' | 'suspicious' | 'blocked';
77
+ reason?: string;
78
+ rateLimitStatus: RateLimitStatus;
79
+ isBlocked: boolean;
80
+ }
81
+ /**
82
+ * Structural types for Express-compatible HTTP requests and responses.
83
+ * These types do not require installing @types/express in consumer projects.
84
+ */
85
+ export interface ExpressLikeRequest {
86
+ ip?: string;
87
+ url?: string;
88
+ path?: string;
89
+ method?: string;
90
+ socket?: {
91
+ remoteAddress?: string;
92
+ };
93
+ headers?: Record<string, string | string[] | undefined>;
94
+ zShield?: {
95
+ ip: string;
96
+ mode: string;
97
+ appName: string;
98
+ isExcluded: boolean;
99
+ startTime: number;
100
+ detection: unknown;
101
+ blocked: boolean;
102
+ blockReason?: string;
103
+ logPromise?: Promise<boolean> | null;
104
+ };
105
+ }
106
+ export interface ExpressLikeResponse {
107
+ headersSent?: boolean;
108
+ writableEnded?: boolean;
109
+ setHeader?(name: string, value: string): void;
110
+ status?(code: number): this | ExpressLikeResponse;
111
+ json?(body: unknown): void;
112
+ end?(): void;
113
+ }
114
+ export type ExpressLikeNextFunction = (err?: unknown) => void;
115
+ export type ExpressMiddleware<Req = ExpressLikeRequest, Res = ExpressLikeResponse, Next = ExpressLikeNextFunction> = (req: Req, res: Res, next: Next) => void;
116
+ export interface ZShieldInstance {
117
+ readonly middleware: ExpressMiddleware;
118
+ readonly config: ZShieldConfig;
119
+ readonly rateLimiter: RateLimiter;
120
+ readonly detector: Detector;
121
+ readonly blockManager: BlockManager;
122
+ readonly logger: Logger;
123
+ block: (ip: string, duration?: string | number, reason?: string) => BlockRecord;
124
+ unblock: (ip: string) => boolean;
125
+ isBlocked: (ip: string) => boolean;
126
+ getBlockInfo: (ip: string) => BlockInfo | null;
127
+ listBlocks: () => BlockInfo[];
128
+ flush: () => Promise<void>;
129
+ }
130
+ export type ZShieldFullInstance = ZShieldInstance;
131
+ /**
132
+ * Structural type for Web-standard or Next.js requests.
133
+ */
134
+ export interface NextLikeRequest {
135
+ url?: string;
136
+ method?: string;
137
+ headers?: Headers | Record<string, string | string[] | undefined> | {
138
+ get(name: string): string | null;
139
+ };
140
+ ip?: string;
141
+ socket?: {
142
+ remoteAddress?: string;
143
+ };
144
+ }
145
+ export type NextClientIpStrategy = 'x-forwarded-for' | 'x-real-ip' | 'nginx' | 'cf-connecting-ip' | 'cloudflare' | 'true-client-ip' | 'akamai';
146
+ export type NextClientIpExtractor = (request: Request | NextLikeRequest) => string | null | undefined;
147
+ export interface NextShieldOptions extends ZShieldOptions {
148
+ /**
149
+ * Explicit client IP extraction strategy or custom callback function.
150
+ *
151
+ * To safely extract client IPs behind a reverse proxy, explicitly specify your
152
+ * trusted deployment proxy strategy or provide a custom extraction callback:
153
+ * - 'cloudflare' | 'cf-connecting-ip': Trusts only the CF-Connecting-IP header.
154
+ * - 'nginx' | 'x-real-ip': Trusts only the X-Real-IP header.
155
+ * - 'akamai' | 'true-client-ip': Trusts only the True-Client-IP header.
156
+ * - 'x-forwarded-for': Trusts the first valid IP in X-Forwarded-For.
157
+ * - Custom function: `(req) => string | null | undefined`
158
+ */
159
+ getIp?: NextClientIpStrategy | NextClientIpExtractor;
160
+ /**
161
+ * Optional custom 429 response builder.
162
+ */
163
+ customBlockResponse?: (info: {
164
+ reason: string;
165
+ retryAfter: number;
166
+ clientIp: string;
167
+ }) => Response;
168
+ }
169
+ export interface NextShieldCheckResult {
170
+ blocked: boolean;
171
+ isExcluded: boolean;
172
+ clientIp: string;
173
+ headers: Record<string, string>;
174
+ response?: Response;
175
+ detection?: {
176
+ isSuspicious: boolean;
177
+ count: number;
178
+ remaining: number;
179
+ resetTime: number;
180
+ reason: string | null;
181
+ };
182
+ }
183
+ export type NextRouteHandler<TContext = unknown> = (request: Request, context?: TContext) => Promise<Response> | Response;
184
+ export interface NextShieldInstance {
185
+ readonly config: ZShieldConfig;
186
+ readonly rateLimiter: RateLimiter;
187
+ readonly detector: Detector;
188
+ readonly blockManager: BlockManager;
189
+ readonly logger: Logger;
190
+ check: (request: Request | NextLikeRequest) => Promise<NextShieldCheckResult>;
191
+ protect: <TContext = unknown>(handler: (request: Request, context?: TContext) => Promise<Response> | Response) => (request: Request, context?: TContext) => Promise<Response>;
192
+ block: (ip: string, duration?: string | number, reason?: string) => BlockRecord;
193
+ unblock: (ip: string) => boolean;
194
+ isBlocked: (ip: string) => boolean;
195
+ getBlockInfo: (ip: string) => BlockInfo | null;
196
+ listBlocks: () => BlockInfo[];
197
+ flush: () => Promise<void>;
198
+ }
199
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ /**
3
+ * Z-Shield Core Type Definitions
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;GAEG","sourcesContent":["/**\n * Z-Shield Core Type Definitions\n */\n\nimport type { RateLimiter } from './rate-limiter';\nimport type { Detector } from './detector';\nimport type { BlockManager, BlockInfo } from './block-manager';\nimport type { Logger } from './logger';\n\nexport type ZShieldMode = 'monitor' | 'protect';\n\nexport interface ZShieldOptions {\n mode?: ZShieldMode;\n windowMs?: number;\n maxRequests?: number;\n maxTrackedIps?: number;\n autoBlock?: boolean;\n blockDurationMs?: number;\n blockDuration?: string | number;\n maxBlockedIps?: number;\n trustProxy?: boolean;\n logDir?: string;\n logFilename?: string;\n appName?: string;\n excludeRoutes?: string[] | readonly string[];\n maxLogSizeBytes?: number;\n maxLogFiles?: number;\n maxLogQueueSize?: number;\n onLogError?: ((err: Error, event: Record<string, unknown>) => void) | null;\n}\n\nexport interface ZShieldConfig {\n readonly mode: ZShieldMode;\n readonly windowMs: number;\n readonly maxRequests: number;\n readonly maxTrackedIps: number;\n readonly autoBlock: boolean;\n readonly blockDurationMs: number;\n readonly blockDuration?: string | number;\n readonly maxBlockedIps: number;\n readonly trustProxy: boolean;\n readonly logDir: string;\n readonly logFilename: string;\n readonly appName: string;\n readonly excludeRoutes: readonly string[];\n readonly maxLogSizeBytes: number;\n readonly maxLogFiles: number;\n readonly maxLogQueueSize: number;\n readonly onLogError: ((err: Error, event: Record<string, unknown>) => void) | null;\n}\n\nexport interface RateLimitStatus {\n count: number;\n remaining: number;\n resetTime: number;\n isExceeded: boolean;\n}\n\nexport interface BlockRecord {\n ip: string;\n blockedAt: number;\n expiresAt: number;\n reason: string;\n durationMs: number;\n}\n\nexport type { BlockInfo } from './block-manager';\n\nexport type SecurityEventType =\n | 'rate_limit'\n | 'manual_block'\n | 'manual_unblock'\n | 'suspicious_activity'\n | string;\n\nexport type SecurityEventAction =\n | 'blocked'\n | 'warning'\n | 'audit'\n | 'unblocked'\n | string;\n\nexport interface SecurityEvent {\n timestamp: string;\n appName: string;\n ip: string;\n type: SecurityEventType;\n action: SecurityEventAction;\n route?: string;\n method?: string;\n reason?: string;\n details?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\nexport interface DetectionResult {\n status: 'normal' | 'suspicious' | 'blocked';\n reason?: string;\n rateLimitStatus: RateLimitStatus;\n isBlocked: boolean;\n}\n\n/**\n * Structural types for Express-compatible HTTP requests and responses.\n * These types do not require installing @types/express in consumer projects.\n */\nexport interface ExpressLikeRequest {\n ip?: string;\n url?: string;\n path?: string;\n method?: string;\n socket?: { remoteAddress?: string };\n headers?: Record<string, string | string[] | undefined>;\n zShield?: {\n ip: string;\n mode: string;\n appName: string;\n isExcluded: boolean;\n startTime: number;\n detection: unknown;\n blocked: boolean;\n blockReason?: string;\n logPromise?: Promise<boolean> | null;\n };\n}\n\nexport interface ExpressLikeResponse {\n headersSent?: boolean;\n writableEnded?: boolean;\n setHeader?(name: string, value: string): void;\n status?(code: number): this | ExpressLikeResponse;\n json?(body: unknown): void;\n end?(): void;\n}\n\nexport type ExpressLikeNextFunction = (err?: unknown) => void;\n\nexport type ExpressMiddleware<\n Req = ExpressLikeRequest,\n Res = ExpressLikeResponse,\n Next = ExpressLikeNextFunction\n> = (req: Req, res: Res, next: Next) => void;\n\nexport interface ZShieldInstance {\n readonly middleware: ExpressMiddleware;\n readonly config: ZShieldConfig;\n readonly rateLimiter: RateLimiter;\n readonly detector: Detector;\n readonly blockManager: BlockManager;\n readonly logger: Logger;\n block: (ip: string, duration?: string | number, reason?: string) => BlockRecord;\n unblock: (ip: string) => boolean;\n isBlocked: (ip: string) => boolean;\n getBlockInfo: (ip: string) => BlockInfo | null;\n listBlocks: () => BlockInfo[];\n flush: () => Promise<void>;\n}\n\nexport type ZShieldFullInstance = ZShieldInstance;\n\n/**\n * Structural type for Web-standard or Next.js requests.\n */\nexport interface NextLikeRequest {\n url?: string;\n method?: string;\n headers?: Headers | Record<string, string | string[] | undefined> | { get(name: string): string | null };\n ip?: string;\n socket?: { remoteAddress?: string };\n}\n\nexport type NextClientIpStrategy =\n | 'x-forwarded-for'\n | 'x-real-ip'\n | 'nginx'\n | 'cf-connecting-ip'\n | 'cloudflare'\n | 'true-client-ip'\n | 'akamai';\n\nexport type NextClientIpExtractor = (\n request: Request | NextLikeRequest\n) => string | null | undefined;\n\nexport interface NextShieldOptions extends ZShieldOptions {\n /**\n * Explicit client IP extraction strategy or custom callback function.\n *\n * To safely extract client IPs behind a reverse proxy, explicitly specify your\n * trusted deployment proxy strategy or provide a custom extraction callback:\n * - 'cloudflare' | 'cf-connecting-ip': Trusts only the CF-Connecting-IP header.\n * - 'nginx' | 'x-real-ip': Trusts only the X-Real-IP header.\n * - 'akamai' | 'true-client-ip': Trusts only the True-Client-IP header.\n * - 'x-forwarded-for': Trusts the first valid IP in X-Forwarded-For.\n * - Custom function: `(req) => string | null | undefined`\n */\n getIp?: NextClientIpStrategy | NextClientIpExtractor;\n\n /**\n * Optional custom 429 response builder.\n */\n customBlockResponse?: (info: { reason: string; retryAfter: number; clientIp: string }) => Response;\n}\n\nexport interface NextShieldCheckResult {\n blocked: boolean;\n isExcluded: boolean;\n clientIp: string;\n headers: Record<string, string>;\n response?: Response;\n detection?: {\n isSuspicious: boolean;\n count: number;\n remaining: number;\n resetTime: number;\n reason: string | null;\n };\n}\n\nexport type NextRouteHandler<TContext = unknown> = (\n request: Request,\n context?: TContext\n) => Promise<Response> | Response;\n\nexport interface NextShieldInstance {\n readonly config: ZShieldConfig;\n readonly rateLimiter: RateLimiter;\n readonly detector: Detector;\n readonly blockManager: BlockManager;\n readonly logger: Logger;\n check: (request: Request | NextLikeRequest) => Promise<NextShieldCheckResult>;\n protect: <TContext = unknown>(\n handler: (request: Request, context?: TContext) => Promise<Response> | Response\n ) => (request: Request, context?: TContext) => Promise<Response>;\n block: (ip: string, duration?: string | number, reason?: string) => BlockRecord;\n unblock: (ip: string) => boolean;\n isBlocked: (ip: string) => boolean;\n getBlockInfo: (ip: string) => BlockInfo | null;\n listBlocks: () => BlockInfo[];\n flush: () => Promise<void>;\n}\n"]}
package/next.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './dist/next';
package/next.js ADDED
@@ -0,0 +1,2 @@
1
+ 'use strict';
2
+ module.exports = require('./dist/next.js');
package/package.json CHANGED
@@ -1,13 +1,37 @@
1
1
  {
2
2
  "name": "z-shield",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "API abuse detection, rate limiting, and temporary IP blocking middleware for Node.js and Express",
5
- "main": "src/index.js",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "./next": {
13
+ "types": "./dist/next.d.ts",
14
+ "default": "./dist/next.js"
15
+ },
16
+ "./package.json": "./package.json"
17
+ },
18
+ "typesVersions": {
19
+ "*": {
20
+ "next": [
21
+ "dist/next.d.ts"
22
+ ]
23
+ }
24
+ },
6
25
  "bin": {
7
- "z-shield": "src/cli.js"
26
+ "z-shield": "dist/cli.js"
8
27
  },
9
28
  "scripts": {
29
+ "build": "tsc",
30
+ "typecheck": "tsc --noEmit",
31
+ "prepack": "npm run build",
32
+ "pretest": "npm run build",
10
33
  "test": "node --test test/*.test.js",
34
+ "test:coverage": "node --test --experimental-test-coverage test/*.test.js",
11
35
  "start": "node examples/express-app.js"
12
36
  },
13
37
  "repository": {
@@ -29,13 +53,18 @@
29
53
  "author": "zeesu-royalist (https://zeesu-royalist.vercel.app)",
30
54
  "license": "MIT",
31
55
  "files": [
32
- "src",
56
+ "dist",
57
+ "next.js",
58
+ "next.d.ts",
33
59
  "README.md",
34
60
  "LICENSE",
35
61
  "SECURITY.md"
36
62
  ],
37
63
  "devDependencies": {
38
- "express": "^4.21.2"
64
+ "@types/express": "^5.0.6",
65
+ "@types/node": "^26.6.5",
66
+ "express": "^4.21.2",
67
+ "typescript": "^7.0.2"
39
68
  },
40
69
  "engines": {
41
70
  "node": ">=18.0.0"