@veloxts/auth 0.6.83 → 0.6.84
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/CHANGELOG.md +9 -0
- package/dist/guards.d.ts +71 -1
- package/dist/guards.js +120 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -3
- package/dist/testing.d.ts +22 -0
- package/dist/testing.js +25 -0
- package/package.json +9 -5
package/CHANGELOG.md
CHANGED
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
35
|
-
|
|
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
|
// ============================================================================
|
|
@@ -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';
|
package/dist/testing.js
ADDED
|
@@ -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.
|
|
3
|
+
"version": "0.6.84",
|
|
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/
|
|
61
|
-
"@veloxts/
|
|
64
|
+
"@veloxts/router": "0.6.84",
|
|
65
|
+
"@veloxts/core": "0.6.84"
|
|
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/
|
|
86
|
-
"@veloxts/
|
|
89
|
+
"@veloxts/testing": "0.6.84",
|
|
90
|
+
"@veloxts/validation": "0.6.84"
|
|
87
91
|
},
|
|
88
92
|
"keywords": [
|
|
89
93
|
"velox",
|