@veloxts/auth 0.6.83 → 0.6.85

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,31 @@
1
+ /**
2
+ * Shared utilities for auth adapters
3
+ *
4
+ * @module auth/adapters/utils
5
+ * @internal
6
+ */
7
+ /**
8
+ * Extract Bearer token from Authorization header
9
+ *
10
+ * @param headerValue - Authorization header value
11
+ * @returns Token string or null if not a Bearer token
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const token = extractBearerToken('Bearer eyJhbGci...');
16
+ * // Returns: 'eyJhbGci...'
17
+ *
18
+ * const invalid = extractBearerToken('Basic abc123');
19
+ * // Returns: null
20
+ * ```
21
+ */
22
+ export declare function extractBearerToken(headerValue: string): string | null;
23
+ /**
24
+ * Validates that a string value is non-empty
25
+ *
26
+ * @param value - Value to check
27
+ * @param fieldName - Field name for error message
28
+ * @returns The trimmed value if valid
29
+ * @throws Error if value is empty or whitespace-only
30
+ */
31
+ export declare function validateNonEmptyString(value: string | undefined, fieldName: string): string;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Shared utilities for auth adapters
3
+ *
4
+ * @module auth/adapters/utils
5
+ * @internal
6
+ */
7
+ // ============================================================================
8
+ // Token Extraction
9
+ // ============================================================================
10
+ /**
11
+ * Extract Bearer token from Authorization header
12
+ *
13
+ * @param headerValue - Authorization header value
14
+ * @returns Token string or null if not a Bearer token
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const token = extractBearerToken('Bearer eyJhbGci...');
19
+ * // Returns: 'eyJhbGci...'
20
+ *
21
+ * const invalid = extractBearerToken('Basic abc123');
22
+ * // Returns: null
23
+ * ```
24
+ */
25
+ export function extractBearerToken(headerValue) {
26
+ const parts = headerValue.split(' ');
27
+ if (parts.length !== 2 || parts[0].toLowerCase() !== 'bearer') {
28
+ return null;
29
+ }
30
+ // Trim whitespace from token to handle malformed headers
31
+ return parts[1].trim();
32
+ }
33
+ // ============================================================================
34
+ // Validation Helpers
35
+ // ============================================================================
36
+ /**
37
+ * Validates that a string value is non-empty
38
+ *
39
+ * @param value - Value to check
40
+ * @param fieldName - Field name for error message
41
+ * @returns The trimmed value if valid
42
+ * @throws Error if value is empty or whitespace-only
43
+ */
44
+ export function validateNonEmptyString(value, fieldName) {
45
+ if (!value || value.trim() === '') {
46
+ throw new Error(`${fieldName} is required and cannot be empty`);
47
+ }
48
+ return value.trim();
49
+ }
package/dist/guards.d.ts CHANGED
@@ -18,13 +18,83 @@ import type { AuthContext, GuardDefinition, GuardFunction, User } from './types.
18
18
  */
19
19
  export declare function defineGuard<TContext = unknown>(definition: GuardDefinition<TContext>): GuardDefinition<TContext>;
