kroxt 1.3.9 → 1.3.11

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 (43) hide show
  1. package/README.md +270 -231
  2. package/dist/auth/adapters/drizzle.cjs +5 -0
  3. package/dist/auth/adapters/drizzle.cjs.map +2 -2
  4. package/dist/auth/adapters/drizzle.d.ts.map +1 -1
  5. package/dist/auth/adapters/drizzle.js +5 -0
  6. package/dist/auth/adapters/drizzle.js.map +2 -2
  7. package/dist/auth/adapters/index.cjs.map +1 -1
  8. package/dist/auth/adapters/index.d.ts +2 -0
  9. package/dist/auth/adapters/index.d.ts.map +1 -1
  10. package/dist/auth/adapters/memory.cjs +9 -0
  11. package/dist/auth/adapters/memory.cjs.map +2 -2
  12. package/dist/auth/adapters/memory.d.ts.map +1 -1
  13. package/dist/auth/adapters/memory.js +9 -0
  14. package/dist/auth/adapters/memory.js.map +2 -2
  15. package/dist/auth/adapters/mongoose.cjs +5 -0
  16. package/dist/auth/adapters/mongoose.cjs.map +2 -2
  17. package/dist/auth/adapters/mongoose.d.ts.map +1 -1
  18. package/dist/auth/adapters/mongoose.js +5 -0
  19. package/dist/auth/adapters/mongoose.js.map +2 -2
  20. package/dist/auth/adapters/prisma.cjs +8 -0
  21. package/dist/auth/adapters/prisma.cjs.map +2 -2
  22. package/dist/auth/adapters/prisma.d.ts.map +1 -1
  23. package/dist/auth/adapters/prisma.js +8 -0
  24. package/dist/auth/adapters/prisma.js.map +2 -2
  25. package/dist/auth/core/index.cjs +32 -5
  26. package/dist/auth/core/index.cjs.map +2 -2
  27. package/dist/auth/core/index.d.ts +3 -0
  28. package/dist/auth/core/index.d.ts.map +1 -1
  29. package/dist/auth/core/index.js +32 -5
  30. package/dist/auth/core/index.js.map +2 -2
  31. package/dist/cli/index.cjs +49 -18
  32. package/dist/cli/index.cjs.map +3 -3
  33. package/dist/cli/index.js +49 -18
  34. package/dist/cli/index.js.map +2 -2
  35. package/dist/cli/templates.cjs +5 -1
  36. package/dist/cli/templates.cjs.map +2 -2
  37. package/dist/cli/templates.d.ts +1 -1
  38. package/dist/cli/templates.d.ts.map +1 -1
  39. package/dist/cli/templates.js +5 -1
  40. package/dist/cli/templates.js.map +2 -2
  41. package/dist/index.cjs.map +1 -1
  42. package/dist/index.js.map +1 -1
  43. package/package.json +108 -108
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/auth/core/index.ts"],
4
- "sourcesContent": ["import * as argon2 from \"argon2\";\r\nimport { SignJWT, jwtVerify } from \"jose\";\r\nimport crypto from \"crypto\";\r\nimport type { AuthAdapter, User } from \"../adapters/index.js\";\r\nimport type { Provider } from \"../providers/index.js\";\r\nimport { createRateLimiter, type RateLimitOptions } from \"../security/rate-limit.js\";\r\n\r\nexport interface CreateAuthOptions {\r\n adapter: AuthAdapter<any>;\r\n secret: string | Uint8Array;\r\n pepper?: string;\r\n session?: {\r\n expires?: string | number; // For access tokens\r\n refreshExpires?: string | number; // For refresh tokens\r\n enforceStrictRevocation?: boolean; // If true, DB check on access tokens\r\n };\r\n providers?: Provider[];\r\n jwt?: {\r\n /**\r\n * A callback to add custom fields to the JWT payload.\r\n * It receives the user object and the token type ('access' or 'refresh').\r\n * Return an object containing the fields to be merged into the payload.\r\n * You can also override default fields like 'sub'.\r\n */\r\n payload?: (user: User<any>, type: \"access\" | \"refresh\") => Record<string, any>;\r\n };\r\n rateLimit?: RateLimitOptions;\r\n ipBlocking?: { maxStrikes: number; blockDurationMs: number };\r\n passwordPolicy?: {\r\n minLength?: number;\r\n requireUppercase?: boolean;\r\n requireLowercase?: boolean;\r\n requireNumber?: boolean;\r\n requireSpecialCharacter?: boolean;\r\n };\r\n}\r\n\r\nexport function createAuth(options: CreateAuthOptions) {\r\n const { adapter, secret, pepper, session, providers, rateLimit, ipBlocking } = options;\r\n const encodedSecret = typeof secret === \"string\" ? new TextEncoder().encode(secret) : secret;\r\n const expiration = session?.expires || \"1h\"; // Default access token to 1h\r\n const refreshExpiration = session?.refreshExpires || \"7d\";\r\n\r\n const configuredRateLimiter = createRateLimiter(adapter, rateLimit);\r\n\r\n /**\r\n * Generates a stateless JWT for a user session\r\n */\r\n async function generateToken(user: User<any>, type: \"access\" | \"refresh\" = \"access\") {\r\n let payload: Record<string, any> = { sub: user.id, role: user.role, type };\r\n\r\n // Lightweight Session Revocation: Link token to current password hash state\r\n if (user.passwordHash && (type === \"refresh\" || session?.enforceStrictRevocation)) {\r\n payload.pw_frag = user.passwordHash.slice(-10);\r\n }\r\n\r\n if (options.jwt?.payload) {\r\n const customPayload = options.jwt.payload(user, type);\r\n payload = { ...payload, ...customPayload };\r\n }\r\n\r\n return new SignJWT(payload)\r\n .setProtectedHeader({ alg: \"HS256\" })\r\n .setIssuedAt()\r\n .setExpirationTime(type === \"access\" ? expiration : refreshExpiration)\r\n .sign(encodedSecret);\r\n }\r\n\r\n /**\r\n * Verifies a JWT and returns the payload.\r\n * Optionally checks for a specific token type (access/refresh).\r\n */\r\n async function verifyToken(token: string, expectedType: \"access\" | \"refresh\" = \"access\") {\r\n try {\r\n const { payload } = await jwtVerify(token, encodedSecret);\r\n if (payload.type !== expectedType) return null;\r\n\r\n if (expectedType === \"access\" && session?.enforceStrictRevocation && payload.pw_frag) {\r\n const user = await adapter.findUserById(payload.sub as string);\r\n if (!user || payload.pw_frag !== user.passwordHash?.slice(-10)) {\r\n throw new Error(\"Strict Revocation: Access Token revoked due to password change\");\r\n }\r\n }\r\n\r\n return payload;\r\n } catch (e) {\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Refreshes an access token using a valid refresh token.\r\n */\r\n async function refresh(refreshToken: string) {\r\n const payload = await verifyToken(refreshToken, \"refresh\");\r\n if (!payload || !payload.sub) {\r\n throw new Error(\"Invalid or expired refresh token\");\r\n }\r\n\r\n const user = await adapter.findUserById(payload.sub as string);\r\n if (!user) {\r\n throw new Error(\"User not found\");\r\n }\r\n\r\n // Lightweight Session Revocation: Validate password hasn't changed since token issuance\r\n if (payload.pw_frag && user.passwordHash) {\r\n if (payload.pw_frag !== user.passwordHash.slice(-10)) {\r\n throw new Error(\"Session revoked (Password changed)\");\r\n }\r\n }\r\n\r\n const accessToken = await generateToken(user, \"access\");\r\n return { accessToken };\r\n }\r\n\r\n function validatePassword(password?: string) {\r\n if (!password || !options.passwordPolicy) return;\r\n \r\n const p = options.passwordPolicy;\r\n if (p.minLength && password.length < p.minLength) {\r\n throw new Error(`Password must be at least ${p.minLength} characters`);\r\n }\r\n if (p.requireUppercase && !/[A-Z]/.test(password)) {\r\n throw new Error(\"Password must contain at least one uppercase letter\");\r\n }\r\n if (p.requireLowercase && !/[a-z]/.test(password)) {\r\n throw new Error(\"Password must contain at least one lowercase letter\");\r\n }\r\n if (p.requireNumber && !/[0-9]/.test(password)) {\r\n throw new Error(\"Password must contain at least one number\");\r\n }\r\n if (p.requireSpecialCharacter && !/[^A-Za-z0-9]/.test(password)) {\r\n throw new Error(\"Password must contain at least one special character\");\r\n }\r\n }\r\n\r\n /**\r\n * Signup with a new user payload.\r\n * Incorporates server-side pepper for password hashing if provided.\r\n */\r\n async function signup(userData: Omit<User<any>, \"id\">, password?: string) {\r\n let dataToSave = { ...userData };\r\n\r\n if (password) {\r\n validatePassword(password);\r\n const passwordWithPepper = pepper ? `${password}${pepper}` : password;\r\n dataToSave.passwordHash = await argon2.hash(passwordWithPepper);\r\n }\r\n\r\n const newUser = await adapter.createUser(dataToSave);\r\n const accessToken = await generateToken(newUser, \"access\");\r\n const refreshToken = await generateToken(newUser, \"refresh\");\r\n\r\n return { user: newUser, accessToken, refreshToken };\r\n }\r\n\r\n /**\r\n * Standard Email/Password Login.\r\n * Includes timing attack protection and password peppering.\r\n */\r\n async function loginWithPassword(email: string, password: string, clientIp?: string) {\r\n if (ipBlocking && clientIp && configuredRateLimiter) {\r\n const strikeCheck = await configuredRateLimiter.check(`strike_${clientIp}`);\r\n if (strikeCheck && strikeCheck.count >= ipBlocking.maxStrikes) {\r\n throw new Error(\"IP is temporarily blocked.\");\r\n }\r\n }\r\n\r\n if (configuredRateLimiter) {\r\n const limitStatus = await configuredRateLimiter.increment(`login_${email}`);\r\n if (!limitStatus.success) {\r\n if (ipBlocking && clientIp) {\r\n await configuredRateLimiter.increment(`strike_${clientIp}`, ipBlocking.blockDurationMs);\r\n }\r\n throw new Error(\"Too many requests, please try again later.\");\r\n }\r\n }\r\n\r\n const user = await adapter.findUserByEmail(email);\r\n\r\n // Timing attack protection: Always verify a hash, even if user doesn't exist.\r\n // We use a dummy hash to keep execution time consistent.\r\n const dummyHash = \"$argon2id$v=19$m=65536,t=3,p=4$c29tZXNhbHQ$RytpInY7i6C9M5l0D4n8Q+7j/J+i\";\r\n const targetHash = user?.passwordHash || dummyHash;\r\n const passwordWithPepper = pepper ? `${password}${pepper}` : password;\r\n\r\n const isValid = await argon2.verify(targetHash, passwordWithPepper);\r\n\r\n if (!user || !user.passwordHash || !isValid) {\r\n throw new Error(\"Invalid credentials\");\r\n }\r\n\r\n const accessToken = await generateToken(user, \"access\");\r\n const refreshToken = await generateToken(user, \"refresh\");\r\n\r\n return { user, accessToken, refreshToken };\r\n }\r\n\r\n /**\r\n * Changes a user's password securely using the configured pepper and hashing algorithm.\r\n * Instantly revokes all active refresh tokens for the user globally.\r\n */\r\n async function changePassword(userId: string, newPassword: string) {\r\n if (!adapter.updateUser) {\r\n throw new Error(\"The AuthAdapter does not support updating user records natively.\");\r\n }\r\n\r\n validatePassword(newPassword);\r\n const passwordWithPepper = pepper ? `${newPassword}${pepper}` : newPassword;\r\n const newHash = await argon2.hash(passwordWithPepper);\r\n\r\n const updatedUser = await adapter.updateUser(userId, { passwordHash: newHash } as any);\r\n if (!updatedUser) {\r\n throw new Error(\"User not found\");\r\n }\r\n \r\n return updatedUser;\r\n }\r\n\r\n return {\r\n signup,\r\n loginWithPassword,\r\n changePassword,\r\n refresh,\r\n verifyToken,\r\n generateToken,\r\n _providers: providers\r\n };\r\n}\r\n\r\n/**\r\n * Utility to generate a high-entropy cryptographically secure secret.\r\n * Useful for initializing the 'secret' option in createAuth.\r\n */\r\nexport function generateSecret(length: number = 32): Uint8Array {\r\n return crypto.getRandomValues(new Uint8Array(length));\r\n}\r\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as argon2 from \"argon2\";\r\nimport { SignJWT, jwtVerify } from \"jose\";\r\nimport crypto from \"crypto\";\r\nimport type { AuthAdapter, User } from \"../adapters/index.js\";\r\nimport type { Provider } from \"../providers/index.js\";\r\nimport { createRateLimiter, type RateLimitOptions } from \"../security/rate-limit.js\";\r\n\r\nexport interface CreateAuthOptions {\r\n adapter: AuthAdapter<any>;\r\n secret: string | Uint8Array;\r\n pepper?: string;\r\n session?: {\r\n expires?: string | number; // For access tokens\r\n refreshExpires?: string | number; // For refresh tokens\r\n enforceStrictRevocation?: boolean; // If true, DB check on access tokens\r\n };\r\n providers?: Provider[];\r\n jwt?: {\r\n /**\r\n * A callback to add custom fields to the JWT payload.\r\n * It receives the user object and the token type ('access' or 'refresh').\r\n * Return an object containing the fields to be merged into the payload.\r\n * You can also override default fields like 'sub'.\r\n */\r\n payload?: (user: User<any>, type: \"access\" | \"refresh\") => Record<string, any>;\r\n };\r\n rateLimit?: RateLimitOptions;\r\n ipBlocking?: { maxStrikes: number; blockDurationMs: number };\r\n passwordPolicy?: {\r\n minLength?: number;\r\n requireUppercase?: boolean;\r\n requireLowercase?: boolean;\r\n requireNumber?: boolean;\r\n requireSpecialCharacter?: boolean;\r\n };\r\n}\r\n\r\nexport function createAuth(options: CreateAuthOptions) {\r\n const { adapter, secret, pepper, session, providers, rateLimit, ipBlocking } = options;\r\n const encodedSecret = typeof secret === \"string\" ? new TextEncoder().encode(secret) : secret;\r\n const expiration = session?.expires || \"1h\"; // Default access token to 1h\r\n const refreshExpiration = session?.refreshExpires || \"7d\";\r\n\r\n const configuredRateLimiter = createRateLimiter(adapter, rateLimit);\r\n\r\n /**\r\n * Generates a stateless JWT for a user session\r\n */\r\n async function generateToken(user: User<any>, type: \"access\" | \"refresh\" = \"access\") {\r\n let payload: Record<string, any> = { \r\n sub: user.id, \r\n role: user.role, \r\n sv: user.sessionVersion || 0, // Include Session Version\r\n type \r\n };\r\n\r\n // Lightweight Session Revocation: Link token to current password hash state\r\n if (user.passwordHash && (type === \"refresh\" || session?.enforceStrictRevocation)) {\r\n payload.pw_frag = user.passwordHash.slice(-10);\r\n }\r\n\r\n if (options.jwt?.payload) {\r\n const customPayload = options.jwt.payload(user, type);\r\n payload = { ...payload, ...customPayload };\r\n }\r\n\r\n return new SignJWT(payload)\r\n .setProtectedHeader({ alg: \"HS256\" })\r\n .setIssuedAt()\r\n .setExpirationTime(type === \"access\" ? expiration : refreshExpiration)\r\n .sign(encodedSecret);\r\n }\r\n\r\n /**\r\n * Verifies a JWT and returns the payload.\r\n * Optionally checks for a specific token type (access/refresh).\r\n */\r\n async function verifyToken(token: string, expectedType: \"access\" | \"refresh\" = \"access\") {\r\n try {\r\n const { payload } = await jwtVerify(token, encodedSecret);\r\n if (payload.type !== expectedType) return null;\r\n\r\n if (expectedType === \"access\" && session?.enforceStrictRevocation) {\r\n const user = await adapter.findUserById(payload.sub as string);\r\n if (!user) return null;\r\n\r\n // Password change check\r\n if (payload.pw_frag && payload.pw_frag !== user.passwordHash?.slice(-10)) {\r\n throw new Error(\"Strict Revocation: Access Token revoked due to password change\");\r\n }\r\n\r\n // Global Logout (Session Version) check\r\n if (payload.sv !== (user.sessionVersion || 0)) {\r\n throw new Error(\"Strict Revocation: Session revoked (Logged out)\");\r\n }\r\n }\r\n\r\n return payload;\r\n } catch (e) {\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Refreshes an access token using a valid refresh token.\r\n */\r\n async function refresh(refreshToken: string) {\r\n const payload = await verifyToken(refreshToken, \"refresh\");\r\n if (!payload || !payload.sub) {\r\n throw new Error(\"Invalid or expired refresh token\");\r\n }\r\n\r\n const user = await adapter.findUserById(payload.sub as string);\r\n if (!user) {\r\n throw new Error(\"User not found\");\r\n }\r\n\r\n // Lightweight Session Revocation (Password change & Session Version)\r\n if (user.passwordHash) {\r\n if (payload.pw_frag && payload.pw_frag !== user.passwordHash.slice(-10)) {\r\n throw new Error(\"Session revoked (Password changed)\");\r\n }\r\n if (payload.sv !== (user.sessionVersion || 0)) {\r\n throw new Error(\"Session revoked (Logged out)\");\r\n }\r\n }\r\n\r\n const accessToken = await generateToken(user, \"access\");\r\n return { accessToken };\r\n }\r\n\r\n function validatePassword(password?: string) {\r\n if (!password || !options.passwordPolicy) return;\r\n \r\n const p = options.passwordPolicy;\r\n if (p.minLength && password.length < p.minLength) {\r\n throw new Error(`Password must be at least ${p.minLength} characters`);\r\n }\r\n if (p.requireUppercase && !/[A-Z]/.test(password)) {\r\n throw new Error(\"Password must contain at least one uppercase letter\");\r\n }\r\n if (p.requireLowercase && !/[a-z]/.test(password)) {\r\n throw new Error(\"Password must contain at least one lowercase letter\");\r\n }\r\n if (p.requireNumber && !/[0-9]/.test(password)) {\r\n throw new Error(\"Password must contain at least one number\");\r\n }\r\n if (p.requireSpecialCharacter && !/[^A-Za-z0-9]/.test(password)) {\r\n throw new Error(\"Password must contain at least one special character\");\r\n }\r\n }\r\n\r\n /**\r\n * Signup with a new user payload.\r\n * Incorporates server-side pepper for password hashing if provided.\r\n */\r\n async function signup(userData: Omit<User<any>, \"id\">, password?: string) {\r\n let dataToSave = { ...userData };\r\n\r\n if (password) {\r\n validatePassword(password);\r\n const passwordWithPepper = pepper ? `${password}${pepper}` : password;\r\n dataToSave.passwordHash = await argon2.hash(passwordWithPepper);\r\n }\r\n\r\n const newUser = await adapter.createUser(dataToSave);\r\n const accessToken = await generateToken(newUser, \"access\");\r\n const refreshToken = await generateToken(newUser, \"refresh\");\r\n\r\n return { user: newUser, accessToken, refreshToken };\r\n }\r\n\r\n /**\r\n * Standard Email/Password Login.\r\n * Includes timing attack protection and password peppering.\r\n */\r\n async function loginWithPassword(email: string, password: string, clientIp?: string) {\r\n if (ipBlocking && clientIp && configuredRateLimiter) {\r\n const strikeCheck = await configuredRateLimiter.check(`strike_${clientIp}`);\r\n if (strikeCheck && strikeCheck.count >= ipBlocking.maxStrikes) {\r\n throw new Error(\"IP is temporarily blocked.\");\r\n }\r\n }\r\n\r\n if (configuredRateLimiter) {\r\n const limitStatus = await configuredRateLimiter.increment(`login_${email}`);\r\n if (!limitStatus.success) {\r\n if (ipBlocking && clientIp) {\r\n await configuredRateLimiter.increment(`strike_${clientIp}`, ipBlocking.blockDurationMs);\r\n }\r\n throw new Error(\"Too many requests, please try again later.\");\r\n }\r\n }\r\n\r\n const user = await adapter.findUserByEmail(email);\r\n\r\n // Timing attack protection: Always verify a hash, even if user doesn't exist.\r\n // We use a dummy hash to keep execution time consistent.\r\n const dummyHash = \"$argon2id$v=19$m=65536,t=3,p=4$c29tZXNhbHQ$RytpInY7i6C9M5l0D4n8Q+7j/J+i\";\r\n const targetHash = user?.passwordHash || dummyHash;\r\n const passwordWithPepper = pepper ? `${password}${pepper}` : password;\r\n\r\n const isValid = await argon2.verify(targetHash, passwordWithPepper);\r\n\r\n if (!user || !user.passwordHash || !isValid) {\r\n throw new Error(\"Invalid credentials\");\r\n }\r\n\r\n const accessToken = await generateToken(user, \"access\");\r\n const refreshToken = await generateToken(user, \"refresh\");\r\n\r\n return { user, accessToken, refreshToken };\r\n }\r\n\r\n /**\r\n * Changes a user's password securely using the configured pepper and hashing algorithm.\r\n * Instantly revokes all active refresh tokens for the user globally.\r\n */\r\n async function changePassword(userId: string, newPassword: string) {\r\n if (!adapter.updateUser) {\r\n throw new Error(\"The AuthAdapter does not support updating user records natively.\");\r\n }\r\n\r\n validatePassword(newPassword);\r\n const passwordWithPepper = pepper ? `${newPassword}${pepper}` : newPassword;\r\n const newHash = await argon2.hash(passwordWithPepper);\r\n\r\n const updatedUser = await adapter.updateUser(userId, { passwordHash: newHash } as any);\r\n if (!updatedUser) {\r\n throw new Error(\"User not found\");\r\n }\r\n \r\n return updatedUser;\r\n }\r\n\r\n /**\r\n * Terminate all active sessions for a user globally.\r\n * This increments the session version in the database, invalidating all current tokens.\r\n */\r\n async function logout(userId: string) {\r\n if (!adapter.invalidateSession) {\r\n // If the adapter doesn't natively support it, we fall back to manual updateUser\r\n if (adapter.updateUser) {\r\n const user = await adapter.findUserById(userId);\r\n const currentVersion = user?.sessionVersion || 0;\r\n await adapter.updateUser(userId, { sessionVersion: currentVersion + 1 } as any);\r\n return { success: true };\r\n }\r\n throw new Error(\"Logout failed: The AuthAdapter does not support session invalidation.\");\r\n }\r\n await adapter.invalidateSession(userId);\r\n return { success: true };\r\n }\r\n\r\n return {\r\n signup,\r\n loginWithPassword,\r\n changePassword,\r\n logout,\r\n refresh,\r\n verifyToken,\r\n generateToken,\r\n _providers: providers\r\n };\r\n}\r\n\r\n/**\r\n * Utility to generate a high-entropy cryptographically secure secret.\r\n * Useful for initializing the 'secret' option in createAuth.\r\n */\r\nexport function generateSecret(length: number = 32): Uint8Array {\r\n return crypto.getRandomValues(new Uint8Array(length));\r\n}\r\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -30,7 +30,6 @@ var import_path = __toESM(require("path"), 1);
30
30
  var import_crypto = __toESM(require("crypto"), 1);
31
31
  var import_child_process = require("child_process");
32
32
  var import_templates = require("./templates.js");
33
- const { prompt } = import_enquirer.default;
34
33
  const program = new import_commander.Command();
35
34
  program.name("kroxt").description("Kroxt CLI for bootstrapping auth engines").version("1.3.1");
36
35
  program.command("init").description("Initialize Kroxt in your project").option("-y, --yes", "Skip prompts and use defaults").action(async (options) => {
@@ -44,12 +43,20 @@ program.command("init").description("Initialize Kroxt in your project").option("
44
43
  useStrictRevocation: true,
45
44
  usePepper: true,
46
45
  createModel: true,
47
- targetDir: ""
46
+ targetDir: "",
47
+ modelDir: ""
48
48
  };
49
49
  const projectContext = getProjectContext();
50
- const defaultDir = projectContext.isExpress ? projectContext.hasSrc ? "src/config" : "config" : projectContext.hasSrc ? "src/lib/kroxt" : "lib/kroxt";
50
+ let defaultDir = "lib/kroxt";
51
+ if (projectContext.isNext) {
52
+ defaultDir = "lib/kroxt";
53
+ } else if (projectContext.isExpress || projectContext.isHono) {
54
+ defaultDir = projectContext.hasSrc ? "src/config" : "config";
55
+ } else if (projectContext.isVite) {
56
+ defaultDir = projectContext.hasSrc ? "src/plugins/kroxt" : "plugins/kroxt";
57
+ }
51
58
  if (!options.yes) {
52
- response = await prompt([
59
+ const answers1 = await import_enquirer.default.prompt([
53
60
  {
54
61
  type: "select",
55
62
  name: "adapter",
@@ -67,7 +74,9 @@ program.command("init").description("Initialize Kroxt in your project").option("
67
74
  name: "targetDir",
68
75
  message: "Where should I put the Kroxt files?",
69
76
  initial: defaultDir
70
- },
77
+ }
78
+ ]);
79
+ const answers2 = await import_enquirer.default.prompt([
71
80
  {
72
81
  type: "confirm",
73
82
  name: "useRateLimit",
@@ -97,14 +106,25 @@ program.command("init").description("Initialize Kroxt in your project").option("
97
106
  name: "createModel",
98
107
  message: "Create a boilerplate User model for you?",
99
108
  initial: true
100
- },
101
- {
102
- type: "confirm",
103
- name: "generateEnv",
104
- message: "Generate secure secrets in .env?",
105
- initial: true
106
109
  }
107
110
  ]);
111
+ response = { ...response, ...answers1, ...answers2 };
112
+ if (response.createModel && response.adapter !== "memory" && response.adapter !== "none") {
113
+ const { modelDir } = await import_enquirer.default.prompt({
114
+ type: "input",
115
+ name: "modelDir",
116
+ message: "Where should I put the User model?",
117
+ initial: projectContext.isNext ? "models/" : projectContext.hasSrc ? "src/models/" : "models/"
118
+ });
119
+ response.modelDir = modelDir;
120
+ }
121
+ const { generateEnv } = await import_enquirer.default.prompt({
122
+ type: "confirm",
123
+ name: "generateEnv",
124
+ message: "Generate secure secrets in .env?",
125
+ initial: true
126
+ });
127
+ response.generateEnv = generateEnv;
108
128
  } else {
109
129
  console.log(import_chalk.default.gray("Using default settings (--yes)..."));
110
130
  response = {
@@ -134,15 +154,16 @@ program.command("init").description("Initialize Kroxt in your project").option("
134
154
  }
135
155
  if (response.createModel && response.adapter !== "memory" && response.adapter !== "none") {
136
156
  let modelPath = "";
157
+ const targetModelDir = response.modelDir || response.targetDir;
137
158
  switch (response.adapter) {
138
159
  case "mongoose":
139
- modelPath = import_path.default.join(process.cwd(), response.targetDir, "user.model.ts");
160
+ modelPath = import_path.default.join(process.cwd(), targetModelDir, "user.model.ts");
140
161
  break;
141
162
  case "drizzle":
142
- modelPath = import_path.default.join(process.cwd(), response.targetDir, "schema.ts");
163
+ modelPath = import_path.default.join(process.cwd(), targetModelDir, "schema.ts");
143
164
  break;
144
165
  case "prisma":
145
- modelPath = import_path.default.join(process.cwd(), response.targetDir, "user.prisma");
166
+ modelPath = import_path.default.join(process.cwd(), targetModelDir, "user.prisma");
146
167
  break;
147
168
  }
148
169
  if (modelPath) {
@@ -183,6 +204,7 @@ async function checkAndInstallDeps(adapter, skipPrompts = false) {
183
204
  mongoose: ["kroxt", "mongoose", "dotenv"],
184
205
  prisma: ["kroxt", "@prisma/client", "dotenv"],
185
206
  drizzle: ["kroxt", "drizzle-orm", "dotenv"],
207
+ hono: ["kroxt", "hono", "dotenv"],
186
208
  none: ["kroxt"]
187
209
  };
188
210
  const packages = required[adapter] || [];
@@ -190,7 +212,7 @@ async function checkAndInstallDeps(adapter, skipPrompts = false) {
190
212
  if (missing.length > 0) {
191
213
  let confirm = true;
192
214
  if (!skipPrompts) {
193
- const result = await prompt({
215
+ const result = await import_enquirer.default.prompt({
194
216
  type: "confirm",
195
217
  name: "confirm",
196
218
  message: `Required dependencies are missing: ${import_chalk.default.cyan(missing.join(", "))}. Install them now?`,
@@ -212,17 +234,26 @@ Installing ${missing.join(", ")}...`));
212
234
  }
213
235
  }
214
236
  function getProjectContext() {
215
- const hasSrc = import_fs.default.existsSync(import_path.default.join(process.cwd(), "src"));
216
- const pkgPath = import_path.default.join(process.cwd(), "package.json");
237
+ const root = process.cwd();
238
+ const pkgPath = import_path.default.join(root, "package.json");
239
+ const hasSrc = import_fs.default.existsSync(import_path.default.join(root, "src"));
240
+ const hasAppRouter = import_fs.default.existsSync(import_path.default.join(root, "app"));
241
+ const hasSrcAppRouter = import_fs.default.existsSync(import_path.default.join(root, "src", "app"));
242
+ const hasPagesRouter = import_fs.default.existsSync(import_path.default.join(root, "pages"));
243
+ const hasSrcPagesRouter = import_fs.default.existsSync(import_path.default.join(root, "src", "pages"));
217
244
  let isExpress = false;
218
245
  let isNext = false;
246
+ let isHono = false;
247
+ let isVite = false;
219
248
  if (import_fs.default.existsSync(pkgPath)) {
220
249
  const pkg = JSON.parse(import_fs.default.readFileSync(pkgPath, "utf8"));
221
250
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
222
251
  isExpress = !!(deps.express || deps.fastify);
223
252
  isNext = !!deps.next;
253
+ isHono = !!deps.hono;
254
+ isVite = !!deps.vite;
224
255
  }
225
- return { hasSrc, isExpress, isNext };
256
+ return { hasSrc, hasAppRouter, hasSrcAppRouter, hasPagesRouter, hasSrcPagesRouter, isExpress, isNext, isHono, isVite };
226
257
  }
227
258
  program.parse(process.argv);
228
259
  //# sourceMappingURL=index.cjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/cli/index.ts"],
4
- "sourcesContent": ["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport enquirer from 'enquirer';\nconst { prompt } = enquirer;\nimport chalk from 'chalk';\nimport fs from 'fs';\nimport path from 'path';\nimport crypto from 'crypto';\nimport { spawn } from 'child_process';\nimport { authTemplate, envTemplate, tsConfigTemplate } from './templates.js';\n\nconst program = new Command();\n\nprogram\n .name('kroxt')\n .description('Kroxt CLI for bootstrapping auth engines')\n .version('1.3.1');\n\nprogram\n .command('init')\n .description('Initialize Kroxt in your project')\n .option('-y, --yes', 'Skip prompts and use defaults')\n .action(async (options) => {\n console.log(chalk.bold.white('\\n\u2B22 KROXT AUTH INITIALIZER\\n'));\n\n try {\n let response = {\n adapter: 'memory',\n generateEnv: true,\n useRateLimit: true,\n useIPBlocking: true,\n useStrictRevocation: true,\n usePepper: true,\n createModel: true,\n targetDir: ''\n };\n\n const projectContext = getProjectContext();\n const defaultDir = projectContext.isExpress ? (projectContext.hasSrc ? 'src/config' : 'config') : (projectContext.hasSrc ? 'src/lib/kroxt' : 'lib/kroxt');\n\n if (!options.yes) {\n response = await prompt([\n {\n type: 'select',\n name: 'adapter',\n message: 'Choose your database adapter:',\n choices: [\n { name: 'memory', message: 'In-Memory (Testing)' },\n { name: 'mongoose', message: 'Mongoose (MongoDB)' },\n { name: 'prisma', message: 'Prisma (PostgreSQL/MySQL)' },\n { name: 'drizzle', message: 'Drizzle (SQLite/PostgreSQL)' },\n { name: 'none', message: 'None (Manual Setup)' }\n ]\n },\n {\n type: 'input',\n name: 'targetDir',\n message: 'Where should I put the Kroxt files?',\n initial: defaultDir\n },\n {\n type: 'confirm',\n name: 'useRateLimit',\n message: 'Enable rate limiting defensive layer?',\n initial: true\n },\n {\n type: 'confirm',\n name: 'useIPBlocking',\n message: 'Enable automatic IP blocking?',\n initial: true\n },\n {\n type: 'confirm',\n name: 'useStrictRevocation',\n message: 'Enforce strict session revocation?',\n initial: true\n },\n {\n type: 'confirm',\n name: 'usePepper',\n message: 'Use server-side password peppering?',\n initial: true\n },\n {\n type: 'confirm',\n name: 'createModel',\n message: 'Create a boilerplate User model for you?',\n initial: true\n },\n {\n type: 'confirm',\n name: 'generateEnv',\n message: 'Generate secure secrets in .env?',\n initial: true\n }\n ]) as any;\n } else {\n console.log(chalk.gray('Using default settings (--yes)...'));\n response = {\n ...response,\n useRateLimit: true,\n useIPBlocking: true,\n useStrictRevocation: true,\n usePepper: true,\n createModel: true,\n targetDir: defaultDir\n } as any;\n }\n\n const secret = crypto.randomBytes(32).toString('hex');\n const authContent = authTemplate(response.adapter, secret, response);\n\n // Write auth.ts\n const authPath = path.join(process.cwd(), response.targetDir, 'auth.ts');\n const dirPath = path.dirname(authPath);\n\n if (!fs.existsSync(dirPath)) {\n fs.mkdirSync(dirPath, { recursive: true });\n }\n\n fs.writeFileSync(authPath, authContent);\n console.log(chalk.green(`\\n\u2714 Created: ${chalk.white(path.relative(process.cwd(), authPath))}`));\n\n // Write tsconfig.json if not present\n const tsConfigPath = path.join(process.cwd(), 'tsconfig.json');\n if (!fs.existsSync(tsConfigPath)) {\n fs.writeFileSync(tsConfigPath, tsConfigTemplate);\n console.log(chalk.green(`\u2714 Created: ${chalk.white('tsconfig.json')}`));\n }\n\n // Write User Model if requested\n if (response.createModel && response.adapter !== 'memory' && response.adapter !== 'none') {\n let modelPath = '';\n switch (response.adapter) {\n case 'mongoose':\n modelPath = path.join(process.cwd(), response.targetDir, 'user.model.ts');\n break;\n case 'drizzle':\n modelPath = path.join(process.cwd(), response.targetDir, 'schema.ts');\n break;\n case 'prisma':\n modelPath = path.join(process.cwd(), response.targetDir, 'user.prisma');\n break;\n }\n\n if (modelPath) {\n const modelDirPath = path.dirname(modelPath);\n if (!fs.existsSync(modelDirPath)) {\n fs.mkdirSync(modelDirPath, { recursive: true });\n }\n \n const { userModelTemplate } = await import('./templates.js');\n fs.writeFileSync(modelPath, userModelTemplate(response.adapter));\n console.log(chalk.green(`\u2714 Created: ${chalk.white(path.relative(process.cwd(), modelPath))}`));\n }\n }\n\n // Write .env\n if (response.generateEnv) {\n const envPath = path.join(process.cwd(), '.env');\n const envContent = envTemplate(secret, response.usePepper);\n \n if (fs.existsSync(envPath)) {\n fs.appendFileSync(envPath, envContent);\n console.log(chalk.green(`\u2714 Updated: ${chalk.white('.env')} (Appended JWT_SECRET)`));\n } else {\n fs.writeFileSync(envPath, envContent);\n console.log(chalk.green(`\u2714 Created: ${chalk.white('.env')}`));\n }\n }\n\n console.log(chalk.bold.white('\\nKroxt is ready. Happy coding! \uD83D\uDE80\\n'));\n\n // --- DEPENDENCY AUTO-INSTALLATION ---\n await checkAndInstallDeps(response.adapter, !!options.yes);\n\n } catch (err) {\n console.error(chalk.red('\\n\u2716 Initialization cancelled.'));\n process.exit(1);\n }\n });\n\nasync function checkAndInstallDeps(adapter: string, skipPrompts: boolean = false) {\n const pkgPath = path.join(process.cwd(), 'package.json');\n if (!fs.existsSync(pkgPath)) return;\n\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n\n const required: Record<string, string[]> = {\n memory: ['kroxt', 'dotenv'],\n mongoose: ['kroxt', 'mongoose', 'dotenv'],\n prisma: ['kroxt', '@prisma/client', 'dotenv'],\n drizzle: ['kroxt', 'drizzle-orm', 'dotenv'],\n none: ['kroxt']\n };\n\n const packages = required[adapter] || [];\n const missing = packages.filter(dep => !deps[dep]);\n\n if (missing.length > 0) {\n let confirm = true;\n\n if (!skipPrompts) {\n const result = await prompt({\n type: 'confirm',\n name: 'confirm',\n message: `Required dependencies are missing: ${chalk.cyan(missing.join(', '))}. Install them now?`,\n initial: true\n }) as any;\n confirm = result.confirm;\n }\n\n if (confirm) {\n console.log(chalk.gray(`\\nInstalling ${missing.join(', ')}...`));\n return new Promise((resolve) => {\n const child = spawn('npm', ['install', ...missing], { \n stdio: 'inherit',\n shell: true \n });\n child.on('close', resolve);\n });\n }\n }\n}\n\nfunction getProjectContext() {\n const hasSrc = fs.existsSync(path.join(process.cwd(), 'src'));\n const pkgPath = path.join(process.cwd(), 'package.json');\n let isExpress = false;\n let isNext = false;\n\n if (fs.existsSync(pkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n isExpress = !!(deps.express || deps.fastify);\n isNext = !!deps.next;\n }\n\n return { hasSrc, isExpress, isNext };\n}\n\nprogram.parse(process.argv);\n"],
5
- "mappings": "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",
6
- "names": ["enquirer", "chalk", "crypto", "path", "fs"]
4
+ "sourcesContent": ["#!/usr/bin/env node\r\nimport { Command } from 'commander';\r\nimport enquirer from 'enquirer';\r\nimport chalk from 'chalk';\r\nimport fs from 'fs';\r\nimport path from 'path';\r\nimport crypto from 'crypto';\r\nimport { spawn } from 'child_process';\r\nimport { authTemplate, envTemplate, tsConfigTemplate } from './templates.js';\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name('kroxt')\r\n .description('Kroxt CLI for bootstrapping auth engines')\r\n .version('1.3.1');\r\n\r\nprogram\r\n .command('init')\r\n .description('Initialize Kroxt in your project')\r\n .option('-y, --yes', 'Skip prompts and use defaults')\r\n .action(async (options) => {\r\n console.log(chalk.bold.white('\\n\u2B22 KROXT AUTH INITIALIZER\\n'));\r\n\r\n try {\r\n let response = {\r\n adapter: 'memory',\r\n generateEnv: true,\r\n useRateLimit: true,\r\n useIPBlocking: true,\r\n useStrictRevocation: true,\r\n usePepper: true,\r\n createModel: true,\r\n targetDir: '',\r\n modelDir: ''\r\n };\r\n\r\n const projectContext = getProjectContext();\r\n let defaultDir = 'lib/kroxt';\r\n \r\n if (projectContext.isNext) {\r\n defaultDir = 'lib/kroxt';\r\n } else if (projectContext.isExpress || projectContext.isHono) {\r\n defaultDir = projectContext.hasSrc ? 'src/config' : 'config';\r\n } else if (projectContext.isVite) {\r\n defaultDir = projectContext.hasSrc ? 'src/plugins/kroxt' : 'plugins/kroxt';\r\n }\r\n\r\n if (!options.yes) {\r\n const answers1 = await (enquirer as any).prompt([\r\n {\r\n type: 'select',\r\n name: 'adapter',\r\n message: 'Choose your database adapter:',\r\n choices: [\r\n { name: 'memory', message: 'In-Memory (Testing)' },\r\n { name: 'mongoose', message: 'Mongoose (MongoDB)' },\r\n { name: 'prisma', message: 'Prisma (PostgreSQL/MySQL)' },\r\n { name: 'drizzle', message: 'Drizzle (SQLite/PostgreSQL)' },\r\n { name: 'none', message: 'None (Manual Setup)' }\r\n ]\r\n },\r\n {\r\n type: 'input',\r\n name: 'targetDir',\r\n message: 'Where should I put the Kroxt files?',\r\n initial: defaultDir\r\n }\r\n ]);\r\n\r\n const answers2 = await (enquirer as any).prompt([\r\n {\r\n type: 'confirm',\r\n name: 'useRateLimit',\r\n message: 'Enable rate limiting defensive layer?',\r\n initial: true\r\n },\r\n {\r\n type: 'confirm',\r\n name: 'useIPBlocking',\r\n message: 'Enable automatic IP blocking?',\r\n initial: true\r\n },\r\n {\r\n type: 'confirm',\r\n name: 'useStrictRevocation',\r\n message: 'Enforce strict session revocation?',\r\n initial: true\r\n },\r\n {\r\n type: 'confirm',\r\n name: 'usePepper',\r\n message: 'Use server-side password peppering?',\r\n initial: true\r\n },\r\n {\r\n type: 'confirm',\r\n name: 'createModel',\r\n message: 'Create a boilerplate User model for you?',\r\n initial: true\r\n }\r\n ]);\r\n\r\n response = { ...response, ...answers1, ...answers2 };\r\n\r\n if (response.createModel && response.adapter !== 'memory' && response.adapter !== 'none') {\r\n const { modelDir } = await (enquirer as any).prompt({\r\n type: 'input',\r\n name: 'modelDir',\r\n message: 'Where should I put the User model?',\r\n initial: projectContext.isNext ? 'models/' : (projectContext.hasSrc ? 'src/models/' : 'models/')\r\n });\r\n response.modelDir = modelDir;\r\n }\r\n\r\n const { generateEnv } = await (enquirer as any).prompt({\r\n type: 'confirm',\r\n name: 'generateEnv',\r\n message: 'Generate secure secrets in .env?',\r\n initial: true\r\n });\r\n response.generateEnv = generateEnv;\r\n } else {\r\n console.log(chalk.gray('Using default settings (--yes)...'));\r\n response = {\r\n ...response,\r\n useRateLimit: true,\r\n useIPBlocking: true,\r\n useStrictRevocation: true,\r\n usePepper: true,\r\n createModel: true,\r\n targetDir: defaultDir\r\n } as any;\r\n }\r\n\r\n const secret = crypto.randomBytes(32).toString('hex');\r\n const authContent = authTemplate(response.adapter, secret, response);\r\n\r\n // Write auth.ts\r\n const authPath = path.join(process.cwd(), response.targetDir, 'auth.ts');\r\n const dirPath = path.dirname(authPath);\r\n\r\n if (!fs.existsSync(dirPath)) {\r\n fs.mkdirSync(dirPath, { recursive: true });\r\n }\r\n\r\n fs.writeFileSync(authPath, authContent);\r\n console.log(chalk.green(`\\n\u2714 Created: ${chalk.white(path.relative(process.cwd(), authPath))}`));\r\n\r\n // Write tsconfig.json if not present\r\n const tsConfigPath = path.join(process.cwd(), 'tsconfig.json');\r\n if (!fs.existsSync(tsConfigPath)) {\r\n fs.writeFileSync(tsConfigPath, tsConfigTemplate);\r\n console.log(chalk.green(`\u2714 Created: ${chalk.white('tsconfig.json')}`));\r\n }\r\n\r\n // Write User Model if requested\r\n if (response.createModel && response.adapter !== 'memory' && response.adapter !== 'none') {\r\n let modelPath = '';\r\n const targetModelDir = response.modelDir || response.targetDir;\r\n \r\n switch (response.adapter) {\r\n case 'mongoose':\r\n modelPath = path.join(process.cwd(), targetModelDir, 'user.model.ts');\r\n break;\r\n case 'drizzle':\r\n modelPath = path.join(process.cwd(), targetModelDir, 'schema.ts');\r\n break;\r\n case 'prisma':\r\n modelPath = path.join(process.cwd(), targetModelDir, 'user.prisma');\r\n break;\r\n }\r\n\r\n if (modelPath) {\r\n const modelDirPath = path.dirname(modelPath);\r\n if (!fs.existsSync(modelDirPath)) {\r\n fs.mkdirSync(modelDirPath, { recursive: true });\r\n }\r\n \r\n const { userModelTemplate } = await import('./templates.js');\r\n fs.writeFileSync(modelPath, userModelTemplate(response.adapter));\r\n console.log(chalk.green(`\u2714 Created: ${chalk.white(path.relative(process.cwd(), modelPath))}`));\r\n }\r\n }\r\n\r\n // Write .env\r\n if (response.generateEnv) {\r\n const envPath = path.join(process.cwd(), '.env');\r\n const envContent = envTemplate(secret, response.usePepper);\r\n \r\n if (fs.existsSync(envPath)) {\r\n fs.appendFileSync(envPath, envContent);\r\n console.log(chalk.green(`\u2714 Updated: ${chalk.white('.env')} (Appended JWT_SECRET)`));\r\n } else {\r\n fs.writeFileSync(envPath, envContent);\r\n console.log(chalk.green(`\u2714 Created: ${chalk.white('.env')}`));\r\n }\r\n }\r\n\r\n console.log(chalk.bold.white('\\nKroxt is ready. Happy coding! \uD83D\uDE80\\n'));\r\n\r\n // --- DEPENDENCY AUTO-INSTALLATION ---\r\n await checkAndInstallDeps(response.adapter, !!options.yes);\r\n\r\n } catch (err) {\r\n console.error(chalk.red('\\n\u2716 Initialization cancelled.'));\r\n process.exit(1);\r\n }\r\n });\r\n\r\nasync function checkAndInstallDeps(adapter: string, skipPrompts: boolean = false) {\r\n const pkgPath = path.join(process.cwd(), 'package.json');\r\n if (!fs.existsSync(pkgPath)) return;\r\n\r\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));\r\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\r\n\r\n const required: Record<string, string[]> = {\r\n memory: ['kroxt', 'dotenv'],\r\n mongoose: ['kroxt', 'mongoose', 'dotenv'],\r\n prisma: ['kroxt', '@prisma/client', 'dotenv'],\r\n drizzle: ['kroxt', 'drizzle-orm', 'dotenv'],\r\n hono: ['kroxt', 'hono', 'dotenv'],\r\n none: ['kroxt']\r\n };\r\n\r\n const packages = required[adapter] || [];\r\n const missing = packages.filter(dep => !deps[dep]);\r\n\r\n if (missing.length > 0) {\r\n let confirm = true;\r\n\r\n if (!skipPrompts) {\r\n const result = await (enquirer as any).prompt({\r\n type: 'confirm',\r\n name: 'confirm',\r\n message: `Required dependencies are missing: ${chalk.cyan(missing.join(', '))}. Install them now?`,\r\n initial: true\r\n });\r\n confirm = result.confirm;\r\n }\r\n\r\n if (confirm) {\r\n console.log(chalk.gray(`\\nInstalling ${missing.join(', ')}...`));\r\n return new Promise((resolve) => {\r\n const child = spawn('npm', ['install', ...missing], { \r\n stdio: 'inherit',\r\n shell: true \r\n });\r\n child.on('close', resolve);\r\n });\r\n }\r\n }\r\n}\r\n\r\nfunction getProjectContext() {\r\n const root = process.cwd();\r\n const pkgPath = path.join(root, 'package.json');\r\n const hasSrc = fs.existsSync(path.join(root, 'src'));\r\n const hasAppRouter = fs.existsSync(path.join(root, 'app'));\r\n const hasSrcAppRouter = fs.existsSync(path.join(root, 'src', 'app'));\r\n const hasPagesRouter = fs.existsSync(path.join(root, 'pages'));\r\n const hasSrcPagesRouter = fs.existsSync(path.join(root, 'src', 'pages'));\r\n \r\n let isExpress = false;\r\n let isNext = false;\r\n let isHono = false;\r\n let isVite = false;\r\n\r\n if (fs.existsSync(pkgPath)) {\r\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));\r\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\r\n isExpress = !!(deps.express || deps.fastify);\r\n isNext = !!deps.next;\r\n isHono = !!deps.hono;\r\n isVite = !!deps.vite;\r\n }\r\n\r\n return { hasSrc, hasAppRouter, hasSrcAppRouter, hasPagesRouter, hasSrcPagesRouter, isExpress, isNext, isHono, isVite };\r\n}\r\n\r\nprogram.parse(process.argv);\r\n"],
5
+ "mappings": "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",
6
+ "names": ["chalk", "enquirer", "crypto", "path", "fs"]
7
7
  }
package/dist/cli/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from "commander";
3
3
  import enquirer from "enquirer";
4
- const { prompt } = enquirer;
5
4
  import chalk from "chalk";
6
5
  import fs from "fs";
7
6
  import path from "path";
@@ -21,12 +20,20 @@ program.command("init").description("Initialize Kroxt in your project").option("
21
20
  useStrictRevocation: true,
22
21
  usePepper: true,
23
22
  createModel: true,
24
- targetDir: ""
23
+ targetDir: "",
24
+ modelDir: ""
25
25
  };
26
26
  const projectContext = getProjectContext();
27
- const defaultDir = projectContext.isExpress ? projectContext.hasSrc ? "src/config" : "config" : projectContext.hasSrc ? "src/lib/kroxt" : "lib/kroxt";
27
+ let defaultDir = "lib/kroxt";
28
+ if (projectContext.isNext) {
29
+ defaultDir = "lib/kroxt";
30
+ } else if (projectContext.isExpress || projectContext.isHono) {
31
+ defaultDir = projectContext.hasSrc ? "src/config" : "config";
32
+ } else if (projectContext.isVite) {
33
+ defaultDir = projectContext.hasSrc ? "src/plugins/kroxt" : "plugins/kroxt";
34
+ }
28
35
  if (!options.yes) {
29
- response = await prompt([
36
+ const answers1 = await enquirer.prompt([
30
37
  {
31
38
  type: "select",
32
39
  name: "adapter",
@@ -44,7 +51,9 @@ program.command("init").description("Initialize Kroxt in your project").option("
44
51
  name: "targetDir",
45
52
  message: "Where should I put the Kroxt files?",
46
53
  initial: defaultDir
47
- },
54
+ }
55
+ ]);
56
+ const answers2 = await enquirer.prompt([
48
57
  {
49
58
  type: "confirm",
50
59
  name: "useRateLimit",
@@ -74,14 +83,25 @@ program.command("init").description("Initialize Kroxt in your project").option("
74
83
  name: "createModel",
75
84
  message: "Create a boilerplate User model for you?",
76
85
  initial: true
77
- },
78
- {
79
- type: "confirm",
80
- name: "generateEnv",
81
- message: "Generate secure secrets in .env?",
82
- initial: true
83
86
  }
84
87
  ]);
88
+ response = { ...response, ...answers1, ...answers2 };
89
+ if (response.createModel && response.adapter !== "memory" && response.adapter !== "none") {
90
+ const { modelDir } = await enquirer.prompt({
91
+ type: "input",
92
+ name: "modelDir",
93
+ message: "Where should I put the User model?",
94
+ initial: projectContext.isNext ? "models/" : projectContext.hasSrc ? "src/models/" : "models/"
95
+ });
96
+ response.modelDir = modelDir;
97
+ }
98
+ const { generateEnv } = await enquirer.prompt({
99
+ type: "confirm",
100
+ name: "generateEnv",
101
+ message: "Generate secure secrets in .env?",
102
+ initial: true
103
+ });
104
+ response.generateEnv = generateEnv;
85
105
  } else {
86
106
  console.log(chalk.gray("Using default settings (--yes)..."));
87
107
  response = {
@@ -111,15 +131,16 @@ program.command("init").description("Initialize Kroxt in your project").option("
111
131
  }
112
132
  if (response.createModel && response.adapter !== "memory" && response.adapter !== "none") {
113
133
  let modelPath = "";
134
+ const targetModelDir = response.modelDir || response.targetDir;
114
135
  switch (response.adapter) {
115
136
  case "mongoose":
116
- modelPath = path.join(process.cwd(), response.targetDir, "user.model.ts");
137
+ modelPath = path.join(process.cwd(), targetModelDir, "user.model.ts");
117
138
  break;
118
139
  case "drizzle":
119
- modelPath = path.join(process.cwd(), response.targetDir, "schema.ts");
140
+ modelPath = path.join(process.cwd(), targetModelDir, "schema.ts");
120
141
  break;
121
142
  case "prisma":
122
- modelPath = path.join(process.cwd(), response.targetDir, "user.prisma");
143
+ modelPath = path.join(process.cwd(), targetModelDir, "user.prisma");
123
144
  break;
124
145
  }
125
146
  if (modelPath) {
@@ -160,6 +181,7 @@ async function checkAndInstallDeps(adapter, skipPrompts = false) {
160
181
  mongoose: ["kroxt", "mongoose", "dotenv"],
161
182
  prisma: ["kroxt", "@prisma/client", "dotenv"],
162
183
  drizzle: ["kroxt", "drizzle-orm", "dotenv"],
184
+ hono: ["kroxt", "hono", "dotenv"],
163
185
  none: ["kroxt"]
164
186
  };
165
187
  const packages = required[adapter] || [];
@@ -167,7 +189,7 @@ async function checkAndInstallDeps(adapter, skipPrompts = false) {
167
189
  if (missing.length > 0) {
168
190
  let confirm = true;
169
191
  if (!skipPrompts) {
170
- const result = await prompt({
192
+ const result = await enquirer.prompt({
171
193
  type: "confirm",
172
194
  name: "confirm",
173
195
  message: `Required dependencies are missing: ${chalk.cyan(missing.join(", "))}. Install them now?`,
@@ -189,17 +211,26 @@ Installing ${missing.join(", ")}...`));
189
211
  }
190
212
  }
191
213
  function getProjectContext() {
192
- const hasSrc = fs.existsSync(path.join(process.cwd(), "src"));
193
- const pkgPath = path.join(process.cwd(), "package.json");
214
+ const root = process.cwd();
215
+ const pkgPath = path.join(root, "package.json");
216
+ const hasSrc = fs.existsSync(path.join(root, "src"));
217
+ const hasAppRouter = fs.existsSync(path.join(root, "app"));
218
+ const hasSrcAppRouter = fs.existsSync(path.join(root, "src", "app"));
219
+ const hasPagesRouter = fs.existsSync(path.join(root, "pages"));
220
+ const hasSrcPagesRouter = fs.existsSync(path.join(root, "src", "pages"));
194
221
  let isExpress = false;
195
222
  let isNext = false;
223
+ let isHono = false;
224
+ let isVite = false;
196
225
  if (fs.existsSync(pkgPath)) {
197
226
  const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
198
227
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
199
228
  isExpress = !!(deps.express || deps.fastify);
200
229
  isNext = !!deps.next;
230
+ isHono = !!deps.hono;
231
+ isVite = !!deps.vite;
201
232
  }
202
- return { hasSrc, isExpress, isNext };
233
+ return { hasSrc, hasAppRouter, hasSrcAppRouter, hasPagesRouter, hasSrcPagesRouter, isExpress, isNext, isHono, isVite };
203
234
  }
204
235
  program.parse(process.argv);
205
236
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/cli/index.ts"],
4
- "sourcesContent": ["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport enquirer from 'enquirer';\nconst { prompt } = enquirer;\nimport chalk from 'chalk';\nimport fs from 'fs';\nimport path from 'path';\nimport crypto from 'crypto';\nimport { spawn } from 'child_process';\nimport { authTemplate, envTemplate, tsConfigTemplate } from './templates.js';\n\nconst program = new Command();\n\nprogram\n .name('kroxt')\n .description('Kroxt CLI for bootstrapping auth engines')\n .version('1.3.1');\n\nprogram\n .command('init')\n .description('Initialize Kroxt in your project')\n .option('-y, --yes', 'Skip prompts and use defaults')\n .action(async (options) => {\n console.log(chalk.bold.white('\\n\u2B22 KROXT AUTH INITIALIZER\\n'));\n\n try {\n let response = {\n adapter: 'memory',\n generateEnv: true,\n useRateLimit: true,\n useIPBlocking: true,\n useStrictRevocation: true,\n usePepper: true,\n createModel: true,\n targetDir: ''\n };\n\n const projectContext = getProjectContext();\n const defaultDir = projectContext.isExpress ? (projectContext.hasSrc ? 'src/config' : 'config') : (projectContext.hasSrc ? 'src/lib/kroxt' : 'lib/kroxt');\n\n if (!options.yes) {\n response = await prompt([\n {\n type: 'select',\n name: 'adapter',\n message: 'Choose your database adapter:',\n choices: [\n { name: 'memory', message: 'In-Memory (Testing)' },\n { name: 'mongoose', message: 'Mongoose (MongoDB)' },\n { name: 'prisma', message: 'Prisma (PostgreSQL/MySQL)' },\n { name: 'drizzle', message: 'Drizzle (SQLite/PostgreSQL)' },\n { name: 'none', message: 'None (Manual Setup)' }\n ]\n },\n {\n type: 'input',\n name: 'targetDir',\n message: 'Where should I put the Kroxt files?',\n initial: defaultDir\n },\n {\n type: 'confirm',\n name: 'useRateLimit',\n message: 'Enable rate limiting defensive layer?',\n initial: true\n },\n {\n type: 'confirm',\n name: 'useIPBlocking',\n message: 'Enable automatic IP blocking?',\n initial: true\n },\n {\n type: 'confirm',\n name: 'useStrictRevocation',\n message: 'Enforce strict session revocation?',\n initial: true\n },\n {\n type: 'confirm',\n name: 'usePepper',\n message: 'Use server-side password peppering?',\n initial: true\n },\n {\n type: 'confirm',\n name: 'createModel',\n message: 'Create a boilerplate User model for you?',\n initial: true\n },\n {\n type: 'confirm',\n name: 'generateEnv',\n message: 'Generate secure secrets in .env?',\n initial: true\n }\n ]) as any;\n } else {\n console.log(chalk.gray('Using default settings (--yes)...'));\n response = {\n ...response,\n useRateLimit: true,\n useIPBlocking: true,\n useStrictRevocation: true,\n usePepper: true,\n createModel: true,\n targetDir: defaultDir\n } as any;\n }\n\n const secret = crypto.randomBytes(32).toString('hex');\n const authContent = authTemplate(response.adapter, secret, response);\n\n // Write auth.ts\n const authPath = path.join(process.cwd(), response.targetDir, 'auth.ts');\n const dirPath = path.dirname(authPath);\n\n if (!fs.existsSync(dirPath)) {\n fs.mkdirSync(dirPath, { recursive: true });\n }\n\n fs.writeFileSync(authPath, authContent);\n console.log(chalk.green(`\\n\u2714 Created: ${chalk.white(path.relative(process.cwd(), authPath))}`));\n\n // Write tsconfig.json if not present\n const tsConfigPath = path.join(process.cwd(), 'tsconfig.json');\n if (!fs.existsSync(tsConfigPath)) {\n fs.writeFileSync(tsConfigPath, tsConfigTemplate);\n console.log(chalk.green(`\u2714 Created: ${chalk.white('tsconfig.json')}`));\n }\n\n // Write User Model if requested\n if (response.createModel && response.adapter !== 'memory' && response.adapter !== 'none') {\n let modelPath = '';\n switch (response.adapter) {\n case 'mongoose':\n modelPath = path.join(process.cwd(), response.targetDir, 'user.model.ts');\n break;\n case 'drizzle':\n modelPath = path.join(process.cwd(), response.targetDir, 'schema.ts');\n break;\n case 'prisma':\n modelPath = path.join(process.cwd(), response.targetDir, 'user.prisma');\n break;\n }\n\n if (modelPath) {\n const modelDirPath = path.dirname(modelPath);\n if (!fs.existsSync(modelDirPath)) {\n fs.mkdirSync(modelDirPath, { recursive: true });\n }\n \n const { userModelTemplate } = await import('./templates.js');\n fs.writeFileSync(modelPath, userModelTemplate(response.adapter));\n console.log(chalk.green(`\u2714 Created: ${chalk.white(path.relative(process.cwd(), modelPath))}`));\n }\n }\n\n // Write .env\n if (response.generateEnv) {\n const envPath = path.join(process.cwd(), '.env');\n const envContent = envTemplate(secret, response.usePepper);\n \n if (fs.existsSync(envPath)) {\n fs.appendFileSync(envPath, envContent);\n console.log(chalk.green(`\u2714 Updated: ${chalk.white('.env')} (Appended JWT_SECRET)`));\n } else {\n fs.writeFileSync(envPath, envContent);\n console.log(chalk.green(`\u2714 Created: ${chalk.white('.env')}`));\n }\n }\n\n console.log(chalk.bold.white('\\nKroxt is ready. Happy coding! \uD83D\uDE80\\n'));\n\n // --- DEPENDENCY AUTO-INSTALLATION ---\n await checkAndInstallDeps(response.adapter, !!options.yes);\n\n } catch (err) {\n console.error(chalk.red('\\n\u2716 Initialization cancelled.'));\n process.exit(1);\n }\n });\n\nasync function checkAndInstallDeps(adapter: string, skipPrompts: boolean = false) {\n const pkgPath = path.join(process.cwd(), 'package.json');\n if (!fs.existsSync(pkgPath)) return;\n\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n\n const required: Record<string, string[]> = {\n memory: ['kroxt', 'dotenv'],\n mongoose: ['kroxt', 'mongoose', 'dotenv'],\n prisma: ['kroxt', '@prisma/client', 'dotenv'],\n drizzle: ['kroxt', 'drizzle-orm', 'dotenv'],\n none: ['kroxt']\n };\n\n const packages = required[adapter] || [];\n const missing = packages.filter(dep => !deps[dep]);\n\n if (missing.length > 0) {\n let confirm = true;\n\n if (!skipPrompts) {\n const result = await prompt({\n type: 'confirm',\n name: 'confirm',\n message: `Required dependencies are missing: ${chalk.cyan(missing.join(', '))}. Install them now?`,\n initial: true\n }) as any;\n confirm = result.confirm;\n }\n\n if (confirm) {\n console.log(chalk.gray(`\\nInstalling ${missing.join(', ')}...`));\n return new Promise((resolve) => {\n const child = spawn('npm', ['install', ...missing], { \n stdio: 'inherit',\n shell: true \n });\n child.on('close', resolve);\n });\n }\n }\n}\n\nfunction getProjectContext() {\n const hasSrc = fs.existsSync(path.join(process.cwd(), 'src'));\n const pkgPath = path.join(process.cwd(), 'package.json');\n let isExpress = false;\n let isNext = false;\n\n if (fs.existsSync(pkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n isExpress = !!(deps.express || deps.fastify);\n isNext = !!deps.next;\n }\n\n return { hasSrc, isExpress, isNext };\n}\n\nprogram.parse(process.argv);\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["#!/usr/bin/env node\r\nimport { Command } from 'commander';\r\nimport enquirer from 'enquirer';\r\nimport chalk from 'chalk';\r\nimport fs from 'fs';\r\nimport path from 'path';\r\nimport crypto from 'crypto';\r\nimport { spawn } from 'child_process';\r\nimport { authTemplate, envTemplate, tsConfigTemplate } from './templates.js';\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name('kroxt')\r\n .description('Kroxt CLI for bootstrapping auth engines')\r\n .version('1.3.1');\r\n\r\nprogram\r\n .command('init')\r\n .description('Initialize Kroxt in your project')\r\n .option('-y, --yes', 'Skip prompts and use defaults')\r\n .action(async (options) => {\r\n console.log(chalk.bold.white('\\n\u2B22 KROXT AUTH INITIALIZER\\n'));\r\n\r\n try {\r\n let response = {\r\n adapter: 'memory',\r\n generateEnv: true,\r\n useRateLimit: true,\r\n useIPBlocking: true,\r\n useStrictRevocation: true,\r\n usePepper: true,\r\n createModel: true,\r\n targetDir: '',\r\n modelDir: ''\r\n };\r\n\r\n const projectContext = getProjectContext();\r\n let defaultDir = 'lib/kroxt';\r\n \r\n if (projectContext.isNext) {\r\n defaultDir = 'lib/kroxt';\r\n } else if (projectContext.isExpress || projectContext.isHono) {\r\n defaultDir = projectContext.hasSrc ? 'src/config' : 'config';\r\n } else if (projectContext.isVite) {\r\n defaultDir = projectContext.hasSrc ? 'src/plugins/kroxt' : 'plugins/kroxt';\r\n }\r\n\r\n if (!options.yes) {\r\n const answers1 = await (enquirer as any).prompt([\r\n {\r\n type: 'select',\r\n name: 'adapter',\r\n message: 'Choose your database adapter:',\r\n choices: [\r\n { name: 'memory', message: 'In-Memory (Testing)' },\r\n { name: 'mongoose', message: 'Mongoose (MongoDB)' },\r\n { name: 'prisma', message: 'Prisma (PostgreSQL/MySQL)' },\r\n { name: 'drizzle', message: 'Drizzle (SQLite/PostgreSQL)' },\r\n { name: 'none', message: 'None (Manual Setup)' }\r\n ]\r\n },\r\n {\r\n type: 'input',\r\n name: 'targetDir',\r\n message: 'Where should I put the Kroxt files?',\r\n initial: defaultDir\r\n }\r\n ]);\r\n\r\n const answers2 = await (enquirer as any).prompt([\r\n {\r\n type: 'confirm',\r\n name: 'useRateLimit',\r\n message: 'Enable rate limiting defensive layer?',\r\n initial: true\r\n },\r\n {\r\n type: 'confirm',\r\n name: 'useIPBlocking',\r\n message: 'Enable automatic IP blocking?',\r\n initial: true\r\n },\r\n {\r\n type: 'confirm',\r\n name: 'useStrictRevocation',\r\n message: 'Enforce strict session revocation?',\r\n initial: true\r\n },\r\n {\r\n type: 'confirm',\r\n name: 'usePepper',\r\n message: 'Use server-side password peppering?',\r\n initial: true\r\n },\r\n {\r\n type: 'confirm',\r\n name: 'createModel',\r\n message: 'Create a boilerplate User model for you?',\r\n initial: true\r\n }\r\n ]);\r\n\r\n response = { ...response, ...answers1, ...answers2 };\r\n\r\n if (response.createModel && response.adapter !== 'memory' && response.adapter !== 'none') {\r\n const { modelDir } = await (enquirer as any).prompt({\r\n type: 'input',\r\n name: 'modelDir',\r\n message: 'Where should I put the User model?',\r\n initial: projectContext.isNext ? 'models/' : (projectContext.hasSrc ? 'src/models/' : 'models/')\r\n });\r\n response.modelDir = modelDir;\r\n }\r\n\r\n const { generateEnv } = await (enquirer as any).prompt({\r\n type: 'confirm',\r\n name: 'generateEnv',\r\n message: 'Generate secure secrets in .env?',\r\n initial: true\r\n });\r\n response.generateEnv = generateEnv;\r\n } else {\r\n console.log(chalk.gray('Using default settings (--yes)...'));\r\n response = {\r\n ...response,\r\n useRateLimit: true,\r\n useIPBlocking: true,\r\n useStrictRevocation: true,\r\n usePepper: true,\r\n createModel: true,\r\n targetDir: defaultDir\r\n } as any;\r\n }\r\n\r\n const secret = crypto.randomBytes(32).toString('hex');\r\n const authContent = authTemplate(response.adapter, secret, response);\r\n\r\n // Write auth.ts\r\n const authPath = path.join(process.cwd(), response.targetDir, 'auth.ts');\r\n const dirPath = path.dirname(authPath);\r\n\r\n if (!fs.existsSync(dirPath)) {\r\n fs.mkdirSync(dirPath, { recursive: true });\r\n }\r\n\r\n fs.writeFileSync(authPath, authContent);\r\n console.log(chalk.green(`\\n\u2714 Created: ${chalk.white(path.relative(process.cwd(), authPath))}`));\r\n\r\n // Write tsconfig.json if not present\r\n const tsConfigPath = path.join(process.cwd(), 'tsconfig.json');\r\n if (!fs.existsSync(tsConfigPath)) {\r\n fs.writeFileSync(tsConfigPath, tsConfigTemplate);\r\n console.log(chalk.green(`\u2714 Created: ${chalk.white('tsconfig.json')}`));\r\n }\r\n\r\n // Write User Model if requested\r\n if (response.createModel && response.adapter !== 'memory' && response.adapter !== 'none') {\r\n let modelPath = '';\r\n const targetModelDir = response.modelDir || response.targetDir;\r\n \r\n switch (response.adapter) {\r\n case 'mongoose':\r\n modelPath = path.join(process.cwd(), targetModelDir, 'user.model.ts');\r\n break;\r\n case 'drizzle':\r\n modelPath = path.join(process.cwd(), targetModelDir, 'schema.ts');\r\n break;\r\n case 'prisma':\r\n modelPath = path.join(process.cwd(), targetModelDir, 'user.prisma');\r\n break;\r\n }\r\n\r\n if (modelPath) {\r\n const modelDirPath = path.dirname(modelPath);\r\n if (!fs.existsSync(modelDirPath)) {\r\n fs.mkdirSync(modelDirPath, { recursive: true });\r\n }\r\n \r\n const { userModelTemplate } = await import('./templates.js');\r\n fs.writeFileSync(modelPath, userModelTemplate(response.adapter));\r\n console.log(chalk.green(`\u2714 Created: ${chalk.white(path.relative(process.cwd(), modelPath))}`));\r\n }\r\n }\r\n\r\n // Write .env\r\n if (response.generateEnv) {\r\n const envPath = path.join(process.cwd(), '.env');\r\n const envContent = envTemplate(secret, response.usePepper);\r\n \r\n if (fs.existsSync(envPath)) {\r\n fs.appendFileSync(envPath, envContent);\r\n console.log(chalk.green(`\u2714 Updated: ${chalk.white('.env')} (Appended JWT_SECRET)`));\r\n } else {\r\n fs.writeFileSync(envPath, envContent);\r\n console.log(chalk.green(`\u2714 Created: ${chalk.white('.env')}`));\r\n }\r\n }\r\n\r\n console.log(chalk.bold.white('\\nKroxt is ready. Happy coding! \uD83D\uDE80\\n'));\r\n\r\n // --- DEPENDENCY AUTO-INSTALLATION ---\r\n await checkAndInstallDeps(response.adapter, !!options.yes);\r\n\r\n } catch (err) {\r\n console.error(chalk.red('\\n\u2716 Initialization cancelled.'));\r\n process.exit(1);\r\n }\r\n });\r\n\r\nasync function checkAndInstallDeps(adapter: string, skipPrompts: boolean = false) {\r\n const pkgPath = path.join(process.cwd(), 'package.json');\r\n if (!fs.existsSync(pkgPath)) return;\r\n\r\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));\r\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\r\n\r\n const required: Record<string, string[]> = {\r\n memory: ['kroxt', 'dotenv'],\r\n mongoose: ['kroxt', 'mongoose', 'dotenv'],\r\n prisma: ['kroxt', '@prisma/client', 'dotenv'],\r\n drizzle: ['kroxt', 'drizzle-orm', 'dotenv'],\r\n hono: ['kroxt', 'hono', 'dotenv'],\r\n none: ['kroxt']\r\n };\r\n\r\n const packages = required[adapter] || [];\r\n const missing = packages.filter(dep => !deps[dep]);\r\n\r\n if (missing.length > 0) {\r\n let confirm = true;\r\n\r\n if (!skipPrompts) {\r\n const result = await (enquirer as any).prompt({\r\n type: 'confirm',\r\n name: 'confirm',\r\n message: `Required dependencies are missing: ${chalk.cyan(missing.join(', '))}. Install them now?`,\r\n initial: true\r\n });\r\n confirm = result.confirm;\r\n }\r\n\r\n if (confirm) {\r\n console.log(chalk.gray(`\\nInstalling ${missing.join(', ')}...`));\r\n return new Promise((resolve) => {\r\n const child = spawn('npm', ['install', ...missing], { \r\n stdio: 'inherit',\r\n shell: true \r\n });\r\n child.on('close', resolve);\r\n });\r\n }\r\n }\r\n}\r\n\r\nfunction getProjectContext() {\r\n const root = process.cwd();\r\n const pkgPath = path.join(root, 'package.json');\r\n const hasSrc = fs.existsSync(path.join(root, 'src'));\r\n const hasAppRouter = fs.existsSync(path.join(root, 'app'));\r\n const hasSrcAppRouter = fs.existsSync(path.join(root, 'src', 'app'));\r\n const hasPagesRouter = fs.existsSync(path.join(root, 'pages'));\r\n const hasSrcPagesRouter = fs.existsSync(path.join(root, 'src', 'pages'));\r\n \r\n let isExpress = false;\r\n let isNext = false;\r\n let isHono = false;\r\n let isVite = false;\r\n\r\n if (fs.existsSync(pkgPath)) {\r\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));\r\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\r\n isExpress = !!(deps.express || deps.fastify);\r\n isNext = !!deps.next;\r\n isHono = !!deps.hono;\r\n isVite = !!deps.vite;\r\n }\r\n\r\n return { hasSrc, hasAppRouter, hasSrcAppRouter, hasPagesRouter, hasSrcPagesRouter, isExpress, isNext, isHono, isVite };\r\n}\r\n\r\nprogram.parse(process.argv);\r\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -129,6 +129,7 @@ export interface IUser extends Document {
129
129
  email: string;
130
130
  passwordHash?: string;
131
131
  role?: string;
132
+ sessionVersion: number;
132
133
  createdAt: Date;
133
134
  updatedAt: Date;
134
135
  }
@@ -138,6 +139,7 @@ const UserSchema: Schema = new Schema(
138
139
  email: { type: String, required: true, unique: true },
139
140
  passwordHash: { type: String },
140
141
  role: { type: String, default: "user" },
142
+ sessionVersion: { type: Number, default: 0 },
141
143
  },
142
144
  { timestamps: true }
143
145
  );
@@ -145,13 +147,14 @@ const UserSchema: Schema = new Schema(
145
147
  export const User = mongoose.models.User || mongoose.model<IUser>("User", UserSchema);
146
148
  `;
147
149
  case "drizzle":
148
- return `import { pgTable, text, timestamp } from "drizzle-orm/pg-core";
150
+ return `import { pgTable, text, timestamp, integer } from "drizzle-orm/pg-core";
149
151
 
150
152
  export const users = pgTable("users", {
151
153
  id: text("id").primaryKey(),
152
154
  email: text("email").unique().notNull(),
153
155
  passwordHash: text("password_hash"),
154
156
  role: text("role").default("user"),
157
+ sessionVersion: integer("session_version").default(0).notNull(),
155
158
  createdAt: timestamp("created_at").defaultNow().notNull(),
156
159
  updatedAt: timestamp("updated_at").defaultNow().notNull(),
157
160
  });
@@ -164,6 +167,7 @@ model User {
164
167
  email String @unique
165
168
  passwordHash String?
166
169
  role String @default("user")
170
+ sessionVersion Int @default(0)
167
171
  createdAt DateTime @default(now())
168
172
  updatedAt DateTime @updatedAt
169
173
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/cli/templates.ts"],
4
- "sourcesContent": ["import crypto from 'crypto';\n\nexport const authTemplate = (adapter: string, secret: string, options: any) => `import { createAuth } from \"kroxt\";\n${getAdapterImportBlock(adapter)}\nimport dotenv from \"dotenv\";\n\ndotenv.config();\n\n${getAdapterInitialization(adapter, options)}\n\nexport const auth = createAuth({\n adapter: authAdapter,\n secret: process.env.JWT_SECRET || \"${secret}\",\n ${options.usePepper ? 'pepper: process.env.JWT_PEPPER || \"\",' : ''}\n \n // Global Security Configurations\n session: {\n expires: \"15m\",\n refreshExpires: \"7d\",\n enforceStrictRevocation: ${options.useStrictRevocation ? 'true' : 'false'}\n },\n \n // Custom JWT Payload logic\n jwt: {\n payload: (user: any, type: \"access\" | \"refresh\") => {\n if (type === \"access\") {\n return {\n role: user.role,\n // schoolId: user.schoolId \n };\n }\n return {};\n }\n },\n\n ${options.useRateLimit ? `rateLimit: {\n max: 100, // Requests per minute\n windowMs: 60 * 1000\n },` : ''}\n ${options.useIPBlocking ? `ipBlocking: {\n maxStrikes: 5,\n blockDurationMs: 15 * 60 * 1000\n },` : ''}\n passwordPolicy: {\n minLength: 6,\n requireUppercase: true,\n requireSpecialCharacter: true\n }\n});\n`;\n\nfunction getAdapterImportBlock(adapter: string) {\n switch (adapter) {\n case 'mongoose':\n return `import { createMongoAdapter, createRateLimitModel } from \"kroxt/adapters/mongoose\";\\nimport mongoose from \"mongoose\";`;\n case 'prisma':\n return `import { createPrismaAdapter } from \"kroxt/adapters/prisma\";`;\n case 'drizzle':\n return `import { createDrizzleAdapter } from \"kroxt/adapters/drizzle\";\\nimport { eq } from \"drizzle-orm\";`;\n case 'memory':\n return `import { createMemoryAdapter } from \"kroxt/adapters/memory\";`;\n default:\n return '';\n }\n}\n\nfunction getAdapterInitialization(adapter: string, options: any) {\n const modelImported = options.createModel;\n switch (adapter) {\n case 'mongoose':\n return `${modelImported ? 'import { User } from \"./user.model.js\";' : '// import { User } from \"./user.model.js\";'}\\n\\n// The rate limit model is optional but recommended\\nconst authAdapter = createMongoAdapter(User, createRateLimitModel(mongoose));`;\n case 'prisma':\n return `// import { prisma } from \"./lib/prisma\";\\nconst authAdapter = createPrismaAdapter(prisma.user);`;\n case 'drizzle':\n return `${modelImported ? 'import { db } from \"./index.js\";\\nimport { users } from \"./schema.js\";' : '// import { db } from \"./index\";\\n// import { users } from \"./schema\";'}\\n\\nconst authAdapter = createDrizzleAdapter(db, users, eq);`;\n case 'memory':\n return `const authAdapter = createMemoryAdapter();`;\n default:\n return `// const authAdapter = ...;`;\n }\n}\n\nexport const userModelTemplate = (adapter: string) => {\n switch (adapter) {\n case 'mongoose':\n return `import mongoose, { Schema, Document } from \"mongoose\";\n\nexport interface IUser extends Document {\n email: string;\n passwordHash?: string;\n role?: string;\n createdAt: Date;\n updatedAt: Date;\n}\n\nconst UserSchema: Schema = new Schema(\n {\n email: { type: String, required: true, unique: true },\n passwordHash: { type: String },\n role: { type: String, default: \"user\" },\n },\n { timestamps: true }\n);\n\nexport const User = mongoose.models.User || mongoose.model<IUser>(\"User\", UserSchema);\n`;\n case 'drizzle':\n return `import { pgTable, text, timestamp } from \"drizzle-orm/pg-core\";\n\nexport const users = pgTable(\"users\", {\n id: text(\"id\").primaryKey(),\n email: text(\"email\").unique().notNull(),\n passwordHash: text(\"password_hash\"),\n role: text(\"role\").default(\"user\"),\n createdAt: timestamp(\"created_at\").defaultNow().notNull(),\n updatedAt: timestamp(\"updated_at\").defaultNow().notNull(),\n});\n`;\n case 'prisma':\n return `// Add this to your schema.prisma file:\n\nmodel User {\n id String @id @default(cuid())\n email String @unique\n passwordHash String?\n role String @default(\"user\")\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n}\n`;\n default:\n return '';\n }\n};\n\nexport const envTemplate = (secret: string, usePepper: boolean) => `\n# Kroxt Auth Secrets\nJWT_SECRET=\"${secret}\"\n${usePepper ? `JWT_PEPPER=\"${crypto.randomBytes(16).toString('hex')}\"` : ''}\n`;\n\nexport const tsConfigTemplate = `{\n \"compilerOptions\": {\n \"target\": \"ESNext\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"bundler\",\n \"esModuleInterop\": true,\n \"forceConsistentCasingInFileNames\": true,\n \"strict\": true,\n \"skipLibCheck\": true,\n \"types\": [\"node\"]\n },\n \"include\": [\"src/**/*\"],\n \"exclude\": [\"node_modules\"]\n}\n`;\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAmB;AAEZ,MAAM,eAAe,CAAC,SAAiB,QAAgB,YAAiB;AAAA,EAC7E,sBAAsB,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAK9B,yBAAyB,SAAS,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA,uCAIL,MAAM;AAAA,IACzC,QAAQ,YAAY,0CAA0C,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+BAMrC,QAAQ,sBAAsB,SAAS,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAgBzE,QAAQ,eAAe;AAAA;AAAA;AAAA,QAGnB,EAAE;AAAA,IACN,QAAQ,gBAAgB;AAAA;AAAA;AAAA,QAGpB,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASV,SAAS,sBAAsB,SAAiB;AAC9C,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,aAAO;AAAA;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,yBAAyB,SAAiB,SAAc;AAC/D,QAAM,gBAAgB,QAAQ;AAC9B,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,aAAO,GAAG,gBAAgB,4CAA4C,4CAA4C;AAAA;AAAA;AAAA;AAAA,IACpH,KAAK;AACH,aAAO;AAAA;AAAA,IACT,KAAK;AACH,aAAO,GAAG,gBAAgB,2EAA2E,wEAAwE;AAAA;AAAA;AAAA,IAC/K,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEO,MAAM,oBAAoB,CAAC,YAAoB;AACpD,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAqBT,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAWT,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAWT;AACE,aAAO;AAAA,EACX;AACF;AAEO,MAAM,cAAc,CAAC,QAAgB,cAAuB;AAAA;AAAA,cAErD,MAAM;AAAA,EAClB,YAAY,eAAe,cAAAA,QAAO,YAAY,EAAE,EAAE,SAAS,KAAK,CAAC,MAAM,EAAE;AAAA;AAGpE,MAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
4
+ "sourcesContent": ["import crypto from 'crypto';\r\n\r\nexport const authTemplate = (adapter: string, secret: string, options: any) => `import { createAuth } from \"kroxt\";\r\n${getAdapterImportBlock(adapter)}\r\nimport dotenv from \"dotenv\";\r\n\r\ndotenv.config();\r\n\r\n${getAdapterInitialization(adapter, options)}\r\n\r\nexport const auth = createAuth({\r\n adapter: authAdapter,\r\n secret: process.env.JWT_SECRET || \"${secret}\",\r\n ${options.usePepper ? 'pepper: process.env.JWT_PEPPER || \"\",' : ''}\r\n \r\n // Global Security Configurations\r\n session: {\r\n expires: \"15m\",\r\n refreshExpires: \"7d\",\r\n enforceStrictRevocation: ${options.useStrictRevocation ? 'true' : 'false'}\r\n },\r\n \r\n // Custom JWT Payload logic\r\n jwt: {\r\n payload: (user: any, type: \"access\" | \"refresh\") => {\r\n if (type === \"access\") {\r\n return {\r\n role: user.role,\r\n // schoolId: user.schoolId \r\n };\r\n }\r\n return {};\r\n }\r\n },\r\n\r\n ${options.useRateLimit ? `rateLimit: {\r\n max: 100, // Requests per minute\r\n windowMs: 60 * 1000\r\n },` : ''}\r\n ${options.useIPBlocking ? `ipBlocking: {\r\n maxStrikes: 5,\r\n blockDurationMs: 15 * 60 * 1000\r\n },` : ''}\r\n passwordPolicy: {\r\n minLength: 6,\r\n requireUppercase: true,\r\n requireSpecialCharacter: true\r\n }\r\n});\r\n`;\r\n\r\nfunction getAdapterImportBlock(adapter: string) {\r\n switch (adapter) {\r\n case 'mongoose':\r\n return `import { createMongoAdapter, createRateLimitModel } from \"kroxt/adapters/mongoose\";\\nimport mongoose from \"mongoose\";`;\r\n case 'prisma':\r\n return `import { createPrismaAdapter } from \"kroxt/adapters/prisma\";`;\r\n case 'drizzle':\r\n return `import { createDrizzleAdapter } from \"kroxt/adapters/drizzle\";\\nimport { eq } from \"drizzle-orm\";`;\r\n case 'memory':\r\n return `import { createMemoryAdapter } from \"kroxt/adapters/memory\";`;\r\n default:\r\n return '';\r\n }\r\n}\r\n\r\nfunction getAdapterInitialization(adapter: string, options: any) {\r\n const modelImported = options.createModel;\r\n switch (adapter) {\r\n case 'mongoose':\r\n return `${modelImported ? 'import { User } from \"./user.model.js\";' : '// import { User } from \"./user.model.js\";'}\\n\\n// The rate limit model is optional but recommended\\nconst authAdapter = createMongoAdapter(User, createRateLimitModel(mongoose));`;\r\n case 'prisma':\r\n return `// import { prisma } from \"./lib/prisma\";\\nconst authAdapter = createPrismaAdapter(prisma.user);`;\r\n case 'drizzle':\r\n return `${modelImported ? 'import { db } from \"./index.js\";\\nimport { users } from \"./schema.js\";' : '// import { db } from \"./index\";\\n// import { users } from \"./schema\";'}\\n\\nconst authAdapter = createDrizzleAdapter(db, users, eq);`;\r\n case 'memory':\r\n return `const authAdapter = createMemoryAdapter();`;\r\n default:\r\n return `// const authAdapter = ...;`;\r\n }\r\n}\r\n\r\nexport const userModelTemplate = (adapter: string) => {\r\n switch (adapter) {\r\n case 'mongoose':\r\n return `import mongoose, { Schema, Document } from \"mongoose\";\r\n\r\nexport interface IUser extends Document {\r\n email: string;\r\n passwordHash?: string;\r\n role?: string;\r\n sessionVersion: number;\r\n createdAt: Date;\r\n updatedAt: Date;\r\n}\r\n\r\nconst UserSchema: Schema = new Schema(\r\n {\r\n email: { type: String, required: true, unique: true },\r\n passwordHash: { type: String },\r\n role: { type: String, default: \"user\" },\r\n sessionVersion: { type: Number, default: 0 },\r\n },\r\n { timestamps: true }\r\n);\r\n\r\nexport const User = mongoose.models.User || mongoose.model<IUser>(\"User\", UserSchema);\r\n`;\r\n case 'drizzle':\r\n return `import { pgTable, text, timestamp, integer } from \"drizzle-orm/pg-core\";\r\n\r\nexport const users = pgTable(\"users\", {\r\n id: text(\"id\").primaryKey(),\r\n email: text(\"email\").unique().notNull(),\r\n passwordHash: text(\"password_hash\"),\r\n role: text(\"role\").default(\"user\"),\r\n sessionVersion: integer(\"session_version\").default(0).notNull(),\r\n createdAt: timestamp(\"created_at\").defaultNow().notNull(),\r\n updatedAt: timestamp(\"updated_at\").defaultNow().notNull(),\r\n});\r\n`;\r\n case 'prisma':\r\n return `// Add this to your schema.prisma file:\r\n\r\nmodel User {\r\n id String @id @default(cuid())\r\n email String @unique\r\n passwordHash String?\r\n role String @default(\"user\")\r\n sessionVersion Int @default(0)\r\n createdAt DateTime @default(now())\r\n updatedAt DateTime @updatedAt\r\n}\r\n`;\r\n default:\r\n return '';\r\n }\r\n};\r\n\r\nexport const envTemplate = (secret: string, usePepper: boolean) => `\r\n# Kroxt Auth Secrets\r\nJWT_SECRET=\"${secret}\"\r\n${usePepper ? `JWT_PEPPER=\"${crypto.randomBytes(16).toString('hex')}\"` : ''}\r\n`;\r\n\r\nexport const tsConfigTemplate = `{\r\n \"compilerOptions\": {\r\n \"target\": \"ESNext\",\r\n \"module\": \"ESNext\",\r\n \"moduleResolution\": \"bundler\",\r\n \"esModuleInterop\": true,\r\n \"forceConsistentCasingInFileNames\": true,\r\n \"strict\": true,\r\n \"skipLibCheck\": true,\r\n \"types\": [\"node\"]\r\n },\r\n \"include\": [\"src/**/*\"],\r\n \"exclude\": [\"node_modules\"]\r\n}\r\n`;\r\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAmB;AAEZ,MAAM,eAAe,CAAC,SAAiB,QAAgB,YAAiB;AAAA,EAC7E,sBAAsB,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAK9B,yBAAyB,SAAS,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA,uCAIL,MAAM;AAAA,IACzC,QAAQ,YAAY,0CAA0C,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+BAMrC,QAAQ,sBAAsB,SAAS,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAgBzE,QAAQ,eAAe;AAAA;AAAA;AAAA,QAGnB,EAAE;AAAA,IACN,QAAQ,gBAAgB;AAAA;AAAA;AAAA,QAGpB,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASV,SAAS,sBAAsB,SAAiB;AAC9C,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,aAAO;AAAA;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,yBAAyB,SAAiB,SAAc;AAC/D,QAAM,gBAAgB,QAAQ;AAC9B,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,aAAO,GAAG,gBAAgB,4CAA4C,4CAA4C;AAAA;AAAA;AAAA;AAAA,IACpH,KAAK;AACH,aAAO;AAAA;AAAA,IACT,KAAK;AACH,aAAO,GAAG,gBAAgB,2EAA2E,wEAAwE;AAAA;AAAA;AAAA,IAC/K,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEO,MAAM,oBAAoB,CAAC,YAAoB;AACpD,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAuBT,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAYT,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAYT;AACE,aAAO;AAAA,EACX;AACF;AAEO,MAAM,cAAc,CAAC,QAAgB,cAAuB;AAAA;AAAA,cAErD,MAAM;AAAA,EAClB,YAAY,eAAe,cAAAA,QAAO,YAAY,EAAE,EAAE,SAAS,KAAK,CAAC,MAAM,EAAE;AAAA;AAGpE,MAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
6
6
  "names": ["crypto"]
7
7
  }