@factiii/auth 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Factiii
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # @factiii/auth
2
+
3
+ Drop-in authentication for tRPC. JWT sessions, OAuth, 2FA—all type-safe.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @factiii/auth @prisma/client
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ **1. Add Prisma models:**
14
+
15
+ ```bash
16
+ npx @factiii/auth init
17
+ npx prisma generate && npx prisma db push
18
+ npx @factiii/auth doctor # Verify setup
19
+ ```
20
+
21
+ **2. Create auth router:**
22
+
23
+ ```typescript
24
+ import { createAuthRouter } from '@factiii/auth';
25
+ import { prisma } from './prisma';
26
+
27
+ export const { router, authProcedure, createContext } = createAuthRouter({
28
+ prisma,
29
+ secrets: { jwt: process.env.JWT_SECRET! },
30
+ });
31
+ ```
32
+
33
+ **3. Use protected routes:**
34
+
35
+ ```typescript
36
+ const protectedRouter = router({
37
+ getProfile: authProcedure.query(({ ctx }) => {
38
+ return { userId: ctx.userId };
39
+ }),
40
+ });
41
+ ```
42
+
43
+ ## Config
44
+
45
+ ```typescript
46
+ createAuthRouter({
47
+ prisma,
48
+ secrets: { jwt: 'your-secret' },
49
+
50
+ // Optional
51
+ features: {
52
+ emailVerification: true,
53
+ twoFa: true,
54
+ oauth: { google: true, apple: true },
55
+ biometric: false,
56
+ },
57
+ oauthKeys: {
58
+ google: { clientId: '...' },
59
+ apple: { clientId: '...' },
60
+ },
61
+ emailService: {
62
+ sendVerificationEmail: async (email, code) => {},
63
+ sendPasswordResetEmail: async (email, token) => {},
64
+ sendOTPEmail: async (email, otp) => {},
65
+ },
66
+ hooks: {
67
+ onUserCreated: async (userId) => {},
68
+ onUserLogin: async (userId, sessionId) => {},
69
+ // ... 15+ lifecycle hooks
70
+ },
71
+ tokenSettings: {
72
+ accessTokenExpiry: '5m', // JWT expiry (default: 5 minutes)
73
+ passwordResetExpiryMs: 3600000, // Reset token expiry (default: 1 hour)
74
+ otpValidityMs: 900000, // OTP validity window (default: 15 minutes)
75
+ },
76
+ });
77
+ ```
78
+
79
+ ## Procedures
80
+
81
+ Auth procedures: `register`, `login`, `logout`, `refresh`, `changePassword`, `resetPassword`, `oAuthLogin`, `enableTwofa`, `disableTwofa`, `sendVerificationEmail`, `verifyEmail`, and more.
82
+
83
+ ## Lifecycle Hooks
84
+
85
+ ```typescript
86
+ interface AuthHooks {
87
+ // Registration & Login
88
+ beforeRegister?: (input) => Promise<void>;
89
+ beforeLogin?: (input) => Promise<void>;
90
+ onUserCreated?: (userId, input) => Promise<void>;
91
+ onUserLogin?: (userId, sessionId) => Promise<void>;
92
+
93
+ // Sessions
94
+ onSessionCreated?: (sessionId) => Promise<void>;
95
+ onSessionRevoked?: (sessionId, socketId, reason) => Promise<void>;
96
+ afterLogout?: (userId, sessionId, socketId) => Promise<void>;
97
+ onRefresh?: (userId) => Promise<void>;
98
+
99
+ // Security
100
+ onPasswordChanged?: (userId) => Promise<void>;
101
+ onEmailVerified?: (userId) => Promise<void>;
102
+ onTwoFaStatusChanged?: (userId, enabled) => Promise<void>;
103
+ onOAuthLinked?: (userId, provider) => Promise<void>;
104
+ onBiometricVerified?: (userId) => Promise<void>;
105
+ getBiometricTimeout?: () => Promise<number | null>;
106
+ }
107
+ ```
108
+
109
+ ## CLI
110
+
111
+ ```bash
112
+ npx @factiii/auth init # Copy Prisma schema to your project
113
+ npx @factiii/auth schema # Print schema path for manual copying
114
+ npx @factiii/auth doctor # Check setup for common issues
115
+ npx @factiii/auth help # Show help
116
+ ```
117
+
118
+ ## License
119
+
120
+ MIT
package/bin/init.mjs ADDED
@@ -0,0 +1,315 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync } from 'node:fs';
4
+ import { dirname, join, resolve } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const schemaSource = join(__dirname, '..', 'prisma', 'schema.prisma');
9
+ const targetDir = resolve(process.cwd(), 'prisma');
10
+ const targetFile = join(targetDir, 'auth-schema.prisma');
11
+
12
+ const args = process.argv.slice(2);
13
+ const command = args[0];
14
+
15
+ function printHelp() {
16
+ console.log(`
17
+ @factiii/auth CLI
18
+
19
+ Usage:
20
+ npx @factiii/auth <command>
21
+
22
+ Commands:
23
+ init Copy the reference Prisma schema to your project
24
+ schema Print the schema path (for manual copying)
25
+ doctor Check your project setup for common issues
26
+ help Show this help message
27
+
28
+ Examples:
29
+ npx @factiii/auth init
30
+ npx @factiii/auth doctor
31
+ `);
32
+ }
33
+
34
+ function init() {
35
+ // Ensure prisma directory exists
36
+ if (!existsSync(targetDir)) {
37
+ mkdirSync(targetDir, { recursive: true });
38
+ console.log('Created prisma/ directory');
39
+ }
40
+
41
+ // Check if file already exists
42
+ if (existsSync(targetFile)) {
43
+ console.log(`⚠️ ${targetFile} already exists`);
44
+ console.log(' To overwrite, delete it first and run again.');
45
+ process.exit(1);
46
+ }
47
+
48
+ // Copy schema
49
+ try {
50
+ copyFileSync(schemaSource, targetFile);
51
+ console.log(`✓ Copied schema to ${targetFile}`);
52
+ console.log('');
53
+ console.log('Next steps:');
54
+ console.log(' 1. Review and customize the schema for your database provider');
55
+ console.log(' 2. Merge models into your existing schema.prisma (if you have one)');
56
+ console.log(' 3. Run: npx prisma generate');
57
+ console.log(' 4. Run: npx prisma db push (or prisma migrate dev)');
58
+ } catch (err) {
59
+ console.error('Failed to copy schema:', err.message);
60
+ process.exit(1);
61
+ }
62
+ }
63
+
64
+ function printSchemaPath() {
65
+ console.log('Schema location:');
66
+ console.log(` ${schemaSource}`);
67
+ console.log('');
68
+ console.log('Copy manually:');
69
+ console.log(` cp "${schemaSource}" ./prisma/auth-schema.prisma`);
70
+ }
71
+
72
+ // ANSI color codes
73
+ const colors = {
74
+ reset: '\x1b[0m',
75
+ red: '\x1b[31m',
76
+ green: '\x1b[32m',
77
+ yellow: '\x1b[33m',
78
+ cyan: '\x1b[36m',
79
+ bold: '\x1b[1m',
80
+ dim: '\x1b[2m'
81
+ };
82
+
83
+ const ok = (msg) => console.log(`${colors.green}✓${colors.reset} ${msg}`);
84
+ const fail = (msg) => console.log(`${colors.red}✗${colors.reset} ${msg}`);
85
+ const warn = (msg) => console.log(`${colors.yellow}⚠${colors.reset} ${msg}`);
86
+ const hint = (msg) => console.log(`${colors.dim} ${msg}${colors.reset}`);
87
+
88
+ /**
89
+ * Recursively find all .prisma files in a directory
90
+ * @param {string} dir - Directory to search
91
+ * @param {string[]} files - Accumulator for found files
92
+ * @returns {string[]} Array of file paths
93
+ */
94
+ function findPrismaFiles(dir, files = []) {
95
+ if (!existsSync(dir)) return files;
96
+
97
+ try {
98
+ const entries = readdirSync(dir, { withFileTypes: true });
99
+ for (const entry of entries) {
100
+ const fullPath = join(dir, entry.name);
101
+ if (entry.isDirectory() && entry.name !== 'migrations') {
102
+ findPrismaFiles(fullPath, files);
103
+ } else if (entry.isFile() && entry.name.endsWith('.prisma')) {
104
+ files.push(fullPath);
105
+ }
106
+ }
107
+ } catch (e) {
108
+ // Directory read failed
109
+ }
110
+
111
+ return files;
112
+ }
113
+
114
+ /**
115
+ * Find and read all Prisma schema files (supports single file and modularized schemas)
116
+ * Patterns supported:
117
+ * - prisma/schema.prisma (single file)
118
+ * - prisma/schema/*.prisma (modularized in schema dir)
119
+ * - prisma/schema.prisma + prisma/models/*.prisma (hybrid)
120
+ * - prisma/*.prisma (multiple files in prisma dir)
121
+ * @returns {{ found: boolean, schemaContent: string, location: string }}
122
+ */
123
+ function findPrismaSchema() {
124
+ const prismaDir = resolve(process.cwd(), 'prisma');
125
+
126
+ if (!existsSync(prismaDir)) {
127
+ return { found: false, schemaContent: '', location: '' };
128
+ }
129
+
130
+ // Find all .prisma files recursively (excluding migrations)
131
+ const allFiles = findPrismaFiles(prismaDir);
132
+
133
+ if (allFiles.length === 0) {
134
+ return { found: false, schemaContent: '', location: '' };
135
+ }
136
+
137
+ // Read and combine all schema files
138
+ const combinedSchema = allFiles
139
+ .map(f => readFileSync(f, 'utf-8'))
140
+ .join('\n');
141
+
142
+ // Build location description
143
+ let location;
144
+ if (allFiles.length === 1 && allFiles[0] === join(prismaDir, 'schema.prisma')) {
145
+ location = 'prisma/schema.prisma';
146
+ } else {
147
+ // Get unique directories
148
+ const dirs = [...new Set(allFiles.map(f => dirname(f).replace(prismaDir, 'prisma')))];
149
+ location = `${dirs.join(', ')} (${allFiles.length} files)`;
150
+ }
151
+
152
+ return {
153
+ found: true,
154
+ schemaContent: combinedSchema,
155
+ location
156
+ };
157
+ }
158
+
159
+ function parseReferenceSchema() {
160
+ const schema = readFileSync(schemaSource, 'utf-8');
161
+
162
+ // Extract model names
163
+ const models = [...schema.matchAll(/model\s+(\w+)\s*\{/g)].map(m => m[1]);
164
+
165
+ // Extract enum names
166
+ const enums = [...schema.matchAll(/enum\s+(\w+)\s*\{/g)].map(m => m[1]);
167
+
168
+ // Extract fields for each model
169
+ const modelFields = {};
170
+ for (const model of models) {
171
+ const modelMatch = schema.match(new RegExp(`model\\s+${model}\\s*\\{([^}]+)\\}`, 'm'));
172
+ if (modelMatch) {
173
+ const block = modelMatch[1];
174
+ // Match field names (first word on lines that aren't comments or @@)
175
+ const fields = [...block.matchAll(/^\s*(\w+)\s+\w+/gm)]
176
+ .map(m => m[1])
177
+ .filter(f => !f.startsWith('@@'));
178
+ modelFields[model] = fields;
179
+ }
180
+ }
181
+
182
+ return { models, enums, modelFields };
183
+ }
184
+
185
+ function doctor() {
186
+ console.log(`${colors.bold}${colors.cyan}Running diagnostics...${colors.reset}\n`);
187
+
188
+ let issues = 0;
189
+ let warnings = 0;
190
+
191
+ // Parse reference schema to get required models/enums/fields
192
+ let reference;
193
+ try {
194
+ reference = parseReferenceSchema();
195
+ } catch (e) {
196
+ fail('Could not read reference schema from package');
197
+ process.exit(1);
198
+ }
199
+
200
+ // Check 1: package.json exists
201
+ const packageJsonPath = resolve(process.cwd(), 'package.json');
202
+ if (!existsSync(packageJsonPath)) {
203
+ fail('No package.json found in current directory');
204
+ issues++;
205
+ } else {
206
+ ok('package.json found');
207
+
208
+ // Check for @prisma/client dependency
209
+ try {
210
+ const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
211
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
212
+ if (deps['@prisma/client']) {
213
+ ok('@prisma/client is installed');
214
+ } else {
215
+ fail('@prisma/client not found in dependencies');
216
+ hint('Run: npm install @prisma/client');
217
+ issues++;
218
+ }
219
+ } catch (e) {
220
+ warn('Could not parse package.json');
221
+ warnings++;
222
+ }
223
+ }
224
+
225
+ // Check 2: Prisma schema exists (supports single file and modularized schemas)
226
+ const { found: schemaFound, schemaContent: schema, location: schemaLocation } = findPrismaSchema();
227
+ if (!schemaFound) {
228
+ fail('No Prisma schema found');
229
+ hint('Checked: prisma/schema.prisma, prisma/schema/*.prisma, prisma/*.prisma');
230
+ hint('Run: npx @factiii/auth init');
231
+ issues++;
232
+ } else {
233
+ ok(`Prisma schema found (${schemaLocation})`);
234
+
235
+ // Parse user's schema
236
+ try {
237
+ // Check models
238
+ for (const model of reference.models) {
239
+ const regex = new RegExp(`model\\s+${model}\\s*\\{`, 'm');
240
+ if (regex.test(schema)) {
241
+ ok(`Model ${colors.cyan}${model}${colors.reset} found`);
242
+ } else {
243
+ fail(`Model ${colors.cyan}${model}${colors.reset} not found in schema`);
244
+ issues++;
245
+ }
246
+ }
247
+
248
+ // Check enums
249
+ for (const enumName of reference.enums) {
250
+ const regex = new RegExp(`enum\\s+${enumName}\\s*\\{`, 'm');
251
+ if (regex.test(schema)) {
252
+ ok(`Enum ${colors.cyan}${enumName}${colors.reset} found`);
253
+ } else {
254
+ fail(`Enum ${colors.cyan}${enumName}${colors.reset} not found in schema`);
255
+ issues++;
256
+ }
257
+ }
258
+
259
+ // Check fields for each model
260
+ for (const [model, fields] of Object.entries(reference.modelFields)) {
261
+ const modelMatch = schema.match(new RegExp(`model\\s+${model}\\s*\\{([^}]+)\\}`, 'm'));
262
+ if (modelMatch) {
263
+ const block = modelMatch[1];
264
+ for (const field of fields) {
265
+ const fieldRegex = new RegExp(`\\b${field}\\b`, 'm');
266
+ if (!fieldRegex.test(block)) {
267
+ warn(`${model}.${colors.cyan}${field}${colors.reset} field not found`);
268
+ warnings++;
269
+ }
270
+ }
271
+ }
272
+ }
273
+ } catch (e) {
274
+ warn('Could not parse Prisma schema');
275
+ warnings++;
276
+ }
277
+ }
278
+
279
+ // Summary
280
+ console.log(`\n${colors.bold}--- Summary ---${colors.reset}`);
281
+ if (issues === 0 && warnings === 0) {
282
+ console.log(`${colors.green}${colors.bold}✓ All checks passed!${colors.reset} Your setup looks good.`);
283
+ } else {
284
+ if (issues > 0) {
285
+ console.log(`${colors.red}${colors.bold}✗ ${issues} issue(s) found${colors.reset}`);
286
+ }
287
+ if (warnings > 0) {
288
+ console.log(`${colors.yellow}${colors.bold}⚠ ${warnings} warning(s) found${colors.reset}`);
289
+ }
290
+ }
291
+
292
+ process.exit(issues > 0 ? 1 : 0);
293
+ }
294
+
295
+ switch (command) {
296
+ case 'init':
297
+ init();
298
+ break;
299
+ case 'schema':
300
+ printSchemaPath();
301
+ break;
302
+ case 'doctor':
303
+ doctor();
304
+ break;
305
+ case 'help':
306
+ case '--help':
307
+ case '-h':
308
+ case undefined:
309
+ printHelp();
310
+ break;
311
+ default:
312
+ console.error(`Unknown command: ${command}`);
313
+ printHelp();
314
+ process.exit(1);
315
+ }
@@ -0,0 +1,118 @@
1
+ // src/validators.ts
2
+ import { z } from "zod";
3
+ var usernameValidationRegex = /^[a-zA-Z0-9_]+$/;
4
+ var signupSchema = z.object({
5
+ username: z.string().min(1, { message: "Username is required" }).max(30, { message: "Username must be 30 characters or less" }).regex(usernameValidationRegex, {
6
+ message: "Username can only contain letters, numbers, and underscores"
7
+ }),
8
+ email: z.string().email({ message: "Invalid email address" }),
9
+ password: z.string().min(6, { message: "Password must contain at least 6 characters" })
10
+ });
11
+ var loginSchema = z.object({
12
+ username: z.string().min(1, { message: "Username or email is required" }),
13
+ password: z.string().min(1, { message: "Password is required" }),
14
+ code: z.string().optional()
15
+ // 2FA code
16
+ });
17
+ var oAuthLoginSchema = z.object({
18
+ idToken: z.string(),
19
+ user: z.object({
20
+ email: z.string().email().optional()
21
+ }).optional(),
22
+ provider: z.enum(["GOOGLE", "APPLE"])
23
+ });
24
+ var requestPasswordResetSchema = z.object({
25
+ email: z.string().email({ message: "Invalid email address" })
26
+ });
27
+ var resetPasswordSchema = z.object({
28
+ token: z.string().min(1, { message: "Reset token is required" }),
29
+ password: z.string().min(6, { message: "Password must contain at least 6 characters" })
30
+ });
31
+ var checkPasswordResetSchema = z.object({
32
+ token: z.string().min(1, { message: "Reset token is required" })
33
+ });
34
+ var changePasswordSchema = z.object({
35
+ currentPassword: z.string().min(1, { message: "Current password is required" }),
36
+ newPassword: z.string().min(6, { message: "New password must contain at least 6 characters" })
37
+ });
38
+ var twoFaVerifySchema = z.object({
39
+ code: z.string().min(6, { message: "Verification code is required" }),
40
+ sessionId: z.number().optional()
41
+ });
42
+ var twoFaSetupSchema = z.object({
43
+ code: z.string().min(6, { message: "Verification code is required" })
44
+ });
45
+ var twoFaResetSchema = z.object({
46
+ username: z.string().min(1),
47
+ password: z.string().min(1)
48
+ });
49
+ var twoFaResetVerifySchema = z.object({
50
+ code: z.number().min(1e5).max(999999),
51
+ username: z.string().min(1)
52
+ });
53
+ var verifyEmailSchema = z.object({
54
+ code: z.string().min(1, { message: "Verification code is required" })
55
+ });
56
+ var resendVerificationSchema = z.object({
57
+ email: z.string().email().optional()
58
+ });
59
+ var biometricVerifySchema = z.object({});
60
+ var registerPushTokenSchema = z.object({
61
+ pushToken: z.string().min(1, { message: "Push token is required" })
62
+ });
63
+ var deregisterPushTokenSchema = z.object({
64
+ pushToken: z.string().min(1, { message: "Push token is required" })
65
+ });
66
+ var getTwofaSecretSchema = z.object({
67
+ pushCode: z.string().min(6, { message: "Push code is required" })
68
+ });
69
+ var disableTwofaSchema = z.object({
70
+ password: z.string().min(1, { message: "Password is required" })
71
+ });
72
+ var logoutSchema = z.object({
73
+ allDevices: z.boolean().optional().default(false)
74
+ });
75
+ var endAllSessionsSchema = z.object({
76
+ skipCurrentSession: z.boolean().optional().default(true)
77
+ });
78
+ var otpLoginRequestSchema = z.object({
79
+ email: z.string().email({ message: "Invalid email address" })
80
+ });
81
+ var otpLoginVerifySchema = z.object({
82
+ email: z.string().email(),
83
+ code: z.number().min(1e5).max(999999)
84
+ });
85
+ function createSchemas(extensions) {
86
+ return {
87
+ signup: extensions?.signup ? signupSchema.merge(extensions.signup) : signupSchema,
88
+ login: extensions?.login ? loginSchema.merge(extensions.login) : loginSchema,
89
+ oauth: extensions?.oauth ? oAuthLoginSchema.merge(extensions.oauth) : oAuthLoginSchema
90
+ };
91
+ }
92
+
93
+ export {
94
+ signupSchema,
95
+ loginSchema,
96
+ oAuthLoginSchema,
97
+ requestPasswordResetSchema,
98
+ resetPasswordSchema,
99
+ checkPasswordResetSchema,
100
+ changePasswordSchema,
101
+ twoFaVerifySchema,
102
+ twoFaSetupSchema,
103
+ twoFaResetSchema,
104
+ twoFaResetVerifySchema,
105
+ verifyEmailSchema,
106
+ resendVerificationSchema,
107
+ biometricVerifySchema,
108
+ registerPushTokenSchema,
109
+ deregisterPushTokenSchema,
110
+ getTwofaSecretSchema,
111
+ disableTwofaSchema,
112
+ logoutSchema,
113
+ endAllSessionsSchema,
114
+ otpLoginRequestSchema,
115
+ otpLoginVerifySchema,
116
+ createSchemas
117
+ };
118
+ //# sourceMappingURL=chunk-CLHDX2R2.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/validators.ts"],"sourcesContent":["import { z, type AnyZodObject } from 'zod';\n\nimport type { SchemaExtensions } from './types/hooks';\n\n/**\n * Username validation regex - allows letters, numbers, and underscores\n */\nconst usernameValidationRegex = /^[a-zA-Z0-9_]+$/;\n\n/**\n * Schema for user registration\n */\nexport const signupSchema = z.object({\n username: z\n .string()\n .min(1, { message: 'Username is required' })\n .max(30, { message: 'Username must be 30 characters or less' })\n .regex(usernameValidationRegex, {\n message: 'Username can only contain letters, numbers, and underscores'\n }),\n email: z.string().email({ message: 'Invalid email address' }),\n password: z\n .string()\n .min(6, { message: 'Password must contain at least 6 characters' })\n});\n\n/**\n * Schema for user login\n */\nexport const loginSchema = z.object({\n username: z.string().min(1, { message: 'Username or email is required' }),\n password: z.string().min(1, { message: 'Password is required' }),\n code: z.string().optional() // 2FA code\n});\n\n/**\n * Schema for OAuth login\n */\nexport const oAuthLoginSchema = z.object({\n idToken: z.string(),\n user: z\n .object({\n email: z.string().email().optional()\n })\n .optional(),\n provider: z.enum(['GOOGLE', 'APPLE'])\n});\n\n/**\n * Schema for password reset request\n */\nexport const requestPasswordResetSchema = z.object({\n email: z.string().email({ message: 'Invalid email address' })\n});\n\n/**\n * Schema for password reset confirmation\n */\nexport const resetPasswordSchema = z.object({\n token: z.string().min(1, { message: 'Reset token is required' }),\n password: z\n .string()\n .min(6, { message: 'Password must contain at least 6 characters' })\n});\n\n/**\n * Schema for checking password reset token\n */\nexport const checkPasswordResetSchema = z.object({\n token: z.string().min(1, { message: 'Reset token is required' })\n});\n\n/**\n * Schema for changing password (authenticated)\n */\nexport const changePasswordSchema = z.object({\n currentPassword: z\n .string()\n .min(1, { message: 'Current password is required' }),\n newPassword: z\n .string()\n .min(6, { message: 'New password must contain at least 6 characters' })\n});\n\n/**\n * Schema for 2FA verification\n */\nexport const twoFaVerifySchema = z.object({\n code: z.string().min(6, { message: 'Verification code is required' }),\n sessionId: z.number().optional()\n});\n\n/**\n * Schema for 2FA setup\n */\nexport const twoFaSetupSchema = z.object({\n code: z.string().min(6, { message: 'Verification code is required' })\n});\n\n/**\n * Schema for 2FA reset request\n */\nexport const twoFaResetSchema = z.object({\n username: z.string().min(1),\n password: z.string().min(1)\n});\n\n/**\n * Schema for 2FA reset verification\n */\nexport const twoFaResetVerifySchema = z.object({\n code: z.number().min(100000).max(999999),\n username: z.string().min(1)\n});\n\n/**\n * Schema for email verification\n */\nexport const verifyEmailSchema = z.object({\n code: z.string().min(1, { message: 'Verification code is required' })\n});\n\n/**\n * Schema for resending verification email\n */\nexport const resendVerificationSchema = z.object({\n email: z.string().email().optional()\n});\n\n/**\n * Schema for biometric verification\n */\nexport const biometricVerifySchema = z.object({});\n\n/**\n * Schema for push token registration\n */\nexport const registerPushTokenSchema = z.object({\n pushToken: z.string().min(1, { message: 'Push token is required' })\n});\n\n/**\n * Schema for push token deregistration\n */\nexport const deregisterPushTokenSchema = z.object({\n pushToken: z.string().min(1, { message: 'Push token is required' })\n});\n\n/**\n * Schema for getting 2FA secret\n */\nexport const getTwofaSecretSchema = z.object({\n pushCode: z.string().min(6, { message: 'Push code is required' })\n});\n\n/**\n * Schema for disabling 2FA\n */\nexport const disableTwofaSchema = z.object({\n password: z.string().min(1, { message: 'Password is required' })\n});\n\n/**\n * Schema for logout\n */\nexport const logoutSchema = z.object({\n allDevices: z.boolean().optional().default(false)\n});\n\n/**\n * Schema for ending all sessions\n */\nexport const endAllSessionsSchema = z.object({\n skipCurrentSession: z.boolean().optional().default(true)\n});\n\n/**\n * Schema for OTP-based login request\n */\nexport const otpLoginRequestSchema = z.object({\n email: z.string().email({ message: 'Invalid email address' })\n});\n\n/**\n * Schema for OTP-based login verification\n */\nexport const otpLoginVerifySchema = z.object({\n email: z.string().email(),\n code: z.number().min(100000).max(999999)\n});\n\nexport type SignupInput = z.infer<typeof signupSchema>;\nexport type LoginInput = z.infer<typeof loginSchema>;\nexport type OAuthLoginInput = z.infer<typeof oAuthLoginSchema>;\nexport type ResetPasswordInput = z.infer<typeof resetPasswordSchema>;\nexport type ChangePasswordInput = z.infer<typeof changePasswordSchema>;\nexport type TwoFaVerifyInput = z.infer<typeof twoFaVerifySchema>;\nexport type VerifyEmailInput = z.infer<typeof verifyEmailSchema>;\nexport type LogoutInput = z.infer<typeof logoutSchema>;\n\n/** Schemas used by auth procedures */\nexport interface AuthSchemas {\n signup: AnyZodObject;\n login: AnyZodObject;\n oauth: AnyZodObject;\n}\n\n\n/**\n * Compute merged ZodObject type.\n * When TExt is defined, produces a schema with both base and extension shapes.\n * When TExt is undefined, produces the base schema.\n */\ntype MergedSchema<TBase extends AnyZodObject, TExt extends AnyZodObject | undefined> =\n [TExt] extends [AnyZodObject]\n ? z.ZodObject<TBase['shape'] & TExt['shape'], 'strip', z.ZodTypeAny>\n : TBase;\n\n/** Result type from createSchemas - preserves concrete schema types */\nexport type CreatedSchemas<TExtensions extends SchemaExtensions = {}> = {\n signup: MergedSchema<typeof signupSchema, TExtensions['signup']>;\n login: MergedSchema<typeof loginSchema, TExtensions['login']>;\n oauth: MergedSchema<typeof oAuthLoginSchema, TExtensions['oauth']>;\n};\n\nexport type SignupSchemaInput<TExtensions extends SchemaExtensions = {}> =\n SignupInput & (TExtensions['signup'] extends AnyZodObject ? z.infer<TExtensions['signup']> : {});\n\nexport type LoginSchemaInput<TExtensions extends SchemaExtensions = {}> =\n LoginInput & (TExtensions['login'] extends AnyZodObject ? z.infer<TExtensions['login']> : {});\n\nexport type OAuthSchemaInput<TExtensions extends SchemaExtensions = {}> =\n OAuthLoginInput & (TExtensions['oauth'] extends AnyZodObject ? z.infer<TExtensions['oauth']> : {});\n\n/** Create schemas with optional extensions merged in */\nexport function createSchemas<TExtensions extends SchemaExtensions = {}>(\n extensions?: TExtensions\n): CreatedSchemas<TExtensions> {\n return {\n signup: extensions?.signup\n ? signupSchema.merge(extensions.signup)\n : signupSchema,\n login: extensions?.login\n ? loginSchema.merge(extensions.login)\n : loginSchema,\n oauth: extensions?.oauth\n ? oAuthLoginSchema.merge(extensions.oauth)\n : oAuthLoginSchema\n } as CreatedSchemas<TExtensions>;\n}\n"],"mappings":"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","names":[]}