20
20
  /**
21
- * Creates a simple guard from a check function
21
+ * Fluent guard builder for progressive configuration
22
22
  *
23
+ * Allows building guards step-by-step with method chaining.
24
+ * The builder is compatible with GuardLike, so it can be used
25
+ * directly with `.guard()` on procedures.
26
+ *
27
+ * **Note**: This builder uses mutable internal state. Each method
28
+ * modifies the builder and returns the same instance. See
29
+ * `createGuardBuilder` for usage patterns and caveats.
30
+ */
31
+ export interface GuardBuilder<TContext> {
32
+ /** Guard name for error messages (read-only, set via named()) */
33
+ readonly name: string;
34
+ /** Guard check function (read-only) */
35
+ readonly check: GuardFunction<TContext>;
36
+ /** Custom error message (read-only, set via msg()) */
37
+ readonly message: string | undefined;
38
+ /** HTTP status code on failure (read-only, set via status()) */
39
+ readonly statusCode: number;
40
+ /** Set a descriptive name (used in error messages and debugging) */
41
+ named(name: string): GuardBuilder<TContext>;
42
+ /** Set custom error message shown when guard fails */
43
+ msg(message: string): GuardBuilder<TContext>;
44
+ /** Set HTTP status code returned when guard fails (default: 403) */
45
+ status(code: number): GuardBuilder<TContext>;
46
+ }
47
+ /**
48
+ * Resets the guard counter to zero.
49
+ *
50
+ * This is intended for testing purposes only to ensure deterministic
51
+ * guard naming across test runs. Should not be used in production code.
52
+ *
53
+ * @internal
23
54
  * @example
24
55
  * ```typescript
56
+ * import { _resetGuardCounter } from '@veloxts/auth';
57
+ *
58
+ * beforeEach(() => {
59
+ * _resetGuardCounter();
60
+ * });
61
+ * ```
62
+ */
63
+ export declare function _resetGuardCounter(): void;
64
+ /**
65
+ * Creates a guard with simplified syntax
66
+ *
67
+ * Supports three usage patterns with progressive disclosure:
68
+ *
69
+ * @example Simple check function (returns builder for configuration)
70
+ * ```typescript
71
+ * const isVerified = guard((ctx) => ctx.user?.emailVerified === true)
72
+ * .msg('Email verification required');
73
+ * ```
74
+ *
75
+ * @example Check with message (most common - auto-generates name)
76
+ * ```typescript
77
+ * const isVerified = guard(
78
+ * (ctx) => ctx.user?.emailVerified === true,
79
+ * 'Email verification required'
80
+ * );
81
+ * ```
82
+ *
83
+ * @example Named guard (explicit name for debugging)
84
+ * ```typescript
25
85
  * const isActive = guard('isActive', (ctx) => ctx.user?.status === 'active');
26
86
  * ```
87
+ *
88
+ * @example Full fluent configuration
89
+ * ```typescript
90
+ * const isPremium = guard((ctx) => ctx.user?.subscription === 'premium')
91
+ * .named('isPremium')
92
+ * .msg('Premium subscription required')
93
+ * .status(402);
94
+ * ```
27
95
  */
96
+ export declare function guard<TContext = unknown>(check: GuardFunction<TContext>): GuardBuilder<TContext>;
97
+ export declare function guard<TContext = unknown>(check: GuardFunction<TContext>, message: string): GuardDefinition<TContext>;
28
98
  export declare function guard<TContext = unknown>(name: string, check: GuardFunction<TContext>): GuardDefinition<TContext>;
