@veloxts/router 0.6.57 → 0.6.58

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,131 @@
1
+ /**
2
+ * Security Mapper
3
+ *
4
+ * Maps VeloxTS guards to OpenAPI security schemes.
5
+ *
6
+ * @module @veloxts/router/openapi/security-mapper
7
+ */
8
+ import type { GuardLike } from '../types.js';
9
+ import type { OpenAPISecurityRequirement, OpenAPISecurityScheme } from './types.js';
10
+ /**
11
+ * Default mapping from guard names to security scheme names
12
+ *
13
+ * Maps common @veloxts/auth guard names to their corresponding
14
+ * OpenAPI security scheme identifiers.
15
+ */
16
+ export declare const DEFAULT_GUARD_MAPPINGS: Record<string, string>;
17
+ /**
18
+ * Default security schemes for VeloxTS auth patterns
19
+ *
20
+ * These are commonly used security schemes that can be merged
21
+ * with user-provided schemes.
22
+ */
23
+ export declare const DEFAULT_SECURITY_SCHEMES: Record<string, OpenAPISecurityScheme>;
24
+ /**
25
+ * Options for mapping guards to security
26
+ */
27
+ export interface GuardMappingOptions {
28
+ /**
29
+ * Custom guard name to security scheme mapping
30
+ * Merged with default mappings (custom takes precedence)
31
+ */
32
+ customMappings?: Record<string, string>;
33
+ /**
34
+ * Extract scopes from guard names
35
+ * e.g., 'hasRole:admin' -> scopes: ['admin']
36
+ * @default true
37
+ */
38
+ extractScopes?: boolean;
39
+ /**
40
+ * Scope separator in guard names
41
+ * @default ':'
42
+ */
43
+ scopeSeparator?: string;
44
+ }
45
+ /**
46
+ * Maps a single guard to an OpenAPI security requirement
47
+ *
48
+ * @param guard - Guard to map
49
+ * @param options - Mapping options
50
+ * @returns Security requirement or undefined if no mapping
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * mapGuardToSecurity({ name: 'authenticated', check: () => true })
55
+ * // { bearerAuth: [] }
56
+ *
57
+ * mapGuardToSecurity({ name: 'hasRole:admin', check: () => true })
58
+ * // { bearerAuth: ['admin'] }
59
+ * ```
60
+ */
61
+ export declare function mapGuardToSecurity(guard: GuardLike<unknown>, options?: GuardMappingOptions): OpenAPISecurityRequirement | undefined;
62
+ /**
63
+ * Maps multiple guards to OpenAPI security requirements
64
+ *
65
+ * Multiple guards result in multiple security requirements,
66
+ * meaning ALL must be satisfied (AND logic in OpenAPI).
67
+ *
68
+ * @param guards - Guards to map
69
+ * @param options - Mapping options
70
+ * @returns Array of security requirements
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * guardsToSecurity([
75
+ * { name: 'authenticated', check: () => true },
76
+ * { name: 'hasRole:admin', check: () => true }
77
+ * ])
78
+ * // [{ bearerAuth: [] }, { bearerAuth: ['admin'] }]
79
+ * ```
80
+ */
81
+ export declare function guardsToSecurity(guards: ReadonlyArray<GuardLike<unknown>>, options?: GuardMappingOptions): OpenAPISecurityRequirement[];
82
+ /**
83
+ * Merges security schemes, with custom schemes taking precedence
84
+ *
85
+ * @param customSchemes - Custom security schemes
86
+ * @param includeDefaults - Whether to include default schemes
87
+ * @returns Merged security schemes
88
+ */
89
+ export declare function mergeSecuritySchemes(customSchemes?: Record<string, OpenAPISecurityScheme>, includeDefaults?: boolean): Record<string, OpenAPISecurityScheme>;
90
+ /**
91
+ * Extracts unique security scheme names from guards
92
+ *
93
+ * Useful for determining which schemes to include in the spec.
94
+ *
95
+ * @param guards - Guards to analyze
96
+ * @param options - Mapping options
97
+ * @returns Set of security scheme names used
98
+ */
99
+ export declare function extractUsedSecuritySchemes(guards: ReadonlyArray<GuardLike<unknown>>, options?: GuardMappingOptions): Set<string>;
100
+ /**
101
+ * Filters security schemes to only include those that are used
102
+ *
103
+ * @param allSchemes - All available security schemes
104
+ * @param usedNames - Names of schemes that are actually used
105
+ * @returns Filtered security schemes
106
+ */
107
+ export declare function filterUsedSecuritySchemes(allSchemes: Record<string, OpenAPISecurityScheme>, usedNames: Set<string>): Record<string, OpenAPISecurityScheme>;
108
+ /**
109
+ * Checks if guards require authentication
110
+ *
111
+ * @param guards - Guards to check
112
+ * @param options - Mapping options
113
+ * @returns True if any guard maps to a security scheme
114
+ */
115
+ export declare function guardsRequireAuth(guards: ReadonlyArray<GuardLike<unknown>>, options?: GuardMappingOptions): boolean;
116
+ /**
117
+ * Extracts all scopes from guards
118
+ *
119
+ * @param guards - Guards to analyze
120
+ * @param options - Mapping options
121
+ * @returns Array of unique scopes
122
+ */
123
+ export declare function extractGuardScopes(guards: ReadonlyArray<GuardLike<unknown>>, options?: GuardMappingOptions): string[];
124
+ /**
125
+ * Creates a security requirement from scheme name and optional scopes
126
+ *
127
+ * @param schemeName - Name of the security scheme
128
+ * @param scopes - Optional scopes
129
+ * @returns Security requirement object
130
+ */
131
+ export declare function createSecurityRequirement(schemeName: string, scopes?: string[]): OpenAPISecurityRequirement;
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Security Mapper
3
+ *
4
+ * Maps VeloxTS guards to OpenAPI security schemes.
5
+ *
6
+ * @module @veloxts/router/openapi/security-mapper
7
+ */
8
+ // ============================================================================
9
+ // Default Mappings
10
+ // ============================================================================
11
+ /**
12
+ * Default mapping from guard names to security scheme names
13
+ *
14
+ * Maps common @veloxts/auth guard names to their corresponding
15
+ * OpenAPI security scheme identifiers.
16
+ */
17
+ export const DEFAULT_GUARD_MAPPINGS = {
18
+ // Authentication guards
19
+ authenticated: 'bearerAuth',
20
+ requireAuth: 'bearerAuth',
21
+ // Role-based guards (all map to bearer since they require auth)
22
+ hasRole: 'bearerAuth',
23
+ hasAnyRole: 'bearerAuth',
24
+ hasAllRoles: 'bearerAuth',
25
+ // Permission-based guards
26
+ hasPermission: 'bearerAuth',
27
+ hasAnyPermission: 'bearerAuth',
28
+ hasAllPermissions: 'bearerAuth',
29
+ // API key auth
30
+ apiKey: 'apiKeyAuth',
31
+ apiKeyAuth: 'apiKeyAuth',
32
+ };
33
+ /**
34
+ * Default security schemes for VeloxTS auth patterns
35
+ *
36
+ * These are commonly used security schemes that can be merged
37
+ * with user-provided schemes.
38
+ */
39
+ export const DEFAULT_SECURITY_SCHEMES = {
40
+ bearerAuth: {
41
+ type: 'http',
42
+ scheme: 'bearer',
43
+ bearerFormat: 'JWT',
44
+ description: 'JWT authentication token. Obtain via /auth/login endpoint.',
45
+ },
46
+ apiKeyAuth: {
47
+ type: 'apiKey',
48
+ in: 'header',
49
+ name: 'X-API-Key',
50
+ description: 'API key for programmatic access.',
51
+ },
52
+ cookieAuth: {
53
+ type: 'apiKey',
54
+ in: 'cookie',
55
+ name: 'session',
56
+ description: 'Session cookie for browser-based authentication.',
57
+ },
58
+ };
59
+ /**
60
+ * Maps a single guard to an OpenAPI security requirement
61
+ *
62
+ * @param guard - Guard to map
63
+ * @param options - Mapping options
64
+ * @returns Security requirement or undefined if no mapping
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * mapGuardToSecurity({ name: 'authenticated', check: () => true })
69
+ * // { bearerAuth: [] }
70
+ *
71
+ * mapGuardToSecurity({ name: 'hasRole:admin', check: () => true })
72
+ * // { bearerAuth: ['admin'] }
73
+ * ```
74
+ */
75
+ export function mapGuardToSecurity(guard, options = {}) {
76
+ const { customMappings = {}, extractScopes = true, scopeSeparator = ':' } = options;
77
+ // Merge mappings (custom takes precedence)
78
+ const mappings = { ...DEFAULT_GUARD_MAPPINGS, ...customMappings };
79
+ // Parse guard name and optional scopes
80
+ const parts = guard.name.split(scopeSeparator);
81
+ const baseName = parts[0];
82
+ const scopes = extractScopes && parts.length > 1 ? parts.slice(1) : [];
83
+ // Find matching security scheme
84
+ const schemeName = mappings[guard.name] ?? mappings[baseName];
85
+ if (!schemeName) {
86
+ return undefined;
87
+ }
88
+ return { [schemeName]: scopes };
89
+ }
90
+ /**
91
+ * Maps multiple guards to OpenAPI security requirements
92
+ *
93
+ * Multiple guards result in multiple security requirements,
94
+ * meaning ALL must be satisfied (AND logic in OpenAPI).
95
+ *
96
+ * @param guards - Guards to map
97
+ * @param options - Mapping options
98
+ * @returns Array of security requirements
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * guardsToSecurity([
103
+ * { name: 'authenticated', check: () => true },
104
+ * { name: 'hasRole:admin', check: () => true }
105
+ * ])
106
+ * // [{ bearerAuth: [] }, { bearerAuth: ['admin'] }]
107
+ * ```
108
+ */
109
+ export function guardsToSecurity(guards, options = {}) {
110
+ if (guards.length === 0) {
111
+ return [];
112
+ }
113
+ const requirements = [];
114
+ const seen = new Set(); // Deduplicate identical requirements
115
+ for (const guard of guards) {
116
+ const requirement = mapGuardToSecurity(guard, options);
117
+ if (requirement) {
118
+ const key = JSON.stringify(requirement);
119
+ if (!seen.has(key)) {
120
+ seen.add(key);
121
+ requirements.push(requirement);
122
+ }
123
+ }
124
+ }
125
+ return requirements;
126
+ }
127
+ // ============================================================================
128
+ // Security Scheme Utilities
129
+ // ============================================================================
130
+ /**
131
+ * Merges security schemes, with custom schemes taking precedence
132
+ *
133
+ * @param customSchemes - Custom security schemes
134
+ * @param includeDefaults - Whether to include default schemes
135
+ * @returns Merged security schemes
136
+ */
137
+ export function mergeSecuritySchemes(customSchemes, includeDefaults = true) {
138
+ if (!includeDefaults) {
139
+ return customSchemes ?? {};
140
+ }
141
+ return {
142
+ ...DEFAULT_SECURITY_SCHEMES,
143
+ ...customSchemes,
144
+ };
145
+ }
146
+ /**
147
+ * Extracts unique security scheme names from guards
148
+ *
149
+ * Useful for determining which schemes to include in the spec.
150
+ *
151
+ * @param guards - Guards to analyze
152
+ * @param options - Mapping options
153
+ * @returns Set of security scheme names used
154
+ */
155
+ export function extractUsedSecuritySchemes(guards, options = {}) {
156
+ const schemeNames = new Set();
157
+ for (const guard of guards) {
158
+ const requirement = mapGuardToSecurity(guard, options);
159
+ if (requirement) {
160
+ for (const schemeName of Object.keys(requirement)) {
161
+ schemeNames.add(schemeName);
162
+ }
163
+ }
164
+ }
165
+ return schemeNames;
166
+ }
167
+ /**
168
+ * Filters security schemes to only include those that are used
169
+ *
170
+ * @param allSchemes - All available security schemes
171
+ * @param usedNames - Names of schemes that are actually used
172
+ * @returns Filtered security schemes
173
+ */
174
+ export function filterUsedSecuritySchemes(allSchemes, usedNames) {
175
+ const filtered = {};
176
+ for (const name of usedNames) {
177
+ if (allSchemes[name]) {
178
+ filtered[name] = allSchemes[name];
179
+ }
180
+ }
181
+ return filtered;
182
+ }
183
+ // ============================================================================
184
+ // Guard Analysis
185
+ // ============================================================================
186
+ /**
187
+ * Checks if guards require authentication
188
+ *
189
+ * @param guards - Guards to check
190
+ * @param options - Mapping options
191
+ * @returns True if any guard maps to a security scheme
192
+ */
193
+ export function guardsRequireAuth(guards, options = {}) {
194
+ return guardsToSecurity(guards, options).length > 0;
195
+ }
196
+ /**
197
+ * Extracts all scopes from guards
198
+ *
199
+ * @param guards - Guards to analyze
200
+ * @param options - Mapping options
201
+ * @returns Array of unique scopes
202
+ */
203
+ export function extractGuardScopes(guards, options = {}) {
204
+ const scopes = new Set();
205
+ for (const guard of guards) {
206
+ const requirement = mapGuardToSecurity(guard, options);
207
+ if (requirement) {
208
+ for (const scopeList of Object.values(requirement)) {
209
+ for (const scope of scopeList) {
210
+ scopes.add(scope);
211
+ }
212
+ }
213
+ }
214
+ }
215
+ return [...scopes];
216
+ }
217
+ /**
218
+ * Creates a security requirement from scheme name and optional scopes
219
+ *
220
+ * @param schemeName - Name of the security scheme
221
+ * @param scopes - Optional scopes
222
+ * @returns Security requirement object
223
+ */
224
+ export function createSecurityRequirement(schemeName, scopes = []) {
225
+ return { [schemeName]: scopes };
226
+ }