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.
- package/README.md +270 -231
- package/dist/auth/adapters/drizzle.cjs +5 -0
- package/dist/auth/adapters/drizzle.cjs.map +2 -2
- package/dist/auth/adapters/drizzle.d.ts.map +1 -1
- package/dist/auth/adapters/drizzle.js +5 -0
- package/dist/auth/adapters/drizzle.js.map +2 -2
- package/dist/auth/adapters/index.cjs.map +1 -1
- package/dist/auth/adapters/index.d.ts +2 -0
- package/dist/auth/adapters/index.d.ts.map +1 -1
- package/dist/auth/adapters/memory.cjs +9 -0
- package/dist/auth/adapters/memory.cjs.map +2 -2
- package/dist/auth/adapters/memory.d.ts.map +1 -1
- package/dist/auth/adapters/memory.js +9 -0
- package/dist/auth/adapters/memory.js.map +2 -2
- package/dist/auth/adapters/mongoose.cjs +5 -0
- package/dist/auth/adapters/mongoose.cjs.map +2 -2
- package/dist/auth/adapters/mongoose.d.ts.map +1 -1
- package/dist/auth/adapters/mongoose.js +5 -0
- package/dist/auth/adapters/mongoose.js.map +2 -2
- package/dist/auth/adapters/prisma.cjs +8 -0
- package/dist/auth/adapters/prisma.cjs.map +2 -2
- package/dist/auth/adapters/prisma.d.ts.map +1 -1
- package/dist/auth/adapters/prisma.js +8 -0
- package/dist/auth/adapters/prisma.js.map +2 -2
- package/dist/auth/core/index.cjs +32 -5
- package/dist/auth/core/index.cjs.map +2 -2
- package/dist/auth/core/index.d.ts +3 -0
- package/dist/auth/core/index.d.ts.map +1 -1
- package/dist/auth/core/index.js +32 -5
- package/dist/auth/core/index.js.map +2 -2
- package/dist/cli/index.cjs +49 -18
- package/dist/cli/index.cjs.map +3 -3
- package/dist/cli/index.js +49 -18
- package/dist/cli/index.js.map +2 -2
- package/dist/cli/templates.cjs +5 -1
- package/dist/cli/templates.cjs.map +2 -2
- package/dist/cli/templates.d.ts +1 -1
- package/dist/cli/templates.d.ts.map +1 -1
- package/dist/cli/templates.js +5 -1
- package/dist/cli/templates.js.map +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- 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": "AAAA,YAAY,YAAY;AACxB,SAAS,SAAS,iBAAiB;AACnC,OAAO,YAAY;AAGnB,SAAS,yBAAgD;AAgClD,SAAS,WAAW,SAA4B;AACnD,QAAM,EAAE,SAAS,QAAQ,QAAQ,SAAS,WAAW,WAAW,WAAW,IAAI;AAC/E,QAAM,gBAAgB,OAAO,WAAW,WAAW,IAAI,YAAY,EAAE,OAAO,MAAM,IAAI;AACtF,QAAM,aAAa,SAAS,WAAW;AACvC,QAAM,oBAAoB,SAAS,kBAAkB;AAErD,QAAM,wBAAwB,kBAAkB,SAAS,SAAS;AAKlE,iBAAe,cAAc,MAAiB,OAA6B,UAAU;AACjF,QAAI,UAA+B,
|
|
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
|
}
|
package/dist/cli/index.cjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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(),
|
|
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(),
|
|
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(),
|
|
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
|
|
216
|
-
const pkgPath = import_path.default.join(
|
|
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
|
package/dist/cli/index.cjs.map
CHANGED
|
@@ -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": ";;;;;;;;;;;;;;;;;;;;;;;;AACA,uBAAwB;AACxB,sBAAqB;
|
|
6
|
-
"names": ["
|
|
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
|
-
|
|
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
|
-
|
|
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(),
|
|
137
|
+
modelPath = path.join(process.cwd(), targetModelDir, "user.model.ts");
|
|
117
138
|
break;
|
|
118
139
|
case "drizzle":
|
|
119
|
-
modelPath = path.join(process.cwd(),
|
|
140
|
+
modelPath = path.join(process.cwd(), targetModelDir, "schema.ts");
|
|
120
141
|
break;
|
|
121
142
|
case "prisma":
|
|
122
|
-
modelPath = path.join(process.cwd(),
|
|
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
|
|
193
|
-
const pkgPath = path.join(
|
|
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
|
package/dist/cli/index.js.map
CHANGED
|
@@ -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": ";AACA,SAAS,eAAe;AACxB,OAAO,cAAc;AACrB,
|
|
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": ";AACA,SAAS,eAAe;AACxB,OAAO,cAAc;AACrB,OAAO,WAAW;AAClB,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,YAAY;AACnB,SAAS,aAAa;AACtB,SAAS,cAAc,aAAa,wBAAwB;AAE5D,MAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,OAAO,EACZ,YAAY,0CAA0C,EACtD,QAAQ,OAAO;AAElB,QACG,QAAQ,MAAM,EACd,YAAY,kCAAkC,EAC9C,OAAO,aAAa,+BAA+B,EACnD,OAAO,OAAO,YAAY;AACzB,UAAQ,IAAI,MAAM,KAAK,MAAM,mCAA8B,CAAC;AAE5D,MAAI;AACF,QAAI,WAAW;AAAA,MACb,SAAS;AAAA,MACT,aAAa;AAAA,MACb,cAAc;AAAA,MACd,eAAe;AAAA,MACf,qBAAqB;AAAA,MACrB,WAAW;AAAA,MACX,aAAa;AAAA,MACb,WAAW;AAAA,MACX,UAAU;AAAA,IACZ;AAEA,UAAM,iBAAiB,kBAAkB;AACzC,QAAI,aAAa;AAEjB,QAAI,eAAe,QAAQ;AACvB,mBAAa;AAAA,IACjB,WAAW,eAAe,aAAa,eAAe,QAAQ;AAC1D,mBAAa,eAAe,SAAS,eAAe;AAAA,IACxD,WAAW,eAAe,QAAQ;AAC9B,mBAAa,eAAe,SAAS,sBAAsB;AAAA,IAC/D;AAEA,QAAI,CAAC,QAAQ,KAAK;AAChB,YAAM,WAAW,MAAO,SAAiB,OAAO;AAAA,QAC9C;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,SAAS;AAAA,YACP,EAAE,MAAM,UAAU,SAAS,sBAAsB;AAAA,YACjD,EAAE,MAAM,YAAY,SAAS,qBAAqB;AAAA,YAClD,EAAE,MAAM,UAAU,SAAS,4BAA4B;AAAA,YACvD,EAAE,MAAM,WAAW,SAAS,8BAA8B;AAAA,YAC1D,EAAE,MAAM,QAAQ,SAAS,sBAAsB;AAAA,UACjD;AAAA,QACF;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,SAAS;AAAA,QACX;AAAA,MACF,CAAC;AAED,YAAM,WAAW,MAAO,SAAiB,OAAO;AAAA,QAC9C;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,SAAS;AAAA,QACX;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,SAAS;AAAA,QACX;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,SAAS;AAAA,QACX;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,SAAS;AAAA,QACX;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,SAAS;AAAA,QACX;AAAA,MACF,CAAC;AAED,iBAAW,EAAE,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS;AAEnD,UAAI,SAAS,eAAe,SAAS,YAAY,YAAY,SAAS,YAAY,QAAQ;AACxF,cAAM,EAAE,SAAS,IAAI,MAAO,SAAiB,OAAO;AAAA,UAClD,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,SAAS,eAAe,SAAS,YAAa,eAAe,SAAS,gBAAgB;AAAA,QACxF,CAAC;AACD,iBAAS,WAAW;AAAA,MACtB;AAEA,YAAM,EAAE,YAAY,IAAI,MAAO,SAAiB,OAAO;AAAA,QACrD,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,MACX,CAAC;AACD,eAAS,cAAc;AAAA,IACzB,OAAO;AACL,cAAQ,IAAI,MAAM,KAAK,mCAAmC,CAAC;AAC3D,iBAAW;AAAA,QACT,GAAG;AAAA,QACH,cAAc;AAAA,QACd,eAAe;AAAA,QACf,qBAAqB;AAAA,QACrB,WAAW;AAAA,QACX,aAAa;AAAA,QACb,WAAW;AAAA,MACb;AAAA,IACF;AAEA,UAAM,SAAS,OAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AACpD,UAAM,cAAc,aAAa,SAAS,SAAS,QAAQ,QAAQ;AAGnE,UAAM,WAAW,KAAK,KAAK,QAAQ,IAAI,GAAG,SAAS,WAAW,SAAS;AACvE,UAAM,UAAU,KAAK,QAAQ,QAAQ;AAErC,QAAI,CAAC,GAAG,WAAW,OAAO,GAAG;AAC3B,SAAG,UAAU,SAAS,EAAE,WAAW,KAAK,CAAC;AAAA,IAC3C;AAEA,OAAG,cAAc,UAAU,WAAW;AACtC,YAAQ,IAAI,MAAM,MAAM;AAAA,kBAAgB,MAAM,MAAM,KAAK,SAAS,QAAQ,IAAI,GAAG,QAAQ,CAAC,CAAC,EAAE,CAAC;AAG9F,UAAM,eAAe,KAAK,KAAK,QAAQ,IAAI,GAAG,eAAe;AAC7D,QAAI,CAAC,GAAG,WAAW,YAAY,GAAG;AAChC,SAAG,cAAc,cAAc,gBAAgB;AAC/C,cAAQ,IAAI,MAAM,MAAM,mBAAc,MAAM,MAAM,eAAe,CAAC,EAAE,CAAC;AAAA,IACvE;AAGA,QAAI,SAAS,eAAe,SAAS,YAAY,YAAY,SAAS,YAAY,QAAQ;AACxF,UAAI,YAAY;AAChB,YAAM,iBAAiB,SAAS,YAAY,SAAS;AAErD,cAAQ,SAAS,SAAS;AAAA,QACxB,KAAK;AACH,sBAAY,KAAK,KAAK,QAAQ,IAAI,GAAG,gBAAgB,eAAe;AACpE;AAAA,QACF,KAAK;AACH,sBAAY,KAAK,KAAK,QAAQ,IAAI,GAAG,gBAAgB,WAAW;AAChE;AAAA,QACF,KAAK;AACH,sBAAY,KAAK,KAAK,QAAQ,IAAI,GAAG,gBAAgB,aAAa;AAClE;AAAA,MACJ;AAEA,UAAI,WAAW;AACb,cAAM,eAAe,KAAK,QAAQ,SAAS;AAC3C,YAAI,CAAC,GAAG,WAAW,YAAY,GAAG;AAChC,aAAG,UAAU,cAAc,EAAE,WAAW,KAAK,CAAC;AAAA,QAChD;AAEA,cAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,gBAAgB;AAC3D,WAAG,cAAc,WAAW,kBAAkB,SAAS,OAAO,CAAC;AAC/D,gBAAQ,IAAI,MAAM,MAAM,mBAAc,MAAM,MAAM,KAAK,SAAS,QAAQ,IAAI,GAAG,SAAS,CAAC,CAAC,EAAE,CAAC;AAAA,MAC/F;AAAA,IACF;AAGA,QAAI,SAAS,aAAa;AACxB,YAAM,UAAU,KAAK,KAAK,QAAQ,IAAI,GAAG,MAAM;AAC/C,YAAM,aAAa,YAAY,QAAQ,SAAS,SAAS;AAEzD,UAAI,GAAG,WAAW,OAAO,GAAG;AAC1B,WAAG,eAAe,SAAS,UAAU;AACrC,gBAAQ,IAAI,MAAM,MAAM,mBAAc,MAAM,MAAM,MAAM,CAAC,wBAAwB,CAAC;AAAA,MACpF,OAAO;AACL,WAAG,cAAc,SAAS,UAAU;AACpC,gBAAQ,IAAI,MAAM,MAAM,mBAAc,MAAM,MAAM,MAAM,CAAC,EAAE,CAAC;AAAA,MAC9D;AAAA,IACF;AAEA,YAAQ,IAAI,MAAM,KAAK,MAAM,6CAAsC,CAAC;AAGpE,UAAM,oBAAoB,SAAS,SAAS,CAAC,CAAC,QAAQ,GAAG;AAAA,EAE3D,SAAS,KAAK;AACZ,YAAQ,MAAM,MAAM,IAAI,oCAA+B,CAAC;AACxD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,eAAe,oBAAoB,SAAiB,cAAuB,OAAO;AAChF,QAAM,UAAU,KAAK,KAAK,QAAQ,IAAI,GAAG,cAAc;AACvD,MAAI,CAAC,GAAG,WAAW,OAAO,EAAG;AAE7B,QAAM,MAAM,KAAK,MAAM,GAAG,aAAa,SAAS,MAAM,CAAC;AACvD,QAAM,OAAO,EAAE,GAAG,IAAI,cAAc,GAAG,IAAI,gBAAgB;AAE3D,QAAM,WAAqC;AAAA,IACzC,QAAQ,CAAC,SAAS,QAAQ;AAAA,IAC1B,UAAU,CAAC,SAAS,YAAY,QAAQ;AAAA,IACxC,QAAQ,CAAC,SAAS,kBAAkB,QAAQ;AAAA,IAC5C,SAAS,CAAC,SAAS,eAAe,QAAQ;AAAA,IAC1C,MAAM,CAAC,SAAS,QAAQ,QAAQ;AAAA,IAChC,MAAM,CAAC,OAAO;AAAA,EAChB;AAEA,QAAM,WAAW,SAAS,OAAO,KAAK,CAAC;AACvC,QAAM,UAAU,SAAS,OAAO,SAAO,CAAC,KAAK,GAAG,CAAC;AAEjD,MAAI,QAAQ,SAAS,GAAG;AACtB,QAAI,UAAU;AAEd,QAAI,CAAC,aAAa;AAChB,YAAM,SAAS,MAAO,SAAiB,OAAO;AAAA,QAC5C,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS,sCAAsC,MAAM,KAAK,QAAQ,KAAK,IAAI,CAAC,CAAC;AAAA,QAC7E,SAAS;AAAA,MACX,CAAC;AACD,gBAAU,OAAO;AAAA,IACnB;AAEA,QAAI,SAAS;AACX,cAAQ,IAAI,MAAM,KAAK;AAAA,aAAgB,QAAQ,KAAK,IAAI,CAAC,KAAK,CAAC;AAC/D,aAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,cAAM,QAAQ,MAAM,OAAO,CAAC,WAAW,GAAG,OAAO,GAAG;AAAA,UAClD,OAAO;AAAA,UACP,OAAO;AAAA,QACT,CAAC;AACD,cAAM,GAAG,SAAS,OAAO;AAAA,MAC3B,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,SAAS,oBAAoB;AAC3B,QAAM,OAAO,QAAQ,IAAI;AACzB,QAAM,UAAU,KAAK,KAAK,MAAM,cAAc;AAC9C,QAAM,SAAS,GAAG,WAAW,KAAK,KAAK,MAAM,KAAK,CAAC;AACnD,QAAM,eAAe,GAAG,WAAW,KAAK,KAAK,MAAM,KAAK,CAAC;AACzD,QAAM,kBAAkB,GAAG,WAAW,KAAK,KAAK,MAAM,OAAO,KAAK,CAAC;AACnE,QAAM,iBAAiB,GAAG,WAAW,KAAK,KAAK,MAAM,OAAO,CAAC;AAC7D,QAAM,oBAAoB,GAAG,WAAW,KAAK,KAAK,MAAM,OAAO,OAAO,CAAC;AAEvE,MAAI,YAAY;AAChB,MAAI,SAAS;AACb,MAAI,SAAS;AACb,MAAI,SAAS;AAEb,MAAI,GAAG,WAAW,OAAO,GAAG;AAC1B,UAAM,MAAM,KAAK,MAAM,GAAG,aAAa,SAAS,MAAM,CAAC;AACvD,UAAM,OAAO,EAAE,GAAG,IAAI,cAAc,GAAG,IAAI,gBAAgB;AAC3D,gBAAY,CAAC,EAAE,KAAK,WAAW,KAAK;AACpC,aAAS,CAAC,CAAC,KAAK;AAChB,aAAS,CAAC,CAAC,KAAK;AAChB,aAAS,CAAC,CAAC,KAAK;AAAA,EAClB;AAEA,SAAO,EAAE,QAAQ,cAAc,iBAAiB,gBAAgB,mBAAmB,WAAW,QAAQ,QAAQ,OAAO;AACvH;AAEA,QAAQ,MAAM,QAAQ,IAAI;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/cli/templates.cjs
CHANGED
|
@@ -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,
|
|
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
|
}
|