29
99
  /**
30
100
  * Guard that requires authentication
package/dist/guards.js CHANGED
@@ -24,15 +24,131 @@ export function defineGuard(definition) {
24
24
  };
25
25
  }
26
26
  /**
27
- * Creates a simple guard from a check function
27
+ * Counter for generating unique guard names when not provided
28
+ * @internal
29
+ */
30
+ let guardCounter = 0;
31
+ /**
32
+ * Resets the guard counter to zero.
33
+ *
34
+ * This is intended for testing purposes only to ensure deterministic
35
+ * guard naming across test runs. Should not be used in production code.
28
36
  *
37
+ * @internal
29
38
  * @example
30
39
  * ```typescript
31
- * const isActive = guard('isActive', (ctx) => ctx.user?.status === 'active');
40
+ * import { _resetGuardCounter } from '@veloxts/auth';
41
+ *
42
+ * beforeEach(() => {
43
+ * _resetGuardCounter();
44
+ * });
45
+ * ```
46
+ */
47
+ export function _resetGuardCounter() {
48
+ guardCounter = 0;
49
+ }
50
+ /**
51
+ * Attempts to infer a meaningful name from a guard check function
52
+ * @internal
53
+ */
54
+ function inferGuardName(check) {
55
+ // Try to use function name if it exists and isn't generic
56
+ if (check.name && check.name !== 'check' && check.name !== 'anonymous') {
57
+ return check.name;
58
+ }
59
+ // Fall back to generated name
60
+ return `guard_${++guardCounter}`;
61
+ }
62
+ /**
63
+ * Creates a guard builder instance.
64
+ *
65
+ * The builder uses **mutable internal state** with method chaining that returns
66
+ * the same instance. This is intentional for performance and API simplicity:
67
+ *
68
+ * ```typescript
69
+ * // Each method mutates the builder and returns `this`
70
+ * const myGuard = guard((ctx) => ctx.user?.active)
71
+ * .named('isActive') // Mutates name, returns same builder
72
+ * .msg('User inactive') // Mutates message, returns same builder
73
+ * .status(403); // Mutates statusCode, returns same builder
74
+ *
75
+ * // The builder IS the guard definition (implements GuardLike)
76
+ * // No need to call .build() - use directly with .guard()
77
+ * ```
78
+ *
79
+ * **Important**: Because the builder mutates, avoid patterns like:
80
+ * ```typescript
81
+ * // DON'T do this - both variables reference the same mutable builder
82
+ * const base = guard((ctx) => ctx.user != null);
83
+ * const withMsg = base.msg('Auth required');
84
+ * const withOtherMsg = base.msg('Login needed'); // Overwrites previous msg!
85
+ * ```
86
+ *
87
+ * If you need variations, create separate guards:
88
+ * ```typescript
89
+ * const authRequired = guard((ctx) => ctx.user != null, 'Auth required');
90
+ * const loginNeeded = guard((ctx) => ctx.user != null, 'Login needed');
32
91
  * ```
92
+ *
93
+ * @internal
33
94
  */
34
- export function guard(name, check) {
35
- return defineGuard({ name, check });
95
+ function createGuardBuilder(check, initialName) {
96
+ // Mutable internal state - intentionally not exposed directly
97
+ let guardName = initialName;
98
+ let guardMessage;
99
+ let guardStatusCode = 403;
100
+ const builder = {
101
+ // GuardLike compatible properties (getters for current values)
102
+ get name() {
103
+ return guardName;
104
+ },
105
+ get check() {
106
+ return check;
107
+ },
108
+ get message() {
109
+ return guardMessage;
110
+ },
111
+ get statusCode() {
112
+ return guardStatusCode;
113
+ },
114
+ // Builder methods (return self for chaining)
115
+ named(name) {
116
+ guardName = name;
117
+ return builder;
118
+ },
119
+ msg(message) {
120
+ guardMessage = message;
121
+ return builder;
122
+ },
123
+ status(code) {
124
+ guardStatusCode = code;
125
+ return builder;
126
+ },
127
+ };
128
+ return builder;
129
+ }
130
+ // Implementation
131
+ export function guard(nameOrCheck, checkOrMessage) {
132
+ // Overload 3: Legacy (name, check)
133
+ if (typeof nameOrCheck === 'string' && typeof checkOrMessage === 'function') {
134
+ return defineGuard({ name: nameOrCheck, check: checkOrMessage });
135
+ }
136
+ // Overloads 1 & 2: (check) or (check, message)
137
+ if (typeof nameOrCheck === 'function') {
138
+ const check = nameOrCheck;
139
+ const message = typeof checkOrMessage === 'string' ? checkOrMessage : undefined;
140
+ if (message !== undefined) {
141
+ // Overload 2: Simple form with message - return completed guard
142
+ return defineGuard({
143
+ name: inferGuardName(check),
144
+ check,
145
+ message,
146
+ });
147
+ }
148
+ // Overload 1: Return builder for fluent configuration
149
+ return createGuardBuilder(check, inferGuardName(check));
150
+ }
151
+ throw new Error('Invalid guard arguments: expected (check), (check, message), or (name, check)');
36
152
  }
37
153
  // ============================================================================
38
154
  // Built-in Guards
package/dist/index.d.ts CHANGED
@@ -18,6 +18,7 @@ export { createEnhancedTokenStore, DEFAULT_ALLOWED_ROLES, parseUserRoles, } from
18
18
  export type { AuthenticatedContext, InferNarrowedContext, NarrowingGuard, RoleNarrowedContext, } from './guards-narrowing.js';
19
19
  export { authenticatedNarrow, hasRoleNarrow } from './guards-narrowing.js';
20
20
  export { hashPassword, PasswordHasher, passwordHasher, verifyPassword, } from './hash.js';
21
+ export type { GuardBuilder } from './guards.js';
21
22
  export { allOf, anyOf, authenticated, defineGuard, emailVerified, executeGuard, executeGuards, guard, hasAnyPermission, hasPermission, hasRole, not, userCan, } from './guards.js';
22
23
  export { authorize, can, cannot, clearPolicies, createAdminOnlyPolicy, createOwnerOrAdminPolicy, createPolicyBuilder, createReadOnlyPolicy, definePolicy, getPolicy, registerPolicy, } from './policies.js';
23
24
  export { authMiddleware, clearRateLimitStore, rateLimitMiddleware, } from './middleware.js';
package/dist/index.js CHANGED
@@ -23,9 +23,6 @@ export { authenticatedNarrow, hasRoleNarrow } from './guards-narrowing.js';
23
23
  // Password Hashing
24
24
  // ============================================================================
25
25
  export { hashPassword, PasswordHasher, passwordHasher, verifyPassword, } from './hash.js';
26
- // ============================================================================
27
- // Guards
28
- // ============================================================================
29
26
  export {
30
27
  // Combinators
31
28
  allOf, anyOf,
@@ -35,6 +32,7 @@ authenticated,
35
32
  defineGuard, emailVerified,
36
33
  // Execution
37
34
  executeGuard, executeGuards, guard, hasAnyPermission, hasPermission, hasRole, not, userCan, } from './guards.js';
35
+ // NOTE: _resetGuardCounter is available via '@veloxts/auth/testing' for test isolation
38
36
  // ============================================================================
39
37
  // Policies
40
38
  // ============================================================================
@@ -70,6 +70,70 @@ export function clearAuthRateLimitStore() {
70
70
  // Start cleanup on module load
71
71
  startCleanup();
72
72
  // ============================================================================
73
+ // Configuration Helpers
74
+ // ============================================================================
75
+ /** Time constants for readability */
76
+ const FIFTEEN_MINUTES_MS = 15 * 60 * 1000;
77
+ const ONE_HOUR_MS = 60 * 60 * 1000;
78
+ const ONE_MINUTE_MS = 60 * 1000;
79
+ /** IP-only key generator */
80
+ const ipOnlyKeyGenerator = (ctx) => ctx.request.ip ?? 'unknown';
81
+ /**
82
+ * Default key generator combining IP and identifier
83
+ */
84
+ function defaultKeyGenerator(ctx, identifier) {
85
+ const ip = ctx.request.ip ?? 'unknown';
86
+ return identifier ? `${ip}:${identifier.toLowerCase()}` : ip;
87
+ }
88
+ const OPERATION_DEFAULTS = {
89
+ login: {
90
+ maxAttempts: 5,
91
+ windowMs: FIFTEEN_MINUTES_MS,
92
+ lockoutDurationMs: FIFTEEN_MINUTES_MS,
93
+ keyGenerator: defaultKeyGenerator,
94
+ message: 'Too many login attempts. Please try again later.',
95
+ progressiveBackoff: true,
96
+ },
97
+ register: {
98
+ maxAttempts: 3,
99
+ windowMs: ONE_HOUR_MS,
100
+ lockoutDurationMs: ONE_HOUR_MS,
101
+ keyGenerator: ipOnlyKeyGenerator,
102
+ message: 'Too many registration attempts. Please try again later.',
103
+ progressiveBackoff: false,
104
+ },
105
+ passwordReset: {
106
+ maxAttempts: 3,
107
+ windowMs: ONE_HOUR_MS,
108
+ lockoutDurationMs: ONE_HOUR_MS,
109
+ keyGenerator: ipOnlyKeyGenerator,
110
+ message: 'Too many password reset attempts. Please try again later.',
111
+ progressiveBackoff: false,
112
+ },
113
+ refresh: {
114
+ maxAttempts: 10,
115
+ windowMs: ONE_MINUTE_MS,
116
+ lockoutDurationMs: ONE_MINUTE_MS,
117
+ keyGenerator: ipOnlyKeyGenerator,
118
+ message: 'Too many token refresh attempts. Please try again later.',
119
+ progressiveBackoff: false,
120
+ },
121
+ };
122
+ /**
123
+ * Build a complete rate limit config by merging user config with defaults
124
+ */
125
+ function buildRateLimitConfig(userConfig, operation) {
126
+ const defaults = OPERATION_DEFAULTS[operation];
127
+ return {
128
+ maxAttempts: userConfig?.maxAttempts ?? defaults.maxAttempts,
129
+ windowMs: userConfig?.windowMs ?? defaults.windowMs,
130
+ lockoutDurationMs: userConfig?.lockoutDurationMs ?? defaults.lockoutDurationMs,
131
+ keyGenerator: userConfig?.keyGenerator ?? defaults.keyGenerator,
132
+ message: userConfig?.message ?? defaults.message,
133
+ progressiveBackoff: userConfig?.progressiveBackoff ?? defaults.progressiveBackoff,
134
+ };
135
+ }
136
+ // ============================================================================
73
137
  // Auth Rate Limiter
74
138
  // ============================================================================
75
139
  /**
@@ -96,38 +160,17 @@ startCleanup();
96
160
  * ```
97
161
  */
98
162
  export function createAuthRateLimiter(config = {}) {
99
- // Default configurations
100
- const loginConfig = {
101
- maxAttempts: config.login?.maxAttempts ?? 5,
102
- windowMs: config.login?.windowMs ?? 15 * 60 * 1000, // 15 minutes
103
- lockoutDurationMs: config.login?.lockoutDurationMs ?? 15 * 60 * 1000,
104
- keyGenerator: config.login?.keyGenerator ?? defaultKeyGenerator,
105
- message: config.login?.message ?? 'Too many login attempts. Please try again later.',
106
- progressiveBackoff: config.login?.progressiveBackoff ?? true,
107
- };
108
- const registerConfig = {
109
- maxAttempts: config.register?.maxAttempts ?? 3,
110
- windowMs: config.register?.windowMs ?? 60 * 60 * 1000, // 1 hour
111
- lockoutDurationMs: config.register?.lockoutDurationMs ?? 60 * 60 * 1000,
112
- keyGenerator: config.register?.keyGenerator ?? ((ctx) => ctx.request.ip ?? 'unknown'),
113
- message: config.register?.message ?? 'Too many registration attempts. Please try again later.',
114
- progressiveBackoff: config.register?.progressiveBackoff ?? false,
115
- };
116
- const passwordResetConfig = {
117
- maxAttempts: config.passwordReset?.maxAttempts ?? 3,
118
- windowMs: config.passwordReset?.windowMs ?? 60 * 60 * 1000, // 1 hour
119
- lockoutDurationMs: config.passwordReset?.lockoutDurationMs ?? 60 * 60 * 1000,
120
- keyGenerator: config.passwordReset?.keyGenerator ?? ((ctx) => ctx.request.ip ?? 'unknown'),
121
- message: config.passwordReset?.message ?? 'Too many password reset attempts. Please try again later.',
122
- progressiveBackoff: config.passwordReset?.progressiveBackoff ?? false,
123
- };
124
- const refreshConfig = {
125
- maxAttempts: config.refresh?.maxAttempts ?? 10,
126
- windowMs: config.refresh?.windowMs ?? 60 * 1000, // 1 minute
127
- lockoutDurationMs: config.refresh?.lockoutDurationMs ?? 60 * 1000,
128
- keyGenerator: config.refresh?.keyGenerator ?? ((ctx) => ctx.request.ip ?? 'unknown'),
129
- message: config.refresh?.message ?? 'Too many token refresh attempts. Please try again later.',
130
- progressiveBackoff: config.refresh?.progressiveBackoff ?? false,
163
+ // Build configurations using helper
164
+ const loginConfig = buildRateLimitConfig(config.login, 'login');
165
+ const registerConfig = buildRateLimitConfig(config.register, 'register');
166
+ const passwordResetConfig = buildRateLimitConfig(config.passwordReset, 'passwordReset');
167
+ const refreshConfig = buildRateLimitConfig(config.refresh, 'refresh');
168
+ // Config lookup for operations
169
+ const configs = {
170
+ login: loginConfig,
171
+ register: registerConfig,
172
+ 'password-reset': passwordResetConfig,
173
+ refresh: refreshConfig,
131
174
  };
132
175
  return {
133
176
  /**
@@ -178,18 +221,14 @@ export function createAuthRateLimiter(config = {}) {
178
221
  */
179
222
  recordFailure: (key, operation) => {
180
223
  const fullKey = `auth:${operation}:${key}`;
181
- const config = operation === 'login'
182
- ? loginConfig
183
- : operation === 'register'
184
- ? registerConfig
185
- : passwordResetConfig;
224
+ const operationConfig = configs[operation];
186
225
  const now = Date.now();
187
226
  const entry = authRateLimitStore.get(fullKey);
188
227
  if (!entry || entry.windowResetAt <= now) {
189
228
  // Start new window
190
229
  authRateLimitStore.set(fullKey, {
191
230
  attempts: 1,
192
- windowResetAt: now + config.windowMs,
231
+ windowResetAt: now + operationConfig.windowMs,
193
232
  lockoutUntil: null,
194
233
  lockoutCount: entry?.lockoutCount ?? 0,
195
234
  });
@@ -198,9 +237,11 @@ export function createAuthRateLimiter(config = {}) {
198
237
  // Increment in current window
199
238
  entry.attempts++;
200
239
  // Check if lockout should trigger
201
- if (entry.attempts >= config.maxAttempts) {
202
- const lockoutMultiplier = config.progressiveBackoff ? 2 ** entry.lockoutCount : 1;
203
- entry.lockoutUntil = now + config.lockoutDurationMs * lockoutMultiplier;
240
+ if (entry.attempts >= operationConfig.maxAttempts) {
241
+ const lockoutMultiplier = operationConfig.progressiveBackoff
242
+ ? 2 ** entry.lockoutCount
243
+ : 1;
244
+ entry.lockoutUntil = now + operationConfig.lockoutDurationMs * lockoutMultiplier;
204
245
  entry.lockoutCount++;
205
246
  }
206
247
  }
@@ -231,31 +272,18 @@ export function createAuthRateLimiter(config = {}) {
231
272
  */
232
273
  getRemainingAttempts: (key, operation) => {
233
274
  const fullKey = `auth:${operation}:${key}`;
234
- const config = operation === 'login'
235
- ? loginConfig
236
- : operation === 'register'
237
- ? registerConfig
238
- : operation === 'refresh'
239
- ? refreshConfig
240
- : passwordResetConfig;
275
+ const operationConfig = configs[operation];
241
276
  const entry = authRateLimitStore.get(fullKey);
242
277
  if (!entry || entry.windowResetAt <= Date.now()) {
243
- return config.maxAttempts;
278
+ return operationConfig.maxAttempts;
244
279
  }
245
- return Math.max(0, config.maxAttempts - entry.attempts);
280
+ return Math.max(0, operationConfig.maxAttempts - entry.attempts);
246
281
  },
247
282
  };
248
283
  }
249
284
  // ============================================================================
250
285
  // Internal Helpers
251
286
  // ============================================================================
252
- /**
253
- * Default key generator combining IP and identifier
254
- */
255
- function defaultKeyGenerator(ctx, identifier) {
256
- const ip = ctx.request.ip ?? 'unknown';
257
- return identifier ? `${ip}:${identifier.toLowerCase()}` : ip;
258
- }
259
287
  /**
260
288
  * Creates the actual rate limit middleware
261
289
  */
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @veloxts/auth/testing - Internal testing utilities
3
+ *
4
+ * This module exports utilities intended for testing purposes only.
5
+ * These are NOT part of the public API and may change without notice.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { _resetGuardCounter } from '@veloxts/auth/testing';
10
+ *
11
+ * beforeEach(() => {
12
+ * _resetGuardCounter();
13
+ * });
14
+ * ```
15
+ *
16
+ * @packageDocumentation
17
+ * @module @veloxts/auth/testing
18
+ */
19
+ export { _resetGuardCounter } from './guards.js';
20
+ export { clearRateLimitStore } from './middleware.js';
21
+ export { clearPolicies } from './policies.js';
22
+ export { clearAuthRateLimitStore, stopAuthRateLimitCleanup } from './rate-limit.js';
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @veloxts/auth/testing - Internal testing utilities
3
+ *
4
+ * This module exports utilities intended for testing purposes only.
5
+ * These are NOT part of the public API and may change without notice.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { _resetGuardCounter } from '@veloxts/auth/testing';
10
+ *
11
+ * beforeEach(() => {
12
+ * _resetGuardCounter();
13
+ * });
14
+ * ```
15
+ *
16
+ * @packageDocumentation
17
+ * @module @veloxts/auth/testing
18
+ */
19
+ // Guard testing utilities
20
+ export { _resetGuardCounter } from './guards.js';
21
+ // Rate limit store clearing (for test isolation)
22
+ export { clearRateLimitStore } from './middleware.js';
23
+ // Policy registry clearing (for test isolation)
24
+ export { clearPolicies } from './policies.js';
25
+ export { clearAuthRateLimitStore, stopAuthRateLimitCleanup } from './rate-limit.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veloxts/auth",
3
- "version": "0.6.83",
3
+ "version": "0.6.85",
4
4
  "description": "Authentication and authorization system for VeloxTS framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -10,6 +10,10 @@
10
10
  "types": "./dist/index.d.ts",
11
11
  "import": "./dist/index.js"
12
12
  },
13
+ "./testing": {
14
+ "types": "./dist/testing.d.ts",
15
+ "import": "./dist/testing.js"
16
+ },
13
17
  "./adapters": {
14
18
  "types": "./dist/adapters/index.d.ts",
15
19
  "import": "./dist/adapters/index.js"
@@ -57,8 +61,8 @@
57
61
  "dependencies": {
58
62
  "@fastify/cookie": "11.0.2",
59
63
  "fastify": "5.6.2",
60
- "@veloxts/core": "0.6.83",
61
- "@veloxts/router": "0.6.83"
64
+ "@veloxts/core": "0.6.85",
65
+ "@veloxts/router": "0.6.85"
62
66
  },
63
67
  "peerDependencies": {
64
68
  "argon2": ">=0.30.0",
@@ -82,8 +86,8 @@
82
86
  "fastify-plugin": "5.1.0",
83
87
  "typescript": "5.9.3",
84
88
  "vitest": "4.0.16",
85
- "@veloxts/validation": "0.6.83",
86
- "@veloxts/testing": "0.6.83"
89
+ "@veloxts/validation": "0.6.85",
90
+ "@veloxts/testing": "0.6.85"
87
91
  },
88
92
  "keywords": [
89
93
  "velox",